diff --git a/DEPS b/DEPS
index c82c461..4c3d259 100644
--- a/DEPS
+++ b/DEPS
@@ -283,15 +283,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': 'cef14e607db06caf09e0218ed11af63ce1db9967',
+  'skia_revision': '64b50bc70866846c79ec985a6ea4edeeea1e1925',
   # 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': '35c454fd7c5aea0f86563e2c6c2600f7e2da1511',
+  'v8_revision': '75da9e87f8a9691661cf79e00521dab5ae7803d8',
   # 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': 'da9c06780f378840f86e6a0af17636822111d392',
+  'angle_revision': 'b47603e0448d6cedd5d56b8ca4a2658702fcdfb1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -310,7 +310,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Fuchsia sdk
   # and whatever else without interference from each other.
-  'fuchsia_version': 'version:8.20220713.1.1',
+  'fuchsia_version': 'version:8.20220713.2.1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling google-toolbox-for-mac
   # and whatever else without interference from each other.
@@ -354,7 +354,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': '8e958343e521b3f317baf02da37fd2b4d52b210a',
+  'catapult_revision': 'c49cba8235e8cc0795f75f98baceaf3c45633bde',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -362,7 +362,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': '8c025e1c151be163a5f1700ff0392028de88810b',
+  'devtools_frontend_revision': '73bf6e1c8f5bf851287f146cb6825ca0db834e3c',
   # 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.
@@ -1142,7 +1142,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' + '@' + '14d41c9b5f037f90c1308f16738986ceb430a411',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '338f8b7d8a223b8d8e1a984f3f0f00e02cf1067c',
       'condition': 'checkout_chromeos',
   },
 
@@ -1160,7 +1160,7 @@
 
   # For Linux and Chromium OS.
   'src/third_party/cros_system_api': {
-      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + 'b25c4015bf76a84f0132546a2176802330b7f86c',
+      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + 'f3b1373caf7bd717be4f0d21ab8c738c6bfcf418',
       'condition': 'checkout_linux',
   },
 
@@ -1562,7 +1562,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '00a6e13924cea678f0054f8e3ba096b6c6d91e5e',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '29a81e33e6959bb0fd1264ee62a756b99168ca62',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1693,7 +1693,7 @@
       'dep_type': 'cipd',
   },
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@b3ca833ea15a10d8e779b65850ce4f52e48ab4c8',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@07a6dec37162e62dc905236610256620c643853d',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + 'ebe84bec02c041d28f902da0214bf442743fc907',
@@ -1732,7 +1732,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '06954d98b3f95e55b4810f422b64b4bcd440d62f',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '02bfcf513280188ac8fb2280bdb9c680f9c8cbfd',
+    Var('webrtc_git') + '/src.git' + '@' + '94d259c7af0a8a7638c86f6cf1bf801d6c25a5f9',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1805,7 +1805,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@b26dfef6841608a6770368e3d1a3a8d7716a32b1',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@1eaba7b7023aae733825e35d1781ce368e25a3d8',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/BUILD.gn b/android_webview/browser/BUILD.gn
index 79ffdb3..85a26c4 100644
--- a/android_webview/browser/BUILD.gn
+++ b/android_webview/browser/BUILD.gn
@@ -221,6 +221,7 @@
     "//components/content_capture/android",
     "//components/content_capture/browser",
     "//components/embedder_support:browser_util",
+    "//components/embedder_support:embedder_support",
     "//components/embedder_support/android:util",
     "//components/embedder_support/origin_trials",
     "//components/favicon_base:favicon_base",
diff --git a/android_webview/browser/aw_autofill_client.cc b/android_webview/browser/aw_autofill_client.cc
index a098d6f..e4ebd7a0 100644
--- a/android_webview/browser/aw_autofill_client.cc
+++ b/android_webview/browser/aw_autofill_client.cc
@@ -315,7 +315,7 @@
   NOTIMPLEMENTED();
 }
 
-void AwAutofillClient::OnPromoCodeSuggestionsFooterSelected(const GURL& url) {
+void AwAutofillClient::OpenPromoCodeOfferDetailsURL(const GURL& url) {
   NOTIMPLEMENTED();
 }
 
diff --git a/android_webview/browser/aw_autofill_client.h b/android_webview/browser/aw_autofill_client.h
index 7d4f2f7..2286be827 100644
--- a/android_webview/browser/aw_autofill_client.h
+++ b/android_webview/browser/aw_autofill_client.h
@@ -139,7 +139,7 @@
   bool ShouldShowSigninPromo() override;
   bool AreServerCardsSupported() const override;
   void ExecuteCommand(int id) override;
-  void OnPromoCodeSuggestionsFooterSelected(const GURL& url) override;
+  void OpenPromoCodeOfferDetailsURL(const GURL& url) override;
 
   // RiskDataLoader:
   void LoadRiskData(
diff --git a/android_webview/browser/aw_content_browser_client.cc b/android_webview/browser/aw_content_browser_client.cc
index 4686fd2..496d9d63 100644
--- a/android_webview/browser/aw_content_browser_client.cc
+++ b/android_webview/browser/aw_content_browser_client.cc
@@ -54,6 +54,7 @@
 #include "build/build_config.h"
 #include "components/cdm/browser/cdm_message_filter_android.h"
 #include "components/crash/content/browser/crash_handler_host_linux.h"
+#include "components/embedder_support/switches.h"
 #include "components/embedder_support/user_agent_utils.h"
 #include "components/navigation_interception/intercept_navigation_delegate.h"
 #include "components/page_load_metrics/browser/metrics_navigation_throttle.h"
@@ -349,15 +350,15 @@
     DCHECK(process_type == switches::kRendererProcess ||
            process_type == switches::kUtilityProcess)
         << process_type;
-    // Pass crash reporter enabled state to renderer processes.
-    if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-            ::switches::kEnableCrashReporter)) {
-      command_line->AppendSwitch(::switches::kEnableCrashReporter);
-    }
-    if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-            ::switches::kEnableCrashReporterForTesting)) {
-      command_line->AppendSwitch(::switches::kEnableCrashReporterForTesting);
-    }
+
+    static const char* const kSwitchNames[] = {
+        ::switches::kEnableCrashReporter,
+        ::switches::kEnableCrashReporterForTesting,
+        embedder_support::kOriginTrialDisabledFeatures,
+    };
+
+    command_line->CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(),
+                                   kSwitchNames, std::size(kSwitchNames));
   }
 }
 
diff --git a/android_webview/lib/BUILD.gn b/android_webview/lib/BUILD.gn
index 66e2365..689557a 100644
--- a/android_webview/lib/BUILD.gn
+++ b/android_webview/lib/BUILD.gn
@@ -25,6 +25,7 @@
     "//cc/base",
     "//components/autofill/core/common",
     "//components/crash/core/common",
+    "//components/embedder_support:embedder_support",
     "//components/gwp_asan/buildflags",
     "//components/metrics",
     "//components/power_scheduler",
diff --git a/android_webview/lib/DEPS b/android_webview/lib/DEPS
index 3f9040a..992e3c2 100644
--- a/android_webview/lib/DEPS
+++ b/android_webview/lib/DEPS
@@ -2,6 +2,7 @@
   "+cc/base/switches.h",
   "+components/autofill/core",
   "+components/crash/core",
+  "+components/embedder_support/switches.h",
   "+components/safe_browsing/android",
   "+components/spellcheck/common",
   "+components/translate/core/common",
diff --git a/android_webview/lib/aw_main_delegate.cc b/android_webview/lib/aw_main_delegate.cc
index 394297e..e5ce4b2 100644
--- a/android_webview/lib/aw_main_delegate.cc
+++ b/android_webview/lib/aw_main_delegate.cc
@@ -33,11 +33,14 @@
 #include "base/posix/global_descriptors.h"
 #include "base/scoped_add_feature_flags.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
 #include "base/threading/thread_restrictions.h"
 #include "build/build_config.h"
 #include "cc/base/switches.h"
 #include "components/autofill/core/common/autofill_features.h"
 #include "components/crash/core/common/crash_key.h"
+#include "components/embedder_support/switches.h"
 #include "components/gwp_asan/buildflags/buildflags.h"
 #include "components/metrics/unsent_log_store_metrics.h"
 #include "components/safe_browsing/android/safe_browsing_api_handler_bridge.h"
@@ -178,6 +181,28 @@
     base::android::RegisterApkAssetWithFileDescriptorStore(
         content::kV8Snapshot64DataDescriptor,
         gin::V8Initializer::GetSnapshotFilePath(false, file_type));
+
+    {
+      // Disable origin trial features on WebView unless the flag was explicitly
+      // specified via command-line.
+      std::string disabled_origin_trials_switch_value = cl->GetSwitchValueASCII(
+          embedder_support::kOriginTrialDisabledFeatures);
+      base::flat_set<std::string> disabled_origin_trials(
+          base::SplitString(disabled_origin_trials_switch_value, "|",
+                            base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY));
+
+      // Disable origin trials for the FedCM API on WebView because the FedCM
+      // API is not implemented on WebView. The inserted feature string should
+      // match a name in
+      // third_party/blink/renderer/platform/runtime_enabled_features.json5.
+      // Currently, there is no method to obtain these strings directly so it is
+      // hard coded.
+      disabled_origin_trials.insert("FedCM");
+
+      cl->AppendSwitchASCII(
+          embedder_support::kOriginTrialDisabledFeatures,
+          base::JoinString(std::move(disabled_origin_trials).extract(), "|"));
+    }
   }
 
   if (cl->HasSwitch(switches::kWebViewSandboxedRenderer)) {
@@ -306,6 +331,9 @@
     // See crbug.com/1277431 for more details.
     if (version_info::android::GetChannel() < version_info::Channel::BETA)
       features.DisableIfNotSet(blink::features::kEventPath);
+
+    // FedCM is not yet supported on WebView.
+    features.DisableIfNotSet(::features::kFedCm);
   }
 
   android_webview::RegisterPathProvider();
diff --git a/android_webview/tools/system_webview_shell/test/data/webexposed/not-webview-exposed.txt b/android_webview/tools/system_webview_shell/test/data/webexposed/not-webview-exposed.txt
index 27ce2e5..32461a5c 100644
--- a/android_webview/tools/system_webview_shell/test/data/webexposed/not-webview-exposed.txt
+++ b/android_webview/tools/system_webview_shell/test/data/webexposed/not-webview-exposed.txt
@@ -266,6 +266,10 @@
 interface Event
     getter path                        # crbug.com/1277431
 
+# FedCM API is not implemented on WebView. crbug.com/1340252
+interface IdentityCredential : Credential
+    getter token
+
 [GLOBAL OBJECT]
     method openDatabase
     attribute eventSender                    # test only
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 6a0431c..0610987 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -326,6 +326,7 @@
     "clipboard/clipboard_history_item.h",
     "clipboard/clipboard_history_menu_model_adapter.cc",
     "clipboard/clipboard_history_menu_model_adapter.h",
+    "clipboard/clipboard_history_metrics.h",
     "clipboard/clipboard_history_resource_manager.cc",
     "clipboard/clipboard_history_resource_manager.h",
     "clipboard/clipboard_history_util.cc",
diff --git a/ash/clipboard/clipboard_history.cc b/ash/clipboard/clipboard_history.cc
index c47f9c5..72fe6c8 100644
--- a/ash/clipboard/clipboard_history.cc
+++ b/ash/clipboard/clipboard_history.cc
@@ -6,6 +6,7 @@
 
 #include <deque>
 
+#include "ash/clipboard/clipboard_history_metrics.h"
 #include "ash/clipboard/clipboard_history_util.h"
 #include "ash/clipboard/scoped_clipboard_history_pause_impl.h"
 #include "base/bind.h"
@@ -23,25 +24,6 @@
 
 using PauseBehavior = ClipboardHistoryUtil::PauseBehavior;
 
-namespace {
-
-// The different operations `ClipboardHistory` sees. These values are written to
-// logs. New enum values can be added, but existing enums must never be
-// renumbered, deleted, or reused. Keep this up to date with the
-// `ClipboardHistoryOperation` enum in enums.xml.
-enum class ClipboardHistoryOperation {
-  // Emitted when the user initiates a clipboard write.
-  kCopy = 0,
-
-  // Emitted when the user initiates a clipboard read.
-  kPaste = 1,
-
-  // Insert new types above this line.
-  kMaxValue = kPaste
-};
-
-}  // namespace
-
 ClipboardHistory::ClipboardHistory() {
   ui::ClipboardMonitor::GetInstance()->AddObserver(this);
 }
@@ -128,7 +110,10 @@
   base::SequencedTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
       base::BindOnce(&ClipboardHistory::MaybeCommitData,
-                     commit_data_weak_factory_.GetWeakPtr(), *clipboard_data));
+                     commit_data_weak_factory_.GetWeakPtr(), *clipboard_data,
+                     /*is_reorder_on_paste=*/!pauses_.empty() &&
+                         pauses_.front().pause_behavior ==
+                             PauseBehavior::kAllowReorderOnPaste));
 
   // If clipboard history was paused with a contingency that allowed data to be
   // committed, the operation that changed clipboard data was not a user's copy.
@@ -222,7 +207,8 @@
   }
 }
 
-void ClipboardHistory::MaybeCommitData(ui::ClipboardData data) {
+void ClipboardHistory::MaybeCommitData(ui::ClipboardData data,
+                                       bool is_reorder_on_paste) {
   if (!ClipboardHistoryUtil::IsSupported(data))
     return;
 
@@ -235,7 +221,12 @@
     // instead of creating a new one because creating a new one will result in a
     // new unique identifier.
     history_list_.splice(history_list_.begin(), history_list_, iter);
+    base::UmaHistogramEnumeration("Ash.ClipboardHistory.ReorderType",
+                                  is_reorder_on_paste
+                                      ? ClipboardHistoryReorderType::kOnPaste
+                                      : ClipboardHistoryReorderType::kOnCopy);
   } else {
+    DCHECK(!is_reorder_on_paste);
     history_list_.emplace_front(std::move(data));
   }
 
diff --git a/ash/clipboard/clipboard_history.h b/ash/clipboard/clipboard_history.h
index 9e6dae6c..9e1137f 100644
--- a/ash/clipboard/clipboard_history.h
+++ b/ash/clipboard/clipboard_history.h
@@ -83,7 +83,7 @@
   // clipboard history. If `data` is not supported, this method no-ops. If
   // `data` is already in the history list, `data` will be moved to the top of
   // the list.
-  void MaybeCommitData(ui::ClipboardData data);
+  void MaybeCommitData(ui::ClipboardData data, bool is_reorder_on_paste);
 
   // When `Pause()` is called, clipboard accesses will modify clipboard history
   // according to `pause_behavior` until `Resume()` is called with that pause's
diff --git a/ash/clipboard/clipboard_history_metrics.h b/ash/clipboard/clipboard_history_metrics.h
new file mode 100644
index 0000000..8c77fed3
--- /dev/null
+++ b/ash/clipboard/clipboard_history_metrics.h
@@ -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.
+
+#ifndef ASH_CLIPBOARD_CLIPBOARD_HISTORY_METRICS_H_
+#define ASH_CLIPBOARD_CLIPBOARD_HISTORY_METRICS_H_
+
+namespace ash {
+
+// The different operations clipboard history sees. These values are written to
+// logs. New enum values can be added, but existing enums must never be
+// renumbered, deleted, or reused. Keep this up to date with the
+// `ClipboardHistoryOperation` enum in enums.xml.
+enum class ClipboardHistoryOperation {
+  // Emitted when the user initiates a clipboard write.
+  kCopy = 0,
+
+  // Emitted when the user initiates a clipboard read.
+  kPaste = 1,
+
+  // Insert new types above this line.
+  kMaxValue = kPaste
+};
+
+// The different ways a clipboard history list reorder can occur. These values
+// are written to logs. New enum values can be added, but existing enums must
+// never be renumbered, deleted, or reused. Keep this up to date with the
+// `ClipboardHistoryReorderType` enum in enums.xml.
+enum class ClipboardHistoryReorderType {
+  // Emitted when the user copies an existing clipboard history item other than
+  // the top item, causing the copied item to move to the top.
+  kOnCopy = 0,
+
+  // Emitted when the user pastes an existing clipboard history item other than
+  // the top item, causing the pasted item to move to the top. This behavior is
+  // gated by the `kClipboardHistoryReorder` flag.
+  kOnPaste = 1,
+
+  // Insert new types above this line.
+  kMaxValue = kOnPaste
+};
+
+}  // namespace ash
+
+#endif  // ASH_CLIPBOARD_CLIPBOARD_HISTORY_METRICS_H_
diff --git a/ash/clipboard/clipboard_history_unittest.cc b/ash/clipboard/clipboard_history_unittest.cc
index 47386c5..f6011c2 100644
--- a/ash/clipboard/clipboard_history_unittest.cc
+++ b/ash/clipboard/clipboard_history_unittest.cc
@@ -285,8 +285,13 @@
 // behavior allows clipboard history to be modified, but clipboard data changes
 // received during the pause are not recorded as copy operations.
 TEST_F(ClipboardHistoryTest, PauseHistoryAllowReorders) {
-  std::vector<std::u16string> input_strings{u"test"};
-  std::vector<std::u16string> expected_strings = input_strings;
+  std::vector<std::u16string> input_strings{u"test1", u"test2"};
+  std::vector<std::u16string> input_string1{u"test1"};
+  std::vector<std::u16string> expected_strings_initial{u"test2", u"test1"};
+  std::vector<std::u16string> expected_strings_reordered = input_strings;
+
+  // Populate clipboard history to simulate paste-based reorders.
+  WriteAndEnsureTextHistory(input_strings, expected_strings_initial);
 
   base::HistogramTester histogram_tester;
   histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
@@ -294,7 +299,7 @@
   ScopedClipboardHistoryPauseImpl scoped_pause(
       clipboard_history(),
       ClipboardHistoryUtil::PauseBehavior::kAllowReorderOnPaste);
-  WriteAndEnsureTextHistory(input_strings, expected_strings);
+  WriteAndEnsureTextHistory(input_string1, expected_strings_reordered);
   // Clipboard history modifications made during a reorder-on-paste operation
   // should not count as copies (or pastes). A reorder is not a user action.
   histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
@@ -304,11 +309,17 @@
 // different behavior, the newest behavior overrides all others for the duration
 // of the pause's lifetime.
 TEST_F(ClipboardHistoryTest, PauseHistoryNested) {
-  std::vector<std::u16string> input_strings1{u"test1"};
-  std::vector<std::u16string> input_strings2{u"test2"};
-  std::vector<std::u16string> expected_strings_empty = {};
-  std::vector<std::u16string> expected_strings1 = input_strings1;
-  std::vector<std::u16string> expected_strings2 = {u"test2", u"test1"};
+  std::vector<std::u16string> input_strings{u"test1", u"test2"};
+  std::vector<std::u16string> input_string1{u"test1"};
+  std::vector<std::u16string> input_string2{u"test2"};
+  std::vector<std::u16string> input_string3{u"test3"};
+  std::vector<std::u16string> expected_strings_initial{u"test2", u"test1"};
+  std::vector<std::u16string> expected_strings_reordered1 = input_strings;
+  std::vector<std::u16string> expected_strings_reordered2 =
+      expected_strings_initial;
+
+  // Populate clipboard history to simulate paste-based reorders.
+  WriteAndEnsureTextHistory(input_strings, expected_strings_initial);
 
   base::HistogramTester histogram_tester;
   histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
@@ -316,7 +327,7 @@
   // By default, pausing prevents clipboard history modifications.
   ScopedClipboardHistoryPauseImpl scoped_pause_default_1(
       clipboard_history(), ClipboardHistoryUtil::PauseBehavior::kDefault);
-  WriteAndEnsureTextHistory(input_strings1, expected_strings_empty);
+  WriteAndEnsureTextHistory(input_string3, expected_strings_initial);
   histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
 
   {
@@ -326,7 +337,7 @@
     ScopedClipboardHistoryPauseImpl scoped_pause_allow_reorders(
         clipboard_history(),
         ClipboardHistoryUtil::PauseBehavior::kAllowReorderOnPaste);
-    WriteAndEnsureTextHistory(input_strings1, expected_strings1);
+    WriteAndEnsureTextHistory(input_string1, expected_strings_reordered1);
     // Clipboard history modifications made during a reorder-on-paste operation
     // should not count as copies (or pastes). A reorder is not a user action.
     histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
@@ -336,47 +347,52 @@
       // behaviors were overridden.
       ScopedClipboardHistoryPauseImpl scoped_pause_default_2(
           clipboard_history(), ClipboardHistoryUtil::PauseBehavior::kDefault);
-      WriteAndEnsureTextHistory(input_strings2, expected_strings1);
+      WriteAndEnsureTextHistory(input_string3, expected_strings_reordered1);
       histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
     }
 
     // Test that the previous behavior is restored when the newest pause goes
     // out of scope.
-    WriteAndEnsureTextHistory(input_strings2, expected_strings2);
+    WriteAndEnsureTextHistory(input_string2, expected_strings_reordered2);
     histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
   }
 
   // Test that the previous behavior is restored when the newest pause goes out
   // of scope, regardless of what order behaviors were overridden.
-  WriteAndEnsureTextHistory(input_strings1, expected_strings2);
+  WriteAndEnsureTextHistory(input_string3, expected_strings_reordered2);
   histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
 }
 
 // Tests that clipboard history pauses do not have to be destroyed in LIFO
 // order.
 TEST_F(ClipboardHistoryTest, PauseHistoryResumeOutOfOrder) {
-  std::vector<std::u16string> input_strings1{u"test1"};
-  std::vector<std::u16string> input_strings2{u"test2"};
-  std::vector<std::u16string> input_strings3{u"test3"};
-  std::vector<std::u16string> expected_strings_empty = {};
-  std::vector<std::u16string> expected_strings1 = input_strings1;
-  std::vector<std::u16string> expected_strings2 = {u"test2", u"test1"};
-  std::vector<std::u16string> expected_strings3 = {u"test3", u"test2",
-                                                   u"test1"};
+  std::vector<std::u16string> input_strings{u"test1", u"test2"};
+  std::vector<std::u16string> input_string1{u"test1"};
+  std::vector<std::u16string> input_string2{u"test2"};
+  std::vector<std::u16string> input_string3{u"test3"};
+  std::vector<std::u16string> expected_strings_initial{u"test2", u"test1"};
+  std::vector<std::u16string> expected_strings_reordered1 = input_strings;
+  std::vector<std::u16string> expected_strings_reordered2 =
+      expected_strings_initial;
+  std::vector<std::u16string> expected_strings_new_item = {u"test3", u"test2",
+                                                           u"test1"};
+
+  // Populate clipboard history to simulate paste-based reorders.
+  WriteAndEnsureTextHistory(input_strings, expected_strings_initial);
 
   base::HistogramTester histogram_tester;
   histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
 
   auto scoped_pause_default = std::make_unique<ScopedClipboardHistoryPauseImpl>(
       clipboard_history(), ClipboardHistoryUtil::PauseBehavior::kDefault);
-  WriteAndEnsureTextHistory(input_strings1, expected_strings_empty);
+  WriteAndEnsureTextHistory(input_string3, expected_strings_initial);
   histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
 
   auto scoped_pause_allow_reorders =
       std::make_unique<ScopedClipboardHistoryPauseImpl>(
           clipboard_history(),
           ClipboardHistoryUtil::PauseBehavior::kAllowReorderOnPaste);
-  WriteAndEnsureTextHistory(input_strings1, expected_strings1);
+  WriteAndEnsureTextHistory(input_string1, expected_strings_reordered1);
   // Clipboard history modifications made during a reorder-on-paste operation
   // should not count as copies (or pastes). A reorder is not a user action.
   histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
@@ -384,7 +400,7 @@
   // Verify that pauses can be destroyed in non-LIFO order without changing the
   // current pause behavior.
   scoped_pause_default.reset();
-  WriteAndEnsureTextHistory(input_strings2, expected_strings2);
+  WriteAndEnsureTextHistory(input_string2, expected_strings_reordered2);
   histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
 
   // Verify that when all pauses are destroyed, clipboard history is modified as
@@ -395,7 +411,7 @@
       ->set_confirmed_operation_callback_for_test(
           operation_confirmed_future.GetCallback());
   scoped_pause_allow_reorders.reset();
-  WriteAndEnsureTextHistory(input_strings3, expected_strings3);
+  WriteAndEnsureTextHistory(input_string3, expected_strings_new_item);
   // Since clipboard history is not paused in any way, data being written to the
   // clipboard is interpreted as a copy operation.
   EXPECT_EQ(operation_confirmed_future.Take(), true);
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index ee7ef1d15..3050edb 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -1317,6 +1317,10 @@
 const base::Feature kScalableStatusArea{"ScalableStatusArea",
                                         base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Enables the system tray to show more information in larger screen.
+const base::Feature kSeamlessRefreshRateSwitching{
+    "SeamlessRefreshRateSwitching", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Controls whether to enable kSecondaryGoogleAccountUsage policy.
 const base::Feature kSecondaryGoogleAccountUsage{
     "SecondaryGoogleAccountUsage", base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index 2c033933..322cd3b 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -530,6 +530,8 @@
 extern const base::Feature kReverseScrollGestures;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kScalableStatusArea;
 COMPONENT_EXPORT(ASH_CONSTANTS)
+extern const base::Feature kSeamlessRefreshRateSwitching;
+COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kSecondaryGoogleAccountUsage;
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kSemanticColorsDebugOverride;
diff --git a/ash/rgb_keyboard/rgb_keyboard_manager.cc b/ash/rgb_keyboard/rgb_keyboard_manager.cc
index 82e2b68..71cd03c 100644
--- a/ash/rgb_keyboard/rgb_keyboard_manager.cc
+++ b/ash/rgb_keyboard/rgb_keyboard_manager.cc
@@ -29,7 +29,6 @@
   DCHECK(!g_instance);
   g_instance = this;
 
-  ime_controller_ptr_->AddObserver(this);
   RgbkbdClient::Get()->AddObserver(this);
 
   VLOG(1) << "Initializing RGB Keyboard support";
@@ -38,7 +37,9 @@
 
 RgbKeyboardManager::~RgbKeyboardManager() {
   RgbkbdClient::Get()->RemoveObserver(this);
-  ime_controller_ptr_->RemoveObserver(this);
+  if (IsPerKeyKeyboard()) {
+    ime_controller_ptr_->RemoveObserver(this);
+  }
 
   DCHECK_EQ(g_instance, this);
   g_instance = nullptr;
@@ -94,10 +95,8 @@
 }
 
 void RgbKeyboardManager::OnCapsLockChanged(bool enabled) {
-  if (IsRgbKeyboardSupported() && IsPerKeyKeyboard()) {
-    VLOG(1) << "Setting RGB keyboard caps lock state to " << enabled;
-    RgbkbdClient::Get()->SetCapsLockState(enabled);
-  }
+  VLOG(1) << "Setting RGB keyboard caps lock state to " << enabled;
+  RgbkbdClient::Get()->SetCapsLockState(enabled);
 }
 
 // static
@@ -128,11 +127,15 @@
 void RgbKeyboardManager::InitializeRgbKeyboard() {
   DCHECK(RgbkbdClient::Get());
 
-  // Upon login, CapsLock may already be enabled.
-  VLOG(1) << "Setting initial RGB keyboard caps lock state to "
-          << ime_controller_ptr_->IsCapsLockEnabled();
-  RgbkbdClient::Get()->SetCapsLockState(
-      ime_controller_ptr_->IsCapsLockEnabled());
+  // Initialize caps lock color changing if supported
+  if (IsPerKeyKeyboard()) {
+    VLOG(1) << "Setting initial RGB keyboard caps lock state to "
+            << ime_controller_ptr_->IsCapsLockEnabled();
+    RgbkbdClient::Get()->SetCapsLockState(
+        ime_controller_ptr_->IsCapsLockEnabled());
+
+    ime_controller_ptr_->AddObserver(this);
+  }
 
   // Set keyboard to the default color on startup
   RgbkbdClient::Get()->SetStaticBackgroundColor(SkColorGetR(kDefaultColor),
diff --git a/ash/rgb_keyboard/rgb_keyboard_manager_unittest.cc b/ash/rgb_keyboard/rgb_keyboard_manager_unittest.cc
index 035cdb3..17ba62d 100644
--- a/ash/rgb_keyboard/rgb_keyboard_manager_unittest.cc
+++ b/ash/rgb_keyboard/rgb_keyboard_manager_unittest.cc
@@ -133,6 +133,8 @@
 }
 
 TEST_F(RgbKeyboardManagerTest, OnCapsLockChanged) {
+  InitializeManagerWithCapability(
+      rgbkbd::RgbKeyboardCapabilities::kIndividualKey);
   ime_controller_->UpdateCapsLockState(/*caps_enabled=*/true);
   EXPECT_TRUE(client_->get_caps_lock_state());
   ime_controller_->UpdateCapsLockState(/*caps_enabled=*/false);
@@ -179,6 +181,8 @@
 }
 
 TEST_F(RgbKeyboardManagerTest, SetCapsLockStateAllowedForPerKeyKeboards) {
+  InitializeManagerWithCapability(
+      rgbkbd::RgbKeyboardCapabilities::kIndividualKey);
   EXPECT_FALSE(client_->get_caps_lock_state());
   ime_controller_->UpdateCapsLockState(/*caps_enabled=*/true);
   EXPECT_TRUE(client_->get_caps_lock_state());
diff --git a/ash/system/unified/notification_counter_view.cc b/ash/system/unified/notification_counter_view.cc
index 5b7651b..a9a093a 100644
--- a/ash/system/unified/notification_counter_view.cc
+++ b/ash/system/unified/notification_counter_view.cc
@@ -62,6 +62,17 @@
   return ui::kColorAshSystemUIMenuSeparator;
 }
 
+// Returns true if we should show the counter view (e.g. during quiet mode,
+// screen lock, etc.).
+bool ShouldShowCounterView() {
+  SessionControllerImpl* session_controller =
+      Shell::Get()->session_controller();
+  return !message_center::MessageCenter::Get()->IsQuietMode() &&
+         session_controller->ShouldShowNotificationTray() &&
+         (!session_controller->IsScreenLocked() ||
+          AshMessageCenterLockScreenController::IsEnabled());
+}
+
 class NumberIconImageSource : public gfx::CanvasImageSource {
  public:
   explicit NumberIconImageSource(size_t count)
@@ -116,7 +127,7 @@
 
 void NotificationCounterView::Update() {
   if (message_center_utils::GetNotificationCount() == 0 ||
-      !controller_->ShouldShowNotificationItemsInTray()) {
+      !ShouldShowCounterView()) {
     SetVisible(false);
     return;
   }
diff --git a/ash/system/unified/notification_icons_controller.cc b/ash/system/unified/notification_icons_controller.cc
index d2c080c..0ea04d6 100644
--- a/ash/system/unified/notification_icons_controller.cc
+++ b/ash/system/unified/notification_icons_controller.cc
@@ -37,15 +37,31 @@
 constexpr int kMaxNotificationIconsShown = 2;
 constexpr int kNotificationIconSpacing = 1;
 
+const char kCapsLockNotifierId[] = "ash.caps-lock";
 const char kBatteryNotificationNotifierId[] = "ash.battery";
 const char kUsbNotificationNotifierId[] = "ash.power";
 
 bool ShouldShowNotification(message_center::Notification* notification) {
+  SessionControllerImpl* session_controller =
+      Shell::Get()->session_controller();
+  if (!session_controller->ShouldShowNotificationTray() ||
+      (session_controller->IsScreenLocked() &&
+       !AshMessageCenterLockScreenController::IsEnabled())) {
+    return false;
+  }
+
+  std::string notifier_id = notification->notifier_id().id;
+
+  if (message_center::MessageCenter::Get()->IsQuietMode() &&
+      notifier_id != kCapsLockNotifierId) {
+    return false;
+  }
+
   // We don't want to show these notifications since the information is already
   // indicated by another item in tray.
-  std::string id = notification->notifier_id().id;
-  if (id == kVmCameraMicNotifierId || id == kBatteryNotificationNotifierId ||
-      id == kUsbNotificationNotifierId)
+  if (notifier_id == kVmCameraMicNotifierId ||
+      notifier_id == kBatteryNotificationNotifierId ||
+      notifier_id == kUsbNotificationNotifierId)
     return false;
 
   // We only show notification icon in the tray if it is either:
@@ -164,15 +180,6 @@
   return first_unused_item_index_;
 }
 
-bool NotificationIconsController::ShouldShowNotificationItemsInTray() {
-  SessionControllerImpl* session_controller =
-      Shell::Get()->session_controller();
-  return !message_center::MessageCenter::Get()->IsQuietMode() &&
-         session_controller->ShouldShowNotificationTray() &&
-         (!session_controller->IsScreenLocked() ||
-          AshMessageCenterLockScreenController::IsEnabled());
-}
-
 std::u16string NotificationIconsController::GetAccessibleNameString() const {
   if (!TrayItemHasNotification())
     return notification_counter_view_->GetAccessibleNameString();
@@ -237,9 +244,6 @@
 }
 
 void NotificationIconsController::UpdateNotificationIcons() {
-  const bool should_show_icons =
-      icons_view_visible_ && ShouldShowNotificationItemsInTray();
-
   // Iterates `tray_items_` and notifications in reverse order so new pinned
   // notifications get shown on the left side.
   auto notifications =
@@ -252,7 +256,7 @@
       break;
     if (ShouldShowNotification(*notification_it)) {
       (*tray_it)->SetNotification(*notification_it);
-      (*tray_it)->SetVisible(should_show_icons);
+      (*tray_it)->SetVisible(icons_view_visible_);
       ++tray_it;
     }
   }
@@ -263,7 +267,7 @@
     (*tray_it)->Reset();
     (*tray_it)->SetVisible(false);
   }
-  separator_->SetVisible(should_show_icons && TrayItemHasNotification());
+  separator_->SetVisible(icons_view_visible_ && TrayItemHasNotification());
 }
 
 NotificationIconTrayItemView*
diff --git a/ash/system/unified/notification_icons_controller.h b/ash/system/unified/notification_icons_controller.h
index b7bc25be..3590fc22 100644
--- a/ash/system/unified/notification_icons_controller.h
+++ b/ash/system/unified/notification_icons_controller.h
@@ -83,10 +83,6 @@
   // Returns the number of notification icons showing in |tray_items_|.
   size_t TrayNotificationIconsCount() const;
 
-  // Returns true if we should not show notification related items in tray (e.g.
-  // during quiet mode, screen lock, etc.).
-  bool ShouldShowNotificationItemsInTray();
-
   // Returns a string describing the current state for accessibility.
   std::u16string GetAccessibleNameString() const;
 
diff --git a/ash/system/unified/notification_icons_controller_unittest.cc b/ash/system/unified/notification_icons_controller_unittest.cc
index f971bd4..93d745d 100644
--- a/ash/system/unified/notification_icons_controller_unittest.cc
+++ b/ash/system/unified/notification_icons_controller_unittest.cc
@@ -19,6 +19,7 @@
 namespace ash {
 
 namespace {
+const char kCapsLockNotifierId[] = "ash.caps-lock";
 const char kBatteryNotificationNotifierId[] = "ash.battery";
 const char kUsbNotificationNotifierId[] = "ash.power";
 }  // namespace
@@ -220,4 +221,35 @@
                    ->count_for_display_for_testing());
 }
 
+TEST_P(NotificationIconsControllerTest, NotificationItemInQuietMode) {
+  UpdateDisplay("800x700");
+  message_center::MessageCenter::Get()->SetQuietMode(true);
+
+  // Icons get added from RTL, so we check the end of the vector first. At
+  // first, no icons should be shown.
+  EXPECT_FALSE(
+      notification_icons_controller_->tray_items().back()->GetVisible());
+
+  // In quiet mode, notification other than capslock notification should not
+  // show an item in the tray.
+  auto id1 = AddNotification(/*is_pinned=*/true, /*is_critical_warning=*/false);
+  EXPECT_FALSE(
+      notification_icons_controller_->tray_items().back()->GetVisible());
+
+  auto id2 = AddNotification(/*is_pinned=*/true, /*is_critical_warning=*/false,
+                             kCapsLockNotifierId);
+  EXPECT_EQ(IsScalableStatusAreaEnabled(),
+            notification_icons_controller_->tray_items().back()->GetVisible());
+  if (IsScalableStatusAreaEnabled()) {
+    EXPECT_EQ(id2, notification_icons_controller_->tray_items()
+                       .back()
+                       ->GetNotificationId());
+  }
+
+  message_center::MessageCenter::Get()->RemoveNotification(id2,
+                                                           /*by_user=*/false);
+  EXPECT_FALSE(
+      notification_icons_controller_->tray_items().back()->GetVisible());
+}
+
 }  // namespace ash
diff --git a/ash/webui/.eslintrc.js b/ash/webui/.eslintrc.js
index 7b1f71f..6c48f22a 100644
--- a/ash/webui/.eslintrc.js
+++ b/ash/webui/.eslintrc.js
@@ -3,7 +3,8 @@
 // found in the LICENSE file.
 
 module.exports = {
-  'rules': {
-    'no-console': 'off',
+  'rules' : {
+    'comma-dangle' : ['error', 'always-multiline'],
+    'no-console' : 'off',
   },
 };
diff --git a/ash/webui/media_app_ui/resources/js/error_reporter.js b/ash/webui/media_app_ui/resources/js/error_reporter.js
index a6fcb1e9..b99744d 100644
--- a/ash/webui/media_app_ui/resources/js/error_reporter.js
+++ b/ash/webui/media_app_ui/resources/js/error_reporter.js
@@ -124,5 +124,5 @@
 
 export const TEST_ONLY = {
   reportCrashError,
-  captureConsoleErrors
+  captureConsoleErrors,
 };
diff --git a/ash/webui/media_app_ui/resources/js/launch.js b/ash/webui/media_app_ui/resources/js/launch.js
index 65d6a18..f676fe8 100644
--- a/ash/webui/media_app_ui/resources/js/launch.js
+++ b/ash/webui/media_app_ui/resources/js/launch.js
@@ -23,25 +23,37 @@
 const IMAGE_EXTENSIONS = [
   '.jpg',  '.png', '.webp', '.gif', '.avif', '.bmp',   '.ico', '.svg',
   '.jpeg', '.jpe', '.jfif', '.jif', '.jfi',  '.pjpeg', '.pjp', '.arw',
-  '.cr2',  '.dng', '.nef',  '.nrw', '.orf',  '.raf',   '.rw2', '.svgz'
+  '.cr2',  '.dng', '.nef',  '.nrw', '.orf',  '.raf',   '.rw2', '.svgz',
 ];
 const VIDEO_EXTENSIONS = [
-  '.3gp', '.avi', '.m4v', '.mkv', '.mov', '.mp4', '.mpeg', '.mpeg4', '.mpg',
-  '.mpg4', '.ogv', '.ogx', '.ogm', '.webm'
+  '.3gp',
+  '.avi',
+  '.m4v',
+  '.mkv',
+  '.mov',
+  '.mp4',
+  '.mpeg',
+  '.mpeg4',
+  '.mpg',
+  '.mpg4',
+  '.ogv',
+  '.ogx',
+  '.ogm',
+  '.webm',
 ];
 const PDF_EXTENSIONS = ['.pdf'];
 const OPEN_ACCEPT_ARGS = {
   'AUDIO': {
     description: loadTimeData.getString('fileFilterAudio'),
-    accept: {'audio/*': AUDIO_EXTENSIONS}
+    accept: {'audio/*': AUDIO_EXTENSIONS},
   },
   'IMAGE': {
     description: loadTimeData.getString('fileFilterImage'),
-    accept: {'image/*': IMAGE_EXTENSIONS}
+    accept: {'image/*': IMAGE_EXTENSIONS},
   },
   'VIDEO': {
     description: loadTimeData.getString('fileFilterVideo'),
-    accept: {'video/*': VIDEO_EXTENSIONS}
+    accept: {'video/*': VIDEO_EXTENSIONS},
   },
   'PDF': {description: 'PDF', accept: {'application/pdf': PDF_EXTENSIONS}},
   // All supported file types, excluding text files (see b/183150750).
@@ -53,8 +65,8 @@
         ...IMAGE_EXTENSIONS,
         ...VIDEO_EXTENSIONS,
         ...PDF_EXTENSIONS,
-      ]
-    }
+      ],
+    },
   },
 };
 
@@ -370,7 +382,7 @@
     token: renameMsg.token,
     file: null,
     handle: renamedFileHandle,
-    inCurrentDirectory: true
+    inCurrentDirectory: true,
   });
 
   return {renameResult: RenameResult.SUCCESS};
@@ -396,7 +408,7 @@
       error: '',
       canDelete: false,
       canRename: false,
-    }
+    },
   };
   return response;
 });
@@ -412,7 +424,7 @@
     // dragged into the media app.
     token: oldFileToken || tokenGenerator.next().value,
     file: null,
-    handle: tokenMap.get(pickedFileToken)
+    handle: tokenMap.get(pickedFileToken),
   };
   const oldFileIndex = currentFiles.findIndex(fd => fd.token === oldFileToken);
   tokenMap.set(pickedFileDescriptor.token, pickedFileDescriptor.handle);
@@ -474,7 +486,7 @@
       token: generateToken(handle),
       file: null,
       handle: handle,
-      inCurrentDirectory: false
+      inCurrentDirectory: false,
     });
   }
   if (newDescriptors.length === 0) {
@@ -831,7 +843,7 @@
   const loadFilesMessage = {
     currentFileIndex: focusIndex,
     // Handle can't be passed through a message pipe.
-    files: snapshot.map(fileDescriptorToFileContext)
+    files: snapshot.map(fileDescriptorToFileContext),
   };
 
   // Clear handles to the open files in the privileged context so they are
@@ -868,7 +880,7 @@
 
   return {
     handle: fileHandleForToken(editFileToken),
-    directory: currentDirectoryHandle
+    directory: currentDirectoryHandle,
   };
 }
 
diff --git a/ash/webui/media_app_ui/resources/js/receiver.js b/ash/webui/media_app_ui/resources/js/receiver.js
index abc4a145..fdfe1a712 100644
--- a/ash/webui/media_app_ui/resources/js/receiver.js
+++ b/ash/webui/media_app_ui/resources/js/receiver.js
@@ -434,7 +434,7 @@
 // empty file list available.
 window.customLaunchData = {
   delegate: DELEGATE,
-  files: new ReceivedFileList({files: [], currentFileIndex: -1})
+  files: new ReceivedFileList({files: [], currentFileIndex: -1}),
 };
 
 // Attempting to show file pickers in the sandboxed <iframe> is guaranteed to
diff --git a/ash/webui/media_app_ui/resources/mock/js/app_main.js b/ash/webui/media_app_ui/resources/mock/js/app_main.js
index 26555b4..e941916 100644
--- a/ash/webui/media_app_ui/resources/mock/js/app_main.js
+++ b/ash/webui/media_app_ui/resources/mock/js/app_main.js
@@ -252,7 +252,7 @@
     url: window.location.href,
     product: 'ChromeOS_MediaAppMock',
     lineNumber: errorEvent.lineno,
-    columnNumber: errorEvent.colno
+    columnNumber: errorEvent.colno,
   });
 });
 self.addEventListener('unhandledrejection', (event) => {
diff --git a/ash/webui/media_app_ui/test/driver.js b/ash/webui/media_app_ui/test/driver.js
index 1dcd33b..c62d4c7e 100644
--- a/ash/webui/media_app_ui/test/driver.js
+++ b/ash/webui/media_app_ui/test/driver.js
@@ -486,7 +486,7 @@
   const focusFile = {
     /** @type {!FakeFileSystemFileHandle} */
     handle: directory.files[0],
-    file: directory.files[0].getFileSync()
+    file: directory.files[0].getFileSync(),
   };
   incrementLaunchNumber();
   setCurrentDirectory(directory, focusFile);
diff --git a/ash/webui/media_app_ui/test/guest_query_receiver.js b/ash/webui/media_app_ui/test/guest_query_receiver.js
index b9b05ad..cce0b977 100644
--- a/ash/webui/media_app_ui/test/guest_query_receiver.js
+++ b/ash/webui/media_app_ui/test/guest_query_receiver.js
@@ -84,7 +84,7 @@
     token,
     lastModified,
     hasDelete,
-    hasRename
+    hasRename,
   };
 }
 
@@ -200,7 +200,7 @@
     }
     extraResultData = {
       receiverFileName: file.name,
-      receiverErrorName: file.error
+      receiverErrorName: file.error,
     };
   } else if (data.deleteLastFile) {
     // Simulate a user deleting the currently open file.
diff --git a/ash/webui/media_app_ui/test/media_app_guest_ui_browsertest.js b/ash/webui/media_app_ui/test/media_app_guest_ui_browsertest.js
index 3deb3fbe..2f2a537 100644
--- a/ash/webui/media_app_ui/test/media_app_guest_ui_browsertest.js
+++ b/ash/webui/media_app_ui/test/media_app_guest_ui_browsertest.js
@@ -107,7 +107,7 @@
       name: `${name}`,
       error: '',
       canDelete: true,
-      canRename: true
+      canRename: true,
     };
   }
   const message = {currentFileIndex: 0, files: [0, 1, 2].map(makeTestFile)};
diff --git a/ash/webui/media_app_ui/test/media_app_ui_browsertest.js b/ash/webui/media_app_ui/test/media_app_ui_browsertest.js
index 95739c02..f9a1d10 100644
--- a/ash/webui/media_app_ui/test/media_app_ui_browsertest.js
+++ b/ash/webui/media_app_ui/test/media_app_ui_browsertest.js
@@ -351,7 +351,7 @@
 MediaAppUIBrowserTest.MultipleFilesHaveTokens = async () => {
   const directory = await launchWithFiles([
     await createTestImageFile(1, 1, 'file1.png'),
-    await createTestImageFile(1, 1, 'file2.png')
+    await createTestImageFile(1, 1, 'file2.png'),
   ]);
 
   assertEquals(currentFiles.length, 2);
@@ -444,7 +444,7 @@
 MediaAppUIBrowserTest.NotifyCurrentFileAppIconDark = async () => {
   await sendTestMessage({
     simple: 'notifyCurrentFile',
-    simpleArgs: {name: undefined, type: undefined}
+    simpleArgs: {name: undefined, type: undefined},
   });
 
   assertEquals(getIcon().href.includes('app'), true);
@@ -1024,7 +1024,7 @@
 MediaAppUIBrowserTest.RenameOriginalIPC = async () => {
   const directory = await launchWithFiles([
     await createTestImageFile(1, 1, 'file1.png'),
-    await createTestImageFile(1, 1, 'file2.png')
+    await createTestImageFile(1, 1, 'file2.png'),
   ]);
 
   // Nothing should be deleted initially.
diff --git a/ash/webui/media_app_ui/test/media_app_ui_gtest_browsertest.js b/ash/webui/media_app_ui/test/media_app_ui_gtest_browsertest.js
index 0677711..c1ca350a 100644
--- a/ash/webui/media_app_ui/test/media_app_ui_gtest_browsertest.js
+++ b/ash/webui/media_app_ui/test/media_app_ui_gtest_browsertest.js
@@ -52,7 +52,7 @@
       enabled: [
         ...super.featureList.enabled,
         'chromeos::features::kDarkLightMode',
-      ]
+      ],
     };
   }
 
@@ -75,7 +75,7 @@
       enabled: [
         ...super.featureList.enabled,
         'chromeos::features::kDarkLightMode',
-      ]
+      ],
     };
   }
 
diff --git a/ash/webui/personalization_app/resources/trusted/ambient/ambient_preview_element.ts b/ash/webui/personalization_app/resources/trusted/ambient/ambient_preview_element.ts
index 706c091..72c1502 100644
--- a/ash/webui/personalization_app/resources/trusted/ambient/ambient_preview_element.ts
+++ b/ash/webui/personalization_app/resources/trusted/ambient/ambient_preview_element.ts
@@ -75,7 +75,7 @@
         type: Array,
         computed:
             'computeCollageImages_(topicSource_, previewAlbums_, googlePhotosAlbumsPreviews_)',
-      }
+      },
     };
   }
 
diff --git a/ash/webui/personalization_app/resources/trusted/ambient/ambient_subpage_element.ts b/ash/webui/personalization_app/resources/trusted/ambient/ambient_subpage_element.ts
index 05f81ca..85668dd 100644
--- a/ash/webui/personalization_app/resources/trusted/ambient/ambient_subpage_element.ts
+++ b/ash/webui/personalization_app/resources/trusted/ambient/ambient_subpage_element.ts
@@ -47,7 +47,7 @@
         type: Boolean,
         value() {
           return loadTimeData.getBoolean('isAmbientModeAnimationEnabled');
-        }
+        },
       },
       albums_: {
         type: Array,
diff --git a/ash/webui/personalization_app/resources/trusted/ambient/animation_theme_list_element.ts b/ash/webui/personalization_app/resources/trusted/ambient/animation_theme_list_element.ts
index acf2dcd..637113c6 100644
--- a/ash/webui/personalization_app/resources/trusted/ambient/animation_theme_list_element.ts
+++ b/ash/webui/personalization_app/resources/trusted/ambient/animation_theme_list_element.ts
@@ -27,8 +27,9 @@
       animationThemes: {
         type: Array,
         value: [
-          AnimationTheme.kSlideshow, AnimationTheme.kFeelTheBreeze,
-          AnimationTheme.kFloatOnBy
+          AnimationTheme.kSlideshow,
+          AnimationTheme.kFeelTheBreeze,
+          AnimationTheme.kFloatOnBy,
         ],
       },
 
diff --git a/ash/webui/personalization_app/resources/trusted/iframe_api.ts b/ash/webui/personalization_app/resources/trusted/iframe_api.ts
index 2f76e95..08d0136 100644
--- a/ash/webui/personalization_app/resources/trusted/iframe_api.ts
+++ b/ash/webui/personalization_app/resources/trusted/iframe_api.ts
@@ -32,7 +32,7 @@
   sendCollections(target: CollectionsGrid, collections: WallpaperCollection[]) {
     const event: constants.SendCollectionsEvent = {
       type: constants.EventType.SEND_COLLECTIONS,
-      collections
+      collections,
     };
     target.onMessageReceived(event);
   }
@@ -45,7 +45,7 @@
       target: CollectionsGrid, enabled: GooglePhotosEnablementState) {
     const event: constants.SendGooglePhotosEnabledEvent = {
       type: constants.EventType.SEND_GOOGLE_PHOTOS_ENABLED,
-      enabled
+      enabled,
     };
     target.onMessageReceived(event);
   }
@@ -59,7 +59,7 @@
       target: CollectionsGrid, counts: {[key: string]: number|null}) {
     const event: constants.SendImageCountsEvent = {
       type: constants.EventType.SEND_IMAGE_COUNTS,
-      counts
+      counts,
     };
     target.onMessageReceived(event);
   }
@@ -72,7 +72,7 @@
   sendVisible(target: CollectionsGrid, visible: boolean) {
     const event: constants.SendVisibleEvent = {
       type: constants.EventType.SEND_VISIBLE,
-      visible
+      visible,
     };
     target.onMessageReceived(event);
   }
@@ -85,7 +85,7 @@
       images: Array<FilePath|constants.DefaultImageSymbol>) {
     const event: constants.SendLocalImagesEvent = {
       type: constants.EventType.SEND_LOCAL_IMAGES,
-      images
+      images,
     };
     target.onMessageReceived(event);
   }
@@ -98,7 +98,7 @@
       data: Record<string|constants.DefaultImageSymbol, string>) {
     const event: constants.SendLocalImageDataEvent = {
       type: constants.EventType.SEND_LOCAL_IMAGE_DATA,
-      data
+      data,
     };
     target.onMessageReceived(event);
   }
diff --git a/ash/webui/personalization_app/resources/trusted/keyboard_backlight/keyboard_backlight_actions.ts b/ash/webui/personalization_app/resources/trusted/keyboard_backlight/keyboard_backlight_actions.ts
index db1ebb4..7906634 100644
--- a/ash/webui/personalization_app/resources/trusted/keyboard_backlight/keyboard_backlight_actions.ts
+++ b/ash/webui/personalization_app/resources/trusted/keyboard_backlight/keyboard_backlight_actions.ts
@@ -43,7 +43,7 @@
     SetBacklightColorAction {
   return {
     name: KeyboardBacklightActionName.SET_BACKLIGHT_COLOR,
-    backlightColor
+    backlightColor,
   };
 }
 
@@ -51,7 +51,7 @@
     SetShouldShowNudgeAction {
   return {
     name: KeyboardBacklightActionName.SET_SHOULD_SHOW_NUDGE,
-    shouldShowNudge
+    shouldShowNudge,
   };
 }
 
@@ -62,6 +62,6 @@
     SetWallpaperColorAction {
   return {
     name: KeyboardBacklightActionName.SET_WALLPAPER_COLOR,
-    wallpaperColor
+    wallpaperColor,
   };
 }
diff --git a/ash/webui/personalization_app/resources/trusted/personalization_test_api.ts b/ash/webui/personalization_app/resources/trusted/personalization_test_api.ts
index 05f1c1b..867bf8b 100644
--- a/ash/webui/personalization_app/resources/trusted/personalization_test_api.ts
+++ b/ash/webui/personalization_app/resources/trusted/personalization_test_api.ts
@@ -34,5 +34,5 @@
 
 window.personalizationTestApi = {
   enterFullscreen,
-  makeTransparent
+  makeTransparent,
 };
diff --git a/ash/webui/personalization_app/resources/trusted/user/user_selectors.ts b/ash/webui/personalization_app/resources/trusted/user/user_selectors.ts
index ea299cb..28c13ee 100644
--- a/ash/webui/personalization_app/resources/trusted/user/user_selectors.ts
+++ b/ash/webui/personalization_app/resources/trusted/user/user_selectors.ts
@@ -46,7 +46,7 @@
     }
 
     const result = {
-      url: URL.createObjectURL(new Blob([bytes], {type: 'image/png'}))
+      url: URL.createObjectURL(new Blob([bytes], {type: 'image/png'})),
     };
     objectUrlCache.set(value, result);
     return result;
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_zero_state_element.ts b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_zero_state_element.ts
index 813a2d49..242e985 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_zero_state_element.ts
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_zero_state_element.ts
@@ -38,8 +38,8 @@
   private getMessage_(): string {
     return this.i18nAdvanced('googlePhotosZeroStateMessage', {
       substitutions: [
-        '<a target="_blank" href="https://photos.google.com">photos.google.com</a>'
-      ]
+        '<a target="_blank" href="https://photos.google.com">photos.google.com</a>',
+      ],
     });
   }
 
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_actions.ts b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_actions.ts
index ddf0a1f..2462253 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_actions.ts
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_actions.ts
@@ -77,7 +77,7 @@
     albumId,
     photos,
     resumeToken,
-    name: WallpaperActionName.APPEND_GOOGLE_PHOTOS_ALBUM
+    name: WallpaperActionName.APPEND_GOOGLE_PHOTOS_ALBUM,
   };
 }
 
@@ -97,7 +97,7 @@
   return {
     albums,
     resumeToken,
-    name: WallpaperActionName.APPEND_GOOGLE_PHOTOS_ALBUMS
+    name: WallpaperActionName.APPEND_GOOGLE_PHOTOS_ALBUMS,
   };
 }
 
@@ -117,7 +117,7 @@
   return {
     photos,
     resumeToken,
-    name: WallpaperActionName.APPEND_GOOGLE_PHOTOS_PHOTOS
+    name: WallpaperActionName.APPEND_GOOGLE_PHOTOS_PHOTOS,
   };
 }
 
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_controller.ts b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_controller.ts
index 5aa4fe78..fe503ed4 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_controller.ts
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_controller.ts
@@ -384,7 +384,7 @@
     store: PersonalizationStore): Promise<void> {
   const [{collectionId}, {albumId}] = await Promise.all([
     provider.getDailyRefreshCollectionId(),
-    provider.getGooglePhotosDailyRefreshAlbumId()
+    provider.getGooglePhotosDailyRefreshAlbumId(),
   ]);
 
   // Daily refresh should only be active for either Backdrop or Google Photos
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_reducers.ts b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_reducers.ts
index bb17168..8e7fd0e 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_reducers.ts
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_reducers.ts
@@ -34,7 +34,7 @@
       }
       return {
         ...state,
-        images: {...state.images, [action.collectionId]: action.images}
+        images: {...state.images, [action.collectionId]: action.images},
       };
     default:
       return state;
@@ -53,12 +53,12 @@
               result[id] = true;
               return result;
             },
-            {} as Record<WallpaperCollection['id'], boolean>)
+            {} as Record<WallpaperCollection['id'], boolean>),
       };
     case WallpaperActionName.BEGIN_LOAD_LOCAL_IMAGE_DATA:
       return {
         ...state,
-        local: {...state.local, data: {...state.local.data, [action.id]: true}}
+        local: {...state.local, data: {...state.local.data, [action.id]: true}},
       };
     case WallpaperActionName.BEGIN_LOAD_SELECTED_IMAGE:
       return {...state, selected: true};
@@ -241,7 +241,7 @@
         return {
           images: [
             kDefaultImageSymbol,
-            ...(state.images || []).filter(img => isFilePath(img))
+            ...(state.images || []).filter(img => isFilePath(img)),
           ],
           data: {
             ...state.data,
@@ -293,7 +293,7 @@
         data: {
           ...state.data,
           [action.id]: action.data,
-        }
+        },
       };
     default:
       return state;
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_selected_element.ts b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_selected_element.ts
index ea1550b..aa37a56 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_selected_element.ts
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_selected_element.ts
@@ -135,8 +135,8 @@
         type: Boolean,
         value() {
           return loadTimeData.getBoolean('fullScreenPreviewEnabled');
-        }
-      }
+        },
+      },
     };
   }
 
@@ -386,8 +386,9 @@
     if (isNonEmptyArray(image.attribution)) {
       return isDailyRefreshActive ?
           [
-            this.i18n('currentlySet'), this.i18n('dailyRefresh'),
-            ...image.attribution
+            this.i18n('currentlySet'),
+            this.i18n('dailyRefresh'),
+            ...image.attribution,
           ].join(' ') :
           [this.i18n('currentlySet'), ...image.attribution].join(' ');
     }
@@ -396,8 +397,9 @@
     if (isNonEmptyArray(attribution)) {
       return isDailyRefreshActive ?
           [
-            this.i18n('currentlySet'), this.i18n('dailyRefresh'),
-            ...image.attribution
+            this.i18n('currentlySet'),
+            this.i18n('dailyRefresh'),
+            ...image.attribution,
           ].join(' ') :
           [this.i18n('currentlySet'), ...attribution].join(' ');
     }
diff --git a/ash/webui/personalization_app/resources/untrusted/collections_grid.ts b/ash/webui/personalization_app/resources/untrusted/collections_grid.ts
index d6f4899..362295cc 100644
--- a/ash/webui/personalization_app/resources/untrusted/collections_grid.ts
+++ b/ash/webui/personalization_app/resources/untrusted/collections_grid.ts
@@ -178,7 +178,7 @@
           // Fill the view with loading tiles. Will be adjusted to the correct
           // number of tiles when collections are received.
           return getLoadingPlaceholders(() => ({type: TileType.LOADING}));
-        }
+        },
       },
     };
   }
diff --git a/ash/webui/personalization_app/resources/untrusted/iframe_api.ts b/ash/webui/personalization_app/resources/untrusted/iframe_api.ts
index b1bcb52..a415abc 100644
--- a/ash/webui/personalization_app/resources/untrusted/iframe_api.ts
+++ b/ash/webui/personalization_app/resources/untrusted/iframe_api.ts
@@ -20,7 +20,7 @@
 export function selectCollection(collectionId: string) {
   const event: constants.SelectCollectionEvent = {
     type: constants.EventType.SELECT_COLLECTION,
-    collectionId
+    collectionId,
   };
   onMessageReceived(event);
 }
@@ -30,7 +30,7 @@
  */
 export function selectGooglePhotosCollection() {
   const event: constants.SelectGooglePhotosCollectionEvent = {
-    type: constants.EventType.SELECT_GOOGLE_PHOTOS_COLLECTION
+    type: constants.EventType.SELECT_GOOGLE_PHOTOS_COLLECTION,
   };
   onMessageReceived(event);
 }
@@ -40,7 +40,7 @@
  */
 export function selectLocalCollection() {
   const event: constants.SelectLocalCollectionEvent = {
-    type: constants.EventType.SELECT_LOCAL_COLLECTION
+    type: constants.EventType.SELECT_LOCAL_COLLECTION,
   };
   onMessageReceived(event);
 }
diff --git a/ash/webui/sample_system_web_app_ui/resources/trusted/inter_frame_communication.ts b/ash/webui/sample_system_web_app_ui/resources/trusted/inter_frame_communication.ts
index fb5634aa..d71b3099 100644
--- a/ash/webui/sample_system_web_app_ui/resources/trusted/inter_frame_communication.ts
+++ b/ash/webui/sample_system_web_app_ui/resources/trusted/inter_frame_communication.ts
@@ -47,7 +47,7 @@
         resolve({
           childPage: childPageRemote,
           parentPageReceiver:
-              new ParentTrustedPageImpl(parentPagePendingReceiver)
+              new ParentTrustedPageImpl(parentPagePendingReceiver),
         });
       });
 });
diff --git a/ash/wm/float/float_controller.cc b/ash/wm/float/float_controller.cc
index 72124236..85fd638 100644
--- a/ash/wm/float/float_controller.cc
+++ b/ash/wm/float/float_controller.cc
@@ -28,6 +28,10 @@
 constexpr float kFloatWindowTabletWidthRatio = 0.3333333f;
 constexpr float kFloatWindowTabletHeightRatio = 0.8f;
 
+// TODO(sophiewen): Remove this once the untuck window widget is implemented. It
+// is temporarily here to give users a way to untuck the window.
+constexpr int kTuckedFloatWindowVisibleWidth = 100;
+
 bool InTabletMode() {
   return Shell::Get()->tablet_mode_controller()->InTabletMode();
 }
@@ -64,11 +68,26 @@
 
 }  // namespace
 
+// Scoped class which makes modifications while a window is tucked. It owns a
+// widget which is used to untuck the window.
+// TODO(sophiewen): Fill in this class.
+class FloatController::ScopedWindowTucker {
+ public:
+  explicit ScopedWindowTucker(aura::Window* window) : window_(window) {
+    DCHECK(window_);
+  }
+  ScopedWindowTucker(const ScopedWindowTucker&) = delete;
+  ScopedWindowTucker& operator=(const ScopedWindowTucker&) = delete;
+  ~ScopedWindowTucker() = default;
+
+ private:
+  aura::Window* window_;
+};
+
 FloatController::FloatController() = default;
 
 FloatController::~FloatController() = default;
 
-// static
 gfx::Rect FloatController::GetPreferredFloatWindowTabletBounds(
     aura::Window* window) {
   DCHECK(CanFloatWindowInTablet(window));
@@ -88,10 +107,8 @@
 
   // Update the origin of the float window based on whichever corner it is
   // magnetized to.
-  const MagnetismCorner corner =
-      Shell::Get()->float_controller()->magnetism_corner();
   gfx::Point origin;
-  switch (corner) {
+  switch (magnetism_corner_) {
     case MagnetismCorner::kTopLeft:
       origin = gfx::Point(kFloatWindowPaddingDp, kFloatWindowPaddingDp);
       break;
@@ -109,6 +126,25 @@
       break;
   }
 
+  // If the window is tucked, shift it so `kTuckedFloatWindowVisibleWidth` is
+  // visible on one side, depending on `corner`.
+  if (scoped_window_tucker_) {
+    int x_offset;
+    switch (magnetism_corner_) {
+      case MagnetismCorner::kTopLeft:
+      case MagnetismCorner::kBottomLeft:
+        x_offset =
+            -width - kFloatWindowPaddingDp + kTuckedFloatWindowVisibleWidth;
+        break;
+      case MagnetismCorner::kTopRight:
+      case MagnetismCorner::kBottomRight:
+        x_offset =
+            width + kFloatWindowPaddingDp - kTuckedFloatWindowVisibleWidth;
+        break;
+    }
+    origin.Offset(x_offset, 0);
+  }
+
   return gfx::Rect(origin, gfx::Size(width, height));
 }
 
@@ -144,6 +180,19 @@
   return float_window_ == window;
 }
 
+void FloatController::MaybeTuckFloatedWindow() {
+  if (scoped_window_tucker_)
+    return;
+
+  DCHECK(float_window_);
+  scoped_window_tucker_ = std::make_unique<ScopedWindowTucker>(float_window_);
+  UpdateWindowBoundsForTablet(float_window_);
+}
+
+void FloatController::MaybeUntuckFloatedWindow() {
+  scoped_window_tucker_.reset();
+}
+
 void FloatController::OnDragCompleted(
     const gfx::PointF& last_location_in_parent) {
   DCHECK(float_window_);
@@ -174,6 +223,22 @@
   UpdateWindowBoundsForTablet(float_window_);
 }
 
+void FloatController::OnFlingOrSwipe(bool left, bool up) {
+  DCHECK(float_window_);
+  if (left && up) {
+    magnetism_corner_ = MagnetismCorner::kTopLeft;
+  } else if (left && !up) {
+    magnetism_corner_ = MagnetismCorner::kBottomLeft;
+  } else if (!left && up) {
+    magnetism_corner_ = MagnetismCorner::kTopRight;
+  } else {
+    DCHECK(!left && !up);
+    magnetism_corner_ = MagnetismCorner::kBottomRight;
+  }
+
+  MaybeTuckFloatedWindow();
+}
+
 void FloatController::OnWindowDestroying(aura::Window* window) {
   DCHECK_EQ(float_window_, window);
   float_window_observation_.Reset();
@@ -193,6 +258,7 @@
 
 void FloatController::OnTabletModeEnded() {
   DCHECK(float_window_);
+  scoped_window_tucker_.reset();
   MaybeUpdateWindowUIAndBoundsForTablet(float_window_);
 }
 
@@ -216,8 +282,8 @@
   if (window == float_window_)
     return;
 
-  // TODO(shidi): temporary remove the DCHECK, will implement proper trigger on
-  // crbug/1339095.
+  // TODO(shidi): temporary remove the DCHECK, will implement proper trigger
+  // on crbug/1339095.
 
   // Only one floating window is allowed, reset previously floated window.
   ResetFloatedWindow();
@@ -245,6 +311,7 @@
 
   tablet_mode_observation_.Reset();
   display_observer_.reset();
+  scoped_window_tucker_.reset();
   MaybeUpdateWindowUIAndBoundsForTablet(window);
 }
 
diff --git a/ash/wm/float/float_controller.h b/ash/wm/float/float_controller.h
index 8ffae0c9..3e18339c 100644
--- a/ash/wm/float/float_controller.h
+++ b/ash/wm/float/float_controller.h
@@ -41,10 +41,6 @@
   // area when it is floated.
   static constexpr int kFloatWindowPaddingDp = 8;
 
-  // Gets the ideal float bounds of `window` in tablet mode if it were to be
-  // floated.
-  static gfx::Rect GetPreferredFloatWindowTabletBounds(aura::Window* window);
-
   // Determines if a window can be floated in tablet mode.
   static bool CanFloatWindowInTablet(aura::Window* window);
 
@@ -52,14 +48,29 @@
 
   MagnetismCorner magnetism_corner() const { return magnetism_corner_; }
 
+  // Gets the ideal float bounds of `window` in tablet mode if it were to be
+  // floated.
+  gfx::Rect GetPreferredFloatWindowTabletBounds(aura::Window* window);
+
   // Return true if `window` is floated, otherwise false.
   bool IsFloated(const aura::Window* window) const;
 
+  // Tucks or untucks `float_window_`. Does nothing if the window is already
+  // tucked or untucked.
+  void MaybeTuckFloatedWindow();
+  void MaybeUntuckFloatedWindow();
+
   // Called by the resizer when a drag is completed. This assumes the dragged
   // window associated with the resizer is `float_window_`. Updates the bounds
   // and magnetism of the floated window.
   void OnDragCompleted(const gfx::PointF& last_location_in_parent);
 
+  // Called by the resizer when a drag is completed by a fling or swipe gesture
+  // event. Updates the magnetism of the window and then tucks the window
+  // offscreen. `left` and `up` are used to determine the direction of the fling
+  // or swipe gesture.
+  void OnFlingOrSwipe(bool left, bool up);
+
   // aura::WindowObserver:
   void OnWindowDestroying(aura::Window* window) override;
 
@@ -73,8 +84,10 @@
                                uint32_t metrics) override;
 
  private:
+  class ScopedWindowTucker;
   friend class DefaultState;
   friend class TabletModeWindowState;
+  friend class WindowFloatTest;
 
   // Floats/Unfloats `window`.
   // Only one floating window is allowed, floating a new window will
@@ -98,6 +111,10 @@
   // window, that window will also be magnetized to the bottom left.
   MagnetismCorner magnetism_corner_ = MagnetismCorner::kBottomRight;
 
+  // Scoped class that handles the special tucked window state, which is not a
+  // normal window state. Null when there is no current tucked window.
+  std::unique_ptr<ScopedWindowTucker> scoped_window_tucker_;
+
   // Observes floated window.
   base::ScopedObservation<aura::Window, aura::WindowObserver>
       float_window_observation_{this};
diff --git a/ash/wm/float/float_controller_unittest.cc b/ash/wm/float/float_controller_unittest.cc
index bce93d6..b265c4ea 100644
--- a/ash/wm/float/float_controller_unittest.cc
+++ b/ash/wm/float/float_controller_unittest.cc
@@ -10,6 +10,7 @@
 #include "ash/frame/header_view.h"
 #include "ash/frame/non_client_frame_view_ash.h"
 #include "ash/public/cpp/shelf_config.h"
+#include "ash/screen_util.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/wm/overview/overview_test_util.h"
@@ -44,6 +45,10 @@
     return floated_window;
   }
 
+  bool IsFloatedWindowTucked() const {
+    return !!Shell::Get()->float_controller()->scoped_window_tucker_;
+  }
+
   void SetUp() override {
     // Ensure float feature is enabled.
     scoped_feature_list_.InitAndEnableFeature(
@@ -297,4 +302,36 @@
             window->bounds().bottom_left());
 }
 
+// Tests the functionality of tucking a window in tablet mode. Tucking a window
+// is hiding partially offscreen to the side.
+TEST_F(TabletWindowFloatTest, TuckedWindow) {
+  Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
+
+  std::unique_ptr<aura::Window> window = CreateFloatedWindow();
+
+  // Exiting immersive mode because of float does not seem to trigger a layout
+  // like it does in production code. Here we force a layout, otherwise the
+  // client view will remain the size of the widget, and dragging it will give
+  // us HTCLIENT.
+  auto* frame = NonClientFrameViewAsh::Get(window.get());
+  frame->Layout();
+
+  // Generate a fling to the top left corner. Tests that the window is tucked,
+  // and 100 pixels are visible to the user.
+  const gfx::Point header_center =
+      frame->GetHeaderView()->GetBoundsInScreen().CenterPoint();
+  GetEventGenerator()->GestureScrollSequence(
+      header_center, header_center - gfx::Vector2d(10, 10),
+      base::Milliseconds(10), /*steps=*/2);
+  EXPECT_TRUE(IsFloatedWindowTucked());
+  EXPECT_EQ(100, window->bounds().right());
+
+  // Tests that after we exit tablet mode, the window is untucked and fully
+  // visible, but is still floated.
+  Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false);
+  ASSERT_TRUE(Shell::Get()->float_controller()->IsFloated(window.get()));
+  EXPECT_TRUE(screen_util::GetDisplayBoundsInParent(window.get())
+                  .Contains(window->bounds()));
+}
+
 }  // namespace ash
diff --git a/ash/wm/float/tablet_mode_float_window_resizer.cc b/ash/wm/float/tablet_mode_float_window_resizer.cc
index 53209238..0414143 100644
--- a/ash/wm/float/tablet_mode_float_window_resizer.cc
+++ b/ash/wm/float/tablet_mode_float_window_resizer.cc
@@ -16,6 +16,8 @@
     WindowState* window_state)
     : WindowResizer(window_state) {
   DCHECK(chromeos::wm::features::IsFloatWindowEnabled());
+  // TODO(sophiewen): Remove this once the untuck window widget is implemented.
+  Shell::Get()->float_controller()->MaybeUntuckFloatedWindow();
 }
 
 TabletModeFloatWindowResizer::~TabletModeFloatWindowResizer() {
@@ -43,7 +45,18 @@
 }
 
 void TabletModeFloatWindowResizer::FlingOrSwipe(ui::GestureEvent* event) {
-  // TODO(crbug.com/1338715): Tuck the window to the side on fling or swipe.
+  const ui::GestureEventDetails& details = event->details();
+  bool left, up;
+  if (event->type() == ui::ET_SCROLL_FLING_START) {
+    left = details.velocity_x() < 0.f;
+    up = details.velocity_y() < 0.f;
+  } else {
+    DCHECK_EQ(ui::ET_GESTURE_SWIPE, event->type());
+    left = details.swipe_left();
+    up = details.swipe_up();
+  }
+
+  Shell::Get()->float_controller()->OnFlingOrSwipe(left, up);
 }
 
 }  // namespace ash
diff --git a/ash/wm/tablet_mode/tablet_mode_window_state.cc b/ash/wm/tablet_mode/tablet_mode_window_state.cc
index 67b08374..9dea074 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_state.cc
+++ b/ash/wm/tablet_mode/tablet_mode_window_state.cc
@@ -103,7 +103,9 @@
 
   if (chromeos::wm::features::IsFloatWindowEnabled() &&
       state_object->IsFloated()) {
-    return FloatController::GetPreferredFloatWindowTabletBounds(window);
+    return Shell::Get()
+        ->float_controller()
+        ->GetPreferredFloatWindowTabletBounds(window);
   }
 
   gfx::Rect bounds_in_parent;
diff --git a/base/memory/safe_ref.h b/base/memory/safe_ref.h
index 162c6744..97c2c1ea 100644
--- a/base/memory/safe_ref.h
+++ b/base/memory/safe_ref.h
@@ -47,30 +47,56 @@
 class SafeRef {
  public:
   // No default constructor, since there's no null state. Use an optional
-  // SafeRef if the pointer can not always be present.
+  // SafeRef if the pointer may not be present.
 
   // Copy construction and assignment.
-  //
-  // Note there is no move-construction since this type can not be null, and we
-  // have decided that it should support moved-from as a valid state that does
-  // not crash on use, since it is a non-owning smart pointer.
-  SafeRef(const SafeRef& p) : w_(p.w_) {}
+  SafeRef(const SafeRef& p) : w_(p.w_) {
+    // Avoid use-after-move.
+    CHECK(w_);
+  }
   SafeRef& operator=(const SafeRef& p) {
     w_ = p.w_;
+    // Avoid use-after-move.
+    CHECK(w_);
     return *this;
   }
 
-  // Conversion copy from SafeRef<U>.
-  //
-  // Note there is no move-construction since this type can not be null, and we
-  // have decided that it should support moved-from as a valid state that does
-  // not crash on use, since it is a non-owning smart pointer.
+  // Move construction and assignment.
+  SafeRef(SafeRef&& p) : w_(std::move(p.w_)) { CHECK(w_); }
+  SafeRef& operator=(SafeRef&& p) {
+    w_ = std::move(p.w_);
+    // Avoid use-after-move.
+    CHECK(w_);
+    return *this;
+  }
+
+  // Copy conversion from SafeRef<U>.
   template <typename U>
-  SafeRef(const SafeRef<U>& p)  // NOLINTNEXTLINE(google-explicit-constructor)
-      : w_(p.w_) {}
+  // NOLINTNEXTLINE(google-explicit-constructor)
+  SafeRef(const SafeRef<U>& p) : w_(p.w_) {
+    // Avoid use-after-move.
+    CHECK(w_);
+  }
   template <typename U>
   SafeRef& operator=(const SafeRef<U>& p) {
     w_ = p.w_;
+    // Avoid use-after-move.
+    CHECK(w_);
+    return *this;
+  }
+
+  // Move conversion from SafeRef<U>.
+  template <typename U>
+  // NOLINTNEXTLINE(google-explicit-constructor)
+  SafeRef(SafeRef<U>&& p) : w_(std::move(p.w_)) {
+    // Avoid use-after-move.
+    CHECK(w_);
+  }
+  template <typename U>
+  SafeRef& operator=(SafeRef<U>&& p) {
+    w_ = std::move(p.w_);
+    // Avoid use-after-move.
+    CHECK(w_);
     return *this;
   }
 
diff --git a/base/memory/safe_ref_unittest.cc b/base/memory/safe_ref_unittest.cc
index 6b48661..e52e5f43 100644
--- a/base/memory/safe_ref_unittest.cc
+++ b/base/memory/safe_ref_unittest.cc
@@ -6,6 +6,8 @@
 
 #include <utility>
 
+#include "base/bind.h"
+#include "base/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "base/test/gtest_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -14,11 +16,14 @@
 namespace base {
 namespace {
 
-struct BaseClass {};
+struct ReallyBaseClass {};
+struct BaseClass : ReallyBaseClass {};
 
 struct WithWeak : BaseClass {
   ~WithWeak() { self = nullptr; }
 
+  void Method() {}
+
   int i = 1;
   WithWeak* self{this};
   base::WeakPtrFactory<WithWeak> factory{this};
@@ -49,14 +54,6 @@
   EXPECT_EQ(safe3->self->i, 1);               // Will crash if not live.
 }
 
-TEST(SafeRefTest, StillValidAfterMove) {
-  WithWeak with;
-  SafeRef<WithWeak> safe(with.factory.GetSafeRef());
-  SafeRef<WithWeak> safe2 = std::move(safe);  // Move.
-  EXPECT_EQ(safe->self->i, 1);                // Will crash if not live.
-  EXPECT_EQ(safe2->self->i, 1);               // Will crash if not live.
-}
-
 TEST(SafeRefTest, AssignCopyAndMove) {
   WithWeak with;
   SafeRef<WithWeak> safe(with.factory.GetSafeRef());
@@ -139,5 +136,132 @@
   EXPECT_EQ((*safe).self->i, 1);
 }
 
+TEST(SafeRefTest, InvalidAfterMoveConstruction) {
+  WithWeak with;
+  SafeRef<WithWeak> safe(with.factory.GetSafeRef());
+  SafeRef<WithWeak> safe2 = std::move(safe);
+  // Will crash if not live.
+  EXPECT_EQ(safe2->self->i, 1);
+  // `safe` was previously moved-from, so using it in any way should crash now.
+  { EXPECT_CHECK_DEATH(SafeRef<WithWeak> safe3(safe)); }
+  {
+    SafeRef<WithWeak> safe3(with.factory.GetSafeRef());
+    EXPECT_CHECK_DEATH(safe3 = safe);
+  }
+  { EXPECT_CHECK_DEATH(SafeRef<WithWeak> safe3(std::move(safe))); }
+  {
+    SafeRef<WithWeak> safe3(with.factory.GetSafeRef());
+    EXPECT_CHECK_DEATH(safe3 = std::move(safe));
+  }
+  { EXPECT_CHECK_DEATH(SafeRef<BaseClass> safe3(safe)); }
+  {
+    SafeRef<BaseClass> safe3(with.factory.GetSafeRef());
+    EXPECT_CHECK_DEATH(safe3 = safe);
+  }
+  { EXPECT_CHECK_DEATH(SafeRef<BaseClass> safe3(std::move(safe))); }
+  {
+    SafeRef<BaseClass> safe3(with.factory.GetSafeRef());
+    EXPECT_CHECK_DEATH(safe3 = std::move(safe));
+  }
+  EXPECT_CHECK_DEATH((void)safe->self->i);
+}
+
+TEST(SafeRefTest, InvalidAfterMoveAssignment) {
+  WithWeak with;
+  SafeRef<WithWeak> safe(with.factory.GetSafeRef());
+  SafeRef<WithWeak> safe2(with.factory.GetSafeRef());
+  safe2 = std::move(safe);
+  // Will crash if not live.
+  EXPECT_EQ(safe2->self->i, 1);
+  // `safe` was previously moved-from, so using it in any way should crash now.
+  { EXPECT_CHECK_DEATH(SafeRef<WithWeak> safe3(safe)); }
+  {
+    SafeRef<WithWeak> safe3(with.factory.GetSafeRef());
+    EXPECT_CHECK_DEATH(safe3 = safe);
+  }
+  { EXPECT_CHECK_DEATH(SafeRef<WithWeak> safe3(std::move(safe))); }
+  {
+    SafeRef<WithWeak> safe3(with.factory.GetSafeRef());
+    EXPECT_CHECK_DEATH(safe3 = std::move(safe));
+  }
+  { EXPECT_CHECK_DEATH(SafeRef<BaseClass> safe3(safe)); }
+  {
+    SafeRef<BaseClass> safe3(with.factory.GetSafeRef());
+    EXPECT_CHECK_DEATH(safe3 = safe);
+  }
+  { EXPECT_CHECK_DEATH(SafeRef<BaseClass> safe3(std::move(safe))); }
+  {
+    SafeRef<BaseClass> safe3(with.factory.GetSafeRef());
+    EXPECT_CHECK_DEATH(safe3 = std::move(safe));
+  }
+  EXPECT_CHECK_DEATH((void)safe->self->i);
+}
+
+TEST(SafeRefTest, InvalidAfterMoveConversionConstruction) {
+  WithWeak with;
+  SafeRef<BaseClass> safe(with.factory.GetSafeRef());
+  SafeRef<BaseClass> safe2 = std::move(safe);
+  // Will crash if not live.
+  EXPECT_EQ(static_cast<WithWeak*>(&*safe2)->self->i, 1);
+  // `safe` was previously moved-from, so using it in any way should crash now.
+  { EXPECT_CHECK_DEATH(SafeRef<BaseClass> safe3(safe)); }
+  {
+    SafeRef<BaseClass> safe3(with.factory.GetSafeRef());
+    EXPECT_CHECK_DEATH(safe3 = safe);
+  }
+  { EXPECT_CHECK_DEATH(SafeRef<BaseClass> safe3(std::move(safe))); }
+  {
+    SafeRef<BaseClass> safe3(with.factory.GetSafeRef());
+    EXPECT_CHECK_DEATH(safe3 = std::move(safe));
+  }
+  { EXPECT_CHECK_DEATH(SafeRef<ReallyBaseClass> safe3(safe)); }
+  {
+    SafeRef<ReallyBaseClass> safe3(with.factory.GetSafeRef());
+    EXPECT_CHECK_DEATH(safe3 = safe);
+  }
+  { EXPECT_CHECK_DEATH(SafeRef<ReallyBaseClass> safe3(std::move(safe))); }
+  {
+    SafeRef<ReallyBaseClass> safe3(with.factory.GetSafeRef());
+    EXPECT_CHECK_DEATH(safe3 = std::move(safe));
+  }
+  EXPECT_CHECK_DEATH((void)static_cast<WithWeak*>(&*safe)->self->i);
+}
+
+TEST(SafeRefTest, InvalidAfterMoveConversionAssignment) {
+  WithWeak with;
+  SafeRef<BaseClass> safe(with.factory.GetSafeRef());
+  SafeRef<BaseClass> safe2(with.factory.GetSafeRef());
+  safe2 = std::move(safe);
+  //  // Will crash if not live.
+  EXPECT_EQ(static_cast<WithWeak*>(&*safe2)->self->i, 1);
+  // `safe` was previously moved-from, so using it in any way should crash now.
+  { EXPECT_CHECK_DEATH(SafeRef<BaseClass> safe3(safe)); }
+  {
+    SafeRef<BaseClass> safe3(with.factory.GetSafeRef());
+    EXPECT_CHECK_DEATH(safe3 = safe);
+  }
+  { EXPECT_CHECK_DEATH(SafeRef<BaseClass> safe3(std::move(safe))); }
+  {
+    SafeRef<BaseClass> safe3(with.factory.GetSafeRef());
+    EXPECT_CHECK_DEATH(safe3 = std::move(safe));
+  }
+  { EXPECT_CHECK_DEATH(SafeRef<ReallyBaseClass> safe3(safe)); }
+  {
+    SafeRef<ReallyBaseClass> safe3(with.factory.GetSafeRef());
+    EXPECT_CHECK_DEATH(safe3 = safe);
+  }
+  { EXPECT_CHECK_DEATH(SafeRef<ReallyBaseClass> safe3(std::move(safe))); }
+  {
+    SafeRef<ReallyBaseClass> safe3(with.factory.GetSafeRef());
+    EXPECT_CHECK_DEATH(safe3 = std::move(safe));
+  }
+  EXPECT_CHECK_DEATH((void)static_cast<WithWeak*>(&*safe)->self->i);
+}
+
+TEST(SafeRefTest, Bind) {
+  WithWeak with;
+  BindOnce(&WithWeak::Method, with.factory.GetSafeRef()).Run();
+}
+
 }  // namespace
 }  // namespace base
diff --git a/build/android/pylib/local/device/local_device_gtest_run.py b/build/android/pylib/local/device/local_device_gtest_run.py
index 2a8b59a..7949bfac 100644
--- a/build/android/pylib/local/device/local_device_gtest_run.py
+++ b/build/android/pylib/local/device/local_device_gtest_run.py
@@ -618,8 +618,10 @@
         timeout = None
 
       flags = [
-          f for f in self._test_instance.flags
-          if f not in ['--wait-for-debugger', '--wait-for-java-debugger']
+          f for f in self._test_instance.flags if f not in [
+              '--wait-for-debugger', '--wait-for-java-debugger',
+              '--gtest_also_run_disabled_tests'
+          ]
       ]
       flags.append('--gtest_list_tests')
 
diff --git a/build/rust/rs_bindings_from_cc.gni b/build/rust/rs_bindings_from_cc.gni
index 6b43924..18bb725c 100644
--- a/build/rust/rs_bindings_from_cc.gni
+++ b/build/rust/rs_bindings_from_cc.gni
@@ -10,12 +10,13 @@
 # Template to generate and build Rust bindings for a set of C++ headers using
 # Crubit's `rs_bindings_from_cc` tool.
 #
-# This template expands to a `mixed_static_library` containing the Rust side of
-# the bindings (as well as internal C++ thunks needed to support the bindings).
+# This template expands to a `mixed_static_library` named "<target>_rs_api" and
+# containing the Rust side of the bindings (as well as internal C++ thunks
+# needed to support the bindings).
 #
-# The generated out/.../gen/.../<target>.rs is machine-generated, but should
-# be fairly readable (inspecting it might be useful to discover the imported
-# bindings and their shape).
+# The generated out/.../gen/.../<target>_rs_api.rs is machine-generated, but
+# should be fairly readable (inspecting it might be useful to discover the
+# imported bindings and their shape).
 #
 # Parameters:
 #
@@ -26,9 +27,12 @@
 #     target, because typically only a *subset* of headers provides the
 #     *public* API that bindings are needed for.
 #
-#   deps: TODO(crbug.com/1297592): Not tested.  Most likely broken.  Will
-#                                  require invoking `rs_bindings_from_cc` on
-#                                  the transitive dependencies.
+#   TODO(crbug.com/1297592): Support dependencies in the generated bindings.
+#   Tentative plan:
+#   1) introduce `bindings_target` and use it A) in regular `deps` of
+#      mixed_static_library and B) to build the crate name by appending
+#      "_rs_api" suffix.
+#   2) introduce `bindings_deps` and use it when gathering GN metadata.
 #
 # Usage example:
 #
@@ -44,9 +48,8 @@
 #
 #       # Crubit's convention is to append "_rs_api" suffix to the target name
 #       # when naming the generated Rust crate.
-#       rs_bindings_from_cc("cpp_lib_rs_api") {
+#       rs_bindings_from_cc("cpp_lib") {
 #         public_headers = ["cpp_lib.h"]
-#         deps = [ ":cpp_lib" ]
 #       }
 #
 #       source_set("cpp_lib") {
@@ -62,38 +65,95 @@
 #     }
 #
 template("rs_bindings_from_cc") {
+  # Mandatory parameter: public_headers.
   assert(defined(invoker.public_headers),
          "Must specify the public C headers to make bindings for.")
+  _rebased_public_headers = []
+  foreach(hdr, invoker.public_headers) {
+    _rebased_public_headers += [ rebase_path(hdr) ]
+  }
 
-  _target_name = target_name
-  _action_target_name = "${target_name}_run_rs_bindings_from_cc"
-
+  # Optional parameter: testonly.
   _testonly = false
   if (defined(invoker.testonly)) {
     _testonly = invoker.testonly
   }
+
+  # Optional parameter: visibility.
   if (defined(invoker.visibility)) {
     _visibility = invoker.visibility
   }
-  _deps = []
-  if (defined(invoker.deps)) {
-    _deps += invoker.deps
+
+  # Future parameters:
+  _bindings_target = ":${target_name}"
+  _bindings_deps = []
+
+  # Various names and paths that are shared across multiple targets defined
+  # in the template here.
+  _base_target_name = get_label_info(_bindings_target, "name")
+  _lib_target_name = "${_base_target_name}_rs_api"
+  _gen_bindings_target_name = "${_lib_target_name}_gen_bindings"
+  _gen_metadata_target_name = "${_lib_target_name}_gen_metadata"
+  _metadata_target_name = "${_lib_target_name}_metadata"
+  _metadata_path = "${target_gen_dir}/${_lib_target_name}_meta.json"
+  _rs_out_path = "${target_gen_dir}/${_lib_target_name}.rs"
+  _cc_out_path = "${target_gen_dir}/${_lib_target_name}_impl.cc"
+
+  # Calculating the --targets_and_headers snippet for the *current* target
+  # and putting it into GN's `metadata`.
+  group(_metadata_target_name) {
+    testonly = _testonly
+    visibility = [ ":${_gen_metadata_target_name}" ]
+    deps = []
+
+    metadata = {
+      # The data below corresponds to a single-target entry inside
+      # `--targets_and_headers` cmdline argument of `rs_bindings_from_cc`.
+      crubit_target_and_headers = [
+        {
+          t = _base_target_name
+          h = _rebased_public_headers
+        },
+      ]
+    }
   }
 
-  # Paths where Crubit's `rs_bindings_from_cc` output goes:
-  _rs_out_path = "${target_gen_dir}/${_target_name}.rs"
-  _cc_out_path = "${target_gen_dir}/${_target_name}_impl.cc"
+  # Gathering --targets-and-headers data from *all* transitive dependencies and
+  # putting them into the file at `_metadata_path`.
+  generated_file(_gen_metadata_target_name) {
+    testonly = _testonly
+    visibility = [ ":${_gen_bindings_target_name}" ]
 
-  mixed_static_library(_target_name) {
-    deps = _deps
+    deps = [ ":${_metadata_target_name}" ]
+    deps += _bindings_deps
+
+    testonly = _testonly
+    outputs = [ _metadata_path ]
+    output_conversion = "json"
+    data_keys = [ "crubit_target_and_headers" ]
+
+    # TODO(https://crbug.com/1329611): Use `walk_keys` to limit how deep the
+    # transitive dependency goes.  Crubit doesn't care about all the `public_deps`
+    # - for example if target1.h re-exports a function from target2.h that uses
+    # only built-in C types as parameter types and return type, then Crubit doesn't
+    # need to know about target2.h when generating bindings for target1.h.  Crubit
+    # only needs to know about target2.h if bindings for target1.h need to expose
+    # types that get their bindings from target2.h.
+  }
+
+  # Exposing the generated Rust bindings.
+  mixed_static_library(_lib_target_name) {
     testonly = _testonly
     if (defined(_visibility)) {
       visibility = _visibility
     }
 
     sources = [ _cc_out_path ]
+    deps = _bindings_deps
     deps += [
-      ":${_action_target_name}",
+      ":${_gen_bindings_target_name}",
+      _bindings_target,
+
       # TODO(crbug.com/1297592): Declare the dependencies (the generated
       # ..._rs_api_impl.cc file #includes headers like
       # "third_party/crubit/rs_bindings_from_cc/support/offsetof.h"
@@ -103,10 +163,11 @@
     ]
 
     rs_sources = [ _rs_out_path ]
-    rs_crate_name = _target_name
+    rs_crate_name = _lib_target_name
     rs_crate_root = _rs_out_path
-    rs_deps = [
-      ":${_action_target_name}",
+    rs_deps = _bindings_deps
+    rs_deps += [
+      ":${_gen_bindings_target_name}",
       # TODO(crbug.com/1297592): Add required dependencies (the generated bindings will
       # depend on these extra crates if the generated bindings cover a C++ struct).
       # See also `deps_for_generated_rs_file` in `crubit/rs_bindings_from_cc/BUILD`.
@@ -115,8 +176,8 @@
     ]
   }
 
-  action(_action_target_name) {
-    deps = _deps
+  # Invoking Crubit's `rs_bindings_from_cc` tool to generate Rust bindings.
+  action(_gen_bindings_target_name) {
     testonly = _testonly
     if (defined(_visibility)) {
       visibility = _visibility
@@ -130,28 +191,7 @@
       _cc_out_path,
     ]
 
-    # Several important compiler flags come from default_compiler_configs
-    configs = default_compiler_configs
-    if (defined(invoker.configs)) {
-      configs += invoker.configs
-    }
-
-    # Target-specific inputs for Crubit's `rs_bindings_from_cc`:
-    _rebased_public_headers = []
-    foreach(hdr, invoker.public_headers) {
-      _rebased_public_headers += [ rebase_path(hdr) ]
-    }
-    _quoted_public_headers = []
-    foreach(hdr, _rebased_public_headers) {
-      _quoted_public_headers += [ "\"${hdr}\"" ]
-    }
-    _public_headers_cmdline_arg = string_join(",", _rebased_public_headers)
-    _public_headers_map_snippet = string_join(",", _quoted_public_headers)
-
-    # TODO(crbug.com/1297592): Instead of hardcoding it, we should compute the
-    # targets=>headers map from the local, transitive BUILD.gn dependencies.
-    _targets_and_headers_cmdline_arg = "[{\"t\": \"${_target_name}\", \"h\": [${_public_headers_map_snippet}]}]"
-
+    deps = [ ":${_gen_metadata_target_name}" ]
     args = [
       # Target-specific outputs:
       "--rs_out",
@@ -161,19 +201,16 @@
 
       # Target-specific inputs:
       "--public_headers",
-      _public_headers_cmdline_arg,
-      "--targets_and_headers",
-      _targets_and_headers_cmdline_arg,
-
-      # Fixed config:
-      "--crubit_support_path",  # No rebasing - this is for textual inclusion.
-      "third_party/crubit/rs_bindings_from_cc/support",
-      "--rustfmt_exe_path",
-      rebase_path("//third_party/rust-toolchain/bin/rustfmt"),
-      "--rustfmt_config_path",
-      rebase_path("//.rustfmt.toml"),
+      string_join(",", _rebased_public_headers),
+      "--targets_and_headers_from_gn",
+      rebase_path(_metadata_path),
     ]
 
+    # Several important compiler flags come from default_compiler_configs
+    configs = default_compiler_configs
+    if (defined(invoker.configs)) {
+      configs += invoker.configs
+    }
     args += [
       "--",
       "{{defines}}",
diff --git a/build/rust/run_rs_bindings_from_cc.py b/build/rust/run_rs_bindings_from_cc.py
index 8a9ef4a..24e38091 100755
--- a/build/rust/run_rs_bindings_from_cc.py
+++ b/build/rust/run_rs_bindings_from_cc.py
@@ -19,6 +19,8 @@
 
 RUST_TOOLCHAIN_DIR = os.path.join(CHROMIUM_SRC_DIR, "third_party",
                                   "rust-toolchain")
+RUSTFMT_EXE_PATH = os.path.join(RUST_TOOLCHAIN_DIR, "bin", "rustfmt")
+RUSTFMT_CONFIG_PATH = os.path.join(CHROMIUM_SRC_DIR, ".rustfmt.toml")
 RS_BINDINGS_FROM_CC_EXE_PATH = os.path.join(RUST_TOOLCHAIN_DIR, "bin",
                                             "rs_bindings_from_cc")
 
@@ -48,9 +50,51 @@
 
 
 def main():
+  parser = argparse.ArgumentParser()
+  parser.add_argument("--targets_and_headers_from_gn",
+                      metavar="FILE",
+                      help="File parsed into --targets_and_headers Crubit arg",
+                      required=True),
+  parser.add_argument("--public_headers",
+                      metavar="FILE",
+                      help="Passed through to Crubit",
+                      required=True),
+  parser.add_argument("--rs_out",
+                      metavar="FILE",
+                      help="Passed through to Crubit",
+                      required=True),
+  parser.add_argument("--cc_out",
+                      metavar="FILE",
+                      help="Passed through to Crubit",
+                      required=True),
+  parser.add_argument("clang_args",
+                      metavar="CLANGARGS",
+                      help="Arguments to forward to clang libraries",
+                      nargs=argparse.REMAINDER)
+  args = parser.parse_args()
+
+  # Fixed config.
+  genargs = []
+  genargs.extend([
+      "--crubit_support_path",
+      "third_party/crubit/src/rs_bindings_from_cc/support"
+  ])
+  genargs.extend(["--rustfmt_exe_path", RUSTFMT_EXE_PATH])
+  genargs.extend(["--rustfmt_config_path", RUSTFMT_CONFIG_PATH])
+
+  # Morph contents of the GN-generated JSON file into a single-line string.
+  with open(args.targets_and_headers_from_gn, "r") as f:
+    targets_and_headers = f.read().replace('\n', ' ').replace('\r', ' ')
+  genargs.extend(["--targets_and_headers", targets_and_headers])
+
+  # Other inputs and outputs.
+  genargs.extend(["--public_headers", args.public_headers])
+  genargs.extend(["--rs_out", args.rs_out])
+  genargs.extend(["--cc_out", args.cc_out])
+
   # The call to `filter_clang_args` is needed to avoid the following error:
   # error: unable to find plugin 'find-bad-constructs'
-  args = filter_clang_args(sys.argv)
+  genargs.extend(filter_clang_args(args.clang_args))
 
   # TODO(crbug.com/1297592): This warning needs to be suppressed, because
   # otherwise Crubit/Clang complains as follows:
@@ -58,12 +102,12 @@
   #     'linker' input unused [-Werror,-Wunused-command-line-argument]
   # Maybe `build/rust/rs_bindings_from_cc.gni` gives too much in `args`?  But
   # then `{{cflags}}` seems perfectly reasonable...
-  args += ["-Wno-unused-command-line-argument"]
+  genargs += ["-Wno-unused-command-line-argument"]
 
   # TODO(crbug.com/1297592): run_bindgen.py removes the outputs when the tool
   # fails.  Maybe we need to do something similar here?  OTOH in most failure
   # modes Crubit will fail *before* generating its outputs...
-  return subprocess.run([RS_BINDINGS_FROM_CC_EXE_PATH, *args]).returncode
+  return subprocess.run([RS_BINDINGS_FROM_CC_EXE_PATH, *genargs]).returncode
 
 
 if __name__ == '__main__':
diff --git a/build/rust/tests/test_rs_bindings_from_cc/BUILD.gn b/build/rust/tests/test_rs_bindings_from_cc/BUILD.gn
index 3da18b6..e4cdc3e 100644
--- a/build/rust/tests/test_rs_bindings_from_cc/BUILD.gn
+++ b/build/rust/tests/test_rs_bindings_from_cc/BUILD.gn
@@ -12,7 +12,7 @@
   build_native_rust_unit_tests = true
 }
 
-rs_bindings_from_cc("self_contained_target_rs_api") {
+rs_bindings_from_cc("self_contained_target") {
   # TODO(crbug.com/1297592): Is there something we can do (a convention?) to
   # avoid duplication/repetition across
   # 1a) `public_headers` here and 1b) `sources` in ":self_contained_target"
@@ -22,8 +22,6 @@
     "self_contained_target_header1.h",
     "self_contained_target_header2.h",
   ]
-
-  deps = [ ":self_contained_target" ]
 }
 
 source_set("self_contained_target") {
diff --git a/cc/base/BUILD.gn b/cc/base/BUILD.gn
index 2d9ea40..29d6aaa 100644
--- a/cc/base/BUILD.gn
+++ b/cc/base/BUILD.gn
@@ -56,6 +56,7 @@
     "//base",
     "//base/third_party/dynamic_annotations",
     "//skia",
+    "//ui/base:features",
     "//ui/gfx/animation",
     "//ui/gfx/geometry",
     "//ui/gfx/geometry:geometry_skia",
diff --git a/cc/base/features.cc b/cc/base/features.cc
index c1d274f5..0f5953f6 100644
--- a/cc/base/features.cc
+++ b/cc/base/features.cc
@@ -6,6 +6,7 @@
 
 #include "base/feature_list.h"
 #include "build/build_config.h"
+#include "ui/base/ui_base_features.h"
 
 namespace features {
 
@@ -20,17 +21,8 @@
 const base::Feature kAnimatedImageResume = {"AnimatedImageResume",
                                             base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Enables impulse-style scroll animations in place of the default ones.
-//
-// Note: Do not enable this on the Mac. The animation does not match the system
-// scroll animation curve to such an extent that it makes Chromium stand out in
-// a bad way.
-const base::Feature kImpulseScrollAnimations = {
-    "ImpulseScrollAnimations",
-    base::FEATURE_DISABLED_BY_DEFAULT};
-
 bool IsImpulseScrollAnimationEnabled() {
-  return base::FeatureList::IsEnabled(features::kImpulseScrollAnimations);
+  return base::FeatureList::IsEnabled(features::kWindowsScrollingPersonality);
 }
 
 // Whether the compositor should attempt to sync with the scroll handlers before
diff --git a/cc/base/features.h b/cc/base/features.h
index 4cdbac2..5dd95a6 100644
--- a/cc/base/features.h
+++ b/cc/base/features.h
@@ -12,7 +12,6 @@
 namespace features {
 
 CC_BASE_EXPORT extern const base::Feature kAnimatedImageResume;
-CC_BASE_EXPORT extern const base::Feature kImpulseScrollAnimations;
 CC_BASE_EXPORT extern bool IsImpulseScrollAnimationEnabled();
 CC_BASE_EXPORT extern const base::Feature kSynchronizedScrolling;
 
diff --git a/cc/metrics/dropped_frame_counter.h b/cc/metrics/dropped_frame_counter.h
index 430f510..c6c5978b 100644
--- a/cc/metrics/dropped_frame_counter.h
+++ b/cc/metrics/dropped_frame_counter.h
@@ -161,6 +161,10 @@
     return &sliding_window_histogram_[strategy];
   }
 
+  double sliding_window_current_percent_dropped() const {
+    return sliding_window_current_percent_dropped_;
+  }
+
  private:
   void NotifyFrameResult(const viz::BeginFrameArgs& args,
                          const FrameInfo& frame_info);
diff --git a/cc/paint/paint_canvas.h b/cc/paint/paint_canvas.h
index 04e6bf7..696042ac 100644
--- a/cc/paint/paint_canvas.h
+++ b/cc/paint/paint_canvas.h
@@ -134,6 +134,10 @@
   virtual bool getLocalClipBounds(SkRect* bounds) const = 0;
   virtual SkIRect getDeviceClipBounds() const = 0;
   virtual bool getDeviceClipBounds(SkIRect* bounds) const = 0;
+  virtual void drawColor(SkColor4f color, SkBlendMode mode) = 0;
+  void drawColor(SkColor4f color) { drawColor(color, SkBlendMode::kSrcOver); }
+  // TODO(crbug.com/1308932): Get rid of the SkColor versions of these
+  // functions. They both end up calling the SkColor4f versions anyway.
   virtual void drawColor(SkColor color, SkBlendMode mode) = 0;
   void drawColor(SkColor color) { drawColor(color, SkBlendMode::kSrcOver); }
 
diff --git a/cc/paint/record_paint_canvas.cc b/cc/paint/record_paint_canvas.cc
index 8566369..1a36faae 100644
--- a/cc/paint/record_paint_canvas.cc
+++ b/cc/paint/record_paint_canvas.cc
@@ -227,8 +227,12 @@
   return GetCanvas()->getDeviceClipBounds(bounds);
 }
 
+void RecordPaintCanvas::drawColor(SkColor4f color, SkBlendMode mode) {
+  push<DrawColorOp>(color, mode);
+}
+
 void RecordPaintCanvas::drawColor(SkColor color, SkBlendMode mode) {
-  push<DrawColorOp>(SkColor4f::FromColor(color), mode);
+  drawColor(SkColor4f::FromColor(color), mode);
 }
 
 void RecordPaintCanvas::clear(SkColor color) {
diff --git a/cc/paint/record_paint_canvas.h b/cc/paint/record_paint_canvas.h
index 336e198..c1a7657 100644
--- a/cc/paint/record_paint_canvas.h
+++ b/cc/paint/record_paint_canvas.h
@@ -64,6 +64,7 @@
   SkIRect getDeviceClipBounds() const override;
   bool getDeviceClipBounds(SkIRect* bounds) const override;
   void drawColor(SkColor color, SkBlendMode mode) override;
+  void drawColor(SkColor4f color, SkBlendMode mode) override;
   void clear(SkColor color) override;
   void clear(SkColor4f color) override;
 
diff --git a/cc/paint/skia_paint_canvas.cc b/cc/paint/skia_paint_canvas.cc
index 1de745b..8f9dddaf 100644
--- a/cc/paint/skia_paint_canvas.cc
+++ b/cc/paint/skia_paint_canvas.cc
@@ -164,6 +164,10 @@
   canvas_->drawColor(color, mode);
 }
 
+void SkiaPaintCanvas::drawColor(SkColor4f color, SkBlendMode mode) {
+  canvas_->drawColor(color, mode);
+}
+
 void SkiaPaintCanvas::clear(SkColor color) {
   canvas_->clear(color);
 }
diff --git a/cc/paint/skia_paint_canvas.h b/cc/paint/skia_paint_canvas.h
index 850cd43..9068cf9 100644
--- a/cc/paint/skia_paint_canvas.h
+++ b/cc/paint/skia_paint_canvas.h
@@ -104,6 +104,7 @@
   SkIRect getDeviceClipBounds() const override;
   bool getDeviceClipBounds(SkIRect* bounds) const override;
   void drawColor(SkColor color, SkBlendMode mode) override;
+  void drawColor(SkColor4f color, SkBlendMode mode) override;
   void clear(SkColor color) override;
   void clear(SkColor4f color) override;
 
diff --git a/cc/test/fake_proxy.cc b/cc/test/fake_proxy.cc
index cc7e543..b32c77e 100644
--- a/cc/test/fake_proxy.cc
+++ b/cc/test/fake_proxy.cc
@@ -41,8 +41,8 @@
   return false;
 }
 
-uint32_t FakeProxy::GetAverageThroughput() const {
-  return 0u;
+double FakeProxy::GetPercentDroppedFrames() const {
+  return 0.0;
 }
 
 }  // namespace cc
diff --git a/cc/test/fake_proxy.h b/cc/test/fake_proxy.h
index f7cfc6fb..5fd48c3 100644
--- a/cc/test/fake_proxy.h
+++ b/cc/test/fake_proxy.h
@@ -56,7 +56,7 @@
       base::WritableSharedMemoryMapping ukm_smoothness_data) override {}
   void SetRenderFrameObserver(
       std::unique_ptr<RenderFrameMetadataObserver> observer) override {}
-  uint32_t GetAverageThroughput() const override;
+  double GetPercentDroppedFrames() const override;
 
  private:
   raw_ptr<LayerTreeHost> layer_tree_host_;
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index 901185e..6c695fa0 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -1964,9 +1964,9 @@
   return result;
 }
 
-uint32_t LayerTreeHost::GetAverageThroughput() const {
+double LayerTreeHost::GetPercentDroppedFrames() const {
   DCHECK(IsMainThread());
-  return proxy_->GetAverageThroughput();
+  return proxy_->GetPercentDroppedFrames();
 }
 
 void LayerTreeHost::IncrementVisualUpdateDuration(
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h
index 1a42b84..cdba296 100644
--- a/cc/trees/layer_tree_host.h
+++ b/cc/trees/layer_tree_host.h
@@ -839,8 +839,8 @@
 
   std::vector<base::OnceClosure> TakeDocumentTransitionCallbacksForTesting();
 
-  // Returns a percentage representing average throughput of last X seconds.
-  uint32_t GetAverageThroughput() const;
+  // Returns a percentage of dropped frames of the last second.
+  double GetPercentDroppedFrames() const;
 
   // TODO(szager): Remove these once threaded compositing is enabled for all
   // web_tests.
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 77ffa8508..7a3d5f6 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -2271,8 +2271,7 @@
   metadata.page_scale_factor = active_tree_->current_page_scale_factor();
   metadata.scrollable_viewport_size = active_tree_->ScrollableViewportSize();
 
-  // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f.
-  metadata.root_background_color = active_tree_->background_color().toSkColor();
+  metadata.root_background_color = active_tree_->background_color();
   metadata.may_throttle_if_undrawn_frames = may_throttle_if_undrawn_frames_;
 
   if (active_tree_->has_presentation_callbacks()) {
diff --git a/cc/trees/proxy.h b/cc/trees/proxy.h
index 6c879ed9..9e86584 100644
--- a/cc/trees/proxy.h
+++ b/cc/trees/proxy.h
@@ -101,9 +101,9 @@
   virtual void SetRenderFrameObserver(
       std::unique_ptr<RenderFrameMetadataObserver> observer) = 0;
 
-  // Returns a percentage representing average throughput of last X seconds.
+  // Returns a percentage of dropped frames of the last second.
   // Only implemenented for single threaded proxy.
-  virtual uint32_t GetAverageThroughput() const = 0;
+  virtual double GetPercentDroppedFrames() const = 0;
 };
 
 }  // namespace cc
diff --git a/cc/trees/proxy_main.cc b/cc/trees/proxy_main.cc
index e6419e53..e56b02a 100644
--- a/cc/trees/proxy_main.cc
+++ b/cc/trees/proxy_main.cc
@@ -803,9 +803,9 @@
                      base::Unretained(proxy_impl_.get()), std::move(observer)));
 }
 
-uint32_t ProxyMain::GetAverageThroughput() const {
+double ProxyMain::GetPercentDroppedFrames() const {
   NOTIMPLEMENTED();
-  return 0u;
+  return 0.0;
 }
 
 }  // namespace cc
diff --git a/cc/trees/proxy_main.h b/cc/trees/proxy_main.h
index 1f0be86..bea1deee 100644
--- a/cc/trees/proxy_main.h
+++ b/cc/trees/proxy_main.h
@@ -120,7 +120,7 @@
       base::WritableSharedMemoryMapping ukm_smoothness_data) override;
   void SetRenderFrameObserver(
       std::unique_ptr<RenderFrameMetadataObserver> observer) override;
-  uint32_t GetAverageThroughput() const override;
+  double GetPercentDroppedFrames() const override;
 
   // Returns |true| if the request was actually sent, |false| if one was
   // already outstanding.
diff --git a/cc/trees/single_thread_proxy.cc b/cc/trees/single_thread_proxy.cc
index 06caf94..dfac828 100644
--- a/cc/trees/single_thread_proxy.cc
+++ b/cc/trees/single_thread_proxy.cc
@@ -909,9 +909,10 @@
   host_impl_->SetRenderFrameObserver(std::move(observer));
 }
 
-uint32_t SingleThreadProxy::GetAverageThroughput() const {
+double SingleThreadProxy::GetPercentDroppedFrames() const {
   DebugScopedSetImplThread impl(task_runner_provider_);
-  return host_impl_->dropped_frame_counter()->GetAverageThroughput();
+  return host_impl_->dropped_frame_counter()
+      ->sliding_window_current_percent_dropped();
 }
 
 void SingleThreadProxy::UpdateBrowserControlsState(
diff --git a/cc/trees/single_thread_proxy.h b/cc/trees/single_thread_proxy.h
index fef5133..96ddc045 100644
--- a/cc/trees/single_thread_proxy.h
+++ b/cc/trees/single_thread_proxy.h
@@ -76,7 +76,7 @@
       base::WritableSharedMemoryMapping ukm_smoothness_data) override;
   void SetRenderFrameObserver(
       std::unique_ptr<RenderFrameMetadataObserver> observer) override;
-  uint32_t GetAverageThroughput() const override;
+  double GetPercentDroppedFrames() const override;
 
   void UpdateBrowserControlsState(BrowserControlsState constraints,
                                   BrowserControlsState current,
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 07cde42..6053d89 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -1407,6 +1407,7 @@
     "//chrome/browser/tabmodel:java",
     "//chrome/browser/tabpersistence:java",
     "//chrome/browser/ui/android/omnibox:java",
+    "//chrome/browser/ui/android/theme:java",
     "//chrome/browser/ui/android/toolbar:java",
     "//chrome/browser/ui/messages/android:java",
     "//chrome/test/android:chrome_java_integration_test_support",
diff --git a/chrome/android/features/start_surface/javatests/src/org/chromium/chrome/features/start_surface/TabSwitcherAndStartSurfaceLayoutTest.java b/chrome/android/features/start_surface/javatests/src/org/chromium/chrome/features/start_surface/TabSwitcherAndStartSurfaceLayoutTest.java
index 61ad614..eeecd12 100644
--- a/chrome/android/features/start_surface/javatests/src/org/chromium/chrome/features/start_surface/TabSwitcherAndStartSurfaceLayoutTest.java
+++ b/chrome/android/features/start_surface/javatests/src/org/chromium/chrome/features/start_surface/TabSwitcherAndStartSurfaceLayoutTest.java
@@ -1343,7 +1343,6 @@
     @EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID,
             ChromeFeatureList.TAB_TO_GTS_ANIMATION + "<Study"})
     @CommandLineFlags.Add({BASE_PARAMS + "/thumbnail_aspect_ratio/1.0"})
-    @DisabledTest(message = "https://crbug.com/1205952")
     public void testRenderGrid_withAspectRatioOfOne() throws IOException {
         // clang-format on
         ChromeTabbedActivity cta = mActivityTestRule.getActivity();
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java
index 5c89dd3..dc5fef5 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java
@@ -14,7 +14,6 @@
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.res.Resources;
-import android.graphics.Bitmap;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.SystemClock;
@@ -333,9 +332,9 @@
             }
 
             @Override
-            public Bitmap getBitmap() {
+            public void triggerBitmapCapture() {
                 long startTime = SystemClock.elapsedRealtime();
-                Bitmap bitmap = super.getBitmap();
+                super.triggerBitmapCapture();
                 long elapsed = SystemClock.elapsedRealtime() - startTime;
                 if (elapsed == 0) elapsed = 1;
 
@@ -349,7 +348,6 @@
                 mSuppressedUntil = SystemClock.elapsedRealtime() + suppressedFor;
                 Log.d(TAG, "DynamicView: spent %dms on getBitmap, suppress updating for %dms.",
                         elapsed, suppressedFor);
-                return bitmap;
             }
         };
         mDynamicView.setDownsamplingScale(getDownsamplingScale());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java
index 24886d4..a77f9e63 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java
@@ -212,7 +212,8 @@
         // clang-format off
         mBottomSheetController = BottomSheetControllerFactory.createBottomSheetController(
                 () -> mScrim, (sheet) -> {}, getWindow(),
-                KeyboardVisibilityDelegate.getInstance(), () -> sheetContainer);
+                KeyboardVisibilityDelegate.getInstance(), () -> sheetContainer,
+                () -> findViewById(android.R.id.content).getHeight());
         // clang-format on
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
index 2d8d6fa..eff2261 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
@@ -1312,7 +1312,8 @@
                         -> mScrimCoordinator,
                 sheetInitializedCallback, mActivity.getWindow(),
                 mWindowAndroid.getKeyboardDelegate(),
-                () -> mActivity.findViewById(R.id.sheet_container));
+                () -> mActivity.findViewById(R.id.sheet_container),
+                () -> mActivity.findViewById(R.id.coordinator).getHeight());
         BottomSheetControllerFactory.setExceptionReporter(
                 (throwable)
                         -> ChromePureJavaExceptionReporter.reportJavaException(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/TabsOpenedFromExternalAppTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/TabsOpenedFromExternalAppTest.java
index dd80dd80..bd5a573 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/TabsOpenedFromExternalAppTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/TabsOpenedFromExternalAppTest.java
@@ -27,6 +27,7 @@
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.CriteriaNotSatisfiedException;
 import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.init.AsyncInitializationActivity;
 import org.chromium.chrome.browser.tab.Tab;
@@ -34,6 +35,7 @@
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.chrome.test.util.ChromeTabUtils;
+import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
 import org.chromium.content_public.browser.test.util.DOMUtils;
 import org.chromium.content_public.browser.test.util.JavaScriptUtils;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
@@ -48,6 +50,7 @@
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+@DisableFeatures(ChromeFeatureList.GRID_TAB_SWITCHER_FOR_TABLETS)
 public class TabsOpenedFromExternalAppTest {
     @Rule
     public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/ToolbarSecurityIconTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/ToolbarSecurityIconTest.java
index f3fb906..24a88bb 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/ToolbarSecurityIconTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/ToolbarSecurityIconTest.java
@@ -11,6 +11,7 @@
 import android.content.Context;
 import android.view.ContextThemeWrapper;
 
+import androidx.annotation.ColorRes;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
@@ -35,6 +36,8 @@
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.TabImpl;
+import org.chromium.chrome.browser.theme.ThemeUtils;
+import org.chromium.chrome.browser.ui.theme.BrandedColorScheme;
 import org.chromium.chrome.test.util.ToolbarUnitTestUtils;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.components.prefs.PrefService;
@@ -247,4 +250,65 @@
                 mLocationBarModel.getSecurityIconResource(ConnectionSecurityLevel.SECURE,
                         !IS_SMALL_DEVICE, !IS_OFFLINE_PAGE, !IS_PAINT_PREVIEW));
     }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void testGetSecurityIconColorWithSecurityLevel_DangerousWebsite() {
+        assertEquals(R.color.default_red,
+                mLocationBarModel.getSecurityIconColorWithSecurityLevel(
+                        /*connectionSecurityLevel*/ ConnectionSecurityLevel.DANGEROUS,
+                        /*brandedColorScheme*/ BrandedColorScheme.APP_DEFAULT,
+                        /*isIncognito*/ false));
+    }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void testGetSecurityIconColorWithSecurityLevel_DangerousWebsiteWithIncognito() {
+        assertEquals(R.color.baseline_error_200,
+                mLocationBarModel.getSecurityIconColorWithSecurityLevel(
+                        /*connectionSecurityLevel*/ ConnectionSecurityLevel.DANGEROUS,
+                        /*brandedColorScheme*/ BrandedColorScheme.APP_DEFAULT,
+                        /*isIncognito*/ true));
+    }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void testGetSecurityIconColorWithSecurityLevel_NonDangerousWebsite() {
+        final @ConnectionSecurityLevel int brandedColorScheme = BrandedColorScheme.APP_DEFAULT;
+        final @ColorRes int defaultColorRes =
+                ThemeUtils.getThemedToolbarIconTintRes(brandedColorScheme);
+
+        for (int connectionSecurityLevel : SECURITY_LEVELS) {
+            if (connectionSecurityLevel != ConnectionSecurityLevel.DANGEROUS) {
+                assertEquals(defaultColorRes,
+                        mLocationBarModel.getSecurityIconColorWithSecurityLevel(
+                                connectionSecurityLevel, brandedColorScheme,
+                                /*isIncognito*/ false));
+            }
+        }
+    }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void testGetSecurityIconColorWithSecurityLevel_BrandedTheme() {
+        final @ColorRes int defaultColorResLight =
+                ThemeUtils.getThemedToolbarIconTintRes(BrandedColorScheme.LIGHT_BRANDED_THEME);
+        final @ColorRes int defaultColorResDark =
+                ThemeUtils.getThemedToolbarIconTintRes(BrandedColorScheme.DARK_BRANDED_THEME);
+
+        for (int connectionSecurityLevel : SECURITY_LEVELS) {
+            assertEquals(defaultColorResLight,
+                    mLocationBarModel.getSecurityIconColorWithSecurityLevel(connectionSecurityLevel,
+                            /*brandedColorScheme*/ BrandedColorScheme.LIGHT_BRANDED_THEME,
+                            /*isIncognito*/ false));
+            assertEquals(defaultColorResDark,
+                    mLocationBarModel.getSecurityIconColorWithSecurityLevel(connectionSecurityLevel,
+                            /*brandedColorScheme*/ BrandedColorScheme.DARK_BRANDED_THEME,
+                            /*isIncognito*/ false));
+        }
+    }
 }
diff --git a/chrome/app/chrome_command_ids.h b/chrome/app/chrome_command_ids.h
index c6aad13..ad3782c4 100644
--- a/chrome/app/chrome_command_ids.h
+++ b/chrome/app/chrome_command_ids.h
@@ -344,6 +344,8 @@
 #define IDC_CONTENT_CONTEXT_RELOADFRAME 50170
 #define IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE 50171
 #define IDC_CONTENT_CONTEXT_VIEWFRAMEINFO 50172
+// User Notes.
+#define IDC_CONTENT_CONTEXT_ADD_A_NOTE 50175
 // Search items.
 #define IDC_CONTENT_CONTEXT_GOTOURL 50180
 #define IDC_CONTENT_CONTEXT_SEARCHWEBFOR 50181
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 15f57b2..cec207b 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -644,6 +644,10 @@
             Copy link to highlight
           </message>
 
+          <message name="IDS_CONTENT_CONTEXT_ADD_A_NOTE" desc="The name of the Add Note command in the content area context menu">
+            Add a note
+          </message>
+
           <message name="IDS_CONTENT_CONTEXT_REMOVELINKTOTEXT" desc="The name of the Remove Highlights command in the content area context menu">
             Remove highlight
           </message>
@@ -895,6 +899,10 @@
             Copy Link to Highlight
           </message>
 
+          <message name="IDS_CONTENT_CONTEXT_ADD_A_NOTE" desc="In Title Case: The name of the Add Note command in the content area context menu">
+            Add a Note
+          </message>
+
           <message name="IDS_CONTENT_CONTEXT_REMOVELINKTOTEXT" desc="In TitleCase: The name of the Remove Highlights command in the content area context menu">
             Remove Highlight
           </message>
diff --git a/chrome/app/generated_resources_grd/IDS_CONTENT_CONTEXT_ADD_A_NOTE.png.sha1 b/chrome/app/generated_resources_grd/IDS_CONTENT_CONTEXT_ADD_A_NOTE.png.sha1
new file mode 100644
index 0000000..07d24f5
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_CONTENT_CONTEXT_ADD_A_NOTE.png.sha1
@@ -0,0 +1 @@
+64a67eb1a5c88fb5eb699e41356f57af8d19e45f
\ No newline at end of file
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index a7270088..322ec35b 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1526,13 +1526,13 @@
     {"- Fake Data", kNtpRecipeTasksModuleFakeData,
      std::size(kNtpRecipeTasksModuleFakeData), nullptr},
     {"- Historical Arm (7 days)", kNtpRecipeTasksModuleHistorical7Days,
-     std::size(kNtpRecipeTasksModuleHistorical7Days), nullptr},
+     std::size(kNtpRecipeTasksModuleHistorical7Days), "t3349934"},
     {"- Historical Arm (14 days)", kNtpRecipeTasksModuleHistorical14Days,
-     std::size(kNtpRecipeTasksModuleHistorical14Days), nullptr},
+     std::size(kNtpRecipeTasksModuleHistorical14Days), "t3349935"},
     {"- Recommended Mix Arm (7 days)", kNtpRecipeTasksModuleMix7Days,
-     std::size(kNtpRecipeTasksModuleMix7Days), nullptr},
+     std::size(kNtpRecipeTasksModuleMix7Days), "t3349936"},
     {"- Recommended Mix Arm (14 days)", kNtpRecipeTasksModuleMix14Days,
-     std::size(kNtpRecipeTasksModuleMix14Days), nullptr},
+     std::size(kNtpRecipeTasksModuleMix14Days), "t3349937"},
 };
 
 const FeatureEntry::FeatureParam kNtpDriveModuleFakeData[] = {
@@ -5045,14 +5045,20 @@
      FEATURE_WITH_PARAMS_VALUE_TYPE(omnibox::kOmniboxAssistantVoiceSearch,
                                     kOmniboxAssistantVoiceSearchVariations,
                                     "OmniboxAssistantVoiceSearch")},
-    {"omnibox-most-visited-tiles",
-     flag_descriptions::kOmniboxMostVisitedTilesName,
-     flag_descriptions::kOmniboxMostVisitedTilesDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(omnibox::kMostVisitedTiles)},
     {"omnibox-modernize-visual-update",
      flag_descriptions::kOmniboxModernizeVisualUpdateName,
      flag_descriptions::kOmniboxModernizeVisualUpdateDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(chrome::android::kOmniboxModernizeVisualUpdate)},
+
+    {"omnibox-most-visited-tiles",
+     flag_descriptions::kOmniboxMostVisitedTilesName,
+     flag_descriptions::kOmniboxMostVisitedTilesDescription, kOsAndroid,
+     FEATURE_VALUE_TYPE(omnibox::kMostVisitedTiles)},
+
+    {"omnibox-most-visited-tiles-title-wrap-around",
+     flag_descriptions::kOmniboxMostVisitedTilesTitleWrapAroundName,
+     flag_descriptions::kOmniboxMostVisitedTilesTitleWrapAroundDescription,
+     kOsAndroid, FEATURE_VALUE_TYPE(omnibox::kMostVisitedTilesTitleWrapAround)},
 #endif  // BUILDFLAG(IS_ANDROID)
 
     {"omnibox-on-focus-suggestions-contextual-web",
@@ -6718,14 +6724,10 @@
      FEATURE_VALUE_TYPE(features::kClosedTabCache)},
 #endif  // !BUILDFLAG(IS_ANDROID)
 
-    {"impulse-scroll-animations",
-     flag_descriptions::kImpulseScrollAnimationsName,
-     flag_descriptions::kImpulseScrollAnimationsDescription, kOsAll,
-     FEATURE_VALUE_TYPE(features::kImpulseScrollAnimations)},
-
-    {"percent-based-scrolling", flag_descriptions::kPercentBasedScrollingName,
-     flag_descriptions::kPercentBasedScrollingDescription, kOsAll,
-     FEATURE_VALUE_TYPE(features::kPercentBasedScrolling)},
+    {"windows-scrolling-personality",
+     flag_descriptions::kWindowsScrollingPersonalityName,
+     flag_descriptions::kWindowsScrollingPersonalityDescription, kOsAll,
+     FEATURE_VALUE_TYPE(features::kWindowsScrollingPersonality)},
 
     {"scroll-unification", flag_descriptions::kScrollUnificationName,
      flag_descriptions::kScrollUnificationDescription, kOsAll,
@@ -7848,12 +7850,6 @@
      FEATURE_VALUE_TYPE(
          autofill::features::kAutofillShowManualFallbackInContextMenu)},
 
-    {"autofill-suggest-virtual-cards-on-incomplete-form",
-     flag_descriptions::kAutofillSuggestVirtualCardsOnIncompleteFormName,
-     flag_descriptions::kAutofillSuggestVirtualCardsOnIncompleteFormDescription,
-     kOsDesktop | kOsAndroid,
-     FEATURE_VALUE_TYPE(
-         autofill::features::kAutofillSuggestVirtualCardsOnIncompleteForm)},
     {flag_descriptions::kEnableLensFullscreenSearchFlagId,
      flag_descriptions::kEnableLensFullscreenSearchName,
      flag_descriptions::kEnableLensFullscreenSearchDescription, kOsDesktop,
@@ -8859,6 +8855,20 @@
          password_manager::features::kEnableBiometricAuthenticationInSettings)},
 #endif
 
+    {"autofill-enable-remade-downstream-metrics",
+     flag_descriptions::kAutofillEnableRemadeDownstreamMetricsName,
+     flag_descriptions::kAutofillEnableRemadeDownstreamMetricsDescription,
+     kOsAll,
+     FEATURE_VALUE_TYPE(
+         autofill::features::kAutofillEnableRemadeDownstreamMetrics)},
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+    {"enable-seamless-refresh-rate-switching",
+     flag_descriptions::kEnableSeamlessRefreshRateSwitchingName,
+     flag_descriptions::kEnableSeamlessRefreshRateSwitchingDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(ash::features::kSeamlessRefreshRateSwitching)},
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
     // 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
diff --git a/chrome/browser/ash/BUILD.gn b/chrome/browser/ash/BUILD.gn
index 671aeea..d1552893 100644
--- a/chrome/browser/ash/BUILD.gn
+++ b/chrome/browser/ash/BUILD.gn
@@ -1601,6 +1601,7 @@
     "//chromeos/dbus:metrics_event_proto",
     "//chromeos/dbus:vm_applications_apps_proto",
     "//chromeos/dbus:vm_launch_proto",
+    "//chromeos/dbus/cec_service",
     "//chromeos/dbus/common",
     "//chromeos/dbus/constants",
     "//chromeos/dbus/cros_disks",
diff --git a/chrome/browser/ash/dbus/ash_dbus_helper.cc b/chrome/browser/ash/dbus/ash_dbus_helper.cc
index 1c33b09..08670e4 100644
--- a/chrome/browser/ash/dbus/ash_dbus_helper.cc
+++ b/chrome/browser/ash/dbus/ash_dbus_helper.cc
@@ -68,6 +68,7 @@
 #include "chromeos/dbus/arc/arc_obb_mounter_client.h"
 #include "chromeos/dbus/arc/arc_sensor_service_client.h"
 #include "chromeos/dbus/cdm_factory_daemon/cdm_factory_daemon_client.h"
+#include "chromeos/dbus/cec_service/cec_service_client.h"
 #include "chromeos/dbus/constants/dbus_paths.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/dlcservice/dlcservice_client.h"
@@ -139,6 +140,7 @@
   InitializeDBusClient<AuthPolicyClient>(bus);
   InitializeDBusClient<BiodClient>(bus);  // For device::Fingerprint.
   InitializeDBusClient<chromeos::CdmFactoryDaemonClient>(bus);
+  InitializeDBusClient<chromeos::CecServiceClient>(bus);
   InitializeDBusClient<ChunneldClient>(bus);
   InitializeDBusClient<CiceroneClient>(bus);
   // ConciergeClient depends on CiceroneClient.
@@ -297,6 +299,7 @@
   ConciergeClient::Shutdown();
   CiceroneClient::Shutdown();
   ChunneldClient::Shutdown();
+  chromeos::CecServiceClient::Shutdown();
   chromeos::CdmFactoryDaemonClient::Shutdown();
   BiodClient::Shutdown();
   AuthPolicyClient::Shutdown();
diff --git a/chrome/browser/ash/login/app_mode/test/auto_launched_kiosk_browsertest.cc b/chrome/browser/ash/login/app_mode/test/auto_launched_kiosk_browsertest.cc
index 4041b44..b753f784 100644
--- a/chrome/browser/ash/login/app_mode/test/auto_launched_kiosk_browsertest.cc
+++ b/chrome/browser/ash/login/app_mode/test/auto_launched_kiosk_browsertest.cc
@@ -9,6 +9,7 @@
 #include "apps/test/app_window_waiter.h"
 #include "ash/components/tpm/stub_install_attributes.h"
 #include "ash/constants/ash_features.h"
+#include "base/callback_list.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/run_loop.h"
@@ -25,8 +26,8 @@
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/policy/core/device_local_account.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/browsertest_util.h"
+#include "chrome/browser/lifetime/termination_notification.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/webui/chromeos/login/reset_screen_handler.h"
 #include "chrome/common/chrome_constants.h"
@@ -35,9 +36,6 @@
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/shill/shill_manager_client.h"
 #include "components/crx_file/crx_verifier.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
-#include "content/public/browser/notification_service.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/test_utils.h"
 #include "extensions/browser/app_window/app_window.h"
@@ -83,35 +81,6 @@
 constexpr char kTestManagementApiSecondaryApp[] =
     "kajpgkhinciaiihghpdamekpjpldgpfi";
 
-// Used to listen for app termination notification.
-class TerminationObserver : public content::NotificationObserver {
- public:
-  TerminationObserver() {
-    registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
-                   content::NotificationService::AllSources());
-  }
-
-  TerminationObserver(const TerminationObserver&) = delete;
-  TerminationObserver& operator=(const TerminationObserver&) = delete;
-
-  ~TerminationObserver() override = default;
-
-  // Whether app has been terminated - i.e. whether app termination notification
-  // has been observed.
-  bool terminated() const { return notification_seen_; }
-
- private:
-  void Observe(int type,
-               const content::NotificationSource& source,
-               const content::NotificationDetails& details) override {
-    ASSERT_EQ(chrome::NOTIFICATION_APP_TERMINATING, type);
-    notification_seen_ = true;
-  }
-
-  bool notification_seen_ = false;
-  content::NotificationRegistrar registrar_;
-};
-
 }  // namespace
 
 class AutoLaunchedKioskTestBase : public OobeBaseTest {
@@ -192,7 +161,9 @@
     // from the kiosk app.
     app_window_loaded_listener_ =
         std::make_unique<ExtensionTestMessageListener>("appWindowLoaded");
-    termination_observer_ = std::make_unique<TerminationObserver>();
+    termination_subscription_ =
+        browser_shutdown::AddAppTerminatingCallback(base::DoNothing());
+
     InProcessBrowserTest::PreRunTestOnMainThread();
   }
 
@@ -203,7 +174,7 @@
 
   void TearDownOnMainThread() override {
     app_window_loaded_listener_.reset();
-    termination_observer_.reset();
+    termination_subscription_ = {};
 
     MixinBasedInProcessBrowserTest::TearDownOnMainThread();
   }
@@ -258,7 +229,7 @@
 
  protected:
   std::unique_ptr<ExtensionTestMessageListener> app_window_loaded_listener_;
-  std::unique_ptr<TerminationObserver> termination_observer_;
+  base::CallbackListSubscription termination_subscription_;
 
   DeviceStateMixin device_state_{
       &mixin_host_, DeviceStateMixin::State::OOBE_COMPLETED_CLOUD_ENROLLED};
@@ -292,7 +263,7 @@
 IN_PROC_BROWSER_TEST_P(AutoLaunchedKioskTest, PRE_CrashRestore) {
   // Verify that Chrome hasn't already exited, e.g. in order to apply user
   // session flags.
-  ASSERT_FALSE(termination_observer_->terminated());
+  ASSERT_TRUE(termination_subscription_);
 
   // Check that policy flags have not been lost.
   ExpectCommandLineHasDefaultPolicySwitches(
@@ -308,7 +279,7 @@
 IN_PROC_BROWSER_TEST_P(AutoLaunchedKioskTest, CrashRestore) {
   // Verify that Chrome hasn't already exited, e.g. in order to apply user
   // session flags.
-  ASSERT_FALSE(termination_observer_->terminated());
+  ASSERT_TRUE(termination_subscription_);
 
   ExpectCommandLineHasDefaultPolicySwitches(
       *base::CommandLine::ForCurrentProcess());
@@ -383,18 +354,17 @@
 IN_PROC_BROWSER_TEST_P(AutoLaunchedNonKioskEnabledAppTest, NotLaunched) {
   // Verify that Chrome hasn't already exited, e.g. in order to apply user
   // session flags.
-  ASSERT_FALSE(termination_observer_->terminated());
+  ASSERT_TRUE(termination_subscription_);
 
   EXPECT_TRUE(IsKioskAppAutoLaunched(kTestNonKioskEnabledApp));
 
   ExtensionTestMessageListener listener("launchRequested");
 
-  content::WindowedNotificationObserver termination_waiter(
-      chrome::NOTIFICATION_APP_TERMINATING,
-      content::NotificationService::AllSources());
-
   // App launch should be canceled, and user session stopped.
-  termination_waiter.Wait();
+  base::RunLoop run_loop;
+  auto subscription =
+      browser_shutdown::AddAppTerminatingCallback(run_loop.QuitClosure());
+  run_loop.Run();
 
   EXPECT_FALSE(listener.was_satisfied());
   EXPECT_EQ(KioskAppLaunchError::Error::kNotKioskEnabled,
diff --git a/chrome/browser/ash/login/app_mode/test/kiosk_device_owned_browsertest.cc b/chrome/browser/ash/login/app_mode/test/kiosk_device_owned_browsertest.cc
index 2064efe..6beddb6 100644
--- a/chrome/browser/ash/login/app_mode/test/kiosk_device_owned_browsertest.cc
+++ b/chrome/browser/ash/login/app_mode/test/kiosk_device_owned_browsertest.cc
@@ -21,8 +21,8 @@
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/app_mode/kiosk_settings_navigation_throttle.h"
+#include "chrome/browser/lifetime/termination_notification.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
@@ -399,14 +399,14 @@
   BlockAppLaunch(true);
   splash_waiter.Wait();
 
+  base::RunLoop run_loop;
+  auto subscription =
+      browser_shutdown::AddAppTerminatingCallback(run_loop.QuitClosure());
   settings_helper_.SetBoolean(
       kAccountsPrefDeviceLocalAccountAutoLoginBailoutEnabled, true);
-  content::WindowedNotificationObserver signal(
-      chrome::NOTIFICATION_APP_TERMINATING,
-      content::NotificationService::AllSources());
   LoginDisplayHost::default_host()->HandleAccelerator(
       LoginAcceleratorAction::kAppLaunchBailout);
-  signal.Wait();
+  run_loop.Run();
   EXPECT_EQ(KioskAppLaunchError::Error::kUserCancel,
             KioskAppLaunchError::Get());
 }
diff --git a/chrome/browser/ash/login/device_family_link_allowed_policy_browsertest.cc b/chrome/browser/ash/login/device_family_link_allowed_policy_browsertest.cc
index 3003aec..262ae90a 100644
--- a/chrome/browser/ash/login/device_family_link_allowed_policy_browsertest.cc
+++ b/chrome/browser/ash/login/device_family_link_allowed_policy_browsertest.cc
@@ -18,8 +18,8 @@
 #include "chrome/browser/ash/login/test/user_policy_mixin.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/policy/core/device_policy_cros_browser_test.h"
-#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
+#include "chrome/browser/lifetime/termination_notification.h"
 #include "components/account_id/account_id.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
 #include "components/user_manager/user_manager.h"
@@ -164,14 +164,14 @@
   LoginFamilyLinkUser();
   SessionStateWaiter(session_manager::SessionState::ACTIVE).Wait();
 
-  content::WindowedNotificationObserver termination_waiter(
-      chrome::NOTIFICATION_APP_TERMINATING,
-      content::NotificationService::AllSources());
+  base::RunLoop termination_waiter;
+  auto subscription = browser_shutdown::AddAppTerminatingCallback(
+      termination_waiter.QuitClosure());
 
   // Family link off - Family Link user session should be terminated.
   SetDeviceFamilyLinkAccountsAllowedPolicy(false);
   EXPECT_TRUE(chrome::IsAttemptingShutdown());
-  termination_waiter.Wait();
+  termination_waiter.Run();
 }
 
 }  // namespace ash
diff --git a/chrome/browser/ash/login/password_change_browsertest.cc b/chrome/browser/ash/login/password_change_browsertest.cc
index dc274ea8..3e29885f 100644
--- a/chrome/browser/ash/login/password_change_browsertest.cc
+++ b/chrome/browser/ash/login/password_change_browsertest.cc
@@ -32,6 +32,7 @@
 #include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/lifetime/termination_notification.h"
 #include "chrome/browser/notifications/notification_display_service_tester.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/profiles/profile_manager_observer.h"
@@ -396,13 +397,14 @@
   ASSERT_EQ(notifications.size(), 1u);
 
   // Click on notification should trigger Chrome restart.
-  content::WindowedNotificationObserver exit_waiter(
-      chrome::NOTIFICATION_APP_TERMINATING,
-      content::NotificationService::AllSources());
+  base::RunLoop exit_waiter;
+  auto subscription =
+      browser_shutdown::AddAppTerminatingCallback(exit_waiter.QuitClosure());
+
   display_service_tester->SimulateClick(NotificationHandler::Type::TRANSIENT,
                                         notifications[0].id(), absl::nullopt,
                                         absl::nullopt);
-  exit_waiter.Wait();
+  exit_waiter.Run();
 }
 
 IN_PROC_BROWSER_TEST_F(PasswordChangeTokenCheck, Session) {
diff --git a/chrome/browser/ash/login/signin/oauth2_browsertest.cc b/chrome/browser/ash/login/signin/oauth2_browsertest.cc
index 1941ec2..18de010 100644
--- a/chrome/browser/ash/login/signin/oauth2_browsertest.cc
+++ b/chrome/browser/ash/login/signin/oauth2_browsertest.cc
@@ -32,9 +32,9 @@
 #include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/chrome_extension_test_notification_observer.h"
 #include "chrome/browser/extensions/chrome_test_extension_loader.h"
+#include "chrome/browser/lifetime/termination_notification.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/ui/browser.h"
@@ -56,7 +56,6 @@
 #include "components/user_manager/user_manager.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/notification_service.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
@@ -656,9 +655,9 @@
   SimulateNetworkOnline();
   WaitForGaiaPageLoad();
 
-  content::WindowedNotificationObserver termination_waiter(
-      chrome::NOTIFICATION_APP_TERMINATING,
-      content::NotificationService::AllSources());
+  base::RunLoop run_loop;
+  auto subscription =
+      browser_shutdown::AddAppTerminatingCallback(run_loop.QuitClosure());
 
   // Configure FakeGaia so that online auth succeeds but merge session fails.
   FakeGaia::MergeSessionParams params;
@@ -677,7 +676,7 @@
                                 kTestAccountServices);
 
   // User session should be terminated.
-  termination_waiter.Wait();
+  run_loop.Run();
 
   // Merge session should fail. Check after `termination_waiter` to ensure
   // user profile is initialized and there is an OAuth2LoginManage.
diff --git a/chrome/browser/ash/login/ui/login_display_host_common.cc b/chrome/browser/ash/login/ui/login_display_host_common.cc
index 0eee39a4..286aee5 100644
--- a/chrome/browser/ash/login/ui/login_display_host_common.cc
+++ b/chrome/browser/ash/login/ui/login_display_host_common.cc
@@ -32,7 +32,7 @@
 #include "chrome/browser/ash/system/device_disabling_manager.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
-#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/lifetime/termination_notification.h"
 #include "chrome/browser/ui/ash/wallpaper_controller_client_impl.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/webui/chromeos/diagnostics_dialog.h"
@@ -47,7 +47,6 @@
 #include "chrome/grit/generated_resources.h"
 #include "components/keep_alive_registry/keep_alive_types.h"
 #include "components/strings/grit/components_strings.h"
-#include "content/public/browser/notification_service.h"
 #include "extensions/common/features/feature_session_type.h"
 #include "ui/base/ime/ash/input_method_manager.h"
 #include "ui/base/ime/ash/input_method_util.h"
@@ -177,10 +176,11 @@
     : keep_alive_(KeepAliveOrigin::LOGIN_DISPLAY_HOST_WEBUI,
                   KeepAliveRestartOption::DISABLED),
       wizard_context_(std::make_unique<WizardContext>()) {
-  // Close the login screen on NOTIFICATION_APP_TERMINATING (for the case where
-  // shutdown occurs before login completes).
-  registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
-                 content::NotificationService::AllSources());
+  // Close the login screen on app termination (for the case where shutdown
+  // occurs before login completes).
+  app_terminating_subscription_ =
+      browser_shutdown::AddAppTerminatingCallback(base::BindOnce(
+          &LoginDisplayHostCommon::OnAppTerminating, base::Unretained(this)));
   BrowserList::AddObserver(this);
 }
 
@@ -598,19 +598,11 @@
     // Lock window has to be closed at this point so that a browser window
     // exists and the window can acquire input focus.
     OnBrowserCreated();
-    registrar_.RemoveAll();
+    app_terminating_subscription_ = {};
     BrowserList::RemoveObserver(this);
   }
 }
 
-void LoginDisplayHostCommon::Observe(
-    int type,
-    const content::NotificationSource& source,
-    const content::NotificationDetails& details) {
-  if (type == chrome::NOTIFICATION_APP_TERMINATING)
-    ShutdownDisplayHost();
-}
-
 WizardContext* LoginDisplayHostCommon::GetWizardContext() {
   return wizard_context_.get();
 }
@@ -674,8 +666,12 @@
 
 void LoginDisplayHostCommon::Cleanup() {
   SigninProfileHandler::Get()->ClearSigninProfile(base::DoNothing());
-  registrar_.RemoveAll();
+  app_terminating_subscription_ = {};
   BrowserList::RemoveObserver(this);
 }
 
+void LoginDisplayHostCommon::OnAppTerminating() {
+  ShutdownDisplayHost();
+}
+
 }  // namespace ash
diff --git a/chrome/browser/ash/login/ui/login_display_host_common.h b/chrome/browser/ash/login/ui/login_display_host_common.h
index d7684ee0..b4baa473 100644
--- a/chrome/browser/ash/login/ui/login_display_host_common.h
+++ b/chrome/browser/ash/login/ui/login_display_host_common.h
@@ -11,6 +11,7 @@
 
 #include "ash/public/cpp/login_accelerators.h"
 // TODO(https://crbug.com/1164001): use forward declaration.
+#include "base/callback_list.h"
 #include "chrome/browser/ash/login/app_mode/kiosk_launch_controller.h"
 #include "chrome/browser/ash/login/ui/kiosk_app_menu_controller.h"
 #include "chrome/browser/ash/login/ui/login_display_host.h"
@@ -19,8 +20,6 @@
 #include "chromeos/ash/components/oobe_quick_start/target_device_bootstrap_controller.h"
 #include "components/keep_alive_registry/scoped_keep_alive.h"
 #include "components/user_manager/user_type.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 class AccountId;
@@ -33,7 +32,6 @@
 // LoginDisplayHostMojo and LoginDisplayHostWebUI.
 class LoginDisplayHostCommon : public LoginDisplayHost,
                                public BrowserListObserver,
-                               public content::NotificationObserver,
                                public SigninUI {
  public:
   LoginDisplayHostCommon();
@@ -93,11 +91,6 @@
   // BrowserListObserver:
   void OnBrowserAdded(Browser* browser) override;
 
-  // content::NotificationObserver:
-  void Observe(int type,
-               const content::NotificationSource& source,
-               const content::NotificationDetails& details) override;
-
   WizardContext* GetWizardContext() override;
 
  protected:
@@ -127,8 +120,6 @@
   // Kiosk launch controller.
   std::unique_ptr<KioskLaunchController> kiosk_launch_controller_;
 
-  content::NotificationRegistrar registrar_;
-
  private:
   void Cleanup();
   // Set screen, from which WC flow will continue after attempt to show
@@ -139,6 +130,8 @@
       bool is_reset_allowed,
       absl::optional<tpm_firmware_update::Mode> tpm_firmware_update_mode);
 
+  void OnAppTerminating();
+
   // True if session start is in progress.
   bool session_starting_ = false;
 
@@ -168,6 +161,8 @@
   std::unique_ptr<ash::quick_start::TargetDeviceBootstrapController>
       bootstrap_controller_;
 
+  base::CallbackListSubscription app_terminating_subscription_;
+
   base::WeakPtrFactory<LoginDisplayHostCommon> weak_factory_{this};
 };
 
diff --git a/chrome/browser/ash/login/user_allowlist_policy_browsertest.cc b/chrome/browser/ash/login/user_allowlist_policy_browsertest.cc
index 38b83758..98e254c 100644
--- a/chrome/browser/ash/login/user_allowlist_policy_browsertest.cc
+++ b/chrome/browser/ash/login/user_allowlist_policy_browsertest.cc
@@ -12,8 +12,8 @@
 #include "chrome/browser/ash/login/test/session_manager_state_waiter.h"
 #include "chrome/browser/ash/login/test/user_policy_mixin.h"
 #include "chrome/browser/ash/policy/core/device_policy_cros_browser_test.h"
-#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
+#include "chrome/browser/lifetime/termination_notification.h"
 #include "components/account_id/account_id.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
 #include "components/user_manager/user_manager.h"
@@ -93,14 +93,14 @@
 
   LoginRegularUser();
 
-  content::WindowedNotificationObserver termination_waiter(
-      chrome::NOTIFICATION_APP_TERMINATING,
-      content::NotificationService::AllSources());
+  base::RunLoop run_loop;
+  auto subscription =
+      browser_shutdown::AddAppTerminatingCallback(run_loop.QuitClosure());
 
   // Only school users are allowed. Regular user session should be terminated.
   AllowUniqueUserToSignIn(kSchoolAllowlist);
   EXPECT_TRUE(chrome::IsAttemptingShutdown());
-  termination_waiter.Wait();
+  run_loop.Run();
 }
 
 }  // namespace ash
diff --git a/chrome/browser/ash/login/wizard_controller_browsertest.cc b/chrome/browser/ash/login/wizard_controller_browsertest.cc
index 46e1795..b368844 100644
--- a/chrome/browser/ash/login/wizard_controller_browsertest.cc
+++ b/chrome/browser/ash/login/wizard_controller_browsertest.cc
@@ -76,6 +76,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/lifetime/browser_shutdown.h"
+#include "chrome/browser/lifetime/termination_notification.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/webui/chromeos/login/consolidated_consent_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/error_screen_handler.h"
@@ -106,11 +107,9 @@
 #include "components/prefs/testing_pref_store.h"
 #include "components/session_manager/core/session_manager.h"
 #include "components/session_manager/session_manager_types.h"
-#include "content/public/browser/notification_registrar.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
-#include "content/public/test/mock_notification_observer.h"
 #include "content/public/test/test_utils.h"
 #include "net/test/spawned_test_server/spawned_test_server.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
@@ -3157,19 +3156,14 @@
                                    configuration_file);
   }
 
-  content::MockNotificationObserver observer_;
-  content::NotificationRegistrar registrar_;
-
   FakeRollbackNetworkConfig* network_config_;
 };
 
 IN_PROC_BROWSER_TEST_F(WizardControllerRollbackFlowTest,
                        RestartChromeAfterRollbackEnrollment) {
-  // TODO(crbug/1292955) |NOTIFICATION_APP_TERMINATING| is always sent, even if
-  // Chrome is not restarted properly because of the rollback.
-  registrar_.Add(&observer_, chrome::NOTIFICATION_APP_TERMINATING,
-                 content::NotificationService::AllSources());
-  EXPECT_CALL(observer_, Observe(chrome::NOTIFICATION_APP_TERMINATING, _, _));
+  base::RunLoop run_loop;
+  auto subscription =
+      browser_shutdown::AddAppTerminatingCallback(run_loop.QuitClosure());
 
   CheckCurrentScreen(WelcomeView::kScreenId);
   EXPECT_CALL(*mock_enrollment_screen_, ShowImpl()).Times(1);
@@ -3178,6 +3172,7 @@
       EnrollmentScreenView::kScreenId);
   CheckCurrentScreen(EnrollmentScreenView::kScreenId);
   mock_enrollment_screen_->ExitScreen(EnrollmentScreen::Result::COMPLETED);
+  run_loop.Run();
 }
 
 // TODO(crbug.com/1324410): Disabled due to flakiness.
diff --git a/chrome/browser/ash/policy/handlers/minimum_version_policy_handler_browsertest.cc b/chrome/browser/ash/policy/handlers/minimum_version_policy_handler_browsertest.cc
index 64f81d2..65fa842dd 100644
--- a/chrome/browser/ash/policy/handlers/minimum_version_policy_handler_browsertest.cc
+++ b/chrome/browser/ash/policy/handlers/minimum_version_policy_handler_browsertest.cc
@@ -39,8 +39,8 @@
 #include "chrome/browser/ash/policy/handlers/minimum_version_policy_test_helpers.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
-#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
+#include "chrome/browser/lifetime/termination_notification.h"
 #include "chrome/browser/notifications/notification_display_service_tester.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/browser/profiles/profile.h"
@@ -60,7 +60,6 @@
 #include "components/policy/proto/chrome_device_policy.pb.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/user_type.h"
-#include "content/public/browser/notification_service.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/test_utils.h"
@@ -273,17 +272,16 @@
   // Login the user into the session and mark as managed.
   LoginManagedUser();
 
-  // Create waiter to observe termination notification.
-  content::WindowedNotificationObserver termination_waiter(
-      chrome::NOTIFICATION_APP_TERMINATING,
-      content::NotificationService::AllSources());
+  base::RunLoop run_loop;
+  auto subscription =
+      browser_shutdown::AddAppTerminatingCallback(run_loop.QuitClosure());
 
   // Set new value for policy and check that user is logged out of the session.
   SetDevicePolicyAndWaitForSettingChange(
       CreateMinimumVersionSingleRequirementPolicyValue(
           kNewVersion, kNoWarning, kNoWarning,
           false /* unmanaged_user_restricted */));
-  termination_waiter.Wait();
+  run_loop.Run();
   EXPECT_TRUE(chrome::IsAttemptingShutdown());
 }
 
diff --git a/chrome/browser/ash/policy/reporting/extension_install_event_log_manager_wrapper.cc b/chrome/browser/ash/policy/reporting/extension_install_event_log_manager_wrapper.cc
index 461ef06..34bcd7a 100644
--- a/chrome/browser/ash/policy/reporting/extension_install_event_log_manager_wrapper.cc
+++ b/chrome/browser/ash/policy/reporting/extension_install_event_log_manager_wrapper.cc
@@ -9,12 +9,11 @@
 #include "base/location.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/ash/policy/core/user_cloud_policy_manager_ash.h"
-#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/lifetime/termination_notification.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
-#include "content/public/browser/notification_service.h"
 
 namespace policy {
 
@@ -39,13 +38,6 @@
                                 true);
 }
 
-void ExtensionInstallEventLogManagerWrapper::Observe(
-    int type,
-    const content::NotificationSource& source,
-    const content::NotificationDetails& details) {
-  base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
-}
-
 ExtensionInstallEventLogManagerWrapper::ExtensionInstallEventLogManagerWrapper(
     Profile* profile)
     : profile_(profile) {
@@ -57,8 +49,9 @@
       prefs::kExtensionInstallEventLoggingEnabled,
       base::BindRepeating(&ExtensionInstallEventLogManagerWrapper::EvaluatePref,
                           base::Unretained(this)));
-  notification_registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
-                              content::NotificationService::AllSources());
+  app_terminating_subscription_ = browser_shutdown::AddAppTerminatingCallback(
+      base::BindOnce(&ExtensionInstallEventLogManagerWrapper::OnAppTerminating,
+                     base::Unretained(this)));
 }
 
 void ExtensionInstallEventLogManagerWrapper::Init() {
@@ -89,4 +82,8 @@
   }
 }
 
+void ExtensionInstallEventLogManagerWrapper::OnAppTerminating() {
+  base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
+}
+
 }  // namespace policy
diff --git a/chrome/browser/ash/policy/reporting/extension_install_event_log_manager_wrapper.h b/chrome/browser/ash/policy/reporting/extension_install_event_log_manager_wrapper.h
index 805f40c2..c3539ca 100644
--- a/chrome/browser/ash/policy/reporting/extension_install_event_log_manager_wrapper.h
+++ b/chrome/browser/ash/policy/reporting/extension_install_event_log_manager_wrapper.h
@@ -7,19 +7,13 @@
 
 #include <memory>
 
+#include "base/callback_list.h"
 #include "chrome/browser/ash/policy/reporting/extension_install_event_log_manager.h"
 #include "components/prefs/pref_change_registrar.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
 
 class PrefRegistrySimple;
 class Profile;
 
-namespace content {
-class NotificationDetails;
-class NotificationSource;
-}  // namespace content
-
 namespace policy {
 
 // Observes the pref that indicates whether to log events for extension
@@ -28,12 +22,11 @@
 // |ExtensionInstallEventLogManager|, if any, and clears all data related to the
 // extension install event log. Ensures correct sequencing of I/O operations by
 // using one |InstallEventLogManager::LogTaskRunnerWrapper| for all accesses to
-// the log file. NotificationObserver is used to delete the ThreadTaskRunner
-// when the last browser window has been shut down.
-class ExtensionInstallEventLogManagerWrapper
-    : public content::NotificationObserver {
+// the log file. An AppTerminatingCallback is used to delete the
+// ThreadTaskRunner when the last browser window has been shut down.
+class ExtensionInstallEventLogManagerWrapper {
  public:
-  ~ExtensionInstallEventLogManagerWrapper() override;
+  virtual ~ExtensionInstallEventLogManagerWrapper();
 
   // Creates a new |ExtensionInstallEventLogManager| to handle extension install
   // event logging for |profile|. The object returned manages its own lifetime
@@ -44,11 +37,6 @@
 
   static void RegisterProfilePrefs(PrefRegistrySimple* registry);
 
-  // content::NotificationObserver:
-  void Observe(int type,
-               const content::NotificationSource& source,
-               const content::NotificationDetails& details) override;
-
  protected:
   explicit ExtensionInstallEventLogManagerWrapper(Profile* profile);
 
@@ -73,6 +61,8 @@
   // clears all data related to the extension install event log.
   void EvaluatePref();
 
+  void OnAppTerminating();
+
   // The profile whose extension install events are being logged.
   Profile* const profile_;
 
@@ -82,9 +72,7 @@
   // Pref change observer.
   PrefChangeRegistrar pref_change_registrar_;
 
-  // Notification observer, used to destroy the |log_manager_| when the user is
-  // logging out, giving it an opportunity to log the event.
-  content::NotificationRegistrar notification_registrar_;
+  base::CallbackListSubscription app_terminating_subscription_;
 };
 
 }  // namespace policy
diff --git a/chrome/browser/ash/web_applications/personalization_app/keyboard_backlight_color_metrics_provider.cc b/chrome/browser/ash/web_applications/personalization_app/keyboard_backlight_color_metrics_provider.cc
index 16d8278..9b82ab3 100644
--- a/chrome/browser/ash/web_applications/personalization_app/keyboard_backlight_color_metrics_provider.cc
+++ b/chrome/browser/ash/web_applications/personalization_app/keyboard_backlight_color_metrics_provider.cc
@@ -19,7 +19,7 @@
 
 void KeyboardBacklightColorMetricsProvider::ProvideCurrentSessionData(
     metrics::ChromeUserMetricsExtension* uma_proto_unused) {
-  if (!ash::features::IsRgbKeyboardEnabled() || !ash::Shell::Get() ||
+  if (!ash::features::IsRgbKeyboardEnabled() || !ash::Shell::HasInstance() ||
       !ash::Shell::Get()->rgb_keyboard_manager()->IsRgbKeyboardSupported()) {
     return;
   }
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
index d67b52f..ed3f198 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
@@ -940,7 +940,7 @@
     DCHECK_EQ(windows.size(), 1u);
     auto* root_window = windows[0];
     throughput_.push_back(
-        root_window->GetHost()->compositor()->GetAverageThroughput());
+        100 - root_window->GetHost()->compositor()->GetPercentDroppedFrames());
   }
 
   aura::WindowTracker root_window_tracker_;
@@ -5735,7 +5735,7 @@
 
   auto* root_window = ash::Shell::GetRootWindowForDisplayId(display_id);
   const uint32_t smoothness =
-      root_window->GetHost()->compositor()->GetAverageThroughput();
+      100 - root_window->GetHost()->compositor()->GetPercentDroppedFrames();
   return RespondNow(
       ArgumentList(api::autotest_private::GetDisplaySmoothness::Results::Create(
           smoothness)));
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index f45dcd6..5c1ddbf 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -339,6 +339,10 @@
     "api/settings_private/settings_private_event_router.h",
     "api/settings_private/settings_private_event_router_factory.cc",
     "api/settings_private/settings_private_event_router_factory.h",
+    "api/side_panel/side_panel_api.cc",
+    "api/side_panel/side_panel_api.h",
+    "api/side_panel/side_panel_service.cc",
+    "api/side_panel/side_panel_service.h",
     "api/storage/managed_value_store_cache.cc",
     "api/storage/managed_value_store_cache.h",
     "api/storage/policy_value_store.cc",
diff --git a/chrome/browser/extensions/api/enterprise_reporting_private/conversion_utils.cc b/chrome/browser/extensions/api/enterprise_reporting_private/conversion_utils.cc
index 593afc8..8f36e71 100644
--- a/chrome/browser/extensions/api/enterprise_reporting_private/conversion_utils.cc
+++ b/chrome/browser/extensions/api/enterprise_reporting_private/conversion_utils.cc
@@ -4,12 +4,23 @@
 
 #include "chrome/browser/extensions/api/enterprise_reporting_private/conversion_utils.h"
 
-#if BUILDFLAG(IS_WIN)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 
+#include <memory>
+#include <utility>
+
+#include "base/files/file_path.h"
 #include "components/device_signals/core/browser/signals_types.h"
+#include "components/device_signals/core/common/common_types.h"
+#include "extensions/browser/extension_function.h"
+
+#if BUILDFLAG(IS_WIN)
+#include "base/strings/sys_string_conversions.h"
 #include "components/device_signals/core/common/win/win_types.h"
+#endif  // BUILDFLAG(IS_WIN)
 
 using SignalCollectionError = device_signals::SignalCollectionError;
+using PresenceValue = device_signals::PresenceValue;
 
 namespace extensions {
 
@@ -37,8 +48,103 @@
   return absl::nullopt;
 }
 
+api::enterprise_reporting_private::PresenceValue ConvertPresenceValue(
+    PresenceValue presence) {
+  switch (presence) {
+    case PresenceValue::kUnspecified:
+      return api::enterprise_reporting_private::PRESENCE_VALUE_UNSPECIFIED;
+    case PresenceValue::kAccessDenied:
+      return api::enterprise_reporting_private::PRESENCE_VALUE_ACCESS_DENIED;
+    case PresenceValue::kNotFound:
+      return api::enterprise_reporting_private::PRESENCE_VALUE_NOT_FOUND;
+    case PresenceValue::kFound:
+      return api::enterprise_reporting_private::PRESENCE_VALUE_FOUND;
+  }
+}
+
 }  // namespace
 
+std::vector<device_signals::GetFileSystemInfoOptions>
+ConvertFileSystemInfoOptions(
+    const std::vector<
+        api::enterprise_reporting_private::GetFileSystemInfoOptions>&
+        api_options) {
+  std::vector<device_signals::GetFileSystemInfoOptions> converted_options;
+  for (const auto& api_options_param : api_options) {
+    device_signals::GetFileSystemInfoOptions converted_param;
+    converted_param.file_path =
+        base::FilePath::FromUTF8Unsafe(api_options_param.path);
+    converted_param.compute_sha256 = api_options_param.compute_sha256;
+    converted_param.compute_is_executable =
+        api_options_param.compute_is_executable;
+    converted_options.push_back(std::move(converted_param));
+  }
+  return converted_options;
+}
+
+absl::optional<ParsedSignalsError> ConvertFileSystemInfoResponse(
+    const device_signals::SignalsAggregationResponse& response,
+    std::vector<api::enterprise_reporting_private::GetFileSystemInfoResponse>*
+        arg_list) {
+  auto error = TryParseError(response, response.file_system_info_response);
+  if (error) {
+    return error.value();
+  }
+
+  std::vector<api::enterprise_reporting_private::GetFileSystemInfoResponse>
+      api_responses;
+  const auto& file_system_signal_values =
+      response.file_system_info_response.value();
+  for (const auto& file_system_item :
+       file_system_signal_values.file_system_items) {
+    api::enterprise_reporting_private::GetFileSystemInfoResponse response;
+    response.path = file_system_item.file_path.AsUTF8Unsafe();
+    response.presence = ConvertPresenceValue(file_system_item.presence);
+
+    if (file_system_item.sha256_hash) {
+      response.sha256_hash =
+          std::make_unique<std::string>(file_system_item.sha256_hash.value());
+    }
+
+    if (file_system_item.executable_metadata) {
+      response.is_executable = std::make_unique<bool>(
+          file_system_item.executable_metadata->is_executable);
+
+      if (response.is_executable) {
+        const auto& executable_metadata =
+            file_system_item.executable_metadata.value();
+
+        if (executable_metadata.is_running) {
+          response.is_running =
+              std::make_unique<bool>(executable_metadata.is_running.value());
+        }
+
+        if (executable_metadata.public_key_sha256) {
+          response.public_key_sha256 = std::make_unique<std::string>(
+              executable_metadata.public_key_sha256.value());
+        }
+
+        if (executable_metadata.product_name) {
+          response.product_name = std::make_unique<std::string>(
+              executable_metadata.product_name.value());
+        }
+
+        if (executable_metadata.version) {
+          response.version = std::make_unique<std::string>(
+              executable_metadata.version.value());
+        }
+      }
+    }
+
+    api_responses.push_back(std::move(response));
+  }
+
+  *arg_list = std::move(api_responses);
+  return absl::nullopt;
+}
+
+#if BUILDFLAG(IS_WIN)
+
 absl::optional<ParsedSignalsError> ConvertAvProductsResponse(
     const device_signals::SignalsAggregationResponse& response,
     std::vector<api::enterprise_reporting_private::AntiVirusSignal>* arg_list) {
@@ -102,6 +208,8 @@
   return absl::nullopt;
 }
 
+#endif  // BUILDFLAG(IS_WIN)
+
 }  // namespace extensions
 
-#endif  // BUILDFLAG(IS_WIN)
+#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
diff --git a/chrome/browser/extensions/api/enterprise_reporting_private/conversion_utils.h b/chrome/browser/extensions/api/enterprise_reporting_private/conversion_utils.h
index 55a5b68a..764aa73 100644
--- a/chrome/browser/extensions/api/enterprise_reporting_private/conversion_utils.h
+++ b/chrome/browser/extensions/api/enterprise_reporting_private/conversion_utils.h
@@ -7,7 +7,7 @@
 
 #include "build/build_config.h"
 
-#if BUILDFLAG(IS_WIN)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 
 #include <vector>
 
@@ -15,6 +15,7 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace device_signals {
+struct GetFileSystemInfoOptions;
 struct SignalsAggregationResponse;
 enum class SignalCollectionError;
 }  // namespace device_signals
@@ -26,6 +27,24 @@
   bool is_top_level_error;
 };
 
+// Converts GetFileSystemInfoOptions from the Extension API struct definition,
+// `api_options`, to the device_signals component definition.
+std::vector<device_signals::GetFileSystemInfoOptions>
+ConvertFileSystemInfoOptions(
+    const std::vector<
+        api::enterprise_reporting_private::GetFileSystemInfoOptions>&
+        api_options);
+
+// Parses and converts the File System info signal values from `response` into
+// `arg_list`. If any error occurred during signal collection, it will be
+// returned and `arg_list` will remain unchanged.
+absl::optional<ParsedSignalsError> ConvertFileSystemInfoResponse(
+    const device_signals::SignalsAggregationResponse& response,
+    std::vector<api::enterprise_reporting_private::GetFileSystemInfoResponse>*
+        arg_list);
+
+#if BUILDFLAG(IS_WIN)
+
 // Parses and converts the Antivirus signal values from `response` into
 // `arg_list`. If any error occurred during signal collection, it will be
 // returned and `arg_list` will remain unchanged.
@@ -40,8 +59,10 @@
     const device_signals::SignalsAggregationResponse& response,
     std::vector<api::enterprise_reporting_private::HotfixSignal>* arg_list);
 
+#endif  // BUILDFLAG(IS_WIN)
+
 }  // namespace extensions
 
-#endif  // BUILDFLAG(IS_WIN)
+#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 
 #endif  // CHROME_BROWSER_EXTENSIONS_API_ENTERPRISE_REPORTING_PRIVATE_CONVERSION_UTILS_H_
diff --git a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc
index b51da9e..ae82bcb6 100644
--- a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc
+++ b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc
@@ -31,7 +31,8 @@
 #include "components/reporting/util/statusor.h"
 #endif
 
-#if BUILDFLAG(IS_WIN)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
+#include "base/strings/string_util.h"
 #include "chrome/browser/enterprise/signals/signals_aggregator_factory.h"
 #include "chrome/browser/extensions/api/enterprise_reporting_private/conversion_utils.h"
 #include "components/device_signals/core/browser/metrics_utils.h"
@@ -40,7 +41,7 @@
 #include "components/device_signals/core/browser/user_context.h"
 #include "components/device_signals/core/common/signals_features.h"  // nogncheck
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#endif  // BUILDFLAG(IS_WIN)
+#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 
 #include "components/content_settings/core/common/pref_names.h"
 #include "components/enterprise/browser/controller/browser_dm_token_storage.h"
@@ -154,7 +155,7 @@
   return info;
 }
 
-#if BUILDFLAG(IS_WIN)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 
 device_signals::SignalsAggregationRequest CreateAggregationRequest(
     const std::string& user_id,
@@ -182,7 +183,11 @@
   signals_aggregator->GetSignals(std::move(request), std::move(callback));
 }
 
-#endif  // BUILDFLAG(IS_WIN)
+bool CanReturnResponse(content::BrowserContext* browser_context) {
+  return browser_context && !browser_context->ShutdownStarted();
+}
+
+#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 
 }  // namespace
 
@@ -650,6 +655,79 @@
 }
 #endif
 
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
+
+// getFileSystemInfo
+
+EnterpriseReportingPrivateGetFileSystemInfoFunction::
+    EnterpriseReportingPrivateGetFileSystemInfoFunction() = default;
+EnterpriseReportingPrivateGetFileSystemInfoFunction::
+    ~EnterpriseReportingPrivateGetFileSystemInfoFunction() = default;
+
+ExtensionFunction::ResponseAction
+EnterpriseReportingPrivateGetFileSystemInfoFunction::Run() {
+  if (!IsNewFunctionEnabled(
+          enterprise_signals::features::NewEvFunction::kFileSystemInfo)) {
+    return RespondNow(Error(device_signals::ErrorToString(
+        device_signals::SignalCollectionError::kUnsupported)));
+  }
+
+  std::unique_ptr<api::enterprise_reporting_private::GetFileSystemInfo::Params>
+      params(
+          api::enterprise_reporting_private::GetFileSystemInfo::Params::Create(
+              args()));
+  EXTENSION_FUNCTION_VALIDATE(params.get());
+
+  // Verify that all file paths are UTF8.
+  bool paths_are_all_utf8 = true;
+  for (const auto& api_options_param : params->request.options) {
+    if (!base::IsStringUTF8(api_options_param.path)) {
+      paths_are_all_utf8 = false;
+      break;
+    }
+  }
+  EXTENSION_FUNCTION_VALIDATE(paths_are_all_utf8);
+
+  auto aggregation_request = CreateAggregationRequest(
+      params->request.user_context.user_id, signal_name());
+  aggregation_request.file_system_signal_parameters =
+      ConvertFileSystemInfoOptions(params->request.options);
+
+  StartSignalCollection(
+      aggregation_request, browser_context(),
+      base::BindOnce(&EnterpriseReportingPrivateGetFileSystemInfoFunction::
+                         OnSignalRetrieved,
+                     this));
+
+  return RespondLater();
+}
+
+void EnterpriseReportingPrivateGetFileSystemInfoFunction::OnSignalRetrieved(
+    device_signals::SignalsAggregationResponse response) {
+  if (!CanReturnResponse(browser_context())) {
+    // The browser is no longer accepting responses, so just bail.
+    return;
+  }
+
+  std::vector<api::enterprise_reporting_private::GetFileSystemInfoResponse>
+      arg_list;
+  auto parsed_error = ConvertFileSystemInfoResponse(response, &arg_list);
+
+  if (parsed_error) {
+    LogSignalCollectionFailed(signal_name(), parsed_error->error,
+                              parsed_error->is_top_level_error);
+    Respond(Error(device_signals::ErrorToString(parsed_error->error)));
+    return;
+  }
+
+  LogSignalCollectionSucceeded(signal_name(), arg_list.size());
+  Respond(ArgumentList(
+      api::enterprise_reporting_private::GetFileSystemInfo::Results::Create(
+          arg_list)));
+}
+
+#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
+
 #if BUILDFLAG(IS_WIN)
 
 // getAvInfo
@@ -683,6 +761,11 @@
 
 void EnterpriseReportingPrivateGetAvInfoFunction::OnSignalRetrieved(
     device_signals::SignalsAggregationResponse response) {
+  if (!CanReturnResponse(browser_context())) {
+    // The browser is no longer accepting responses, so just bail.
+    return;
+  }
+
   std::vector<api::enterprise_reporting_private::AntiVirusSignal> arg_list;
   auto parsed_error = ConvertAvProductsResponse(response, &arg_list);
 
@@ -730,6 +813,11 @@
 
 void EnterpriseReportingPrivateGetHotfixesFunction::OnSignalRetrieved(
     device_signals::SignalsAggregationResponse response) {
+  if (!CanReturnResponse(browser_context())) {
+    // The browser is no longer accepting responses, so just bail.
+    return;
+  }
+
   std::vector<api::enterprise_reporting_private::HotfixSignal> arg_list;
   auto parsed_error = ConvertHotfixesResponse(response, &arg_list);
 
diff --git a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.h b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.h
index 136d53b..8afa2b5c 100644
--- a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.h
+++ b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.h
@@ -19,7 +19,7 @@
 #include "components/reporting/proto/synced/record.pb.h"
 #include "components/reporting/proto/synced/record_constants.pb.h"
 #include "components/reporting/util/statusor.h"
-#elif BUILDFLAG(IS_WIN)
+#elif BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 #include "components/device_signals/core/browser/signals_types.h"
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
@@ -264,6 +264,35 @@
 
 #endif
 
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
+
+class EnterpriseReportingPrivateGetFileSystemInfoFunction
+    : public ExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("enterprise.reportingPrivate.getFileSystemInfo",
+                             ENTERPRISEREPORTINGPRIVATE_GETFILESYSTEMINFO)
+
+  EnterpriseReportingPrivateGetFileSystemInfoFunction();
+  EnterpriseReportingPrivateGetFileSystemInfoFunction(
+      const EnterpriseReportingPrivateGetFileSystemInfoFunction&) = delete;
+  EnterpriseReportingPrivateGetFileSystemInfoFunction& operator=(
+      const EnterpriseReportingPrivateGetFileSystemInfoFunction&) = delete;
+
+ private:
+  ~EnterpriseReportingPrivateGetFileSystemInfoFunction() override;
+
+  // ExtensionFunction
+  ExtensionFunction::ResponseAction Run() override;
+
+  void OnSignalRetrieved(device_signals::SignalsAggregationResponse response);
+
+  device_signals::SignalName signal_name() {
+    return device_signals::SignalName::kFileSystemInfo;
+  }
+};
+
+#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
+
 #if BUILDFLAG(IS_WIN)
 
 class EnterpriseReportingPrivateGetAvInfoFunction : public ExtensionFunction {
diff --git a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
index a371300..80ba9ac 100644
--- a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
+++ b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
@@ -50,15 +50,20 @@
 #include <windows.h>
 #include <wrl/client.h>
 
+#include "base/test/test_reg_util_win.h"
+#endif  // BUILDFLAG(IS_WIN)
+
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
-#include "base/test/test_reg_util_win.h"
 #include "chrome/browser/enterprise/signals/signals_aggregator_factory.h"
 #include "components/device_signals/core/browser/mock_signals_aggregator.h"  // nogncheck
 #include "components/device_signals/core/browser/signals_aggregator.h"  // nogncheck
+#include "components/device_signals/core/browser/signals_types.h"  // nogncheck
+#include "components/device_signals/core/common/common_types.h"    // nogncheck
 #include "components/device_signals/core/common/signals_constants.h"  // nogncheck
 #include "components/device_signals/core/common/signals_features.h"  // nogncheck
-#endif
+#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 #include "base/nix/xdg_util.h"
@@ -1344,6 +1349,160 @@
   base::HistogramTester histogram_tester_;
 };
 
+// Tests for API enterprise.reportingPrivate.getFileSystemInfo
+class EnterpriseReportingPrivateGetFileSystemInfoTest
+    : public UserContextGatedTest {
+ protected:
+  void SetUp() override {
+    UserContextGatedTest::SetUp();
+
+    SetFeatureFlag();
+
+    function_ = base::MakeRefCounted<
+        EnterpriseReportingPrivateGetFileSystemInfoFunction>();
+  }
+
+  device_signals::SignalName signal_name() {
+    return device_signals::SignalName::kFileSystemInfo;
+  }
+
+  enterprise_reporting_private::GetFileSystemInfoOptions
+  GetFakeFileSystemOptionsParam() const {
+    enterprise_reporting_private::GetFileSystemInfoOptions api_param;
+    api_param.path = "some file path";
+    api_param.compute_sha256 = true;
+    return api_param;
+  }
+
+  std::string GetFakeRequest() const {
+    enterprise_reporting_private::GetFileSystemInfoRequest request;
+    request.user_context = GetFakeUserContext();
+    request.options.push_back(GetFakeFileSystemOptionsParam());
+    base::ListValue params;
+    params.Append(base::Value::FromUniquePtrValue(request.ToValue()));
+    std::string json_value;
+    base::JSONWriter::Write(params, &json_value);
+    return json_value;
+  }
+
+  scoped_refptr<extensions::EnterpriseReportingPrivateGetFileSystemInfoFunction>
+      function_;
+};
+
+TEST_F(EnterpriseReportingPrivateGetFileSystemInfoTest, Success) {
+  device_signals::FileSystemItem fake_file_item;
+  fake_file_item.file_path = base::FilePath();
+  fake_file_item.presence = device_signals::PresenceValue::kFound;
+  fake_file_item.sha256_hash = "some hashed value";
+
+  device_signals::FileSystemInfoResponse signal_response;
+  signal_response.file_system_items.push_back(fake_file_item);
+
+  device_signals::SignalsAggregationResponse expected_response;
+  expected_response.file_system_info_response = signal_response;
+
+  SetFakeResponse(expected_response);
+
+  auto response = api_test_utils::RunFunctionAndReturnSingleResult(
+      function_.get(), GetFakeRequest(), profile());
+
+  EXPECT_EQ(function_->GetError(), kNoError);
+
+  ASSERT_TRUE(response);
+  ASSERT_TRUE(response->is_list());
+  const base::Value::List& list_value = response->GetList();
+  ASSERT_EQ(list_value.size(), signal_response.file_system_items.size());
+
+  const base::Value& file_system_value = list_value.front();
+  auto parsed_file_system_signal =
+      enterprise_reporting_private::GetFileSystemInfoResponse::FromValue(
+          file_system_value);
+  ASSERT_TRUE(parsed_file_system_signal);
+  EXPECT_EQ(parsed_file_system_signal->path,
+            fake_file_item.file_path.AsUTF8Unsafe());
+  EXPECT_EQ(parsed_file_system_signal->presence,
+            enterprise_reporting_private::PRESENCE_VALUE_FOUND);
+  EXPECT_EQ(*parsed_file_system_signal->sha256_hash.get(),
+            fake_file_item.sha256_hash.value());
+
+  histogram_tester_.ExpectUniqueSample(
+      "Enterprise.DeviceSignals.Collection.Success", signal_name(), 1);
+  histogram_tester_.ExpectUniqueSample(
+      "Enterprise.DeviceSignals.Collection.Success.FileSystemInfo.Items",
+      /*number_of_items=*/1,
+      /*number_of_occurrences=*/1);
+}
+
+TEST_F(EnterpriseReportingPrivateGetFileSystemInfoTest, TopLevelError) {
+  device_signals::SignalCollectionError expected_error =
+      device_signals::SignalCollectionError::kConsentRequired;
+
+  device_signals::SignalsAggregationResponse expected_response;
+  expected_response.top_level_error = expected_error;
+  SetFakeResponse(expected_response);
+
+  auto error = api_test_utils::RunFunctionAndReturnError(
+      function_.get(), GetFakeRequest(), profile());
+
+  EXPECT_EQ(error, function_->GetError());
+  EXPECT_EQ(error, device_signals::ErrorToString(expected_error));
+
+  histogram_tester_.ExpectUniqueSample(
+      "Enterprise.DeviceSignals.Collection.Failure", signal_name(), 1);
+  histogram_tester_.ExpectUniqueSample(
+      "Enterprise.DeviceSignals.Collection.Failure.FileSystemInfo."
+      "TopLevelError",
+      /*error=*/expected_error,
+      /*number_of_occurrences=*/1);
+}
+
+TEST_F(EnterpriseReportingPrivateGetFileSystemInfoTest, CollectionError) {
+  device_signals::SignalCollectionError expected_error =
+      device_signals::SignalCollectionError::kMissingSystemService;
+
+  device_signals::FileSystemInfoResponse signal_response;
+  signal_response.collection_error = expected_error;
+
+  device_signals::SignalsAggregationResponse expected_response;
+  expected_response.file_system_info_response = signal_response;
+  SetFakeResponse(expected_response);
+
+  auto error = api_test_utils::RunFunctionAndReturnError(
+      function_.get(), GetFakeRequest(), profile());
+
+  EXPECT_EQ(error, function_->GetError());
+  EXPECT_EQ(error, device_signals::ErrorToString(expected_error));
+
+  histogram_tester_.ExpectUniqueSample(
+      "Enterprise.DeviceSignals.Collection.Failure", signal_name(), 1);
+  histogram_tester_.ExpectUniqueSample(
+      "Enterprise.DeviceSignals.Collection.Failure.FileSystemInfo."
+      "CollectionLevelError",
+      /*error=*/expected_error,
+      /*number_of_occurrences=*/1);
+}
+
+class EnterpriseReportingPrivateGetFileSystemInfoDisabledTest
+    : public EnterpriseReportingPrivateGetFileSystemInfoTest {
+ protected:
+  // Overwrite this function to disable the feature flag for tests using this
+  // specific fixture.
+  void SetFeatureFlag() override {
+    scoped_features_.InitAndEnableFeatureWithParameters(
+        enterprise_signals::features::kNewEvSignalsEnabled,
+        {{"DisableFileSystemInfo", "true"}});
+  }
+};
+
+TEST_F(EnterpriseReportingPrivateGetFileSystemInfoDisabledTest,
+       FlagDisabled_Test) {
+  auto error = api_test_utils::RunFunctionAndReturnError(
+      function_.get(), GetFakeRequest(), profile());
+  EXPECT_EQ(error, function_->GetError());
+  EXPECT_EQ(error, device_signals::ErrorToString(
+                       device_signals::SignalCollectionError::kUnsupported));
+}
+
 // Tests for API enterprise.reportingPrivate.getAvInfo
 class EnterpriseReportingPrivateGetAvInfoTest : public UserContextGatedTest {
  protected:
diff --git a/chrome/browser/extensions/api/side_panel/side_panel_api.cc b/chrome/browser/extensions/api/side_panel/side_panel_api.cc
new file mode 100644
index 0000000..055f84c1
--- /dev/null
+++ b/chrome/browser/extensions/api/side_panel/side_panel_api.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/extensions/api/side_panel/side_panel_api.h"
+
+#include <memory>
+
+#include "base/strings/stringprintf.h"
+#include "base/values.h"
+#include "chrome/browser/extensions/api/side_panel/side_panel_service.h"
+#include "chrome/common/extensions/api/side_panel.h"
+#include "extensions/common/extension_features.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace extensions {
+namespace {
+
+bool IsSidePanelApiAvailable() {
+  return base::FeatureList::IsEnabled(
+      extensions_features::kExtensionSidePanelIntegration);
+}
+
+}  // namespace
+
+SidePanelApiFunction::SidePanelApiFunction() = default;
+SidePanelApiFunction::~SidePanelApiFunction() = default;
+SidePanelService* SidePanelApiFunction::GetService() {
+  return extensions::SidePanelService::Get(browser_context());
+}
+
+ExtensionFunction::ResponseAction SidePanelApiFunction::Run() {
+  if (!IsSidePanelApiAvailable())
+    return RespondNow(Error("API Unavailable"));
+  return RunFunction();
+}
+
+ExtensionFunction::ResponseAction SidePanelGetOptionsFunction::RunFunction() {
+  std::unique_ptr<api::side_panel::GetOptions::Params> params(
+      api::side_panel::GetOptions::Params::Create(args()));
+  EXTENSION_FUNCTION_VALIDATE(params);
+  auto tab_id = params->options.tab_id
+                    ? absl::optional<int>(*(params->options.tab_id))
+                    : absl::nullopt;
+  const api::side_panel::PanelOptions& options =
+      GetService()->GetOptions(*extension(), tab_id);
+  return RespondNow(OneArgument(std::move(*options.ToValue())));
+}
+
+ExtensionFunction::ResponseAction SidePanelSetOptionsFunction::RunFunction() {
+  std::unique_ptr<api::side_panel::SetOptions::Params> params(
+      api::side_panel::SetOptions::Params::Create(args()));
+  EXTENSION_FUNCTION_VALIDATE(params);
+  // TODO(crbug.com/1328645): Validate the relative extension path exists.
+  GetService()->SetOptions(*extension(), std::move(params->options));
+  return RespondNow(NoArguments());
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/api/side_panel/side_panel_api.h b/chrome/browser/extensions/api/side_panel/side_panel_api.h
new file mode 100644
index 0000000..604ac15
--- /dev/null
+++ b/chrome/browser/extensions/api/side_panel/side_panel_api.h
@@ -0,0 +1,53 @@
+// 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_EXTENSIONS_API_SIDE_PANEL_SIDE_PANEL_API_H_
+#define CHROME_BROWSER_EXTENSIONS_API_SIDE_PANEL_SIDE_PANEL_API_H_
+
+#include "extensions/browser/extension_function.h"
+#include "extensions/browser/extension_function_histogram_value.h"
+
+namespace extensions {
+
+class SidePanelService;
+
+class SidePanelApiFunction : public ExtensionFunction {
+ protected:
+  SidePanelApiFunction();
+  ~SidePanelApiFunction() override;
+  ResponseAction Run() override;
+
+  virtual ResponseAction RunFunction() = 0;
+  SidePanelService* GetService();
+};
+
+class SidePanelGetOptionsFunction : public SidePanelApiFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("sidePanel.getOptions", SIDEPANEL_GETOPTIONS)
+  SidePanelGetOptionsFunction() = default;
+  SidePanelGetOptionsFunction(const SidePanelGetOptionsFunction&) = delete;
+  SidePanelGetOptionsFunction& operator=(const SidePanelGetOptionsFunction&) =
+      delete;
+
+ private:
+  ~SidePanelGetOptionsFunction() override = default;
+  ResponseAction RunFunction() override;
+};
+
+class SidePanelSetOptionsFunction : public SidePanelApiFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("sidePanel.setOptions", SIDEPANEL_SETOPTIONS)
+  SidePanelSetOptionsFunction() = default;
+  SidePanelSetOptionsFunction(const SidePanelSetOptionsFunction&) = delete;
+  SidePanelSetOptionsFunction& operator=(const SidePanelSetOptionsFunction&) =
+      delete;
+
+ private:
+  ~SidePanelSetOptionsFunction() override = default;
+  ResponseAction RunFunction() override;
+};
+
+}  // namespace extensions
+
+#endif  // CHROME_BROWSER_EXTENSIONS_API_SIDE_PANEL_SIDE_PANEL_API_H_
diff --git a/chrome/browser/extensions/api/side_panel/side_panel_apitest.cc b/chrome/browser/extensions/api/side_panel/side_panel_apitest.cc
new file mode 100644
index 0000000..8331fbc5
--- /dev/null
+++ b/chrome/browser/extensions/api/side_panel/side_panel_apitest.cc
@@ -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.
+
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/extensions/extension_apitest.h"
+#include "components/version_info/channel.h"
+#include "content/public/test/browser_test.h"
+#include "extensions/common/extension_features.h"
+
+namespace extensions {
+
+class SidePanelApiTest : public ExtensionApiTest {
+ public:
+  SidePanelApiTest() {
+    feature_list_.InitAndEnableFeature(
+        extensions_features::kExtensionSidePanelIntegration);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+  ScopedCurrentChannel current_channel_{version_info::Channel::CANARY};
+};
+
+// Verify normal chrome.sidePanel functionality.
+IN_PROC_BROWSER_TEST_F(SidePanelApiTest, Extension) {
+  ASSERT_TRUE(StartEmbeddedTestServer());
+  ASSERT_TRUE(RunExtensionTest("side_panel/extension")) << message_;
+}
+
+// Verify chrome.sidePanel behavior without permissions.
+IN_PROC_BROWSER_TEST_F(SidePanelApiTest, PermissionMissing) {
+  ASSERT_TRUE(RunExtensionTest("side_panel/permission_missing")) << message_;
+}
+
+// Verify chrome.sidePanel.get behavior without side_panel manifest key.
+IN_PROC_BROWSER_TEST_F(SidePanelApiTest, MissingManifestKey) {
+  ASSERT_TRUE(RunExtensionTest("side_panel/missing_manifest_key")) << message_;
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/api/side_panel/side_panel_service.cc b/chrome/browser/extensions/api/side_panel/side_panel_service.cc
new file mode 100644
index 0000000..d97c121
--- /dev/null
+++ b/chrome/browser/extensions/api/side_panel/side_panel_service.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 "chrome/browser/extensions/api/side_panel/side_panel_service.h"
+
+#include <cstddef>
+#include <memory>
+
+#include "base/no_destructor.h"
+#include "chrome/common/extensions/api/side_panel.h"
+#include "chrome/common/extensions/api/side_panel/side_panel_info.h"
+#include "components/sessions/core/session_id.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace extensions {
+
+namespace {
+
+api::side_panel::PanelOptions GetPanelOptionsFromManifest(
+    const Extension& extension) {
+  auto path = SidePanelInfo::GetDefaultPath(&extension);
+  api::side_panel::PanelOptions options;
+  if (!path.empty()) {
+    options.path = std::make_unique<std::string>(std::string(path));
+    options.enabled = std::make_unique<bool>(true);
+  }
+  return options;
+}
+
+// TODO(crbug.com/1332599): Add a Clone() method for generated types.
+api::side_panel::PanelOptions CloneOptions(
+    const api::side_panel::PanelOptions& options) {
+  auto clone = api::side_panel::PanelOptions::FromValue(
+      base::Value(std::move(options.ToValue()->GetDict())));
+  return clone ? std::move(*clone) : api::side_panel::PanelOptions();
+}
+
+}  // namespace
+
+SidePanelService::~SidePanelService() = default;
+
+SidePanelService::SidePanelService(content::BrowserContext* context)
+    : browser_context_(context) {}
+
+api::side_panel::PanelOptions SidePanelService::GetOptions(
+    const Extension& extension,
+    absl::optional<TabId> id) {
+  auto extension_panel_options = panels_.find(extension.id());
+
+  // Get default path from manifest if nothing was stored in this service for
+  // the calling extension.
+  if (extension_panel_options == panels_.end()) {
+    return GetPanelOptionsFromManifest(extension);
+  }
+
+  TabId default_tab_id = SessionID::InvalidValue().id();
+  TabId tab_id = id.has_value() ? id.value() : default_tab_id;
+  TabPanelOptions& tab_panel_options = extension_panel_options->second;
+
+  // The specific `tab_id` may have already been saved.
+  if (tab_id != default_tab_id) {
+    auto specific_tab_options = tab_panel_options.find(tab_id);
+    if (specific_tab_options != tab_panel_options.end())
+      return CloneOptions(specific_tab_options->second);
+  }
+
+  // Fall back to the default tab if no tab ID was specified or entries for the
+  // specific tab weren't found.
+  auto default_options = tab_panel_options.find(default_tab_id);
+  if (default_options != tab_panel_options.end()) {
+    auto options = CloneOptions(default_options->second);
+    return options;
+  }
+
+  // Fall back to the manifest-specified options as a last resort.
+  return GetPanelOptionsFromManifest(extension);
+}
+
+// Upsert to merge `panels_[extension_id][tab_id]` with `set_options`.
+void SidePanelService::SetOptions(const Extension& extension,
+                                  api::side_panel::PanelOptions options) {
+  TabId tab_id = SessionID::InvalidValue().id();
+  if (options.tab_id)
+    tab_id = *options.tab_id;
+  TabPanelOptions& extension_panel_options = panels_[extension.id()];
+  auto it = extension_panel_options.find(tab_id);
+  if (it == extension_panel_options.end()) {
+    extension_panel_options[tab_id] = std::move(options);
+  } else {
+    auto& existing_options = it->second;
+    if (options.path)
+      existing_options.path = std::move(options.path);
+    if (options.enabled)
+      existing_options.enabled = std::move(options.enabled);
+  }
+}
+
+// static
+BrowserContextKeyedAPIFactory<SidePanelService>*
+SidePanelService::GetFactoryInstance() {
+  static base::NoDestructor<BrowserContextKeyedAPIFactory<SidePanelService>>
+      instance;
+  return instance.get();
+}
+
+// static
+SidePanelService* SidePanelService::Get(content::BrowserContext* context) {
+  return BrowserContextKeyedAPIFactory<SidePanelService>::Get(context);
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/api/side_panel/side_panel_service.h b/chrome/browser/extensions/api/side_panel/side_panel_service.h
new file mode 100644
index 0000000..bb9175f
--- /dev/null
+++ b/chrome/browser/extensions/api/side_panel/side_panel_service.h
@@ -0,0 +1,63 @@
+// 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_EXTENSIONS_API_SIDE_PANEL_SIDE_PANEL_SERVICE_H_
+#define CHROME_BROWSER_EXTENSIONS_API_SIDE_PANEL_SIDE_PANEL_SERVICE_H_
+
+#include "base/containers/flat_map.h"
+#include "chrome/common/extensions/api/side_panel.h"
+#include "extensions/browser/browser_context_keyed_api_factory.h"
+#include "extensions/common/extension_id.h"
+
+namespace extensions {
+
+// The single responsibility of this service is to be the source of truth for
+// side panel options. Extensions can interact with this service using the API
+// and side panel UI updates can rely on the response of GetOptions(tab_id).
+class SidePanelService : public BrowserContextKeyedAPI {
+ public:
+  explicit SidePanelService(content::BrowserContext* context);
+
+  SidePanelService(const SidePanelService&) = delete;
+  SidePanelService& operator=(const SidePanelService&) = delete;
+
+  ~SidePanelService() override;
+
+  // Convenience method to get the SidePanelService for a profile.
+  static SidePanelService* Get(content::BrowserContext* context);
+
+  // BrowserContextKeyedAPI implementation.
+  static BrowserContextKeyedAPIFactory<SidePanelService>* GetFactoryInstance();
+
+  // Get options for tab_id. Options are loaded in order first from service
+  // storage, manifest, or an empty object will be returned, if they're unset.
+  using TabId = int;
+  api::side_panel::PanelOptions GetOptions(const Extension& extension,
+                                           absl::optional<TabId> tab_id);
+
+  // Set options for tab_id if specified. Otherwise set default options.
+  void SetOptions(const Extension& extension,
+                  api::side_panel::PanelOptions set_options);
+
+ private:
+  // TODO(crbug.com/1328645): Remove options for matching ExtensionId on
+  // uninstallation.
+  friend class BrowserContextKeyedAPIFactory<SidePanelService>;
+
+  content::BrowserContext* const browser_context_;
+
+  // BrowserContextKeyedAPI implementation.
+  static const char* service_name() { return "SidePanelService"; }
+  static const bool kServiceRedirectedInIncognito = true;
+  static const bool kServiceIsNULLWhileTesting = true;
+
+  using TabPanelOptions = base::flat_map<TabId, api::side_panel::PanelOptions>;
+  using ExtensionPanelOptions = base::flat_map<ExtensionId, TabPanelOptions>;
+
+  ExtensionPanelOptions panels_;
+};
+
+}  // namespace extensions
+
+#endif  // CHROME_BROWSER_EXTENSIONS_API_SIDE_PANEL_SIDE_PANEL_SERVICE_H_
diff --git a/chrome/browser/extensions/browser_context_keyed_service_factories.cc b/chrome/browser/extensions/browser_context_keyed_service_factories.cc
index 5ba46d1..5f536955 100644
--- a/chrome/browser/extensions/browser_context_keyed_service_factories.cc
+++ b/chrome/browser/extensions/browser_context_keyed_service_factories.cc
@@ -29,6 +29,7 @@
 #include "chrome/browser/extensions/api/sessions/sessions_api.h"
 #include "chrome/browser/extensions/api/settings_overrides/settings_overrides_api.h"
 #include "chrome/browser/extensions/api/settings_private/settings_private_event_router_factory.h"
+#include "chrome/browser/extensions/api/side_panel/side_panel_service.h"
 #include "chrome/browser/extensions/api/tab_capture/tab_capture_registry.h"
 #include "chrome/browser/extensions/api/tab_groups/tab_groups_event_router_factory.h"
 #include "chrome/browser/extensions/api/tabs/tabs_windows_api.h"
@@ -120,6 +121,7 @@
   extensions::SessionsAPI::GetFactoryInstance();
   extensions::SettingsPrivateEventRouterFactory::GetInstance();
   extensions::SettingsOverridesAPI::GetFactoryInstance();
+  extensions::SidePanelService::GetFactoryInstance();
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
   extensions::SystemIndicatorManagerFactory::GetInstance();
 #endif
diff --git a/chrome/browser/extensions/permissions_updater.cc b/chrome/browser/extensions/permissions_updater.cc
index a1b35afa..a0b999e 100644
--- a/chrome/browser/extensions/permissions_updater.cc
+++ b/chrome/browser/extensions/permissions_updater.cc
@@ -37,6 +37,7 @@
 #include "extensions/browser/renderer_startup_helper.h"
 #include "extensions/common/cors_util.h"
 #include "extensions/common/extension.h"
+#include "extensions/common/extension_features.h"
 #include "extensions/common/extension_messages.h"
 #include "extensions/common/manifest_handlers/permissions_parser.h"
 #include "extensions/common/mojom/permission_set.mojom.h"
@@ -90,6 +91,22 @@
   ~PermissionsUpdaterShutdownNotifierFactory() override {}
 };
 
+// Returns an URLPatternSet containing the sites that the user has indicated
+// extensions are always allowed to run on.
+URLPatternSet GetUserPermittedPatternSet(
+    content::BrowserContext& browser_context) {
+  PermissionsManager* permissions_manager =
+      PermissionsManager::Get(&browser_context);
+  URLPatternSet user_permitted_sites;
+  for (const url::Origin& origin :
+       permissions_manager->GetUserPermissionsSettings().permitted_sites) {
+    user_permitted_sites.AddOrigin(Extension::kValidHostPermissionSchemes,
+                                   origin);
+  }
+
+  return user_permitted_sites;
+}
+
 }  // namespace
 
 // A helper class to asynchronously dispatch the event to notify policy host
@@ -303,16 +320,46 @@
       << "specified in the manifest.";
 
   // Revoked optional permissions are removed from granted and runtime-granted
-  // permissions only if the user, and not the extension, removed them. This
-  // allows the extension to add them again without prompting the user. They are
-  // always removed from the active set, which is the set of permissions the
-  // the extension currently requests.
+  // permissions only if the user, and not the extension, removed them (i.e.,
+  // `remove_type` == REMOVE_HARD). This allows the extension to add them again
+  // without prompting the user. They are always removed from the active set,
+  // which is the set of permissions the extension currently requests.
   int permissions_store_mask = kActivePermissions;
-  if (remove_type == REMOVE_HARD)
+  if (remove_type == REMOVE_HARD) {
     permissions_store_mask |= kGrantedPermissions | kRuntimeGrantedPermissions;
 
-  RemovePermissionsImpl(extension, permissions, permissions_store_mask,
-                        permissions, std::move(completion_callback));
+    // We don't allow the hard-removal of user-permitted sites on a per-
+    // extension basis. Instead, these permissions must be removed by removing
+    // the user-permitted site entry. If this changes, we'll need to adjust
+    // this to add back these sites, as we do in RevokeRuntimePermissions().
+#if DCHECK_IS_ON()
+    URLPatternSet user_permitted_sites =
+        GetUserPermittedPatternSet(*browser_context_);
+    PermissionSet user_permitted_set(
+        APIPermissionSet(), ManifestPermissionSet(),
+        user_permitted_sites.Clone(), user_permitted_sites.Clone());
+    std::unique_ptr<const PermissionSet> user_permitted_being_removed =
+        PermissionSet::CreateIntersection(
+            permissions, user_permitted_set,
+            URLPatternSet::IntersectionBehavior::kDetailed);
+    DCHECK(user_permitted_being_removed->effective_hosts().is_empty())
+        << "Attempting to hard-remove optional permission to user-permitted "
+           "sites: "
+        << user_permitted_being_removed->effective_hosts();
+#endif
+  }
+
+  // Revoking optional permissions is usually done by the extension, so we allow
+  // revoking user-permitted sites (the extension can opt-out of having
+  // permissions). So in this case, the new active permissions are simply the
+  // current active minus any revoked permissions.
+  std::unique_ptr<const PermissionSet> new_active_permissions =
+      PermissionSet::CreateDifference(
+          extension.permissions_data()->active_permissions(), permissions);
+
+  RemovePermissionsImpl(extension, std::move(new_active_permissions),
+                        permissions, permissions_store_mask,
+                        std::move(completion_callback));
 }
 
 void PermissionsUpdater::RevokeRuntimePermissions(
@@ -325,34 +372,16 @@
   // only has https://maps.google.com/*.
   const PermissionSet& active =
       extension.permissions_data()->active_permissions();
+
   // Unlike adding permissions, we should know that any permissions we remove
   // are a superset of the permissions the extension has active (because we only
   // allow removal origins and the extension can't have a broader origin than
-  // what it has granted).
+  // what it has granted). Because of this, we can just look for any patterns
+  // contained in both sets.
   std::unique_ptr<const PermissionSet> active_permissions_to_remove =
       PermissionSet::CreateIntersection(
           active, permissions,
           URLPatternSet::IntersectionBehavior::kPatternsContainedByBoth);
-  // One exception: If we're revoking a permission like "<all_urls>", we need
-  // to make sure it doesn't revoke the included chrome://favicon permission.
-  std::set<URLPattern> removable_explicit_hosts;
-  bool needs_adjustment = false;
-  for (const auto& pattern : active_permissions_to_remove->explicit_hosts()) {
-    bool is_chrome_favicon = pattern.scheme() == content::kChromeUIScheme &&
-                             pattern.host() == chrome::kChromeUIFaviconHost;
-    if (is_chrome_favicon)
-      needs_adjustment = true;
-    else
-      removable_explicit_hosts.insert(pattern);
-  }
-  if (needs_adjustment) {
-    // Tedious, because PermissionSets are const. :(
-    active_permissions_to_remove = std::make_unique<PermissionSet>(
-        active_permissions_to_remove->apis().Clone(),
-        active_permissions_to_remove->manifest_permissions().Clone(),
-        URLPatternSet(removable_explicit_hosts),
-        active_permissions_to_remove->scriptable_hosts().Clone());
-  }
 
   CHECK(extension.permissions_data()->active_permissions().Contains(
       *active_permissions_to_remove))
@@ -360,14 +389,61 @@
   CHECK(GetRevokablePermissions(&extension)->Contains(permissions))
       << "Cannot remove non-revokable permissions.";
 
-  // Removing runtime-granted permissions does not remove permissions from
-  // the granted permissions store. This is done to ensure behavior taken with
-  // the runtime host permissions feature is confined to when the experiment is
-  // enabled. Similarly, since the runtime-granted permissions were never added
-  // to the active permissions stored in prefs, they are also not removed.
+  // Calculate a set of permissions to keep active on the extension, even if
+  // they were included in the removal set. This includes chrome://favicon
+  // (which would be included in `active_permissions_to_remove` if the removal
+  // set is <all_urls>) and any sites the user indicated all extensions may
+  // always run on.
+  std::unique_ptr<const PermissionSet> permissions_to_keep;
+  {
+    URLPatternSet explicit_hosts;
+    URLPatternSet scriptable_hosts;
+
+    // Don't allow removing chrome://favicon, if it was previously granted.
+    for (const auto& pattern : active_permissions_to_remove->explicit_hosts()) {
+      bool is_chrome_favicon = pattern.scheme() == content::kChromeUIScheme &&
+                               pattern.host() == chrome::kChromeUIFaviconHost;
+      if (is_chrome_favicon) {
+        explicit_hosts.AddPattern(pattern);
+        break;
+      }
+    }
+
+    // If the corresponding feature is enabled, add in user-permitted sites.
+    if (base::FeatureList::IsEnabled(
+            extensions_features::kExtensionsMenuAccessControl)) {
+      URLPatternSet always_permitted_set =
+          GetUserPermittedPatternSet(*browser_context_);
+      explicit_hosts.AddPatterns(always_permitted_set);
+      scriptable_hosts.AddPatterns(always_permitted_set);
+    }
+
+    PermissionSet permitted_set(APIPermissionSet(), ManifestPermissionSet(),
+                                std::move(explicit_hosts),
+                                std::move(scriptable_hosts));
+
+    permissions_to_keep = PermissionSet::CreateIntersection(
+        *active_permissions_to_remove, permitted_set,
+        URLPatternSet::IntersectionBehavior::kDetailed);
+  }
+
+  // Calculate the new set of active permissions. This is the current
+  // permissions minus the permissions to remove, but then adding back in any
+  // of the permissions we've explicitly identified as those we should keep.
+  std::unique_ptr<const PermissionSet> new_active_permissions =
+      PermissionSet::CreateDifference(active, *active_permissions_to_remove);
+  new_active_permissions =
+      PermissionSet::CreateUnion(*new_active_permissions, *permissions_to_keep);
+
+  // Runtime permissions have a separate store in prefs.
+  // Note that we remove all the permissions in `permissions` from
+  // runtime-granted permissions. User-permitted sites are granted
+  // separately, and not considered runtime-granted permissions. This ensures
+  // that when a user changes a site from permitted to non-permitted or vice
+  // versa, and extension's specific stored permissions are unaffected.
   constexpr int permissions_store_mask = kRuntimeGrantedPermissions;
-  RemovePermissionsImpl(extension, *active_permissions_to_remove,
-                        permissions_store_mask, permissions,
+  RemovePermissionsImpl(extension, std::move(new_active_permissions),
+                        permissions, permissions_store_mask,
                         std::move(completion_callback));
 }
 
@@ -671,8 +747,8 @@
 void PermissionsUpdater::AddPermissionsImpl(
     const Extension& extension,
     const PermissionSet& active_permissions_to_add,
-    int permissions_store_mask,
-    const PermissionSet& prefs_permissions_to_add,
+    int prefs_permissions_store_mask,
+    const PermissionSet& permissions_to_add_to_prefs,
     base::OnceClosure completion_callback) {
   std::unique_ptr<const PermissionSet> new_active = PermissionSet::CreateUnion(
       active_permissions_to_add,
@@ -681,18 +757,18 @@
   SetPermissions(&extension, std::move(new_active));
 
   ExtensionPrefs* prefs = ExtensionPrefs::Get(browser_context_);
-  if ((permissions_store_mask & kActivePermissions) != 0) {
+  if ((prefs_permissions_store_mask & kActivePermissions) != 0) {
     prefs->AddDesiredActivePermissions(extension.id(),
-                                       prefs_permissions_to_add);
+                                       permissions_to_add_to_prefs);
   }
 
-  if ((permissions_store_mask & kGrantedPermissions) != 0) {
-    prefs->AddGrantedPermissions(extension.id(), prefs_permissions_to_add);
+  if ((prefs_permissions_store_mask & kGrantedPermissions) != 0) {
+    prefs->AddGrantedPermissions(extension.id(), permissions_to_add_to_prefs);
   }
 
-  if ((permissions_store_mask & kRuntimeGrantedPermissions) != 0) {
+  if ((prefs_permissions_store_mask & kRuntimeGrantedPermissions) != 0) {
     prefs->AddRuntimeGrantedPermissions(extension.id(),
-                                        prefs_permissions_to_add);
+                                        permissions_to_add_to_prefs);
   }
 
   NetworkPermissionsUpdateHelper::UpdatePermissions(
@@ -702,37 +778,35 @@
 
 void PermissionsUpdater::RemovePermissionsImpl(
     const Extension& extension,
-    const PermissionSet& active_permissions_to_remove,
-    int permissions_store_mask,
-    const PermissionSet& prefs_permissions_to_remove,
+    std::unique_ptr<const PermissionSet> new_active_permissions,
+    const PermissionSet& permissions_to_remove_from_prefs,
+    int prefs_permissions_store_mask,
     base::OnceClosure completion_callback) {
-  std::unique_ptr<const PermissionSet> new_active =
-      PermissionSet::CreateDifference(
-          extension.permissions_data()->active_permissions(),
-          active_permissions_to_remove);
-
-  SetPermissions(&extension, std::move(new_active));
+  SetPermissions(&extension, std::move(new_active_permissions));
 
   ExtensionPrefs* prefs = ExtensionPrefs::Get(browser_context_);
-  if ((permissions_store_mask & kActivePermissions) != 0) {
+  if ((prefs_permissions_store_mask & kActivePermissions) != 0) {
     prefs->RemoveDesiredActivePermissions(extension.id(),
-                                          prefs_permissions_to_remove);
+                                          permissions_to_remove_from_prefs);
   }
 
   // NOTE: Currently, this code path is only reached in unit tests. See comment
   // above REMOVE_HARD in the header file.
-  if ((permissions_store_mask & kGrantedPermissions) != 0) {
+  if ((prefs_permissions_store_mask & kGrantedPermissions) != 0) {
     prefs->RemoveGrantedPermissions(extension.id(),
-                                    prefs_permissions_to_remove);
+                                    permissions_to_remove_from_prefs);
   }
 
-  if ((permissions_store_mask & kRuntimeGrantedPermissions) != 0) {
+  if ((prefs_permissions_store_mask & kRuntimeGrantedPermissions) != 0) {
     prefs->RemoveRuntimeGrantedPermissions(extension.id(),
-                                           prefs_permissions_to_remove);
+                                           permissions_to_remove_from_prefs);
   }
 
+  // For the notification, we consider the changed set to be the set of
+  // permissions to remove from preferences, rather than the new active
+  // permissions (which can include things like user-permitted sites).
   NetworkPermissionsUpdateHelper::UpdatePermissions(
-      browser_context_, REMOVED, &extension, active_permissions_to_remove,
+      browser_context_, REMOVED, &extension, permissions_to_remove_from_prefs,
       std::move(completion_callback));
 }
 
diff --git a/chrome/browser/extensions/permissions_updater.h b/chrome/browser/extensions/permissions_updater.h
index 7fac6f3..f47b0aa 100644
--- a/chrome/browser/extensions/permissions_updater.h
+++ b/chrome/browser/extensions/permissions_updater.h
@@ -211,25 +211,27 @@
   // Adds the given |active_permissions_to_add| to |extension|'s current
   // active permissions (i.e., the permissions associated with the |extension|
   // object and the extension's process). Updates the preferences according to
-  // |permission_store_mask| with |prefs_permissions_to_add|.
-  // The sets of |prefs_permissions_to_add| and |active_permissions_to_add| may
-  // differ in the case of granting a wider set of permissions than what the
-  // extension explicitly requested, as described in GrantRuntimePermissions().
+  // |permission_store_mask| with |permissions_to_add_to_prefs|.
+  // The sets of |permissions_to_add_to_prefs| and |active_permissions_to_add|
+  // may differ in the case of granting a wider set of permissions than what
+  // the extension explicitly requested, as described in
+  // GrantRuntimePermissions().
   void AddPermissionsImpl(const Extension& extension,
                           const PermissionSet& active_permissions_to_add,
-                          int permission_store_mask,
-                          const PermissionSet& prefs_permissions_to_add,
+                          int prefs_permissions_store_mask,
+                          const PermissionSet& permissions_to_add_to_prefs,
                           base::OnceClosure completion_callback);
 
-  // Removes the given |active_permissions_to_remove| from |extension|'s current
-  // active permissions. Updates the preferences according to
-  // |permission_store_mask| with |prefs_permissions_to_remove|. As above, the
-  // permission sets may be different.
-  void RemovePermissionsImpl(const Extension& extension,
-                             const PermissionSet& active_permissions_to_remove,
-                             int permission_store_mask,
-                             const PermissionSet& prefs_permissions_to_remove,
-                             base::OnceClosure completion_callback);
+  // Sets the given `extension`'s active permissions to the specified
+  // `new_active_permissions`. Also removes `permissions_to_remove_from_prefs`
+  // from the preferences indicated by `prefs_permissions_store_mask`. Invokes
+  // `completion_callback` when done.
+  void RemovePermissionsImpl(
+      const Extension& extension,
+      std::unique_ptr<const PermissionSet> new_active_permissions,
+      const PermissionSet& permissions_to_remove_from_prefs,
+      int prefs_permissions_store_mask,
+      base::OnceClosure completion_callback);
 
   // The associated BrowserContext.
   raw_ptr<content::BrowserContext> browser_context_;
diff --git a/chrome/browser/extensions/permissions_updater_unittest.cc b/chrome/browser/extensions/permissions_updater_unittest.cc
index cfc317f..b40c270f 100644
--- a/chrome/browser/extensions/permissions_updater_unittest.cc
+++ b/chrome/browser/extensions/permissions_updater_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/values.h"
 #include "chrome/browser/extensions/chrome_test_extension_loader.h"
 #include "chrome/browser/extensions/extension_service.h"
@@ -984,4 +985,195 @@
   }
 }
 
+class PermissionsUpdaterTestWithEnhancedHostControls
+    : public PermissionsUpdaterTest,
+      public testing::WithParamInterface<bool> {
+ public:
+  PermissionsUpdaterTestWithEnhancedHostControls() {
+    const base::Feature& feature =
+        extensions_features::kExtensionsMenuAccessControl;
+    if (GetParam())
+      feature_list_.InitAndEnableFeature(feature);
+    else
+      feature_list_.InitAndDisableFeature(feature);
+  }
+  ~PermissionsUpdaterTestWithEnhancedHostControls() override = default;
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         PermissionsUpdaterTestWithEnhancedHostControls,
+                         testing::Bool());
+
+// Tests the behavior of revoking permissions from the extension while the
+// user has specified a set of sites that all extensions are allowed to run on.
+TEST_P(PermissionsUpdaterTestWithEnhancedHostControls,
+       RevokingPermissionsWithUserPermittedSites) {
+  InitializeEmptyExtensionService();
+
+  // Install and initialize an extension that wants to run everywhere.
+  scoped_refptr<const Extension> extension =
+      ExtensionBuilder("extension").AddPermission("<all_urls>").Build();
+
+  {
+    PermissionsUpdater updater(profile());
+    updater.InitializePermissions(extension.get());
+    updater.GrantActivePermissions(extension.get());
+  }
+
+  // Note that the PermissionsManger requires the extension to be in the
+  // ExtensionRegistry, so add it through the ExtensionService.
+  service()->AddExtension(extension.get());
+
+  const GURL first_url("http://first.example");
+  const GURL second_url("http://second.example");
+
+  PermissionsManager* permissions_manager = PermissionsManager::Get(profile());
+  ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
+
+  {
+    // Simulate the user allowing all extensions to run on `first_url`.
+    PermissionsManagerWaiter waiter(permissions_manager);
+    permissions_manager->AddUserPermittedSite(url::Origin::Create(first_url));
+    waiter.WaitForUserPermissionsSettingsChange();
+  }
+
+  auto get_site_access = [extension](const GURL& url) {
+    return extension->permissions_data()->GetPageAccess(url, -1, nullptr);
+  };
+
+  auto has_desired_active_permission_for_url = [extension,
+                                                prefs](const GURL& url) {
+    return prefs->GetDesiredActivePermissions(extension->id())
+        ->effective_hosts()
+        .MatchesURL(url);
+  };
+
+  auto has_runtime_permission_for_url = [extension, prefs](const GURL& url) {
+    return prefs->GetRuntimeGrantedPermissions(extension->id())
+        ->effective_hosts()
+        .MatchesURL(url);
+  };
+
+  auto has_granted_permission_for_url = [extension, prefs](const GURL& url) {
+    return prefs->GetGrantedPermissions(extension->id())
+        ->effective_hosts()
+        .MatchesURL(url);
+  };
+
+  // By default, the extension should have permission to both sites, since it
+  // has access to all URLs.
+  EXPECT_EQ(PermissionsData::PageAccess::kAllowed, get_site_access(first_url));
+  EXPECT_EQ(PermissionsData::PageAccess::kAllowed, get_site_access(second_url));
+  // The desired permission should include both, as well, as should the
+  // granted.
+  EXPECT_TRUE(has_desired_active_permission_for_url(first_url));
+  EXPECT_TRUE(has_desired_active_permission_for_url(second_url));
+  EXPECT_TRUE(has_granted_permission_for_url(first_url));
+  EXPECT_TRUE(has_granted_permission_for_url(second_url));
+  // The extension does not yet have any runtime granted permissions.
+  EXPECT_FALSE(has_runtime_permission_for_url(first_url));
+  EXPECT_FALSE(has_runtime_permission_for_url(second_url));
+
+  // Withhold host permissions from the extension.
+  ScriptingPermissionsModifier(profile(), extension)
+      .SetWithholdHostPermissions(true);
+
+  // If the enhanced host controls feature is disabled, then both hosts are
+  // withheld.
+  if (!GetParam()) {
+    EXPECT_EQ(PermissionsData::PageAccess::kWithheld,
+              get_site_access(first_url));
+    EXPECT_EQ(PermissionsData::PageAccess::kWithheld,
+              get_site_access(second_url));
+    // There's nothing more we need to test in this case.
+    return;
+  }
+
+  // Otherwise, the feature is enabled, and user host settings are considered
+  // in the permissions adjustment.
+
+  // The extension should be allowed to run on `first_url`, since the
+  // user indicated all extensions can always run there. However, it should not
+  // be allowed on `second_url`.
+  EXPECT_EQ(PermissionsData::PageAccess::kAllowed, get_site_access(first_url));
+  EXPECT_EQ(PermissionsData::PageAccess::kWithheld,
+            get_site_access(second_url));
+  // The desired permissions (indicating the extension's desired state) and
+  // the granted permissions (indicating the install-time granted permissions)
+  // should be unchanged, including both sites.
+  EXPECT_TRUE(has_desired_active_permission_for_url(first_url));
+  EXPECT_TRUE(has_desired_active_permission_for_url(second_url));
+  EXPECT_TRUE(has_granted_permission_for_url(first_url));
+  EXPECT_TRUE(has_granted_permission_for_url(second_url));
+  // The runtime permissions should also be unchanged. Even though the extension
+  // is allowed to run on `first_url`, it does not have runtime access to
+  // that site (this is important if the user later removes the site from
+  // permitted sites).
+  EXPECT_FALSE(has_runtime_permission_for_url(first_url));
+  EXPECT_FALSE(has_runtime_permission_for_url(second_url));
+
+  // Now, grant the extension explicit access to `second_url`.
+  ScriptingPermissionsModifier(profile(), extension)
+      .GrantHostPermission(second_url);
+
+  // The extension should now be allowed to run on both sites.
+  EXPECT_EQ(PermissionsData::PageAccess::kAllowed, get_site_access(first_url));
+  EXPECT_EQ(PermissionsData::PageAccess::kAllowed, get_site_access(second_url));
+  // Desired and granted permissions remain unchanged.
+  EXPECT_TRUE(has_desired_active_permission_for_url(first_url));
+  EXPECT_TRUE(has_desired_active_permission_for_url(second_url));
+  EXPECT_TRUE(has_granted_permission_for_url(first_url));
+  EXPECT_TRUE(has_granted_permission_for_url(second_url));
+  // The extension should have runtime access for `second_url`, since it
+  // was granted explicit access to it by the user.
+  EXPECT_FALSE(has_runtime_permission_for_url(first_url));
+  EXPECT_TRUE(has_runtime_permission_for_url(second_url));
+
+  {
+    // (Temporarily) add `second_url` as a user-permitted site.
+    PermissionsManagerWaiter waiter(permissions_manager);
+    permissions_manager->AddUserPermittedSite(url::Origin::Create(second_url));
+    waiter.WaitForUserPermissionsSettingsChange();
+  }
+
+  // All sites should be accessible; permissions should be unchanged.
+  EXPECT_EQ(PermissionsData::PageAccess::kAllowed, get_site_access(first_url));
+  EXPECT_EQ(PermissionsData::PageAccess::kAllowed, get_site_access(second_url));
+  EXPECT_TRUE(has_desired_active_permission_for_url(first_url));
+  EXPECT_TRUE(has_desired_active_permission_for_url(second_url));
+  EXPECT_TRUE(has_granted_permission_for_url(first_url));
+  EXPECT_TRUE(has_granted_permission_for_url(second_url));
+  EXPECT_FALSE(has_runtime_permission_for_url(first_url));
+  EXPECT_TRUE(has_runtime_permission_for_url(second_url));
+
+  // Remove both sites from the permitted sites.
+  {
+    PermissionsManagerWaiter waiter(permissions_manager);
+    permissions_manager->RemoveUserPermittedSite(
+        url::Origin::Create(first_url));
+    waiter.WaitForUserPermissionsSettingsChange();
+  }
+  {
+    PermissionsManagerWaiter waiter(permissions_manager);
+    permissions_manager->RemoveUserPermittedSite(
+        url::Origin::Create(second_url));
+    waiter.WaitForUserPermissionsSettingsChange();
+  }
+
+  // Now, `first_url` should be withheld, since it's no longer a permitted
+  // site. However, `second_url` should still be accessible, because the
+  // extension had explicit access to that site.
+  EXPECT_EQ(PermissionsData::PageAccess::kWithheld, get_site_access(first_url));
+  EXPECT_EQ(PermissionsData::PageAccess::kAllowed, get_site_access(second_url));
+  EXPECT_TRUE(has_desired_active_permission_for_url(first_url));
+  EXPECT_TRUE(has_desired_active_permission_for_url(second_url));
+  EXPECT_TRUE(has_granted_permission_for_url(first_url));
+  EXPECT_TRUE(has_granted_permission_for_url(second_url));
+  EXPECT_FALSE(has_runtime_permission_for_url(first_url));
+  EXPECT_TRUE(has_runtime_permission_for_url(second_url));
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/site_permissions_helper_unittest.cc b/chrome/browser/extensions/site_permissions_helper_unittest.cc
index 061f990..afc12cd 100644
--- a/chrome/browser/extensions/site_permissions_helper_unittest.cc
+++ b/chrome/browser/extensions/site_permissions_helper_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/extensions/site_permissions_helper.h"
 
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/extensions/extension_action_runner.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_service_test_with_install.h"
 #include "chrome/browser/extensions/permissions_updater.h"
@@ -13,9 +15,12 @@
 #include "components/crx_file/id_util.h"
 #include "content/public/test/navigation_simulator.h"
 #include "content/public/test/web_contents_tester.h"
+#include "extensions/browser/permissions_manager.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_builder.h"
+#include "extensions/common/extension_features.h"
 #include "extensions/common/permissions/permissions_data.h"
+#include "extensions/test/permissions_manager_waiter.h"
 
 namespace extensions {
 
@@ -359,4 +364,107 @@
       *extension, url, SiteAccess::kOnAllSites));
 }
 
+class SitePermissionsHelperWithUserHostControlsUnitTest
+    : public SitePermissionsHelperUnitTest {
+ public:
+  SitePermissionsHelperWithUserHostControlsUnitTest() {
+    feature_list_.InitAndEnableFeature(
+        extensions_features::kExtensionsMenuAccessControl);
+  }
+  ~SitePermissionsHelperWithUserHostControlsUnitTest() override = default;
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+// Tests that setting an extension to on-click retains its access to
+// sites the user explicitly marked as ones that all extensions may run on.
+TEST_F(SitePermissionsHelperWithUserHostControlsUnitTest,
+       DowngradingFromAllSitesToOnClickAppliesUserPermittedSites) {
+  auto extension = InstallExtensionWithPermissions(
+      "extension", /*host_permissions=*/{"<all_urls>"}, /*permissions=*/{});
+
+  const GURL user_permitted_site("https://allowed.example");
+  const GURL non_user_permitted_site("https://not-allowed.example");
+
+  PermissionsManager* permissions_manager = PermissionsManager::Get(profile());
+  {
+    // Add a user-permitted site.
+    PermissionsManagerWaiter waiter(permissions_manager);
+    permissions_manager->AddUserPermittedSite(
+        url::Origin::Create(user_permitted_site));
+    waiter.WaitForUserPermissionsSettingsChange();
+  }
+
+  auto* user_permitted_contents = AddTab(user_permitted_site);
+  auto* non_user_permitted_contents = AddTab(non_user_permitted_site);
+
+  // Right now, the extension should be allowed to run everywhere (on both
+  // `user_permitted_site` and `non_user_permitted_site`).
+  EXPECT_EQ(
+      SitePermissionsHelper::SiteAccess::kOnAllSites,
+      permissions_helper()->GetSiteAccess(*extension, user_permitted_site));
+  EXPECT_EQ(SitePermissionsHelper::SiteInteraction::kGranted,
+            permissions_helper()->GetSiteInteraction(*extension,
+                                                     user_permitted_contents));
+  EXPECT_EQ(PermissionsData::PageAccess::kAllowed,
+            extension->permissions_data()->GetPageAccess(
+                user_permitted_site, extension_misc::kUnknownTabId, nullptr));
+  EXPECT_EQ(
+      SitePermissionsHelper::SiteAccess::kOnAllSites,
+      permissions_helper()->GetSiteAccess(*extension, non_user_permitted_site));
+  EXPECT_EQ(SitePermissionsHelper::SiteInteraction::kGranted,
+            permissions_helper()->GetSiteInteraction(
+                *extension, non_user_permitted_contents));
+  EXPECT_EQ(
+      PermissionsData::PageAccess::kAllowed,
+      extension->permissions_data()->GetPageAccess(
+          non_user_permitted_site, extension_misc::kUnknownTabId, nullptr));
+
+  {
+    // Switch the extension from on all sites to on-click.
+    ExtensionActionRunner* action_runner =
+        ExtensionActionRunner::GetForWebContents(non_user_permitted_contents);
+    ASSERT_TRUE(action_runner);
+    action_runner->accept_bubble_for_testing(true);
+    PermissionsManagerWaiter waiter(permissions_manager);
+    permissions_helper()->UpdateSiteAccess(
+        *extension, non_user_permitted_contents,
+        SitePermissionsHelper::SiteAccess::kOnClick);
+    waiter.WaitForExtensionPermissionsUpdate();
+  }
+
+  // The extension should now be able to run on `user_permitted` site
+  // automatically, since it's a user-permitted site.
+
+  // TODO(https://crbug.com/1268198): The following check should be in place:
+  // EXPECT_EQ(SitePermissionsHelper::SiteAccess::kOnSite,
+  //           permissions_helper()->GetSiteAccess(
+  //               *extension, user_permitted_site));
+  // However, currently PermissionsManager::GetSiteAccess() (which is used by
+  // SitePermissionsHelper::GetSiteAccess()) doesn't take user-permitted sites
+  // into account.
+  EXPECT_EQ(
+      SitePermissionsHelper::SiteAccess::kOnClick,
+      permissions_helper()->GetSiteAccess(*extension, user_permitted_site));
+  EXPECT_EQ(SitePermissionsHelper::SiteInteraction::kGranted,
+            permissions_helper()->GetSiteInteraction(*extension,
+                                                     user_permitted_contents));
+  EXPECT_EQ(PermissionsData::PageAccess::kAllowed,
+            extension->permissions_data()->GetPageAccess(
+                user_permitted_site, extension_misc::kUnknownTabId, nullptr));
+
+  // Non-user-permitted sites should remain withheld.
+  EXPECT_EQ(
+      SitePermissionsHelper::SiteAccess::kOnClick,
+      permissions_helper()->GetSiteAccess(*extension, non_user_permitted_site));
+  EXPECT_EQ(SitePermissionsHelper::SiteInteraction::kWithheld,
+            permissions_helper()->GetSiteInteraction(
+                *extension, non_user_permitted_contents));
+  EXPECT_EQ(
+      PermissionsData::PageAccess::kWithheld,
+      extension->permissions_data()->GetPageAccess(
+          non_user_permitted_site, extension_misc::kUnknownTabId, nullptr));
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/user_host_restrictions_browsertest.cc b/chrome/browser/extensions/user_host_restrictions_browsertest.cc
index 42c8d1e..ec3ce56 100644
--- a/chrome/browser/extensions/user_host_restrictions_browsertest.cc
+++ b/chrome/browser/extensions/user_host_restrictions_browsertest.cc
@@ -460,4 +460,59 @@
   }
 }
 
+// Tests that sites the user indicated all extensions may run on are still
+// available to extensions after a permissions withholding change.
+IN_PROC_BROWSER_TEST_P(UserHostRestrictionsBrowserTest,
+                       UserPermittedSitesAreAppliedOnWithholdingChange) {
+  ASSERT_TRUE(StartEmbeddedTestServer());
+
+  static constexpr char kManifest[] =
+      R"({
+           "name": "Test Extension",
+           "version": "0.1",
+           "manifest_version": 3,
+           "host_permissions": ["<all_urls>"]
+         })";
+
+  TestExtensionDir test_dir;
+  test_dir.WriteManifest(kManifest);
+  const Extension* extension = LoadExtension(test_dir.UnpackedPath());
+  ASSERT_TRUE(extension);
+
+  const GURL user_permitted_site("https://allowed.example");
+  const GURL non_user_permitted_site("https://not-allowed.example");
+
+  AddUserPermittedSite(user_permitted_site);
+
+  // Without withholding permissions, the extension may run on both sites.
+  EXPECT_EQ(PermissionsData::PageAccess::kAllowed,
+            extension->permissions_data()->GetPageAccess(
+                user_permitted_site, extension_misc::kUnknownTabId, nullptr));
+  EXPECT_EQ(
+      PermissionsData::PageAccess::kAllowed,
+      extension->permissions_data()->GetPageAccess(
+          non_user_permitted_site, extension_misc::kUnknownTabId, nullptr));
+
+  WithholdExtensionPermissions(*extension);
+
+  // Once permissions are withheld, with the feature enabled, the extension may
+  // still run on the user-permitted site (without the feature enabled, the
+  // site is withheld).
+  if (GetParam()) {
+    EXPECT_EQ(PermissionsData::PageAccess::kAllowed,
+              extension->permissions_data()->GetPageAccess(
+                  user_permitted_site, extension_misc::kUnknownTabId, nullptr));
+  } else {
+    EXPECT_EQ(PermissionsData::PageAccess::kWithheld,
+              extension->permissions_data()->GetPageAccess(
+                  user_permitted_site, extension_misc::kUnknownTabId, nullptr));
+  }
+
+  // Non-permitted sites remain withheld with and without the feature enabled.
+  EXPECT_EQ(
+      PermissionsData::PageAccess::kWithheld,
+      extension->permissions_data()->GetPageAccess(
+          non_user_permitted_site, extension_misc::kUnknownTabId, nullptr));
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index a345e2a..3684f54 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -452,6 +452,11 @@
     "expiry_milestone": 109
   },
   {
+    "name": "autofill-enable-remade-downstream-metrics",
+    "owners": [ "siyua" ],
+    "expiry_milestone": 110
+  },
+  {
     "name": "autofill-enable-sending-bcn-in-get-upload-details",
     "owners": [
       "jsaul@google.com",
@@ -560,11 +565,6 @@
     "expiry_milestone": 106
   },
   {
-    "name":"autofill-suggest-virtual-cards-on-incomplete-form",
-    "owners": ["siashah", "siyua"],
-    "expiry_milestone": 105
-  },
-  {
     "name": "autofill-type-specific-popup-width",
     "owners": [ "koerber", "mamir" ],
     "expiry_milestone": 103
@@ -2736,6 +2736,11 @@
     "expiry_milestone": 108
   },
   {
+    "name": "enable-seamless-refresh-rate-switching",
+    "owners": [ "ddavenport", "chromeos-gfx-display@google.com" ],
+    "expiry_milestone": 110
+  },
+  {
     "name": "enable-search-resumption-module",
     "owners": ["clank-start","hanxi","spdonghao", "andrewheard"],
     "expiry_milestone": 110
@@ -3775,11 +3780,6 @@
     "expiry_milestone": 110
   },
   {
-    "name": "impulse-scroll-animations",
-    "owners": [ "arakeri@microsoft.com", "gerchiko@microsoft.com" ],
-    "expiry_milestone": 100
-  },
-  {
     "name": "in-product-help-demo-mode-choice",
     "owners": [ "dtrainor", "nyquist" ],
     // This flag is used by teams as they develop in-product help integrations,
@@ -4716,6 +4716,11 @@
     "expiry_milestone": 110
   },
   {
+    "name": "omnibox-most-visited-tiles-title-wrap-around",
+    "owners": [ "fgorski", "ender", "stkhapugin", "chrome-omnibox-team@google.com"],
+    "expiry_milestone": 120
+  },
+  {
     "name": "omnibox-new-popup-ui",
     "owners": ["rkgibson@google.com", "stkhapugin", "chrome-omnibox-team@google.com"],
     "expiry_milestone": 110
@@ -5061,11 +5066,6 @@
     "expiry_milestone": 107
   },
   {
-    "name": "percent-based-scrolling",
-    "owners": [ "arakeri@microsoft.com", "gerchiko@microsoft.com" ],
-    "expiry_milestone": 100
-  },
-  {
     "name": "performant-split-view-resizing",
     "owners": [ "dandersson", "riomat" ],
     "expiry_milestone": 110
@@ -6565,6 +6565,11 @@
     "expiry_milestone": 99
   },
   {
+    "name": "windows-scrolling-personality",
+    "owners": [ "arakeri@microsoft.com", "gastonr@microsoft.com" ],
+    "expiry_milestone": 115
+  },
+  {
     "name": "xsurface-metrics-reporting",
     "owners": [ "//chrome/android/feed/OWNERS", "feed@chromium.org" ],
     "expiry_milestone": 99
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index a0877f30..56c9169 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -383,6 +383,12 @@
     "When enabled, Autofill will use a new ranking formula to rank Autofill "
     "data model suggestions such as credit cards or profiles.";
 
+const char kAutofillEnableRemadeDownstreamMetricsName[] =
+    "Enable remade Autofill Downstream metrics logging";
+const char kAutofillEnableRemadeDownstreamMetricsDescription[] =
+    "When enabled, some extra metrics logging for Autofill Downstream will "
+    "start.";
+
 const char kAutofillEnableSendingBcnInGetUploadDetailsName[] =
     "Enable sending billing customer number in GetUploadDetails";
 const char kAutofillEnableSendingBcnInGetUploadDetailsDescription[] =
@@ -517,14 +523,6 @@
     "When enabled, users would get address/credit cards/passwords autofilling "
     "options in the context menu if the context menu is opened on a text field";
 
-const char kAutofillSuggestVirtualCardsOnIncompleteFormName[] =
-    "Autofill suggests virtual cards on incomplete forms";
-const char kAutofillSuggestVirtualCardsOnIncompleteFormDescription[] =
-    "When enabled, merchant bound virtual cards will be suggested even if not "
-    "all "
-    "of the card number, exp date and CVC fields are detected in a payment "
-    "form.";
-
 const char kAutofillUpstreamAllowAdditionalEmailDomainsName[] =
     "Allow Autofill credit card upload save for select non-Google-based "
     "accounts";
@@ -1612,11 +1610,6 @@
     "Ensure keyboard shortcuts work consistently with international keyboard "
     "layouts and deprecate legacy shortcuts.";
 
-const char kImpulseScrollAnimationsName[] = "Impulse-style scroll animations";
-const char kImpulseScrollAnimationsDescription[] =
-    "Replaces the default scroll animation with Impulse-style scroll "
-    "animations.";
-
 const char kIncognitoBrandConsistencyForAndroidName[] =
     "Enable Incognito brand consistency in Android.";
 const char kIncognitoBrandConsistencyForAndroidDescription[] =
@@ -1900,6 +1893,12 @@
     "Display a list of frequently visited pages from history as a single row "
     "with a carousel instead of one URL per line.";
 
+const char kOmniboxMostVisitedTilesTitleWrapAroundName[] =
+    "Omnibox Most Visited Tiles Title wrap around";
+const char kOmniboxMostVisitedTilesTitleWrapAroundDescription[] =
+    "Permits longer MV Tiles titles to wrap around to the second line "
+    "to reduce ellipsizing longer titles.";
+
 const char kOmniboxTrendingZeroPrefixSuggestionsOnNTPName[] =
     "Omnibox Trending Zero Prefix Suggestions";
 const char kOmniboxTrendingZeroPrefixSuggestionsOnNTPDescription[] =
@@ -2180,10 +2179,11 @@
 const char kForcedColorsDescription[] =
     "Enables forced colors mode for web content.";
 
-const char kPercentBasedScrollingName[] = "Percent-based Scrolling";
-const char kPercentBasedScrollingDescription[] =
+const char kWindowsScrollingPersonalityName[] = "Windows Scrolling Personality";
+const char kWindowsScrollingPersonalityDescription[] =
     "If enabled, mousewheel and keyboard scrolls will scroll by a percentage "
-    "of the scroller size.";
+    "of the scroller size and the default scroll animation is replaced with "
+    "Impulse-style scroll animations.";
 
 const char kPermissionChipName[] = "Permissions Chip Experiment";
 const char kPermissionChipDescription[] =
@@ -4953,6 +4953,12 @@
 const char kEnablePalmSuppressionDescription[] =
     "If enabled, suppresses touch when a stylus is on a touchscreen.";
 
+const char kEnableSeamlessRefreshRateSwitchingName[] =
+    "Seamless Refresh Rate Switching";
+const char kEnableSeamlessRefreshRateSwitchingDescription[] =
+    "This option enables seamlessly changing the refresh rate based on power "
+    "state on devices with supported hardware and drivers.";
+
 const char kEnableTouchpadsInDiagnosticsAppName[] =
     "Enable touchpad cards in the Diagnostics App";
 const char kEnableTouchpadsInDiagnosticsAppDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 88056bb..a565a20 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -214,6 +214,9 @@
 extern const char kAutofillEnableRankingFormulaName[];
 extern const char kAutofillEnableRankingFormulaDescription[];
 
+extern const char kAutofillEnableRemadeDownstreamMetricsName[];
+extern const char kAutofillEnableRemadeDownstreamMetricsDescription[];
+
 extern const char kAutofillEnableSendingBcnInGetUploadDetailsName[];
 extern const char kAutofillEnableSendingBcnInGetUploadDetailsDescription[];
 
@@ -276,9 +279,6 @@
 extern const char kAutofillShowManualFallbackInContextMenuName[];
 extern const char kAutofillShowManualFallbackInContextMenuDescription[];
 
-extern const char kAutofillSuggestVirtualCardsOnIncompleteFormName[];
-extern const char kAutofillSuggestVirtualCardsOnIncompleteFormDescription[];
-
 extern const char kAutofillUpstreamAllowAdditionalEmailDomainsName[];
 extern const char kAutofillUpstreamAllowAdditionalEmailDomainsDescription[];
 
@@ -902,9 +902,6 @@
 extern const char kCompositorThreadedScrollbarScrollingName[];
 extern const char kCompositorThreadedScrollbarScrollingDescription[];
 
-extern const char kImpulseScrollAnimationsName[];
-extern const char kImpulseScrollAnimationsDescription[];
-
 extern const char kIncognitoBrandConsistencyForAndroidName[];
 extern const char kIncognitoBrandConsistencyForAndroidDescription[];
 
@@ -1074,6 +1071,9 @@
 extern const char kOmniboxMostVisitedTilesName[];
 extern const char kOmniboxMostVisitedTilesDescription[];
 
+extern const char kOmniboxMostVisitedTilesTitleWrapAroundName[];
+extern const char kOmniboxMostVisitedTilesTitleWrapAroundDescription[];
+
 extern const char kOmniboxOnDeviceHeadSuggestionsName[];
 extern const char kOmniboxOnDeviceHeadSuggestionsDescription[];
 
@@ -1223,8 +1223,8 @@
 extern const char kForcedColorsName[];
 extern const char kForcedColorsDescription[];
 
-extern const char kPercentBasedScrollingName[];
-extern const char kPercentBasedScrollingDescription[];
+extern const char kWindowsScrollingPersonalityName[];
+extern const char kWindowsScrollingPersonalityDescription[];
 
 extern const char kPermissionChipName[];
 extern const char kPermissionChipDescription[];
@@ -2829,6 +2829,9 @@
 extern const char kEnablePalmSuppressionName[];
 extern const char kEnablePalmSuppressionDescription[];
 
+extern const char kEnableSeamlessRefreshRateSwitchingName[];
+extern const char kEnableSeamlessRefreshRateSwitchingDescription[];
+
 extern const char kEnableTouchpadsInDiagnosticsAppName[];
 extern const char kEnableTouchpadsInDiagnosticsAppDescription[];
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index c83f34a..e68b169 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -345,6 +345,7 @@
     &omnibox::kAdaptiveSuggestionsCount,
     &omnibox::kAndroidAuxiliarySearch,
     &omnibox::kMostVisitedTiles,
+    &omnibox::kMostVisitedTilesTitleWrapAround,
     &omnibox::kOmniboxAssistantVoiceSearch,
     &omnibox::kOmniboxSpareRenderer,
     &omnibox::kSuggestionAnswersColorReverse,
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
index d7fdb88..659ebd2 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -428,6 +428,8 @@
             "AndroidAuxiliarySearch";
     public static final String OMNIBOX_ASSISTANT_VOICE_SEARCH = "OmniboxAssistantVoiceSearch";
     public static final String OMNIBOX_MODERNIZE_VISUAL_UPDATE = "OmniboxModernizeVisualUpdate";
+    public static final String OMNIBOX_MOST_VISITED_TILES_TITLE_WRAP_AROUND =
+            "OmniboxMostVisitedTilesTitleWrapAround";
     public static final String OMNIBOX_UPDATED_CONNECTION_SECURITY_INDICATORS =
             "OmniboxUpdatedConnectionSecurityIndicators";
     public static final String OPTIMIZATION_GUIDE_PUSH_NOTIFICATIONS =
diff --git a/chrome/browser/google/google_brand_code_map_chromeos.cc b/chrome/browser/google/google_brand_code_map_chromeos.cc
index 6c3502b..779e73e 100644
--- a/chrome/browser/google/google_brand_code_map_chromeos.cc
+++ b/chrome/browser/google/google_brand_code_map_chromeos.cc
@@ -95,6 +95,7 @@
                      {"CLQY", {"BBGR", "ULEA", "YDVH"}},
                      {"CLSF", {"OWOB", "RLJX", "OZWK"}},
                      {"CNOR", {"TEUF", "QHOY", "NQZD"}},
+                     {"CPOG", {"UNJV", "ZFSN", "DVWN"}},
                      {"CPPT", {"CQFF", "PCCZ", "HZEW"}},
                      {"CQFV", {"OJUW", "FCMY", "VCYR"}},
                      {"CQPQ", {"GATZ", "QAVU", "WRXC"}},
@@ -106,6 +107,7 @@
                      {"CZPM", {"ULQK", "OVJJ", "DUAD"}},
                      {"DBED", {"JUMI", "UTSY", "RXGS"}},
                      {"DBHI", {"MMGG", "MMQD", "XQDJ"}},
+                     {"DCCV", {"KCXJ", "YFYI", "CERZ"}},
                      {"DEAA", {"HXUG", "BJUN", "IYTV"}},
                      {"DEAB", {"ARPQ", "MFRJ", "JWTH"}},
                      {"DEAC", {"DSMM", "IXET", "KQDV"}},
@@ -198,6 +200,7 @@
                      {"HPZZ", {"FJGP", "GMLT", "SZQX"}},
                      {"HQLQ", {"PGTQ", "NSOB", "GIPH"}},
                      {"HRIZ", {"BJMA", "SKSL", "XBUU"}},
+                     {"HRQS", {"URJW", "GKSH", "VKWJ"}},
                      {"HTPV", {"LAEC", "NGRO", "BGEX"}},
                      {"HUIJ", {"EVJI", "RNMR", "JQZR"}},
                      {"HULX", {"TTWF", "UZES", "TATE"}},
@@ -235,14 +238,17 @@
                      {"JYXK", {"USZT", "XXPU", "LJHH"}},
                      {"KABJ", {"ISGW", "KOHG", "BPGB"}},
                      {"KBOV", {"PGBC", "IKKC", "AHSL"}},
+                     {"KCBW", {"QARD", "MZOT", "CKXF"}},
                      {"KDDA", {"DRSL", "IFNA", "BMDE"}},
                      {"KIMZ", {"FBTQ", "OKNU", "JZIT"}},
                      {"KLKW", {"PIDD", "JIKU", "QTVN"}},
                      {"KOKS", {"XCGR", "ZFVG", "PPCB"}},
                      {"KRTE", {"ILKK", "GNTB", "XFRA"}},
+                     {"KTLR", {"LCPY", "XBHO", "UZEJ"}},
                      {"KXUH", {"RIFT", "DZUO", "ZSEI"}},
                      {"KYYP", {"PGTZ", "VRVC", "HDRK"}},
                      {"LASN", {"ILWC", "BQYG", "RROZ"}},
+                     {"LBTV", {"WKQE", "IDDP", "VZLR"}},
                      {"LEAA", {"DHUB", "OBDS", "YMSJ"}},
                      {"LEAB", {"LRHX", "EFFC", "SZFH"}},
                      {"LEAC", {"DMEA", "EXWD", "PBTU"}},
@@ -468,6 +474,7 @@
                      {"VAFH", {"FMPL", "YJOM", "RNEF"}},
                      {"VEUT", {"JDFA", "ALIR", "DDJM"}},
                      {"VGYW", {"AAXS", "SHZF", "HYJU"}},
+                     {"VHRM", {"MLUQ", "JPNC", "YSRD"}},
                      {"VHUH", {"JYDF", "SFJY", "JMBU"}},
                      {"VICR", {"VNCX", "OLSV", "YCZO"}},
                      {"VJVS", {"BVOQ", "KREV", "QRKT"}},
@@ -496,6 +503,7 @@
                      {"WXZG", {"IUGR", "JOEE", "PTHY"}},
                      {"XAJY", {"UWFH", "BVFB", "OLQX"}},
                      {"XBWL", {"IQEI", "JEGU", "QSKW"}},
+                     {"XFAY", {"PVOG", "BLSS", "MEEN"}},
                      {"XFUX", {"UHAM", "NEHU", "SHMG"}},
                      {"XHVI", {"KVWL", "GQOJ", "JLLW"}},
                      {"XIYN", {"ZZYC", "OJOW", "NTKR"}},
@@ -508,6 +516,7 @@
                      {"XWJE", {"KDZI", "IYPJ", "ERIM"}},
                      {"YAVR", {"DHAY", "KBWN", "BBPJ"}},
                      {"YAZN", {"QIGR", "SHZH", "DKXN"}},
+                     {"YCLN", {"YYQD", "JECY", "XANP"}},
                      {"YEGM", {"SEQF", "OXKW", "OFEF"}},
                      {"YFVF", {"NPWS", "PUZZ", "TTCZ"}},
                      {"YGHA", {"BMDT", "AUXW", "GYPE"}},
@@ -527,6 +536,7 @@
                      {"ZBCZ", {"DUPJ", "ESKI", "KECI"}},
                      {"ZDKS", {"UBRP", "AWQF", "GOVG"}},
                      {"ZDYJ", {"JNGY", "SDRU", "YIEW"}},
+                     {"ZELG", {"TSMI", "QHCG", "BCAC"}},
                      {"ZFCZ", {"JQUA", "SEEH", "RJVV"}},
                      {"ZFNX", {"DMVG", "EVBR", "SUXX"}},
                      {"ZFVI", {"DHXX", "NXUJ", "HVXK"}},
@@ -537,6 +547,7 @@
                      {"ZLBC", {"DJCJ", "HNGZ", "IRYZ"}},
                      {"ZLJE", {"NIZI", "ZWAH", "OAQL"}},
                      {"ZMHB", {"YRPB", "KPOF", "SBIB"}},
+                     {"ZNCE", {"NWKT", "TEQM", "KDDB"}},
                      {"ZPIS", {"VXIY", "HUUG", "GHXQ"}},
                      {"ZSKM", {"JPEZ", "FTUS", "ZFUF"}},
                      {"ZSLY", {"TQED", "GKPV", "BHWH"}},
diff --git a/chrome/browser/media/router/mojo/media_router_mojo_impl_unittest.cc b/chrome/browser/media/router/mojo/media_router_mojo_impl_unittest.cc
index d237d845..e9f3b50 100644
--- a/chrome/browser/media/router/mojo/media_router_mojo_impl_unittest.cc
+++ b/chrome/browser/media/router/mojo/media_router_mojo_impl_unittest.cc
@@ -483,6 +483,7 @@
                               mojom::RouteRequestResultCode::TIMED_OUT, 1);
 }
 
+#if !BUILDFLAG(IS_ANDROID)
 TEST_F(MediaRouterMojoImplTest, HandleIssue) {
   MockIssuesObserver issue_observer1(router()->GetIssueManager());
   MockIssuesObserver issue_observer2(router()->GetIssueManager());
@@ -505,6 +506,7 @@
   EXPECT_EQ(issue_info, issue_from_observer1.info());
   EXPECT_EQ(issue_info, issue_from_observer2.info());
 }
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 TEST_F(MediaRouterMojoImplTest, RegisterAndUnregisterMediaSinksObserver) {
   MediaSource media_source(kSource);
diff --git a/chrome/browser/nearby_sharing/paired_key_verification_runner.cc b/chrome/browser/nearby_sharing/paired_key_verification_runner.cc
index 0e20eac..ac27023 100644
--- a/chrome/browser/nearby_sharing/paired_key_verification_runner.cc
+++ b/chrome/browser/nearby_sharing/paired_key_verification_runner.cc
@@ -298,6 +298,9 @@
   if (!certificate_->VerifySignature(
           PadPrefix(remote_prefix_, raw_token_),
           frame->get_paired_key_encryption()->signed_data)) {
+    NS_LOG(VERBOSE) << __func__
+                    << ": Unable to verify remote paired key encryption frame. "
+                       "Signature verification failed.";
     return PairedKeyVerificationResult::kFail;
   }
 
diff --git a/chrome/browser/policy/BUILD.gn b/chrome/browser/policy/BUILD.gn
index 7ed0774..0ffc7c4 100644
--- a/chrome/browser/policy/BUILD.gn
+++ b/chrome/browser/policy/BUILD.gn
@@ -176,7 +176,6 @@
     "test/certificate_transparency_policy_browsertest.cc",
     "test/event_path_policy_browsertest.cc",
     "test/network_prediction_policy_browsertest.cc",
-    "test/network_time_policy_browsertest.cc",
     "test/safe_browsing_policy_browsertest.cc",
     "test/shared_clipboard_enabled_browsertest.cc",
     "test/ssl_error_overriding_allowed_policy_browsertest.cc",
@@ -252,8 +251,11 @@
     ]
   }
 
-  if (!is_android && !is_chromeos_ash) {
-    sources += [ "policy_startup_browsertest.cc" ]
+  if (!is_android && !is_chromeos) {
+    sources += [
+      "policy_startup_browsertest.cc",
+      "test/network_time_policy_browsertest.cc",
+    ]
   }
 
   if (!is_chromeos_ash) {
@@ -391,14 +393,16 @@
   }
 
   if (!is_android && !is_chromeos) {
-    sources += [ "test/native_messaging_policy_browsertest.cc" ]
+    sources += [
+      "test/hardware_acceleration_mode_enabled_browsertest.cc",
+      "test/native_messaging_policy_browsertest.cc",
+    ]
   }
 
   if (!is_android && !is_chromeos_ash) {
     sources += [
       "cloud/chrome_browser_cloud_management_browsertest_delegate_desktop.cc",
       "cloud/chrome_browser_cloud_management_browsertest_delegate_desktop.h",
-      "test/hardware_acceleration_mode_enabled_browsertest.cc",
     ]
   }
 
diff --git a/chrome/browser/policy/cloud/cloud_policy_invalidator.cc b/chrome/browser/policy/cloud/cloud_policy_invalidator.cc
index c01d69c..b9bb7c4 100644
--- a/chrome/browser/policy/cloud/cloud_policy_invalidator.cc
+++ b/chrome/browser/policy/cloud/cloud_policy_invalidator.cc
@@ -475,6 +475,7 @@
 }
 
 void CloudPolicyInvalidator::UpdateMaxFetchDelay(const PolicyMap& policy_map) {
+#if !BUILDFLAG(IS_ANDROID)
   // Try reading the delay from the policy.
   const base::Value* delay_policy_value = policy_map.GetValue(
       key::kMaxInvalidationFetchDelay, base::Value::Type::INTEGER);
@@ -482,6 +483,7 @@
     set_max_fetch_delay(delay_policy_value->GetInt());
     return;
   }
+#endif
 
   set_max_fetch_delay(kMaxFetchDelayDefault);
 }
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 5a47c7d..cb10d75 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -349,6 +349,15 @@
     prefs::kClickToCallEnabled,
     base::Value::Type::BOOLEAN },
 #endif  // BUILDFLAG(ENABLE_CLICK_TO_CALL)
+  { key::kClipboardAllowedForUrls,
+    prefs::kManagedClipboardAllowedForUrls,
+    base::Value::Type::LIST },
+  { key::kClipboardBlockedForUrls,
+    prefs::kManagedClipboardBlockedForUrls,
+    base::Value::Type::LIST },
+  { key::kDefaultClipboardSetting,
+    prefs::kManagedDefaultClipboardSetting,
+    base::Value::Type::INTEGER },
   { key::kDNSInterceptionChecksEnabled,
     prefs::kDNSInterceptionChecksEnabled,
     base::Value::Type::BOOLEAN },
@@ -421,6 +430,9 @@
   { key::kFileSystemWriteBlockedForUrls,
     prefs::kManagedFileSystemWriteBlockedForUrls,
     base::Value::Type::LIST },
+  { key::kForcedLanguages,
+    language::prefs::kForcedLanguages,
+    base::Value::Type::LIST },
   { key::kGloballyScopeHTTPAuthCacheEnabled,
     prefs::kGloballyScopeHTTPAuthCacheEnabled,
     base::Value::Type::BOOLEAN },
@@ -463,6 +475,9 @@
   { key::kLocalFontsBlockedForUrls,
     prefs::kManagedLocalFontsBlockedForUrls,
     base::Value::Type::LIST },
+  { key::kMaxConnectionsPerProxy,
+    prefs::kMaxConnectionsPerProxy,
+    base::Value::Type::INTEGER },
   { key::kMediaRouterCastAllowAllIPs,
     media_router::prefs::kMediaRouterCastAllowAllIPs,
     base::Value::Type::BOOLEAN },
@@ -668,12 +683,6 @@
   { key::kUnthrottledNestedTimeoutEnabled,
     policy_prefs::kUnthrottledNestedTimeoutEnabled,
     base::Value::Type::BOOLEAN },
-  { key::kDisablePrintPreview,
-    prefs::kPrintPreviewDisabled,
-    base::Value::Type::BOOLEAN },
-  { key::kAlwaysOpenPdfExternally,
-    prefs::kPluginsAlwaysOpenPdfExternally,
-    base::Value::Type::BOOLEAN },
   { key::kDefaultCookiesSetting,
     prefs::kManagedDefaultCookiesSetting,
     base::Value::Type::INTEGER },
@@ -710,13 +719,13 @@
   { key::kDefaultGeolocationSetting,
     prefs::kManagedDefaultGeolocationSetting,
     base::Value::Type::INTEGER },
-  { key::kCloudManagementEnrollmentMandatory,
-    policy_prefs::kCloudManagementEnrollmentMandatory,
-    base::Value::Type::BOOLEAN },
   #if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN)
   { key::kRequireOnlineRevocationChecksForLocalAnchors,
     prefs::kCertRevocationCheckingRequiredLocalAnchors,
     base::Value::Type::BOOLEAN },
+  { key::kFullscreenAllowed,
+    prefs::kFullscreenAllowed,
+    base::Value::Type::BOOLEAN },
 #endif  // #if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN)
   { key::kAuthSchemes,
     prefs::kAuthSchemes,
@@ -729,23 +738,7 @@
     base::Value::Type::STRING },
   { key::kAuthServerAllowlist,
     prefs::kAuthServerAllowlist,
-    base::Value::Type::STRING
-  },
-  { key::kDiskCacheSize,
-    prefs::kDiskCacheSize,
-    base::Value::Type::INTEGER },
-  { key::kDefaultBrowserSettingEnabled,
-    prefs::kDefaultBrowserSettingEnabled,
-    base::Value::Type::BOOLEAN },
-  { key::kCloudPrintProxyEnabled,
-    prefs::kCloudPrintProxyEnabled,
-    base::Value::Type::BOOLEAN },
-  { key::kShowAppsShortcutInBookmarkBar,
-    bookmarks::prefs::kShowAppsShortcutInBookmarkBar,
-    base::Value::Type::BOOLEAN },
-  { key::kAllowFileSelectionDialogs,
-    prefs::kAllowFileSelectionDialogs,
-    base::Value::Type::BOOLEAN },
+    base::Value::Type::STRING },
   { key::kPromptForDownloadLocation,
     prefs::kPromptForDownload,
     base::Value::Type::BOOLEAN },
@@ -761,63 +754,7 @@
   { key::kSensorsBlockedForUrls,
     prefs::kManagedSensorsBlockedForUrls,
     base::Value::Type::LIST },
-  { key::kDefaultClipboardSetting,
-    prefs::kManagedDefaultClipboardSetting,
-    base::Value::Type::INTEGER },
-  { key::kClipboardAllowedForUrls,
-    prefs::kManagedClipboardAllowedForUrls,
-    base::Value::Type::LIST },
-  { key::kClipboardBlockedForUrls,
-    prefs::kManagedClipboardBlockedForUrls,
-    base::Value::Type::LIST },
 
-  // First run import.
-  { key::kImportBookmarks,
-    prefs::kImportBookmarks,
-    base::Value::Type::BOOLEAN },
-  { key::kImportHistory,
-    prefs::kImportHistory,
-    base::Value::Type::BOOLEAN },
-  { key::kImportHomepage,
-    prefs::kImportHomepage,
-    base::Value::Type::BOOLEAN },
-  { key::kImportSearchEngine,
-    prefs::kImportSearchEngine,
-    base::Value::Type::BOOLEAN },
-  { key::kImportSavedPasswords,
-    prefs::kImportSavedPasswords,
-    base::Value::Type::BOOLEAN },
-  { key::kImportAutofillFormData,
-    prefs::kImportAutofillFormData,
-    base::Value::Type::BOOLEAN },
-  { key::kRemoteDebuggingAllowed,
-    prefs::kDevToolsRemoteDebuggingAllowed,
-    base::Value::Type::BOOLEAN },
-
-  // Import data dialog: controlled by same policies as first run import, but
-  // uses different prefs.
-  { key::kImportBookmarks,
-    prefs::kImportDialogBookmarks,
-    base::Value::Type::BOOLEAN },
-  { key::kImportHistory,
-    prefs::kImportDialogHistory,
-    base::Value::Type::BOOLEAN },
-  { key::kImportSearchEngine,
-    prefs::kImportDialogSearchEngine,
-    base::Value::Type::BOOLEAN },
-  { key::kImportSavedPasswords,
-    prefs::kImportDialogSavedPasswords,
-    base::Value::Type::BOOLEAN },
-  { key::kImportAutofillFormData,
-    prefs::kImportDialogAutofillFormData,
-    base::Value::Type::BOOLEAN },
-
-  { key::kMaxConnectionsPerProxy,
-    prefs::kMaxConnectionsPerProxy,
-    base::Value::Type::INTEGER },
-  { key::kRestrictSigninToPattern,
-    prefs::kGoogleServicesUsernamePattern,
-    base::Value::Type::STRING },
   { key::kDefaultWebBluetoothGuardSetting,
     prefs::kManagedDefaultWebBluetoothGuardSetting,
     base::Value::Type::INTEGER },
@@ -830,18 +767,12 @@
   { key::kSSLErrorOverrideAllowedForOrigins,
     prefs::kSSLErrorOverrideAllowedForOrigins,
     base::Value::Type::LIST },
-  { key::kHardwareAccelerationModeEnabled,
-    prefs::kHardwareAccelerationModeEnabled,
-    base::Value::Type::BOOLEAN },
   { key::kAllowedDomainsForApps,
     prefs::kAllowedDomainsForApps,
     base::Value::Type::STRING },
   { key::kVariationsRestrictParameter,
     variations::prefs::kVariationsRestrictParameter,
     base::Value::Type::STRING },
-  { key::kForceEphemeralProfiles,
-    prefs::kForceEphemeralProfiles,
-    base::Value::Type::BOOLEAN },
   { key::kSSLVersionMin,
     prefs::kSSLVersionMin,
     base::Value::Type::STRING },
@@ -851,12 +782,6 @@
   { key::kWebRtcUdpPortRange,
     prefs::kWebRTCUDPPortRange,
     base::Value::Type::STRING },
-  { key::kRoamingProfileSupportEnabled,
-    syncer::prefs::kEnableLocalSyncBackend,
-    base::Value::Type::BOOLEAN },
-  { key::kBrowserNetworkTimeQueriesEnabled,
-    network_time::prefs::kNetworkTimeQueriesEnabled,
-    base::Value::Type::BOOLEAN },
   { key::kDefaultWebUsbGuardSetting,
     prefs::kManagedDefaultWebUsbGuardSetting,
     base::Value::Type::INTEGER },
@@ -872,19 +797,12 @@
   { key::kEnterpriseHardwarePlatformAPIEnabled,
     prefs::kEnterpriseHardwarePlatformAPIEnabled,
     base::Value::Type::BOOLEAN },
-  { key::kExternalProtocolDialogShowAlwaysOpenCheckbox,
-    prefs::kExternalProtocolDialogShowAlwaysOpenCheckbox,
-    base::Value::Type::BOOLEAN },
   { key::kPasswordLeakDetectionEnabled,
     password_manager::prefs::kPasswordLeakDetectionEnabled,
     base::Value::Type::BOOLEAN },
   { key::kPaymentMethodQueryEnabled,
     payments::kCanMakePaymentEnabled,
     base::Value::Type::BOOLEAN },
-  { key::kForcedLanguages,
-    language::prefs::kForcedLanguages,
-    base::Value::Type::LIST },
-
 
 #if !BUILDFLAG(IS_CHROMEOS)
   { key::kMetricsReportingEnabled,
@@ -1474,9 +1392,100 @@
     prefs::kTotalMemoryLimitMb,
     base::Value::Type::INTEGER },
 #endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
-
-
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
+  { key::kBackgroundModeEnabled,
+    prefs::kBackgroundModeEnabled,
+    base::Value::Type::BOOLEAN },
+#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
+  { key::kDefaultBrowserSettingEnabled,
+    prefs::kDefaultBrowserSettingEnabled,
+    base::Value::Type::BOOLEAN },
+  { key::kRoamingProfileSupportEnabled,
+    syncer::prefs::kEnableLocalSyncBackend,
+    base::Value::Type::BOOLEAN },
+#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
+  // First run import.
+  { key::kImportBookmarks,
+    prefs::kImportBookmarks,
+    base::Value::Type::BOOLEAN },
+  { key::kImportHistory,
+    prefs::kImportHistory,
+    base::Value::Type::BOOLEAN },
+  { key::kImportHomepage,
+    prefs::kImportHomepage,
+    base::Value::Type::BOOLEAN },
+  { key::kImportSearchEngine,
+    prefs::kImportSearchEngine,
+    base::Value::Type::BOOLEAN },
+  { key::kImportSavedPasswords,
+    prefs::kImportSavedPasswords,
+    base::Value::Type::BOOLEAN },
+  { key::kImportAutofillFormData,
+    prefs::kImportAutofillFormData,
+    base::Value::Type::BOOLEAN },
+  { key::kRemoteDebuggingAllowed,
+    prefs::kDevToolsRemoteDebuggingAllowed,
+    base::Value::Type::BOOLEAN },
+
+  // Import data dialog: controlled by same policies as first run import, but
+  // uses different prefs.
+  { key::kImportBookmarks,
+    prefs::kImportDialogBookmarks,
+    base::Value::Type::BOOLEAN },
+  { key::kImportHistory,
+    prefs::kImportDialogHistory,
+    base::Value::Type::BOOLEAN },
+  { key::kImportSearchEngine,
+    prefs::kImportDialogSearchEngine,
+    base::Value::Type::BOOLEAN },
+  { key::kImportSavedPasswords,
+    prefs::kImportDialogSavedPasswords,
+    base::Value::Type::BOOLEAN },
+  { key::kImportAutofillFormData,
+    prefs::kImportDialogAutofillFormData,
+    base::Value::Type::BOOLEAN },
+
+  { key::kIdleProfileCloseTimeout,
+    prefs::kIdleProfileCloseTimeout,
+    base::Value::Type::INTEGER },
+  { key::kRestrictSigninToPattern,
+    prefs::kGoogleServicesUsernamePattern,
+    base::Value::Type::STRING },
+  { key::kHardwareAccelerationModeEnabled,
+    prefs::kHardwareAccelerationModeEnabled,
+    base::Value::Type::BOOLEAN },
+  { key::kForceEphemeralProfiles,
+    prefs::kForceEphemeralProfiles,
+    base::Value::Type::BOOLEAN },
+  { key::kBrowserNetworkTimeQueriesEnabled,
+    network_time::prefs::kNetworkTimeQueriesEnabled,
+    base::Value::Type::BOOLEAN },
+  { key::kExternalProtocolDialogShowAlwaysOpenCheckbox,
+    prefs::kExternalProtocolDialogShowAlwaysOpenCheckbox,
+    base::Value::Type::BOOLEAN },
+  { key::kAllowFileSelectionDialogs,
+    prefs::kAllowFileSelectionDialogs,
+    base::Value::Type::BOOLEAN },
+  { key::kShowAppsShortcutInBookmarkBar,
+    bookmarks::prefs::kShowAppsShortcutInBookmarkBar,
+    base::Value::Type::BOOLEAN },
+  { key::kCloudPrintProxyEnabled,
+    prefs::kCloudPrintProxyEnabled,
+    base::Value::Type::BOOLEAN },
+  { key::kDiskCacheSize,
+    prefs::kDiskCacheSize,
+    base::Value::Type::INTEGER },
+  { key::kCloudManagementEnrollmentMandatory,
+    policy_prefs::kCloudManagementEnrollmentMandatory,
+    base::Value::Type::BOOLEAN },
+  { key::kDisablePrintPreview,
+    prefs::kPrintPreviewDisabled,
+    base::Value::Type::BOOLEAN },
+  { key::kAlwaysOpenPdfExternally,
+    prefs::kPluginsAlwaysOpenPdfExternally,
+    base::Value::Type::BOOLEAN },
   { key::kNativeMessagingUserLevelHosts,
     extensions::pref_names::kNativeMessagingUserLevelHosts,
     base::Value::Type::BOOLEAN },
@@ -1569,24 +1578,12 @@
     base::Value::Type::LIST },
 #endif // BUILDFLAG(IS_CHROMEOS)
 
-#if !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_CHROMEOS)
-  { key::kBackgroundModeEnabled,
-    prefs::kBackgroundModeEnabled,
-    base::Value::Type::BOOLEAN },
-#endif // !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_CHROMEOS)
-
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS)
   { key::kAuthNegotiateDelegateByKdcPolicy,
     prefs::kAuthNegotiateDelegateByKdcPolicy,
     base::Value::Type::BOOLEAN },
 #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS)
 
-#if !BUILDFLAG(IS_MAC)
-  { key::kFullscreenAllowed,
-    prefs::kFullscreenAllowed,
-    base::Value::Type::BOOLEAN },
-#endif  // !BUILDFLAG(IS_MAC)
-
 #if !BUILDFLAG(IS_MAC) && BUILDFLAG(ENABLE_EXTENSIONS)
   { key::kFullscreenAllowed,
     extensions::pref_names::kAppFullscreenAllowed,
@@ -1685,9 +1682,6 @@
   { key::kEventPathEnabled,
     policy_prefs::kEventPathEnabled,
     base::Value::Type::BOOLEAN},
-  { key::kIdleProfileCloseTimeout,
-    prefs::kIdleProfileCloseTimeout,
-    base::Value::Type::INTEGER },
 };
 // clang-format on
 
diff --git a/chrome/browser/policy/printing_restrictions_policy_handler.cc b/chrome/browser/policy/printing_restrictions_policy_handler.cc
index d2caecb..0d08203 100644
--- a/chrome/browser/policy/printing_restrictions_policy_handler.cc
+++ b/chrome/browser/policy/printing_restrictions_policy_handler.cc
@@ -144,6 +144,7 @@
 PrintingPinDefaultPolicyHandler::~PrintingPinDefaultPolicyHandler() = default;
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
+#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
 PrintingAllowedBackgroundGraphicsModesPolicyHandler::
     PrintingAllowedBackgroundGraphicsModesPolicyHandler()
     : PrintingEnumPolicyHandler<printing::BackgroundGraphicsModeRestriction>(
@@ -280,8 +281,6 @@
   }
 }
 
-#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
-
 PrintPdfAsImageDefaultPolicyHandler::PrintPdfAsImageDefaultPolicyHandler()
     : TypeCheckingPolicyHandler(key::kPrintPdfAsImageDefault,
                                 base::Value::Type::BOOLEAN) {}
diff --git a/chrome/browser/policy/printing_restrictions_policy_handler.h b/chrome/browser/policy/printing_restrictions_policy_handler.h
index c8e2431..5c6371e1 100644
--- a/chrome/browser/policy/printing_restrictions_policy_handler.h
+++ b/chrome/browser/policy/printing_restrictions_policy_handler.h
@@ -92,6 +92,7 @@
 };
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
+#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
 class PrintingAllowedBackgroundGraphicsModesPolicyHandler
     : public PrintingEnumPolicyHandler<
           printing::BackgroundGraphicsModeRestriction> {
@@ -128,8 +129,6 @@
                 const base::Value** result);
 };
 
-#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
-
 class PrintPdfAsImageDefaultPolicyHandler : public TypeCheckingPolicyHandler {
  public:
   PrintPdfAsImageDefaultPolicyHandler();
diff --git a/chrome/browser/policy/printing_restrictions_policy_handler_unittest.cc b/chrome/browser/policy/printing_restrictions_policy_handler_unittest.cc
index 63d5c3f..249f6a0 100644
--- a/chrome/browser/policy/printing_restrictions_policy_handler_unittest.cc
+++ b/chrome/browser/policy/printing_restrictions_policy_handler_unittest.cc
@@ -18,6 +18,7 @@
 
 namespace policy {
 
+#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
 class PrintingRestrictionsPolicyHandlerTest : public testing::Test {
  protected:
   void SetPolicy(base::Value value) {
@@ -128,8 +129,6 @@
   CheckInvalidPolicy(kNoHeightInCustomSize);
 }
 
-#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
-
 class PrintPdfAsImageRestrictionsPolicyHandlerTest : public testing::Test {
  protected:
   void SetPolicy(const std::string& policy_name, base::Value value) {
diff --git a/chrome/browser/policy/site_isolation_policy_browsertest.cc b/chrome/browser/policy/site_isolation_policy_browsertest.cc
index 4eb431d..0d55eb1 100644
--- a/chrome/browser/policy/site_isolation_policy_browsertest.cc
+++ b/chrome/browser/policy/site_isolation_policy_browsertest.cc
@@ -110,6 +110,37 @@
 typedef SitePerProcessPolicyBrowserTest<false>
     SitePerProcessPolicyBrowserTestDisabled;
 
+// Ensure that --disable-site-isolation-trials and/or
+// --disable-site-isolation-for-enterprise-policy do not override policies.
+class NoOverrideSitePerProcessPolicyBrowserTest
+    : public SitePerProcessPolicyBrowserTestEnabled {
+ public:
+  NoOverrideSitePerProcessPolicyBrowserTest(
+      const NoOverrideSitePerProcessPolicyBrowserTest&) = delete;
+  NoOverrideSitePerProcessPolicyBrowserTest& operator=(
+      const NoOverrideSitePerProcessPolicyBrowserTest&) = delete;
+
+ protected:
+  NoOverrideSitePerProcessPolicyBrowserTest() = default;
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    command_line->AppendSwitch(switches::kDisableSiteIsolation);
+#if BUILDFLAG(IS_ANDROID)
+    command_line->AppendSwitch(switches::kDisableSiteIsolationForPolicy);
+#endif
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(SitePerProcessPolicyBrowserTestEnabled, Simple) {
+  Expectations expectations[] = {
+      {"https://foo.com/noodles.html", true},
+      {"http://foo.com/", true},
+      {"http://example.org/pumpkins.html", true},
+  };
+  CheckExpectations(expectations, std::size(expectations));
+}
+
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
+// The policy is not supported on Android
 class IsolateOriginsPolicyBrowserTest : public SiteIsolationPolicyBrowserTest {
  public:
   IsolateOriginsPolicyBrowserTest(const IsolateOriginsPolicyBrowserTest&) =
@@ -141,37 +172,6 @@
   }
 };
 
-// Ensure that --disable-site-isolation-trials and/or
-// --disable-site-isolation-for-enterprise-policy do not override policies.
-class NoOverrideSitePerProcessPolicyBrowserTest
-    : public SitePerProcessPolicyBrowserTestEnabled {
- public:
-  NoOverrideSitePerProcessPolicyBrowserTest(
-      const NoOverrideSitePerProcessPolicyBrowserTest&) = delete;
-  NoOverrideSitePerProcessPolicyBrowserTest& operator=(
-      const NoOverrideSitePerProcessPolicyBrowserTest&) = delete;
-
- protected:
-  NoOverrideSitePerProcessPolicyBrowserTest() = default;
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitch(switches::kDisableSiteIsolation);
-#if BUILDFLAG(IS_ANDROID)
-    command_line->AppendSwitch(switches::kDisableSiteIsolationForPolicy);
-#endif
-  }
-};
-
-IN_PROC_BROWSER_TEST_F(SitePerProcessPolicyBrowserTestEnabled, Simple) {
-  Expectations expectations[] = {
-      {"https://foo.com/noodles.html", true},
-      {"http://foo.com/", true},
-      {"http://example.org/pumpkins.html", true},
-  };
-  CheckExpectations(expectations, std::size(expectations));
-}
-
-#if !BUILDFLAG(IS_ANDROID)
-// The policy is not supported on Android
 IN_PROC_BROWSER_TEST_F(IsolateOriginsPolicyBrowserTest, Simple) {
   // Verify that the policy present at browser startup is correctly applied.
   Expectations expectations[] = {
diff --git a/chrome/browser/profiles/profile_keyed_service_browsertest.cc b/chrome/browser/profiles/profile_keyed_service_browsertest.cc
index 53e7208..0fc758d 100644
--- a/chrome/browser/profiles/profile_keyed_service_browsertest.cc
+++ b/chrome/browser/profiles/profile_keyed_service_browsertest.cc
@@ -404,6 +404,7 @@
     "SharingMessageBridge",
     "SharingService",
     "ShoppingService",
+    "SidePanelService",
     "SigninErrorController",
     "SigninManager",
     "SigninProfileAttributesUpdater",
diff --git a/chrome/browser/renderer_context_menu/link_to_text_menu_observer.h b/chrome/browser/renderer_context_menu/link_to_text_menu_observer.h
index 6a7914e..8c8d96c 100644
--- a/chrome/browser/renderer_context_menu/link_to_text_menu_observer.h
+++ b/chrome/browser/renderer_context_menu/link_to_text_menu_observer.h
@@ -48,9 +48,6 @@
       RenderViewContextMenuProxy* proxy,
       content::GlobalRenderFrameHostId render_frame_host_id,
       CompletionCallback callback);
-  // Returns true if the link should be generated from the constructor, vs
-  // determined when executed.
-  bool ShouldPreemptivelyGenerateLink();
 
   // Requests link generation if needed.
   void RequestLinkGeneration();
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 374f373..ebf2438 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -147,6 +147,8 @@
 #include "components/translate/core/browser/translate_prefs.h"
 #include "components/translate/core/common/translate_util.h"
 #include "components/url_formatter/url_formatter.h"
+#include "components/user_notes/browser/user_note_manager.h"
+#include "components/user_notes/user_notes_features.h"
 #include "components/user_prefs/user_prefs.h"
 #include "components/web_modal/web_contents_modal_dialog_manager.h"
 #include "content/public/browser/browser_context.h"
@@ -444,13 +446,14 @@
        {IDC_CONTENT_CONTEXT_AUTOFILL_CUSTOM_FIRST, 121},
        {IDC_CONTENT_CONTEXT_RUN_PDF_OCR, 122},
        {IDC_CONTENT_CONTEXT_PARTIAL_TRANSLATE, 123},
+       {IDC_CONTENT_CONTEXT_ADD_A_NOTE, 124},
        // To add new items:
        //   - Add one more line above this comment block, using the UMA value
        //     from the line below this comment block.
        //   - Increment the UMA value in that latter line.
        //   - Add the new item to the RenderViewContextMenuItem enum in
        //     tools/metrics/histograms/enums.xml.
-       {0, 124}});
+       {0, 125}});
 
   // These UMA values are for the the ContextMenuOptionDesktop enum, used for
   // the ContextMenu.SelectedOptionDesktop histograms.
@@ -481,13 +484,14 @@
        {IDC_CONTENT_CONTEXT_WEB_REGION_SEARCH, 23},
        {IDC_CONTENT_CONTEXT_RESHARELINKTOTEXT, 24},
        {IDC_OPEN_LINK_IN_PROFILE_FIRST, 25},
+       {IDC_CONTENT_CONTEXT_ADD_A_NOTE, 26},
        // To add new items:
        //   - Add one more line above this comment block, using the UMA value
        //     from the line below this comment block.
        //   - Increment the UMA value in that latter line.
        //   - Add the new item to the ContextMenuOptionDesktop enum in
        //     tools/metrics/histograms/enums.xml.
-       {0, 26}});
+       {0, 27}});
 
   return *(type == UmaEnumIdLookupType::GeneralEnumId ? kGeneralMap
                                                       : kSpecificMap);
@@ -995,6 +999,11 @@
     AppendLinkToTextItems();
   }
 
+  if (user_notes::IsUserNotesEnabled() &&
+      base::FeatureList::IsEnabled(features::kUnifiedSidePanel)) {
+    AppendUserNotesItems();
+  }
+
   if (!content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_LINK))
     AppendSharingItems();
 
@@ -2439,6 +2448,9 @@
     case IDC_CONTENT_CONTEXT_OPEN_IN_READ_ANYTHING:
       return true;
 
+    case IDC_CONTENT_CONTEXT_ADD_A_NOTE:
+      return IsAddANoteEnabled();
+
     case IDC_CONTENT_CONTEXT_EXIT_FULLSCREEN:
       return true;
 
@@ -2630,6 +2642,10 @@
       ExecOpenInReadAnything();
       break;
 
+    case IDC_CONTENT_CONTEXT_ADD_A_NOTE:
+      ExecAddANote();
+      break;
+
     case IDC_CONTENT_CONTEXT_LENS_REGION_SEARCH:
       ExecRegionSearch(event_flags, true);
       break;
@@ -3216,6 +3232,20 @@
 #endif
 }
 
+bool RenderViewContextMenu::IsAddANoteEnabled() const {
+  DCHECK(user_notes::IsUserNotesEnabled());
+  DCHECK(base::FeatureList::IsEnabled(features::kUnifiedSidePanel));
+
+  RenderFrameHost* render_frame_host = GetRenderFrameHost();
+  if (!render_frame_host)
+    return false;
+
+  if (!render_frame_host->IsInPrimaryMainFrame())
+    return false;
+
+  return user_notes::UserNoteManager::GetForPage(render_frame_host->GetPage());
+}
+
 // Returns true if the item was appended.
 bool RenderViewContextMenu::AppendQRCodeGeneratorItem(bool for_image,
                                                       bool draw_icon,
@@ -3258,6 +3288,11 @@
 #endif
 }
 
+void RenderViewContextMenu::AppendUserNotesItems() {
+  menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_ADD_A_NOTE,
+                                  IDS_CONTENT_CONTEXT_ADD_A_NOTE);
+}
+
 // Returns true if the item was appended (along with a SEPARATOR).
 bool RenderViewContextMenu::AppendFollowUnfollowItem() {
   TabWebFeedFollowState follow_state =
@@ -3502,6 +3537,23 @@
       lens::features::IsLensSidePanelEnabled());
 }
 
+void RenderViewContextMenu::ExecAddANote() {
+  RenderFrameHost* render_frame_host = GetRenderFrameHost();
+  if (!render_frame_host)
+    return;
+
+  DCHECK(render_frame_host->IsInPrimaryMainFrame());
+
+  auto* notes_manager =
+      user_notes::UserNoteManager::GetForPage(render_frame_host->GetPage());
+  if (!notes_manager)
+    return;
+
+  bool has_text_selection = !params().selection_text.empty();
+
+  notes_manager->OnAddNoteRequested(render_frame_host, has_text_selection);
+}
+
 void RenderViewContextMenu::ExecRegionSearch(
     int event_flags,
     bool is_google_default_search_provider) {
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.h b/chrome/browser/renderer_context_menu/render_view_context_menu.h
index 5447107..e2650ee 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.h
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.h
@@ -243,6 +243,7 @@
   void AppendRegionSearchItem();
   bool AppendFollowUnfollowItem();
   void AppendSendTabToSelfItem(bool add_separator);
+  void AppendUserNotesItems();
   bool AppendQRCodeGeneratorItem(bool for_image,
                                  bool draw_icon,
                                  bool add_separator);
@@ -270,6 +271,7 @@
   bool IsOpenLinkOTREnabled() const;
   bool IsSearchWebForEnabled() const;
   bool IsRegionSearchEnabled() const;
+  bool IsAddANoteEnabled() const;
 
   // Command execution functions.
   void ExecOpenWebApp();
@@ -283,6 +285,7 @@
   void ExecCopyLinkText();
   void ExecCopyImageAt();
   void ExecSearchLensForImage();
+  void ExecAddANote();
   void ExecRegionSearch(int event_flags,
                         bool is_google_default_search_provider);
   void ExecSearchWebForImage();
diff --git a/chrome/browser/resources/bookmarks/actions.ts b/chrome/browser/resources/bookmarks/actions.ts
index 511e971c..acd5b98 100644
--- a/chrome/browser/resources/bookmarks/actions.ts
+++ b/chrome/browser/resources/bookmarks/actions.ts
@@ -249,11 +249,11 @@
   results: string[],
 };
 
-export function setSearchResults(ids: string[]): Action {
+export function setSearchResults(ids: string[]): FinishSearchAction {
   return {
     name: 'finish-search',
     results: ids,
-  } as Action;
+  };
 }
 
 export type SetPrefAction = Action&{
diff --git a/chrome/browser/resources/browsing_topics/browsing_topics_internals.ts b/chrome/browser/resources/browsing_topics/browsing_topics_internals.ts
index 31b7518..79bc514f 100644
--- a/chrome/browser/resources/browsing_topics/browsing_topics_internals.ts
+++ b/chrome/browser/resources/browsing_topics/browsing_topics_internals.ts
@@ -10,7 +10,7 @@
 
 import {PageHandler, PageHandlerRemote, WebUITopic} from './browsing_topics_internals.mojom-webui.js';
 
-let pageHandler = {} as PageHandlerRemote;
+let pageHandler: PageHandlerRemote|null = null;
 let hostsClassificationSequenceNumber = 0;
 
 function setElementVisible(id: string, visible: boolean) {
@@ -118,6 +118,7 @@
 }
 
 async function asyncGetBrowsingTopicsConfiguration() {
+  assert(pageHandler);
   const response = await pageHandler.getBrowsingTopicsConfiguration();
 
   const config = response.config;
@@ -171,6 +172,7 @@
   setButtonEnabled('refresh-topics-state-button', false);
   setButtonEnabled('calculate-now-button', false);
 
+  assert(pageHandler);
   const response = await pageHandler.getBrowsingTopicsState(calculateNow);
 
   setButtonEnabled('refresh-topics-state-button', true);
@@ -243,6 +245,7 @@
   let topicsForHosts = [] as WebUITopic[][];
 
   if (hosts.length > 0) {
+    assert(pageHandler);
     const response = await pageHandler.classifyHosts(hosts);
     topicsForHosts = response.topicsForHosts;
   }
@@ -283,6 +286,7 @@
 }
 
 async function asyncGetModelInfo() {
+  assert(pageHandler);
   const response = await pageHandler.getModelInfo();
 
   setElementVisible('model-info-loader', false);
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/accessibility_common_loader.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/accessibility_common_loader.js
index 3c553d8d..d4ccd67 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/accessibility_common_loader.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/accessibility_common_loader.js
@@ -2,10 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {Autoclick} from '/accessibility_common/autoclick/autoclick.js';
-import {Dictation} from '/accessibility_common/dictation/dictation.js';
-import {Magnifier} from '/accessibility_common/magnifier/magnifier.js';
-import {InstanceChecker} from '/common/instance_checker.js';
+import {InstanceChecker} from '../common/instance_checker.js';
+
+import {Autoclick} from './autoclick/autoclick.js';
+import {Dictation} from './dictation/dictation.js';
+import {Magnifier} from './magnifier/magnifier.js';
 
 /**
  * Class to manage loading resources depending on which Accessibility features
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 31962c5f..8808556 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation.js
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {FocusHandler} from '/accessibility_common/dictation/focus_handler.js';
-import {InputController} from '/accessibility_common/dictation/input_controller.js';
-import {HiddenMacroManager} from '/accessibility_common/dictation/macros/hidden_macro_manager.js';
-import {Macro} from '/accessibility_common/dictation/macros/macro.js';
-import {MacroName} from '/accessibility_common/dictation/macros/macro_names.js';
-import {MetricsUtils} from '/accessibility_common/dictation/metrics_utils.js';
-import {SpeechParser} from '/accessibility_common/dictation/parse/speech_parser.js';
-import {HintContext, UIController, UIState} from '/accessibility_common/dictation/ui_controller.js';
+import {FocusHandler} from './focus_handler.js';
+import {InputController} from './input_controller.js';
+import {HiddenMacroManager} from './macros/hidden_macro_manager.js';
+import {Macro} from './macros/macro.js';
+import {MacroName} from './macros/macro_names.js';
+import {MetricsUtils} from './metrics_utils.js';
+import {SpeechParser} from './parse/speech_parser.js';
+import {HintContext, UIController, UIState} from './ui_controller.js';
 
 const ErrorEvent = chrome.speechRecognitionPrivate.SpeechRecognitionErrorEvent;
 const ResultEvent =
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 517baaf7..df8f731d 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
@@ -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 {EditingUtil} from '/accessibility_common/dictation/editing_util.js';
+import {EditingUtil} from './editing_util.js';
 
 const EventType = chrome.automation.EventType;
 
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/delete_prev_sent_macro.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/delete_prev_sent_macro.js
index 0b27936..e3891d0 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/delete_prev_sent_macro.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/delete_prev_sent_macro.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 {Macro, MacroError} from '/accessibility_common/dictation/macros/macro.js';
-import {MacroName} from '/accessibility_common/dictation/macros/macro_names.js';
+import {Macro, MacroError} from './macro.js';
+import {MacroName} from './macro_names.js';
 
 /** Class that implements a macro that deletes the previous sentence. */
 export class DeletePrevSentMacro extends Macro {
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/hidden_macro_manager.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/hidden_macro_manager.js
index e1f16706..5668c14 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/hidden_macro_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/hidden_macro_manager.js
@@ -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 {DeletePrevSentMacro} from '/accessibility_common/dictation/macros/delete_prev_sent_macro.js';
-import {MacroName} from '/accessibility_common/dictation/macros/macro_names.js';
-import {NavNextSentMacro, NavPrevSentMacro} from '/accessibility_common/dictation/macros/nav_sent_macro.js';
-import {DeletePrevWordMacro, NavNextWordMacro, NavPrevWordMacro} from '/accessibility_common/dictation/macros/repeatable_key_press_macro.js';
-import {SmartDeletePhraseMacro} from '/accessibility_common/dictation/macros/smart_delete_phrase_macro.js';
-import {SmartInsertBeforeMacro} from '/accessibility_common/dictation/macros/smart_insert_before_macro.js';
-import {SmartReplacePhraseMacro} from '/accessibility_common/dictation/macros/smart_replace_phrase_macro.js';
-import {SmartSelectBetweenMacro} from '/accessibility_common/dictation/macros/smart_select_between_macro.js';
-import {StopListeningMacro} from '/accessibility_common/dictation/macros/stop_listening_macro.js';
+import {DeletePrevSentMacro} from './delete_prev_sent_macro.js';
+import {MacroName} from './macro_names.js';
+import {NavNextSentMacro, NavPrevSentMacro} from './nav_sent_macro.js';
+import {DeletePrevWordMacro, NavNextWordMacro, NavPrevWordMacro} from './repeatable_key_press_macro.js';
+import {SmartDeletePhraseMacro} from './smart_delete_phrase_macro.js';
+import {SmartInsertBeforeMacro} from './smart_insert_before_macro.js';
+import {SmartReplacePhraseMacro} from './smart_replace_phrase_macro.js';
+import {SmartSelectBetweenMacro} from './smart_select_between_macro.js';
+import {StopListeningMacro} from './stop_listening_macro.js';
 
 /**
  * Class that manages "hidden" macros e.g. macros that have been fully
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/input_text_view_macro.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/input_text_view_macro.js
index 397210f..46ead2f 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/input_text_view_macro.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/input_text_view_macro.js
@@ -2,9 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {InputController} from '/accessibility_common/dictation/input_controller.js';
-import {Macro, MacroError} from '/accessibility_common/dictation/macros/macro.js';
-import {MacroName} from '/accessibility_common/dictation/macros/macro_names.js';
+import {InputController} from '../input_controller.js';
+
+import {Macro, MacroError} from './macro.js';
+import {MacroName} from './macro_names.js';
 
 /**
  * Macro that inputs text at the current cursor position.
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/list_commands_macro.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/list_commands_macro.js
index 3a118a6..9bd08fff 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/list_commands_macro.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/list_commands_macro.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 {Macro} from '/accessibility_common/dictation/macros/macro.js';
-import {MacroName} from '/accessibility_common/dictation/macros/macro_names.js';
+import {Macro} from './macro.js';
+import {MacroName} from './macro_names.js';
 
 /**
  * Class that implements a macro to list Dictation commands (by opening a Help
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/macro.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/macro.js
index 47c53d3..dd4b532 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/macro.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/macro.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 {MacroName} from '/accessibility_common/dictation/macros/macro_names.js';
+import {MacroName} from './macro_names.js';
 
 /**
  * Reasons that canTryAction in CheckContextResult might be false.
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/nav_sent_macro.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/nav_sent_macro.js
index c747b5ce..0547283 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/nav_sent_macro.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/nav_sent_macro.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 {Macro, MacroError} from '/accessibility_common/dictation/macros/macro.js';
-import {MacroName} from '/accessibility_common/dictation/macros/macro_names.js';
+import {Macro, MacroError} from './macro.js';
+import {MacroName} from './macro_names.js';
 
 /** Implements a macro that moves the text caret to the next sentence. */
 export class NavNextSentMacro extends Macro {
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/repeatable_key_press_macro.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/repeatable_key_press_macro.js
index 3488853..65c03c0e 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/repeatable_key_press_macro.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/repeatable_key_press_macro.js
@@ -2,9 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {Macro, MacroError} from '/accessibility_common/dictation/macros/macro.js';
-import {MacroName} from '/accessibility_common/dictation/macros/macro_names.js';
-import {EventGenerator} from '/common/event_generator.js';
+import {EventGenerator} from '../../../common/event_generator.js';
+
+import {Macro, MacroError} from './macro.js';
+import {MacroName} from './macro_names.js';
 
 /**
  * Abstract class that executes a macro using a key press which can optionally
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_delete_phrase_macro.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_delete_phrase_macro.js
index 810c71a69..5a136d5 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_delete_phrase_macro.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_delete_phrase_macro.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 {Macro, MacroError} from '/accessibility_common/dictation/macros/macro.js';
-import {MacroName} from '/accessibility_common/dictation/macros/macro_names.js';
+import {Macro, MacroError} from './macro.js';
+import {MacroName} from './macro_names.js';
 
 /** Implements a macro that deletes a provided word or phrase. */
 export class SmartDeletePhraseMacro extends Macro {
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_insert_before_macro.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_insert_before_macro.js
index 8b52766..6f0003c 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_insert_before_macro.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_insert_before_macro.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 {Macro, MacroError} from '/accessibility_common/dictation/macros/macro.js';
-import {MacroName} from '/accessibility_common/dictation/macros/macro_names.js';
+import {Macro, MacroError} from './macro.js';
+import {MacroName} from './macro_names.js';
 
 /**
  * Implements a macro that inserts a word or phrase before another word or
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_replace_phrase_macro.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_replace_phrase_macro.js
index bc0075f..9d0740b 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_replace_phrase_macro.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_replace_phrase_macro.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 {Macro, MacroError} from '/accessibility_common/dictation/macros/macro.js';
-import {MacroName} from '/accessibility_common/dictation/macros/macro_names.js';
+import {Macro, MacroError} from './macro.js';
+import {MacroName} from './macro_names.js';
 
 /**
  * Implements a macro that replaces a word or phrase with another word or
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_select_between_macro.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_select_between_macro.js
index b2ad72c..847a8ceb 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_select_between_macro.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/smart_select_between_macro.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 {Macro, MacroError} from '/accessibility_common/dictation/macros/macro.js';
-import {MacroName} from '/accessibility_common/dictation/macros/macro_names.js';
+import {Macro, MacroError} from './macro.js';
+import {MacroName} from './macro_names.js';
 
 /** Implements a macro that sets selection between two words or phrases. */
 export class SmartSelectBetweenMacro extends Macro {
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/stop_listening_macro.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/stop_listening_macro.js
index 603e941..0750c68 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/stop_listening_macro.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/stop_listening_macro.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 {Macro} from '/accessibility_common/dictation/macros/macro.js';
-import {MacroName} from '/accessibility_common/dictation/macros/macro_names.js';
+import {Macro} from './macro.js';
+import {MacroName} from './macro_names.js';
 
 /** Class that implements a macro to stop Dictation. */
 export class StopListeningMacro extends Macro {
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 ae83609..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
@@ -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 {Macro} from '/accessibility_common/dictation/macros/macro.js';
+import {Macro} from './macros/macro.js';
 
 const SpeechRecognitionType =
     chrome.speechRecognitionPrivate.SpeechRecognitionType;
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/input_text_strategy.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/input_text_strategy.js
index 74eb7dc..f73b25b 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/input_text_strategy.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/input_text_strategy.js
@@ -2,9 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {InputController} from '/accessibility_common/dictation/input_controller.js';
-import {InputTextViewMacro} from '/accessibility_common/dictation/macros/input_text_view_macro.js';
-import {ParseStrategy} from '/accessibility_common/dictation/parse/parse_strategy.js';
+import {InputController} from '../input_controller.js';
+import {InputTextViewMacro} from '../macros/input_text_view_macro.js';
+
+import {ParseStrategy} from './parse_strategy.js';
 
 /** A parsing strategy that tells text to be input as-is. */
 export class InputTextStrategy extends ParseStrategy {
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/parse_strategy.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/parse_strategy.js
index e84d41fc..35022b61 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/parse_strategy.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/parse_strategy.js
@@ -7,8 +7,8 @@
  * text and converting it into a macro.
  */
 
-import {InputController} from '/accessibility_common/dictation/input_controller.js';
-import {Macro} from '/accessibility_common/dictation/macros/macro.js';
+import {InputController} from '../input_controller.js';
+import {Macro} from '../macros/macro.js';
 
 /**
  * Represents a strategy for parsing speech input and converting it into a
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/pumpkin_parse_strategy.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/pumpkin_parse_strategy.js
index 5d3c4023..738eaa63 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/pumpkin_parse_strategy.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/pumpkin_parse_strategy.js
@@ -7,17 +7,18 @@
  * semantic parser.
  */
 
-import {InputController} from '/accessibility_common/dictation/input_controller.js';
-import {InputTextViewMacro} from '/accessibility_common/dictation/macros/input_text_view_macro.js';
-import {ListCommandsMacro} from '/accessibility_common/dictation/macros/list_commands_macro.js';
-import {Macro} from '/accessibility_common/dictation/macros/macro.js';
-import {MacroName} from '/accessibility_common/dictation/macros/macro_names.js';
-import * as RepeatableKeyPressMacro from '/accessibility_common/dictation/macros/repeatable_key_press_macro.js';
-import {ParseStrategy} from '/accessibility_common/dictation/parse/parse_strategy.js';
+import {InputController} from '../input_controller.js';
+import {InputTextViewMacro} from '../macros/input_text_view_macro.js';
+import {ListCommandsMacro} from '../macros/list_commands_macro.js';
+import {Macro} from '../macros/macro.js';
+import {MacroName} from '../macros/macro_names.js';
+import * as RepeatableKeyPressMacro from '../macros/repeatable_key_press_macro.js';
+
+import {ParseStrategy} from './parse_strategy.js';
 // PumpkinAvailability is based on the gn argument enable_pumpkin_for_dictation,
 // and pumpkin_availability.js is copied from either include_pumpkin.js
 // or exclude_pumpkin.js in the BUILD rule.
-import {PumpkinAvailability} from '/accessibility_common/dictation/parse/pumpkin/pumpkin_availability.js';
+import {PumpkinAvailability} from './pumpkin/pumpkin_availability.js';
 
 /** A parsing strategy that utilizes the Pumpkin semantic parser. */
 export class PumpkinParseStrategy extends ParseStrategy {
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/simple_parse_strategy.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/simple_parse_strategy.js
index 920b067f..26563f8 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/simple_parse_strategy.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/simple_parse_strategy.js
@@ -7,21 +7,22 @@
  * it into a Macro.
  */
 
-import {InputController} from '/accessibility_common/dictation/input_controller.js';
-import {DeletePrevSentMacro} from '/accessibility_common/dictation/macros/delete_prev_sent_macro.js';
-import {HiddenMacroManager} from '/accessibility_common/dictation/macros/hidden_macro_manager.js';
-import {InputTextViewMacro, NewLineMacro} from '/accessibility_common/dictation/macros/input_text_view_macro.js';
-import {ListCommandsMacro} from '/accessibility_common/dictation/macros/list_commands_macro.js';
-import {Macro} from '/accessibility_common/dictation/macros/macro.js';
-import {MacroName} from '/accessibility_common/dictation/macros/macro_names.js';
-import {NavNextSentMacro, NavPrevSentMacro} from '/accessibility_common/dictation/macros/nav_sent_macro.js';
-import * as RepeatableKeyPress from '/accessibility_common/dictation/macros/repeatable_key_press_macro.js';
-import {SmartDeletePhraseMacro} from '/accessibility_common/dictation/macros/smart_delete_phrase_macro.js';
-import {SmartInsertBeforeMacro} from '/accessibility_common/dictation/macros/smart_insert_before_macro.js';
-import {SmartReplacePhraseMacro} from '/accessibility_common/dictation/macros/smart_replace_phrase_macro.js';
-import {SmartSelectBetweenMacro} from '/accessibility_common/dictation/macros/smart_select_between_macro.js';
-import {StopListeningMacro} from '/accessibility_common/dictation/macros/stop_listening_macro.js';
-import {ParseStrategy} from '/accessibility_common/dictation/parse/parse_strategy.js';
+import {InputController} from '../input_controller.js';
+import {DeletePrevSentMacro} from '../macros/delete_prev_sent_macro.js';
+import {HiddenMacroManager} from '../macros/hidden_macro_manager.js';
+import {InputTextViewMacro, NewLineMacro} from '../macros/input_text_view_macro.js';
+import {ListCommandsMacro} from '../macros/list_commands_macro.js';
+import {Macro} from '../macros/macro.js';
+import {MacroName} from '../macros/macro_names.js';
+import {NavNextSentMacro, NavPrevSentMacro} from '../macros/nav_sent_macro.js';
+import * as RepeatableKeyPress from '../macros/repeatable_key_press_macro.js';
+import {SmartDeletePhraseMacro} from '../macros/smart_delete_phrase_macro.js';
+import {SmartInsertBeforeMacro} from '../macros/smart_insert_before_macro.js';
+import {SmartReplacePhraseMacro} from '../macros/smart_replace_phrase_macro.js';
+import {SmartSelectBetweenMacro} from '../macros/smart_select_between_macro.js';
+import {StopListeningMacro} from '../macros/stop_listening_macro.js';
+
+import {ParseStrategy} from './parse_strategy.js';
 
 /**
  * @typedef {{
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 a82d9c0c..e2fda8067 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
@@ -6,12 +6,13 @@
  * @fileoverview Handles speech parsing for dictation.
  */
 
-import {InputController} from '/accessibility_common/dictation/input_controller.js';
-import {Macro} from '/accessibility_common/dictation/macros/macro.js';
-import {InputTextStrategy} from '/accessibility_common/dictation/parse/input_text_strategy.js';
-import {ParseStrategy} from '/accessibility_common/dictation/parse/parse_strategy.js';
-import {PumpkinParseStrategy} from '/accessibility_common/dictation/parse/pumpkin_parse_strategy.js';
-import {SimpleParseStrategy} from '/accessibility_common/dictation/parse/simple_parse_strategy.js';
+import {InputController} from '../input_controller.js';
+import {Macro} from '../macros/macro.js';
+
+import {InputTextStrategy} from './input_text_strategy.js';
+import {ParseStrategy} from './parse_strategy.js';
+import {PumpkinParseStrategy} from './pumpkin_parse_strategy.js';
+import {SimpleParseStrategy} from './simple_parse_strategy.js';
 
 /** SpeechParser handles parsing spoken transcripts into Macros. */
 export class SpeechParser {
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/magnifier/magnifier.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/magnifier/magnifier.js
index 6fa8a6a..e779ddc4 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/magnifier/magnifier.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/magnifier/magnifier.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 {RectUtil} from '/common/rect_util.js';
+import {RectUtil} from '../../common/rect_util.js';
 
 const EventType = chrome.automation.EventType;
 const RoleType = chrome.automation.RoleType;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/auto_scroll_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/auto_scroll_handler.js
index 82ebe7c..ac753d7c 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/auto_scroll_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/auto_scroll_handler.js
@@ -5,10 +5,12 @@
 /**
  * @fileoverview Handles auto scrolling on navigation.
  */
-import {ChromeVoxState} from '/chromevox/background/chromevox_state.js';
-import {CommandHandlerInterface} from '/chromevox/background/command_handler_interface.js';
-import {CursorUnit} from '/common/cursors/cursor.js';
-import {CursorRange} from '/common/cursors/range.js';
+import {CursorUnit} from '../../common/cursors/cursor.js';
+import {CursorRange} from '../../common/cursors/range.js';
+
+import {ChromeVoxState} from './chromevox_state.js';
+import {CommandHandlerInterface} from './command_handler_interface.js';
+
 
 
 // setTimeout and its clean-up are referencing each other. So, we need to set
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
index de57d7a..c6a464b 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
@@ -2,34 +2,35 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BrailleCommandHandler} from '/chromevox/background/braille/braille_command_handler.js';
-import {ChromeVoxState} from '/chromevox/background/chromevox_state.js';
-import {ChromeVoxBackground} from '/chromevox/background/classic_background.js';
-import {CommandHandler} from '/chromevox/background/command_handler.js';
-import {CommandHandlerInterface} from '/chromevox/background/command_handler_interface.js';
-import {ConsoleTts} from '/chromevox/background/console_tts.js';
-import {DesktopAutomationHandler} from '/chromevox/background/desktop_automation_handler.js';
-import {DesktopAutomationInterface} from '/chromevox/background/desktop_automation_interface.js';
-import {DownloadHandler} from '/chromevox/background/download_handler.js';
-import {Earcons} from '/chromevox/background/earcons.js';
-import {FindHandler} from '/chromevox/background/find_handler.js';
-import {FocusAutomationHandler} from '/chromevox/background/focus_automation_handler.js';
-import {GestureCommandHandler} from '/chromevox/background/gesture_command_handler.js';
-import {BackgroundKeyboardHandler} from '/chromevox/background/keyboard_handler.js';
-import {LiveRegions} from '/chromevox/background/live_regions.js';
-import {MathHandler} from '/chromevox/background/math_handler.js';
-import {MediaAutomationHandler} from '/chromevox/background/media_automation_handler.js';
-import {Output} from '/chromevox/background/output/output.js';
-import {PageLoadSoundHandler} from '/chromevox/background/page_load_sound_handler.js';
-import {PanelBackground} from '/chromevox/background/panel/panel_background.js';
-import {ChromeVoxPrefs} from '/chromevox/background/prefs.js';
-import {RangeAutomationHandler} from '/chromevox/background/range_automation_handler.js';
-import {ExtensionBridge} from '/chromevox/common/extension_bridge.js';
-import {LocaleOutputHelper} from '/chromevox/common/locale_output_helper.js';
-import {PanelCommand, PanelCommandType} from '/chromevox/common/panel_command.js';
-import {JaPhoneticMap} from '/chromevox/third_party/tamachiyomi/ja_phonetic_map.js';
-import {CursorRange} from '/common/cursors/range.js';
-import {InstanceChecker} from '/common/instance_checker.js';
+import {CursorRange} from '../../common/cursors/range.js';
+import {InstanceChecker} from '../../common/instance_checker.js';
+import {ExtensionBridge} from '../common/extension_bridge.js';
+import {LocaleOutputHelper} from '../common/locale_output_helper.js';
+import {PanelCommand, PanelCommandType} from '../common/panel_command.js';
+import {JaPhoneticMap} from '../third_party/tamachiyomi/ja_phonetic_map.js';
+
+import {BrailleCommandHandler} from './braille/braille_command_handler.js';
+import {ChromeVoxState} from './chromevox_state.js';
+import {ChromeVoxBackground} from './classic_background.js';
+import {CommandHandler} from './command_handler.js';
+import {CommandHandlerInterface} from './command_handler_interface.js';
+import {ConsoleTts} from './console_tts.js';
+import {DesktopAutomationHandler} from './desktop_automation_handler.js';
+import {DesktopAutomationInterface} from './desktop_automation_interface.js';
+import {DownloadHandler} from './download_handler.js';
+import {Earcons} from './earcons.js';
+import {FindHandler} from './find_handler.js';
+import {FocusAutomationHandler} from './focus_automation_handler.js';
+import {GestureCommandHandler} from './gesture_command_handler.js';
+import {BackgroundKeyboardHandler} from './keyboard_handler.js';
+import {LiveRegions} from './live_regions.js';
+import {MathHandler} from './math_handler.js';
+import {MediaAutomationHandler} from './media_automation_handler.js';
+import {Output} from './output/output.js';
+import {PageLoadSoundHandler} from './page_load_sound_handler.js';
+import {PanelBackground} from './panel/panel_background.js';
+import {ChromeVoxPrefs} from './prefs.js';
+import {RangeAutomationHandler} from './range_automation_handler.js';
 
 
 /**
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/base_automation_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/base_automation_handler.js
index 8041a78..09497f32 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/base_automation_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/base_automation_handler.js
@@ -6,12 +6,13 @@
  * @fileoverview Basic facillities to handle events from a single automation
  * node.
  */
-import {ChromeVoxState} from '/chromevox/background/chromevox_state.js';
-import {EventSourceState} from '/chromevox/background/event_source.js';
-import {Output} from '/chromevox/background/output/output.js';
-import {ChromeVoxEvent} from '/chromevox/common/custom_automation_event.js';
-import {EventSourceType} from '/chromevox/common/event_source_type.js';
-import {CursorRange} from '/common/cursors/range.js';
+import {CursorRange} from '../../common/cursors/range.js';
+import {ChromeVoxEvent} from '../common/custom_automation_event.js';
+import {EventSourceType} from '../common/event_source_type.js';
+
+import {ChromeVoxState} from './chromevox_state.js';
+import {EventSourceState} from './event_source.js';
+import {Output} from './output/output.js';
 
 const ActionType = chrome.automation.ActionType;
 const AutomationEvent = chrome.automation.AutomationEvent;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_background.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_background.js
index 1b6c8a6..f91eaaee 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_background.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_background.js
@@ -5,11 +5,12 @@
 /**
  * @fileoverview Sends Braille commands to the Braille API.
  */
-import {BrailleDisplayManager} from '/chromevox/background/braille/braille_display_manager.js';
-import {BrailleInputHandler} from '/chromevox/background/braille/braille_input_handler.js';
-import {BrailleKeyEventRewriter} from '/chromevox/background/braille/braille_key_event_rewriter.js';
-import {BrailleTranslatorManager} from '/chromevox/background/braille/braille_translator_manager.js';
-import {ChromeVoxState} from '/chromevox/background/chromevox_state.js';
+import {ChromeVoxState} from '../chromevox_state.js';
+
+import {BrailleDisplayManager} from './braille_display_manager.js';
+import {BrailleInputHandler} from './braille_input_handler.js';
+import {BrailleKeyEventRewriter} from './braille_key_event_rewriter.js';
+import {BrailleTranslatorManager} from './braille_translator_manager.js';
 
 /** @implements {BrailleInterface} */
 export class BrailleBackground {
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_captions_background.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_captions_background.js
index ef4bde4..c02b9d1d 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_captions_background.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_captions_background.js
@@ -7,8 +7,8 @@
  * braille content to the Panel on Chrome OS, or a content script on
  * other platforms.
  */
-import {ChromeVoxPrefs} from '/chromevox/background/prefs.js';
-import {PanelCommand, PanelCommandType} from '/chromevox/common/panel_command.js';
+import {PanelCommand, PanelCommandType} from '../../common/panel_command.js';
+import {ChromeVoxPrefs} from '../prefs.js';
 
 export class BrailleCaptionsBackground {
   /**
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_command_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_command_handler.js
index 8a65661..d13f791 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_command_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_command_handler.js
@@ -5,14 +5,14 @@
 /**
  * @fileoverview ChromeVox braille commands.
  */
-import {ChromeVoxState} from '/chromevox/background/chromevox_state.js';
-import {CommandHandlerInterface} from '/chromevox/background/command_handler_interface.js';
-import {DesktopAutomationInterface} from '/chromevox/background/desktop_automation_interface.js';
-import {EventSourceState} from '/chromevox/background/event_source.js';
-import {Output} from '/chromevox/background/output/output.js';
-import {BrailleCommandData} from '/chromevox/common/braille/braille_command_data.js';
-import {EventSourceType} from '/chromevox/common/event_source_type.js';
-import {EventGenerator} from '/common/event_generator.js';
+import {EventGenerator} from '../../../common/event_generator.js';
+import {BrailleCommandData} from '../../common/braille/braille_command_data.js';
+import {EventSourceType} from '../../common/event_source_type.js';
+import {ChromeVoxState} from '../chromevox_state.js';
+import {CommandHandlerInterface} from '../command_handler_interface.js';
+import {DesktopAutomationInterface} from '../desktop_automation_interface.js';
+import {EventSourceState} from '../event_source.js';
+import {Output} from '../output/output.js';
 
 const RoleType = chrome.automation.RoleType;
 const StateType = chrome.automation.StateType;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_display_manager.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_display_manager.js
index b2d37b1..66b86057 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_display_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_display_manager.js
@@ -5,11 +5,11 @@
 /**
  * @fileoverview Puts text on a braille display.
  */
-import {BrailleCaptionsBackground} from '/chromevox/background/braille/braille_captions_background.js';
-import {BrailleTranslatorManager} from '/chromevox/background/braille/braille_translator_manager.js';
-import {ExpandingBrailleTranslator} from '/chromevox/background/braille/expanding_braille_translator.js';
-import {PanStrategy} from '/chromevox/background/braille/pan_strategy.js';
-import {ValueSpan} from '/chromevox/background/braille/spans.js';
+import {BrailleCaptionsBackground} from './braille_captions_background.js';
+import {BrailleTranslatorManager} from './braille_translator_manager.js';
+import {ExpandingBrailleTranslator} from './expanding_braille_translator.js';
+import {PanStrategy} from './pan_strategy.js';
+import {ValueSpan} from './spans.js';
 
 export class BrailleDisplayManager {
   /**
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_input_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_input_handler.js
index c9b660a7..6f01498a 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_input_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_input_handler.js
@@ -7,10 +7,11 @@
  * text in an input field.  This class cooperates with the Braille IME
  * that is built into Chrome OS to do the actual text editing.
  */
-import {BrailleTranslatorManager} from '/chromevox/background/braille/braille_translator_manager.js';
-import {ExpandingBrailleTranslator} from '/chromevox/background/braille/expanding_braille_translator.js';
-import {ExtraCellsSpan, ValueSelectionSpan, ValueSpan} from '/chromevox/background/braille/spans.js';
-import {EventGenerator} from '/common/event_generator.js';
+import {EventGenerator} from '../../../common/event_generator.js';
+
+import {BrailleTranslatorManager} from './braille_translator_manager.js';
+import {ExpandingBrailleTranslator} from './expanding_braille_translator.js';
+import {ExtraCellsSpan, ValueSelectionSpan, ValueSpan} from './spans.js';
 
 export class BrailleInputHandler {
   /**
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_key_event_rewriter.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_key_event_rewriter.js
index 7baf4ee..e1b9499 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_key_event_rewriter.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_key_event_rewriter.js
@@ -5,7 +5,7 @@
 /**
  * @fileoverview Rewrites a braille key event.
  */
-import {Output} from '/chromevox/background/output/output.js';
+import {Output} from '../output/output.js';
 
 /**
  * A class that transforms a sequence of braille key events into a standard key
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_translator_manager.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_translator_manager.js
index 0ca20d67..1d23cc70 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_translator_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_translator_manager.js
@@ -5,8 +5,9 @@
 /**
  * @fileoverview Keeps track of the current braille translators.
  */
-import {ExpandingBrailleTranslator} from '/chromevox/background/braille/expanding_braille_translator.js';
-import {BrailleTable} from '/chromevox/common/braille/braille_table.js';
+import {BrailleTable} from '../../common/braille/braille_table.js';
+
+import {ExpandingBrailleTranslator} from './expanding_braille_translator.js';
 
 export class BrailleTranslatorManager {
   /**
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/expanding_braille_translator.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/expanding_braille_translator.js
index 1018262..8ca5678 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/expanding_braille_translator.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/expanding_braille_translator.js
@@ -6,7 +6,7 @@
  * @fileoverview Translates text to braille, optionally with some parts
  * uncontracted.
  */
-import {BrailleTextStyleSpan, ExtraCellsSpan, ValueSelectionSpan, ValueSpan} from '/chromevox/background/braille/spans.js';
+import {BrailleTextStyleSpan, ExtraCellsSpan, ValueSelectionSpan, ValueSpan} from './spans.js';
 
 /**
  * A wrapper around one or two braille translators that uses contracted
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/pan_strategy.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/pan_strategy.js
index 9909c522..511760c 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/pan_strategy.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/pan_strategy.js
@@ -6,7 +6,7 @@
  * @fileoverview Logic for panning a braille display within a line of braille
  * content that might not fit on a single display.
  */
-import {CURSOR_DOTS} from '/chromevox/background/braille/cursor_dots.js';
+import {CURSOR_DOTS} from './cursor_dots.js';
 
 export class PanStrategy {
   constructor() {
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/chromevox_state.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/chromevox_state.js
index 194fb73..dfcf55c 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/chromevox_state.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/chromevox_state.js
@@ -7,8 +7,9 @@
  *     ChromeVox state, to avoid direct dependencies on the Background
  *     object and to facilitate mocking for tests.
  */
-import {UserActionMonitor} from '/chromevox/background/user_action_monitor.js';
-import {CursorRange} from '/common/cursors/range.js';
+import {CursorRange} from '../../common/cursors/range.js';
+
+import {UserActionMonitor} from './user_action_monitor.js';
 
 /**
  * An interface implemented by objects to observe ChromeVox state changes.
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/classic_background.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/classic_background.js
index 956153a..85b174b 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/classic_background.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/classic_background.js
@@ -5,18 +5,19 @@
 /**
  * @fileoverview Script that runs on the background page.
  */
-import {BrailleBackground} from '/chromevox/background/braille/braille_background.js';
-import {BrailleCaptionsBackground} from '/chromevox/background/braille/braille_captions_background.js';
-import {ChromeVoxState} from '/chromevox/background/chromevox_state.js';
-import {ConsoleTts} from '/chromevox/background/console_tts.js';
-import {ChromeVoxEditableTextBase, TypingEcho} from '/chromevox/background/editing/editable_text_base.js';
-import {InjectedScriptLoader} from '/chromevox/background/injected_script_loader.js';
-import {Output} from '/chromevox/background/output/output.js';
-import {ChromeVoxPrefs} from '/chromevox/background/prefs.js';
-import {TtsBackground} from '/chromevox/background/tts_background.js';
-import {AbstractTts} from '/chromevox/common/abstract_tts.js';
-import {CompositeTts} from '/chromevox/common/composite_tts.js';
-import {ExtensionBridge} from '/chromevox/common/extension_bridge.js';
+import {AbstractTts} from '../common/abstract_tts.js';
+import {CompositeTts} from '../common/composite_tts.js';
+import {ExtensionBridge} from '../common/extension_bridge.js';
+
+import {BrailleBackground} from './braille/braille_background.js';
+import {BrailleCaptionsBackground} from './braille/braille_captions_background.js';
+import {ChromeVoxState} from './chromevox_state.js';
+import {ConsoleTts} from './console_tts.js';
+import {ChromeVoxEditableTextBase, TypingEcho} from './editing/editable_text_base.js';
+import {InjectedScriptLoader} from './injected_script_loader.js';
+import {Output} from './output/output.js';
+import {ChromeVoxPrefs} from './prefs.js';
+import {TtsBackground} from './tts_background.js';
 
 /**
  * This is the legacy ChromeVox background object.
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 f450bfd..da06fe1 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js
@@ -5,31 +5,32 @@
 /**
  * @fileoverview ChromeVox commands.
  */
-import {AutoScrollHandler} from '/chromevox/background/auto_scroll_handler.js';
-import {BrailleBackground} from '/chromevox/background/braille/braille_background.js';
-import {BrailleCaptionsBackground} from '/chromevox/background/braille/braille_captions_background.js';
-import {ChromeVoxState} from '/chromevox/background/chromevox_state.js';
-import {ChromeVoxBackground} from '/chromevox/background/classic_background.js';
-import {Color} from '/chromevox/background/color.js';
-import {CommandHandlerInterface} from '/chromevox/background/command_handler_interface.js';
-import {DesktopAutomationInterface} from '/chromevox/background/desktop_automation_interface.js';
-import {TypingEcho} from '/chromevox/background/editing/editable_text_base.js';
-import {EventSourceState} from '/chromevox/background/event_source.js';
-import {GestureInterface} from '/chromevox/background/gesture_interface.js';
-import {Output} from '/chromevox/background/output/output.js';
-import {PhoneticData} from '/chromevox/background/phonetic_data.js';
-import {ChromeVoxPrefs} from '/chromevox/background/prefs.js';
-import {SmartStickyMode} from '/chromevox/background/smart_sticky_mode.js';
-import {AbstractTts} from '/chromevox/common/abstract_tts.js';
-import {CommandStore} from '/chromevox/common/command_store.js';
-import {ChromeVoxEvent, CustomAutomationEvent} from '/chromevox/common/custom_automation_event.js';
-import {EventSourceType} from '/chromevox/common/event_source_type.js';
-import {GestureGranularity} from '/chromevox/common/gesture_command_data.js';
-import {ChromeVoxKbHandler} from '/chromevox/common/keyboard_handler.js';
-import {PanelCommand, PanelCommandType} from '/chromevox/common/panel_command.js';
-import {Cursor, CursorUnit} from '/common/cursors/cursor.js';
-import {CursorRange} from '/common/cursors/range.js';
-import {EventGenerator} from '/common/event_generator.js';
+import {Cursor, CursorUnit} from '../../common/cursors/cursor.js';
+import {CursorRange} from '../../common/cursors/range.js';
+import {EventGenerator} from '../../common/event_generator.js';
+import {AbstractTts} from '../common/abstract_tts.js';
+import {CommandStore} from '../common/command_store.js';
+import {ChromeVoxEvent, CustomAutomationEvent} from '../common/custom_automation_event.js';
+import {EventSourceType} from '../common/event_source_type.js';
+import {GestureGranularity} from '../common/gesture_command_data.js';
+import {ChromeVoxKbHandler} from '../common/keyboard_handler.js';
+import {PanelCommand, PanelCommandType} from '../common/panel_command.js';
+
+import {AutoScrollHandler} from './auto_scroll_handler.js';
+import {BrailleBackground} from './braille/braille_background.js';
+import {BrailleCaptionsBackground} from './braille/braille_captions_background.js';
+import {ChromeVoxState} from './chromevox_state.js';
+import {ChromeVoxBackground} from './classic_background.js';
+import {Color} from './color.js';
+import {CommandHandlerInterface} from './command_handler_interface.js';
+import {DesktopAutomationInterface} from './desktop_automation_interface.js';
+import {TypingEcho} from './editing/editable_text_base.js';
+import {EventSourceState} from './event_source.js';
+import {GestureInterface} from './gesture_interface.js';
+import {Output} from './output/output.js';
+import {PhoneticData} from './phonetic_data.js';
+import {ChromeVoxPrefs} from './prefs.js';
+import {SmartStickyMode} from './smart_sticky_mode.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
 const Dir = constants.Dir;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler_interface.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler_interface.js
index 63154e3..6ca8afa 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler_interface.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler_interface.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 {CursorRange} from '/common/cursors/range.js';
+import {CursorRange} from '../../common/cursors/range.js';
 
 export class CommandHandlerInterface {
   /**
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/console_tts.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/console_tts.js
index 69b39f0..f929b44 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/console_tts.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/console_tts.js
@@ -5,7 +5,7 @@
 /**
  * @fileoverview A TTS engine that writes to window.console.
  */
-import {ChromeVoxPrefs} from '/chromevox/background/prefs.js';
+import {ChromeVoxPrefs} from './prefs.js';
 
 /**
  * @implements {TtsInterface}
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js
index b815276f..581dd02 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js
@@ -5,18 +5,19 @@
 /**
  * @fileoverview Handles automation from a desktop automation node.
  */
-import {AutoScrollHandler} from '/chromevox/background/auto_scroll_handler.js';
-import {AutomationObjectConstructorInstaller} from '/chromevox/background/automation_object_constructor_installer.js';
-import {ChromeVoxState} from '/chromevox/background/chromevox_state.js';
-import {CommandHandlerInterface} from '/chromevox/background/command_handler_interface.js';
-import {DesktopAutomationInterface} from '/chromevox/background/desktop_automation_interface.js';
-import {TextEditHandler} from '/chromevox/background/editing/editing.js';
-import {EventSourceState} from '/chromevox/background/event_source.js';
-import {Output} from '/chromevox/background/output/output.js';
-import {ChromeVoxEvent, CustomAutomationEvent} from '/chromevox/common/custom_automation_event.js';
-import {EventSourceType} from '/chromevox/common/event_source_type.js';
-import {WrappingCursor} from '/common/cursors/cursor.js';
-import {CursorRange} from '/common/cursors/range.js';
+import {WrappingCursor} from '../../common/cursors/cursor.js';
+import {CursorRange} from '../../common/cursors/range.js';
+import {ChromeVoxEvent, CustomAutomationEvent} from '../common/custom_automation_event.js';
+import {EventSourceType} from '../common/event_source_type.js';
+
+import {AutoScrollHandler} from './auto_scroll_handler.js';
+import {AutomationObjectConstructorInstaller} from './automation_object_constructor_installer.js';
+import {ChromeVoxState} from './chromevox_state.js';
+import {CommandHandlerInterface} from './command_handler_interface.js';
+import {DesktopAutomationInterface} from './desktop_automation_interface.js';
+import {TextEditHandler} from './editing/editing.js';
+import {EventSourceState} from './event_source.js';
+import {Output} from './output/output.js';
 
 const ActionType = chrome.automation.ActionType;
 const AutomationNode = chrome.automation.AutomationNode;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_interface.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_interface.js
index e404c6bf..a723e22f 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_interface.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_interface.js
@@ -5,8 +5,8 @@
 /**
  * @fileoverview Interface to prevent circular dependencies.
  */
-import {BaseAutomationHandler} from '/chromevox/background/base_automation_handler.js';
-import {TextEditHandler} from '/chromevox/background/editing/editing.js';
+import {BaseAutomationHandler} from './base_automation_handler.js';
+import {TextEditHandler} from './editing/editing.js';
 
 export class DesktopAutomationInterface extends BaseAutomationHandler {
   /** @type {TextEditHandler} */
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/download_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/download_handler.js
index 9747dff..1290dd27 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/download_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/download_handler.js
@@ -6,7 +6,7 @@
  * @fileoverview Listens for download events and provides corresponding
  * notifications in ChromeVox.
  */
-import {Output} from '/chromevox/background/output/output.js';
+import {Output} from './output/output.js';
 
 export class DownloadHandler {}
 
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/earcons.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/earcons.js
index 6e9d747..be3f9cb 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/earcons.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/earcons.js
@@ -7,8 +7,8 @@
  * auditory cues.
  */
 
-import {ChromeVoxState} from '/chromevox/background/chromevox_state.js';
-import {EarconEngine} from '/chromevox/background/earcon_engine.js';
+import {ChromeVoxState} from './chromevox_state.js';
+import {EarconEngine} from './earcon_engine.js';
 
 export class Earcons extends AbstractEarcons {
   constructor() {
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editable_line.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editable_line.js
index cb55681..d748018 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editable_line.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editable_line.js
@@ -8,9 +8,9 @@
  * (e.g. start/end offsets) get saved. Line: nodes/offsets at the beginning/end
  * of a line get saved.
  */
-import {Output} from '/chromevox/background/output/output.js';
-import {Cursor, CURSOR_NODE_INDEX, CursorMovement, CursorUnit} from '/common/cursors/cursor.js';
-import {CursorRange} from '/common/cursors/range.js';
+import {Cursor, CURSOR_NODE_INDEX, CursorMovement, CursorUnit} from '../../../common/cursors/cursor.js';
+import {CursorRange} from '../../../common/cursors/range.js';
+import {Output} from '../output/output.js';
 
 const AutomationEvent = chrome.automation.AutomationEvent;
 const AutomationNode = chrome.automation.AutomationNode;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editable_text_base.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editable_text_base.js
index 9faabfe..10715ca9 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editable_text_base.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editable_text_base.js
@@ -13,8 +13,8 @@
  * extended to override methods that extract lines for multiline fields
  * or to provide other customizations.
  */
-import {ChromeVoxState} from '/chromevox/background/chromevox_state.js';
-import {AbstractTts} from '/chromevox/common/abstract_tts.js';
+import {AbstractTts} from '../../common/abstract_tts.js';
+import {ChromeVoxState} from '../chromevox_state.js';
 
 /**
  * A class containing the information needed to speak
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing.js
index 615cc37..06208a6 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing.js
@@ -6,18 +6,19 @@
  * @fileoverview Processes events related to editing text and emits the
  * appropriate spoken and braille feedback.
  */
-import {BrailleBackground} from '/chromevox/background/braille/braille_background.js';
-import {BrailleTextStyleSpan, ValueSelectionSpan, ValueSpan} from '/chromevox/background/braille/spans.js';
-import {ChromeVoxState, ChromeVoxStateObserver} from '/chromevox/background/chromevox_state.js';
-import {Color} from '/chromevox/background/color.js';
-import {EditableLine} from '/chromevox/background/editing/editable_line.js';
-import {ChromeVoxEditableTextBase, TextChangeEvent} from '/chromevox/background/editing/editable_text_base.js';
-import {IntentHandler} from '/chromevox/background/editing/intent_handler.js';
-import {Output} from '/chromevox/background/output/output.js';
-import {AbstractTts} from '/chromevox/common/abstract_tts.js';
-import {ChromeVoxEvent} from '/chromevox/common/custom_automation_event.js';
-import {Cursor, CursorMovement, CursorUnit} from '/common/cursors/cursor.js';
-import {CursorRange} from '/common/cursors/range.js';
+import {Cursor, CursorMovement, CursorUnit} from '../../../common/cursors/cursor.js';
+import {CursorRange} from '../../../common/cursors/range.js';
+import {AbstractTts} from '../../common/abstract_tts.js';
+import {ChromeVoxEvent} from '../../common/custom_automation_event.js';
+import {BrailleBackground} from '../braille/braille_background.js';
+import {BrailleTextStyleSpan, ValueSelectionSpan, ValueSpan} from '../braille/spans.js';
+import {ChromeVoxState, ChromeVoxStateObserver} from '../chromevox_state.js';
+import {Color} from '../color.js';
+import {Output} from '../output/output.js';
+
+import {EditableLine} from './editable_line.js';
+import {ChromeVoxEditableTextBase, TextChangeEvent} from './editable_text_base.js';
+import {IntentHandler} from './intent_handler.js';
 
 const AutomationEvent = chrome.automation.AutomationEvent;
 const AutomationIntent = chrome.automation.AutomationIntent;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler.js
index f3a9404..02b377cf 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler.js
@@ -6,10 +6,11 @@
  * @fileoverview Handles automation intents for speech feedback.
  * Braille is *not* handled in this module.
  */
-import {EditableLine} from '/chromevox/background/editing/editable_line.js';
-import {Output} from '/chromevox/background/output/output.js';
-import {OutputRoleInfo} from '/chromevox/background/output/output_role_info.js';
-import {CursorRange} from '/common/cursors/range.js';
+import {CursorRange} from '../../../common/cursors/range.js';
+import {Output} from '../output/output.js';
+import {OutputRoleInfo} from '../output/output_role_info.js';
+
+import {EditableLine} from './editable_line.js';
 
 const AutomationIntent = chrome.automation.AutomationIntent;
 const Dir = constants.Dir;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/es6_loader.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/es6_loader.js
index 8cb04f2..aa15b1ce 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/es6_loader.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/es6_loader.js
@@ -2,4 +2,4 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {Background} from '/chromevox/background/background.js';
+import {Background} from './background.js';
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/event_source.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/event_source.js
index ab149ce8..329c65dc 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/event_source.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/event_source.js
@@ -5,7 +5,7 @@
 /**
  * @fileoverview Tracks event sources.
  */
-import {EventSourceType} from '/chromevox/common/event_source_type.js';
+import {EventSourceType} from '../common/event_source_type.js';
 
 export const EventSourceState = {};
 
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/find_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/find_handler.js
index 871ea6d3..af736e5 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/find_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/find_handler.js
@@ -5,9 +5,10 @@
 /**
  * @fileoverview Handles output for Chrome's built-in find.
  */
-import {ChromeVoxState} from '/chromevox/background/chromevox_state.js';
-import {Output} from '/chromevox/background/output/output.js';
-import {CursorRange} from '/common/cursors/range.js';
+import {CursorRange} from '../../common/cursors/range.js';
+
+import {ChromeVoxState} from './chromevox_state.js';
+import {Output} from './output/output.js';
 
 const TreeChangeObserverFilter = chrome.automation.TreeChangeObserverFilter;
 
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/focus_automation_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/focus_automation_handler.js
index 203dd4a..8a62250d 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/focus_automation_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/focus_automation_handler.js
@@ -5,11 +5,12 @@
 /**
  * @fileoverview Handles automation events on the currently focused node.
  */
-import {BaseAutomationHandler} from '/chromevox/background/base_automation_handler.js';
-import {ChromeVoxState} from '/chromevox/background/chromevox_state.js';
-import {Output} from '/chromevox/background/output/output.js';
-import {ChromeVoxEvent} from '/chromevox/common/custom_automation_event.js';
-import {CursorRange} from '/common/cursors/range.js';
+import {CursorRange} from '../../common/cursors/range.js';
+import {ChromeVoxEvent} from '../common/custom_automation_event.js';
+
+import {BaseAutomationHandler} from './base_automation_handler.js';
+import {ChromeVoxState} from './chromevox_state.js';
+import {Output} from './output/output.js';
 
 const AutomationEvent = chrome.automation.AutomationEvent;
 const AutomationNode = chrome.automation.AutomationNode;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/gesture_command_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/gesture_command_handler.js
index e92be2e..9012d03c 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/gesture_command_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/gesture_command_handler.js
@@ -5,16 +5,17 @@
 /**
  * @fileoverview Handles gesture-based commands.
  */
-import {ChromeVoxState} from '/chromevox/background/chromevox_state.js';
-import {CommandHandlerInterface} from '/chromevox/background/command_handler_interface.js';
-import {EventSourceState} from '/chromevox/background/event_source.js';
-import {GestureInterface} from '/chromevox/background/gesture_interface.js';
-import {Output} from '/chromevox/background/output/output.js';
-import {PointerHandler} from '/chromevox/background/pointer_handler.js';
-import {UserActionMonitor} from '/chromevox/background/user_action_monitor.js';
-import {EventSourceType} from '/chromevox/common/event_source_type.js';
-import {GestureCommandData, GestureGranularity} from '/chromevox/common/gesture_command_data.js';
-import {EventGenerator} from '/common/event_generator.js';
+import {EventGenerator} from '../../common/event_generator.js';
+import {EventSourceType} from '../common/event_source_type.js';
+import {GestureCommandData, GestureGranularity} from '../common/gesture_command_data.js';
+
+import {ChromeVoxState} from './chromevox_state.js';
+import {CommandHandlerInterface} from './command_handler_interface.js';
+import {EventSourceState} from './event_source.js';
+import {GestureInterface} from './gesture_interface.js';
+import {Output} from './output/output.js';
+import {PointerHandler} from './pointer_handler.js';
+import {UserActionMonitor} from './user_action_monitor.js';
 
 const RoleType = chrome.automation.RoleType;
 const Gesture = chrome.accessibilityPrivate.Gesture;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/gesture_interface.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/gesture_interface.js
index eec2d438..9322dbe 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/gesture_interface.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/gesture_interface.js
@@ -6,7 +6,7 @@
  * @fileoverview Interface to prevent circular dependencies between
  * CommandHandler and GestureCommandHandler.
  */
-import {GestureGranularity} from '/chromevox/common/gesture_command_data.js';
+import {GestureGranularity} from '../common/gesture_command_data.js';
 
 export const GestureInterface = {};
 
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/keyboard_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/keyboard_handler.js
index 5c2d4d1..0fd5bfb 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/keyboard_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/keyboard_handler.js
@@ -5,12 +5,13 @@
 /**
  * @fileoverview ChromeVox keyboard handler.
  */
-import {ChromeVoxState} from '/chromevox/background/chromevox_state.js';
-import {EventSourceState} from '/chromevox/background/event_source.js';
-import {MathHandler} from '/chromevox/background/math_handler.js';
-import {Output} from '/chromevox/background/output/output.js';
-import {EventSourceType} from '/chromevox/common/event_source_type.js';
-import {ChromeVoxKbHandler} from '/chromevox/common/keyboard_handler.js';
+import {EventSourceType} from '../common/event_source_type.js';
+import {ChromeVoxKbHandler} from '../common/keyboard_handler.js';
+
+import {ChromeVoxState} from './chromevox_state.js';
+import {EventSourceState} from './event_source.js';
+import {MathHandler} from './math_handler.js';
+import {Output} from './output/output.js';
 
 /**
  * @enum {string}
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/live_regions.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/live_regions.js
index c7a232b7..384054f7 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/live_regions.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/live_regions.js
@@ -5,9 +5,10 @@
 /**
  * @fileoverview Implements support for live regions in ChromeVox Next.
  */
-import {ChromeVoxState} from '/chromevox/background/chromevox_state.js';
-import {Output} from '/chromevox/background/output/output.js';
-import {CursorRange} from '/common/cursors/range.js';
+import {CursorRange} from '../../common/cursors/range.js';
+
+import {ChromeVoxState} from './chromevox_state.js';
+import {Output} from './output/output.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
 const RoleType = chrome.automation.RoleType;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/logging/log_url_watcher.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/logging/log_url_watcher.js
index 0f810a08..b547ddb 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/logging/log_url_watcher.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/logging/log_url_watcher.js
@@ -6,7 +6,7 @@
  * @fileoverview Watches the currently focused URL to verify if logging should
  * occur.
  */
-import {ChromeVoxState, ChromeVoxStateObserver} from '/chromevox/background/chromevox_state.js';
+import {ChromeVoxState, ChromeVoxStateObserver} from '../chromevox_state.js';
 
 /** @implements {ChromeVoxStateObserver} */
 export class LogUrlWatcher {
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/math_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/math_handler.js
index 5305966..2df9624 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/math_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/math_handler.js
@@ -5,7 +5,7 @@
 /**
  * @fileoverview Handles math output and exploration.
  */
-import {CursorRange} from '/common/cursors/range.js';
+import {CursorRange} from '../../common/cursors/range.js';
 
 /**
  * Initializes math for output and exploration.
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/media_automation_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/media_automation_handler.js
index 5de3adc..f8186dd 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/media_automation_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/media_automation_handler.js
@@ -5,7 +5,7 @@
 /**
  * @fileoverview Handles media automation events.
  */
-import {BaseAutomationHandler} from '/chromevox/background/base_automation_handler.js';
+import {BaseAutomationHandler} from './base_automation_handler.js';
 
 const AutomationEvent = chrome.automation.AutomationEvent;
 const AutomationNode = chrome.automation.AutomationNode;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output.js
index 13b16f1..b9be4fd7 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output.js
@@ -5,18 +5,19 @@
 /**
  * @fileoverview Provides output services for ChromeVox.
  */
-import {ValueSelectionSpan, ValueSpan} from '/chromevox/background/braille/spans.js';
-import {EventSourceState} from '/chromevox/background/event_source.js';
-import {OutputAncestryInfo} from '/chromevox/background/output/output_ancestry_info.js';
-import {OutputFormatParser, OutputFormatParserObserver} from '/chromevox/background/output/output_format_parser.js';
-import {OutputFormatTree} from '/chromevox/background/output/output_format_tree.js';
-import {OutputRulesStr} from '/chromevox/background/output/output_logger.js';
-import {OutputRoleInfo} from '/chromevox/background/output/output_role_info.js';
-import {PhoneticData} from '/chromevox/background/phonetic_data.js';
-import {EventSourceType} from '/chromevox/common/event_source_type.js';
-import {LocaleOutputHelper} from '/chromevox/common/locale_output_helper.js';
-import {Cursor, CURSOR_NODE_INDEX} from '/common/cursors/cursor.js';
-import {CursorRange} from '/common/cursors/range.js';
+import {Cursor, CURSOR_NODE_INDEX} from '../../../common/cursors/cursor.js';
+import {CursorRange} from '../../../common/cursors/range.js';
+import {EventSourceType} from '../../common/event_source_type.js';
+import {LocaleOutputHelper} from '../../common/locale_output_helper.js';
+import {ValueSelectionSpan, ValueSpan} from '../braille/spans.js';
+import {EventSourceState} from '../event_source.js';
+import {PhoneticData} from '../phonetic_data.js';
+
+import {OutputAncestryInfo} from './output_ancestry_info.js';
+import {OutputFormatParser, OutputFormatParserObserver} from './output_format_parser.js';
+import {OutputFormatTree} from './output_format_tree.js';
+import {OutputRulesStr} from './output_logger.js';
+import {OutputRoleInfo} from './output_role_info.js';
 
 const AriaCurrentState = chrome.automation.AriaCurrentState;
 const AutomationNode = chrome.automation.AutomationNode;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_ancestry_info.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_ancestry_info.js
index c45c5571..21523cb0 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_ancestry_info.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_ancestry_info.js
@@ -6,7 +6,7 @@
  * @fileoverview Provides a class which computes various types of ancestor
  * chains given the current node.
  */
-import {OutputRoleInfo} from '/chromevox/background/output/output_role_info.js';
+import {OutputRoleInfo} from './output_role_info.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
 const Dir = constants.Dir;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_format_parser.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_format_parser.js
index f040dbb9..3ea61569 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_format_parser.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_format_parser.js
@@ -5,7 +5,7 @@
 /**
  * @fileoverview Provides a push parser for Output format rules.
  */
-import {OutputFormatTree} from '/chromevox/background/output/output_format_tree.js';
+import {OutputFormatTree} from './output_format_tree.js';
 
 /**
  * Implemented by objects that wish to observe tokens from parsing Output format
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/page_load_sound_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/page_load_sound_handler.js
index 89b4207..d2b433ad 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/page_load_sound_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/page_load_sound_handler.js
@@ -5,9 +5,10 @@
 /**
  * @fileoverview Handles page loading sounds based on automation events.
  */
-import {BaseAutomationHandler} from '/chromevox/background/base_automation_handler.js';
-import {ChromeVoxState, ChromeVoxStateObserver} from '/chromevox/background/chromevox_state.js';
-import {ChromeVoxEvent} from '/chromevox/common/custom_automation_event.js';
+import {ChromeVoxEvent} from '../common/custom_automation_event.js';
+
+import {BaseAutomationHandler} from './base_automation_handler.js';
+import {ChromeVoxState, ChromeVoxStateObserver} from './chromevox_state.js';
 
 const ActionType = chrome.automation.ActionType;
 const AutomationNode = chrome.automation.AutomationNode;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/i_search.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/i_search.js
index 83e0afb..78fb412 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/i_search.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/i_search.js
@@ -5,8 +5,9 @@
 /**
  * @fileoverview The logic behind incremental search.
  */
-import {ISearchHandler} from '/chromevox/background/panel/i_search_handler.js';
-import {Cursor} from '/common/cursors/cursor.js';
+import {Cursor} from '../../../common/cursors/cursor.js';
+
+import {ISearchHandler} from './i_search_handler.js';
 
 const Dir = constants.Dir;
 
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_background.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_background.js
index 6094a2f..ced2585 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_background.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_background.js
@@ -6,12 +6,13 @@
  * @fileoverview Handles logic for the ChromeVox panel that requires state from
  * the background context.
  */
-import {ChromeVoxState, ChromeVoxStateObserver} from '/chromevox/background/chromevox_state.js';
-import {ISearch} from '/chromevox/background/panel/i_search.js';
-import {ISearchHandler} from '/chromevox/background/panel/i_search_handler.js';
-import {PanelNodeMenuBackground} from '/chromevox/background/panel/panel_node_menu_background.js';
-import {PanelTabMenuBackground} from '/chromevox/background/panel/panel_tab_menu_background.js';
-import {CursorRange} from '/common/cursors/range.js';
+import {CursorRange} from '../../../common/cursors/range.js';
+import {ChromeVoxState, ChromeVoxStateObserver} from '../chromevox_state.js';
+
+import {ISearch} from './i_search.js';
+import {ISearchHandler} from './i_search_handler.js';
+import {PanelNodeMenuBackground} from './panel_node_menu_background.js';
+import {PanelTabMenuBackground} from './panel_tab_menu_background.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
 
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_node_menu_background.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_node_menu_background.js
index e0849b5..5da9809 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_node_menu_background.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_node_menu_background.js
@@ -6,9 +6,9 @@
  * @fileoverview Calculates the menu items for the node menus in the ChromeVox
  * panel.
  */
-import {ChromeVoxState} from '/chromevox/background/chromevox_state.js';
-import {Output} from '/chromevox/background/output/output.js';
-import {CursorRange} from '/common/cursors/range.js';
+import {CursorRange} from '../../../common/cursors/range.js';
+import {ChromeVoxState} from '../chromevox_state.js';
+import {Output} from '../output/output.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
 
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/pointer_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/pointer_handler.js
index b1b44a0..6e1cf80 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/pointer_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/pointer_handler.js
@@ -6,12 +6,13 @@
  * @fileoverview ChromeVox pointer handler. A pointer, in this context, is
  * either user touch or mouse input.
  */
-import {BaseAutomationHandler} from '/chromevox/background/base_automation_handler.js';
-import {ChromeVoxState} from '/chromevox/background/chromevox_state.js';
-import {DesktopAutomationInterface} from '/chromevox/background/desktop_automation_interface.js';
-import {Output} from '/chromevox/background/output/output.js';
-import {CustomAutomationEvent} from '/chromevox/common/custom_automation_event.js';
-import {EventGenerator} from '/common/event_generator.js';
+import {EventGenerator} from '../../common/event_generator.js';
+import {CustomAutomationEvent} from '../common/custom_automation_event.js';
+
+import {BaseAutomationHandler} from './base_automation_handler.js';
+import {ChromeVoxState} from './chromevox_state.js';
+import {DesktopAutomationInterface} from './desktop_automation_interface.js';
+import {Output} from './output/output.js';
 
 const AutomationEvent = chrome.automation.AutomationEvent;
 const EventType = chrome.automation.EventType;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/prefs.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/prefs.js
index 7a65525..a60edab 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/prefs.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/prefs.js
@@ -7,9 +7,9 @@
  * the background context (background page or options page).
  *
  */
-import {ConsoleTts} from '/chromevox/background/console_tts.js';
-import {EventStreamLogger} from '/chromevox/background/logging/event_stream_logger.js';
-import {LogUrlWatcher} from '/chromevox/background/logging/log_url_watcher.js';
+import {ConsoleTts} from './console_tts.js';
+import {EventStreamLogger} from './logging/event_stream_logger.js';
+import {LogUrlWatcher} from './logging/log_url_watcher.js';
 
 /**
  * This object has default values of preferences and contains the common
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/range_automation_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/range_automation_handler.js
index 1bb74cd..dfa8fc9 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/range_automation_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/range_automation_handler.js
@@ -5,12 +5,13 @@
 /**
  * @fileoverview Handles automation from ChromeVox's current range.
  */
-import {BaseAutomationHandler} from '/chromevox/background/base_automation_handler.js';
-import {ChromeVoxState, ChromeVoxStateObserver} from '/chromevox/background/chromevox_state.js';
-import {DesktopAutomationHandler} from '/chromevox/background/desktop_automation_handler.js';
-import {Output} from '/chromevox/background/output/output.js';
-import {ChromeVoxEvent, CustomAutomationEvent} from '/chromevox/common/custom_automation_event.js';
-import {CursorRange} from '/common/cursors/range.js';
+import {CursorRange} from '../../common/cursors/range.js';
+import {ChromeVoxEvent, CustomAutomationEvent} from '../common/custom_automation_event.js';
+
+import {BaseAutomationHandler} from './base_automation_handler.js';
+import {ChromeVoxState, ChromeVoxStateObserver} from './chromevox_state.js';
+import {DesktopAutomationHandler} from './desktop_automation_handler.js';
+import {Output} from './output/output.js';
 
 const AutomationEvent = chrome.automation.AutomationEvent;
 const AutomationNode = chrome.automation.AutomationNode;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/smart_sticky_mode.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/smart_sticky_mode.js
index 886f5d4..b29b8008 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/smart_sticky_mode.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/smart_sticky_mode.js
@@ -7,9 +7,10 @@
  * when the current range is over an editable; restores sticky mode when not on
  * an editable.
  */
-import {ChromeVoxState, ChromeVoxStateObserver} from '/chromevox/background/chromevox_state.js';
-import {ChromeVoxBackground} from '/chromevox/background/classic_background.js';
-import {CursorRange} from '/common/cursors/range.js';
+import {CursorRange} from '../../common/cursors/range.js';
+
+import {ChromeVoxState, ChromeVoxStateObserver} from './chromevox_state.js';
+import {ChromeVoxBackground} from './classic_background.js';
 
 /** @implements {ChromeVoxStateObserver} */
 export class SmartStickyMode {
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/tts_background.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/tts_background.js
index 179692a1..e0abeba 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/tts_background.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/tts_background.js
@@ -7,10 +7,11 @@
  * extension API.
  */
 
-import {PhoneticData} from '/chromevox/background/phonetic_data.js';
-import {AbstractTts} from '/chromevox/common/abstract_tts.js';
-import {PanelCommand, PanelCommandType} from '/chromevox/common/panel_command.js';
-import {ChromeTtsBase} from '/chromevox/common/tts_base.js';
+import {AbstractTts} from '../common/abstract_tts.js';
+import {PanelCommand, PanelCommandType} from '../common/panel_command.js';
+import {ChromeTtsBase} from '../common/tts_base.js';
+
+import {PhoneticData} from './phonetic_data.js';
 
 const Utterance = class {
   /**
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/user_action_monitor.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/user_action_monitor.js
index 7254d360..3e6fad84 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/user_action_monitor.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/user_action_monitor.js
@@ -5,10 +5,11 @@
 /**
  * @fileoverview Monitors user actions.
  */
-import {CommandHandlerInterface} from '/chromevox/background/command_handler_interface.js';
-import {Output} from '/chromevox/background/output/output.js';
-import {KeySequence} from '/chromevox/common/key_sequence.js';
-import {PanelCommand, PanelCommandType} from '/chromevox/common/panel_command.js';
+import {KeySequence} from '../common/key_sequence.js';
+import {PanelCommand, PanelCommandType} from '../common/panel_command.js';
+
+import {CommandHandlerInterface} from './command_handler_interface.js';
+import {Output} from './output/output.js';
 
 /**
  * The types of actions we want to monitor.
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/common/key_map.js b/chrome/browser/resources/chromeos/accessibility/chromevox/common/key_map.js
index 32196f1..377091ce 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/common/key_map.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/common/key_map.js
@@ -19,7 +19,7 @@
  * To retrieve static data about user commands, see both CommandStore and
  * UserCommands.
  */
-import {KeySequence} from '/chromevox/common/key_sequence.js';
+import {KeySequence} from './key_sequence.js';
 
 export class KeyMap {
   /**
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/common/key_util.js b/chrome/browser/resources/chromeos/accessibility/chromevox/common/key_util.js
index 466215d..f424875 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/common/key_util.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/common/key_util.js
@@ -6,7 +6,7 @@
  * @fileoverview A collection of JavaScript utilities used to simplify working
  * with keyboard events.
  */
-import {KeySequence} from '/chromevox/common/key_sequence.js';
+import {KeySequence} from './key_sequence.js';
 
 export class KeyUtil {
   /**
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/common/keyboard_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/common/keyboard_handler.js
index 09ac90d..6544dc13 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/common/keyboard_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/common/keyboard_handler.js
@@ -5,9 +5,10 @@
 /**
  * @fileoverview Handles user keyboard input events.
  */
-import {UserActionMonitor} from '/chromevox/background/user_action_monitor.js';
-import {KeyMap} from '/chromevox/common/key_map.js';
-import {KeyUtil} from '/chromevox/common/key_util.js';
+import {UserActionMonitor} from '../background/user_action_monitor.js';
+
+import {KeyMap} from './key_map.js';
+import {KeyUtil} from './key_util.js';
 
 export const ChromeVoxKbHandler = {};
 
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/common/tts_base.js b/chrome/browser/resources/chromeos/accessibility/chromevox/common/tts_base.js
index 4a20409..31b5f042 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/common/tts_base.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/common/tts_base.js
@@ -6,7 +6,7 @@
  * @fileoverview A base class for Tts living on Chrome platforms.
  *
  */
-import {AbstractTts} from '/chromevox/common/abstract_tts.js';
+import {AbstractTts} from './abstract_tts.js';
 
 export class ChromeTtsBase extends AbstractTts {
   constructor() {
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/learn_mode/learn_mode.js b/chrome/browser/resources/chromeos/accessibility/chromevox/learn_mode/learn_mode.js
index a53871f..6d1c8a5 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/learn_mode/learn_mode.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/learn_mode/learn_mode.js
@@ -6,12 +6,12 @@
  * @fileoverview Script for ChromeOS keyboard explorer.
  *
  */
-import {BrailleCommandData} from '/chromevox/common/braille/braille_command_data.js';
-import {CommandStore} from '/chromevox/common/command_store.js';
-import {GestureCommandData} from '/chromevox/common/gesture_command_data.js';
-import {KeyMap} from '/chromevox/common/key_map.js';
-import {KeyUtil} from '/chromevox/common/key_util.js';
-import {ChromeVoxKbHandler} from '/chromevox/common/keyboard_handler.js';
+import {BrailleCommandData} from '../common/braille/braille_command_data.js';
+import {CommandStore} from '../common/command_store.js';
+import {GestureCommandData} from '../common/gesture_command_data.js';
+import {KeyMap} from '../common/key_map.js';
+import {KeyUtil} from '../common/key_util.js';
+import {ChromeVoxKbHandler} from '../common/keyboard_handler.js';
 
 /**
  * Class to manage the keyboard explorer.
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/options/options.js b/chrome/browser/resources/chromeos/accessibility/chromevox/options/options.js
index f77cb01..defa132c 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/options/options.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/options/options.js
@@ -5,11 +5,12 @@
 /**
  * @fileoverview ChromeVox options page.
  */
-import {AbstractTts} from '/chromevox/common/abstract_tts.js';
-import {BrailleTable} from '/chromevox/common/braille/braille_table.js';
-import {ExtensionBridge} from '/chromevox/common/extension_bridge.js';
-import {PanelCommand, PanelCommandType} from '/chromevox/common/panel_command.js';
-import {BluetoothBrailleDisplayUI} from '/chromevox/options/bluetooth_braille_display_ui.js';
+import {AbstractTts} from '../common/abstract_tts.js';
+import {BrailleTable} from '../common/braille/braille_table.js';
+import {ExtensionBridge} from '../common/extension_bridge.js';
+import {PanelCommand, PanelCommandType} from '../common/panel_command.js';
+
+import {BluetoothBrailleDisplayUI} from './bluetooth_braille_display_ui.js';
 
 /** @const {string} */
 const GOOGLE_TTS_EXTENSION_ID = 'gjjabgpgjpampikjhjpfhneeoapjbjaf';
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/i_search_ui.js b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/i_search_ui.js
index dca3210..1986ab82 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/i_search_ui.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/i_search_ui.js
@@ -5,7 +5,7 @@
 /**
  * @fileoverview The driver for the UI for incremental search.
  */
-import {PanelInterface} from '/chromevox/panel/panel_interface.js';
+import {PanelInterface} from './panel_interface.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
 const Dir = constants.Dir;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js
index 36408319..e36c246 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js
@@ -5,20 +5,21 @@
 /**
  * @fileoverview The ChromeVox panel and menus.
  */
-import {BrailleCommandData} from '/chromevox/common/braille/braille_command_data.js';
-import {CommandStore} from '/chromevox/common/command_store.js';
-import {EventSourceType} from '/chromevox/common/event_source_type.js';
-import {GestureCommandData} from '/chromevox/common/gesture_command_data.js';
-import {KeyMap} from '/chromevox/common/key_map.js';
-import {KeyUtil} from '/chromevox/common/key_util.js';
-import {LocaleOutputHelper} from '/chromevox/common/locale_output_helper.js';
-import {PanelCommand, PanelCommandType} from '/chromevox/common/panel_command.js';
-import {ISearchUI} from '/chromevox/panel/i_search_ui.js';
-import {PanelInterface} from '/chromevox/panel/panel_interface.js';
-import {PanelMenu, PanelNodeMenu, PanelSearchMenu} from '/chromevox/panel/panel_menu.js';
-import {PanelMode, PanelModeInfo} from '/chromevox/panel/panel_mode.js';
-import {CursorRange} from '/common/cursors/range.js';
-import {EventGenerator} from '/common/event_generator.js';
+import {CursorRange} from '../../common/cursors/range.js';
+import {EventGenerator} from '../../common/event_generator.js';
+import {BrailleCommandData} from '../common/braille/braille_command_data.js';
+import {CommandStore} from '../common/command_store.js';
+import {EventSourceType} from '../common/event_source_type.js';
+import {GestureCommandData} from '../common/gesture_command_data.js';
+import {KeyMap} from '../common/key_map.js';
+import {KeyUtil} from '../common/key_util.js';
+import {LocaleOutputHelper} from '../common/locale_output_helper.js';
+import {PanelCommand, PanelCommandType} from '../common/panel_command.js';
+
+import {ISearchUI} from './i_search_ui.js';
+import {PanelInterface} from './panel_interface.js';
+import {PanelMenu, PanelNodeMenu, PanelSearchMenu} from './panel_menu.js';
+import {PanelMode, PanelModeInfo} from './panel_mode.js';
 
 /**
  * Class to manage the panel.
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_menu.js b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_menu.js
index ff4ce84..1344c4c 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_menu.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_menu.js
@@ -5,7 +5,7 @@
 /**
  * @fileoverview A drop-down menu in the ChromeVox panel.
  */
-import {PanelMenuItem} from '/chromevox/panel/panel_menu_item.js';
+import {PanelMenuItem} from './panel_menu_item.js';
 
 export class PanelMenu {
   /**
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_menu_item.js b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_menu_item.js
index 3dd739c..98eedbf 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_menu_item.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_menu_item.js
@@ -5,7 +5,7 @@
 /**
  * @fileoverview An item in a drop-down menu in the ChromeVox panel.
  */
-import {EventSourceType} from '/chromevox/common/event_source_type.js';
+import {EventSourceType} from '../common/event_source_type.js';
 
 export class PanelMenuItem {
   /**
diff --git a/chrome/browser/resources/chromeos/accessibility/common/cursors/range.js b/chrome/browser/resources/chromeos/accessibility/common/cursors/range.js
index 9241926..4c8d524 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/cursors/range.js
+++ b/chrome/browser/resources/chromeos/accessibility/common/cursors/range.js
@@ -7,7 +7,7 @@
  * the automation tree.
  */
 
-import {Cursor, CURSOR_NODE_INDEX, CursorMovement, CursorUnit, WrappingCursor} from '/common/cursors/cursor.js';
+import {Cursor, CURSOR_NODE_INDEX, CursorMovement, CursorUnit, WrappingCursor} from './cursor.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
 const RoleType = chrome.automation.RoleType;
diff --git a/chrome/browser/resources/chromeos/accessibility/common/tutorial/lesson_container.js b/chrome/browser/resources/chromeos/accessibility/common/tutorial/lesson_container.js
index 1a8ea3d..de230d0 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/tutorial/lesson_container.js
+++ b/chrome/browser/resources/chromeos/accessibility/common/tutorial/lesson_container.js
@@ -65,4 +65,4 @@
 
     return lessons;
   }
-});
\ No newline at end of file
+});
diff --git a/chrome/browser/resources/chromeos/accessibility/common/tutorial/tutorial_behavior.js b/chrome/browser/resources/chromeos/accessibility/common/tutorial/tutorial_behavior.js
index 887d4d9..3445740 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/tutorial/tutorial_behavior.js
+++ b/chrome/browser/resources/chromeos/accessibility/common/tutorial/tutorial_behavior.js
@@ -270,4 +270,4 @@
 
   /** @private */
   onTutorialVisibilityChanged_() {}
-};
\ No newline at end of file
+};
diff --git a/chrome/browser/resources/chromeos/accessibility/common/tutorial/tutorial_lesson.js b/chrome/browser/resources/chromeos/accessibility/common/tutorial/tutorial_lesson.js
index 31b09a9..02385ad 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/tutorial/tutorial_lesson.js
+++ b/chrome/browser/resources/chromeos/accessibility/common/tutorial/tutorial_lesson.js
@@ -301,4 +301,4 @@
     // interaction mediums are touch and keyboard.
     return this.getMsg('tutorial_touch_lesson_title_description');
   },
-});
\ No newline at end of file
+});
diff --git a/chrome/browser/resources/chromeos/accessibility/enhanced_network_tts/background.js b/chrome/browser/resources/chromeos/accessibility/enhanced_network_tts/background.js
index ab9d4cb..0bb3d23 100644
--- a/chrome/browser/resources/chromeos/accessibility/enhanced_network_tts/background.js
+++ b/chrome/browser/resources/chromeos/accessibility/enhanced_network_tts/background.js
@@ -2,8 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {InstanceChecker} from '/common/instance_checker.js';
-import {EnhancedNetworkTts} from '/enhanced_network_tts/enhanced_network_tts.js';
+import {InstanceChecker} from '../common/instance_checker.js';
+
+import {EnhancedNetworkTts} from './enhanced_network_tts.js';
 
 InstanceChecker.closeExtraInstances();
 export const enhancedNetworkTts = new EnhancedNetworkTts();
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/select_to_speak/BUILD.gn
index dd85a545..8eda498c 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/BUILD.gn
@@ -169,7 +169,6 @@
     "../common:rect_util",
     "../common:tree_walker",
   ]
-  closure_flags = [ "js_module_root=../" ]
 }
 
 js_library("select_to_speak") {
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/input_handler.js b/chrome/browser/resources/chromeos/accessibility/select_to_speak/input_handler.js
index 0f238df2..677def93 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/input_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/input_handler.js
@@ -2,8 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {RectUtil} from '/common/rect_util.js';
-import {SelectToSpeakConstants} from '/select_to_speak/select_to_speak_constants.js';
+import {RectUtil} from '../common/rect_util.js';
+
+import {SelectToSpeakConstants} from './select_to_speak_constants.js';
 
 const SelectToSpeakState = chrome.accessibilityPrivate.SelectToSpeakState;
 
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/metrics_utils.js b/chrome/browser/resources/chromeos/accessibility/select_to_speak/metrics_utils.js
index d0c0382..20d92c2 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/metrics_utils.js
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/metrics_utils.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 {PrefsManager} from '/select_to_speak/prefs_manager.js';
+import {PrefsManager} from './prefs_manager.js';
 
 // Utilities for UMA metrics.
 
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/node_navigation_utils.js b/chrome/browser/resources/chromeos/accessibility/select_to_speak/node_navigation_utils.js
index 1d8efd7..2fab3d7 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/node_navigation_utils.js
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/node_navigation_utils.js
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {NodeUtils} from '/select_to_speak/node_utils.js';
-import {ParagraphUtils} from '/select_to_speak/paragraph_utils.js';
-import {SentenceUtils} from '/select_to_speak/sentence_utils.js';
+import {NodeUtils} from './node_utils.js';
+import {ParagraphUtils} from './paragraph_utils.js';
+import {SentenceUtils} from './sentence_utils.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
 const RoleType = chrome.automation.RoleType;
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/node_utils.js b/chrome/browser/resources/chromeos/accessibility/select_to_speak/node_utils.js
index 86df12d2..a51470c9 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/node_utils.js
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/node_utils.js
@@ -2,8 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {RectUtil} from '/common/rect_util.js';
-import {ParagraphUtils} from '/select_to_speak/paragraph_utils.js';
+import {RectUtil} from '../common/rect_util.js';
+
+import {ParagraphUtils} from './paragraph_utils.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
 const RoleType = chrome.automation.RoleType;
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/paragraph_utils.js b/chrome/browser/resources/chromeos/accessibility/select_to_speak/paragraph_utils.js
index cbb1a76..0eeae60d 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/paragraph_utils.js
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/paragraph_utils.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 {WordUtils} from '/select_to_speak/word_utils.js';
+import {WordUtils} from './word_utils.js';
 
 var AutomationNode = chrome.automation.AutomationNode;
 var RoleType = chrome.automation.RoleType;
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak.js b/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak.js
index 974a402..2054204d 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak.js
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak.js
@@ -2,16 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {InputHandler} from '/select_to_speak/input_handler.js';
-import {MetricsUtils} from '/select_to_speak/metrics_utils.js';
-import {NodeNavigationUtils} from '/select_to_speak/node_navigation_utils.js';
-import {NodeUtils} from '/select_to_speak/node_utils.js';
-import {ParagraphUtils} from '/select_to_speak/paragraph_utils.js';
-import {PrefsManager} from '/select_to_speak/prefs_manager.js';
-import {SelectToSpeakConstants} from '/select_to_speak/select_to_speak_constants.js';
-import {TtsManager} from '/select_to_speak/tts_manager.js';
-import {SelectToSpeakUiListener, UiManager} from '/select_to_speak/ui_manager.js';
-import {WordUtils} from '/select_to_speak/word_utils.js';
+import {InputHandler} from './input_handler.js';
+import {MetricsUtils} from './metrics_utils.js';
+import {NodeNavigationUtils} from './node_navigation_utils.js';
+import {NodeUtils} from './node_utils.js';
+import {ParagraphUtils} from './paragraph_utils.js';
+import {PrefsManager} from './prefs_manager.js';
+import {SelectToSpeakConstants} from './select_to_speak_constants.js';
+import {TtsManager} from './tts_manager.js';
+import {SelectToSpeakUiListener, UiManager} from './ui_manager.js';
+import {WordUtils} from './word_utils.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
 const AutomationEvent = chrome.automation.AutomationEvent;
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_main.js b/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_main.js
index 15a83a24..e6f18cd 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_main.js
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_main.js
@@ -2,8 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {InstanceChecker} from '/common/instance_checker.js';
-import {SelectToSpeak} from '/select_to_speak/select_to_speak.js';
+import {InstanceChecker} from '../common/instance_checker.js';
+
+import {SelectToSpeak} from './select_to_speak.js';
 
 InstanceChecker.closeExtraInstances();
 export const selectToSpeak = new SelectToSpeak();
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_options.js b/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_options.js
index 0bb0e48..9b5edae 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_options.js
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_options.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 {PrefsManager} from '/select_to_speak/prefs_manager.js';
+import {PrefsManager} from './prefs_manager.js';
 
 class SelectToSpeakOptionsPage {
   constructor() {
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/sentence_utils.js b/chrome/browser/resources/chromeos/accessibility/select_to_speak/sentence_utils.js
index 1ce594d..8637d2f 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/sentence_utils.js
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/sentence_utils.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 {ParagraphUtils} from '/select_to_speak/paragraph_utils.js';
+import {ParagraphUtils} from './paragraph_utils.js';
 
 const RoleType = chrome.automation.RoleType;
 
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/test_node_generator.js b/chrome/browser/resources/chromeos/accessibility/select_to_speak/test_node_generator.js
index ebf4bb1..b9134bd 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/test_node_generator.js
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/test_node_generator.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 {ParagraphUtils} from '/select_to_speak/paragraph_utils.js';
+import {ParagraphUtils} from './paragraph_utils.js';
 
 /**
  * Creates a AutomationNode-like object.
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/ui_manager.js b/chrome/browser/resources/chromeos/accessibility/select_to_speak/ui_manager.js
index da63e33..09dce81 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/ui_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/ui_manager.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 {ParagraphUtils} from '/select_to_speak/paragraph_utils.js';
-import {PrefsManager} from '/select_to_speak/prefs_manager.js';
+import {ParagraphUtils} from './paragraph_utils.js';
+import {PrefsManager} from './prefs_manager.js';
 
 const AutomationEvent = chrome.automation.AutomationEvent;
 const AutomationNode = chrome.automation.AutomationNode;
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/word_utils.js b/chrome/browser/resources/chromeos/accessibility/select_to_speak/word_utils.js
index 5285fa3..a46c7ec4 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/word_utils.js
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/word_utils.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 {ParagraphUtils} from '/select_to_speak/paragraph_utils.js';
+import {ParagraphUtils} from './paragraph_utils.js';
 
 // Utilities for processing words within strings and nodes.
 
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/switch_access/BUILD.gn
index c83e11b..feb72d9 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/BUILD.gn
@@ -186,7 +186,6 @@
     "../common:repeated_tree_change_handler",
     "../common:tree_walker",
   ]
-  closure_flags = [ "js_module_root=../" ]
 }
 
 js_library("action_manager") {
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/action_manager.js b/chrome/browser/resources/chromeos/accessibility/switch_access/action_manager.js
index 2fe18e1..0a100d9 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/action_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/action_manager.js
@@ -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 {FocusRingManager} from '/switch_access/focus_ring_manager.js';
-import {MenuManager} from '/switch_access/menu_manager.js';
-import {SwitchAccessMetrics} from '/switch_access/metrics.js';
-import {Navigator} from '/switch_access/navigator.js';
-import {SAChildNode, SARootNode} from '/switch_access/nodes/switch_access_node.js';
-import {SwitchAccess} from '/switch_access/switch_access.js';
-import {SAConstants, SwitchAccessMenuAction} from '/switch_access/switch_access_constants.js';
+import {FocusRingManager} from './focus_ring_manager.js';
+import {MenuManager} from './menu_manager.js';
+import {SwitchAccessMetrics} from './metrics.js';
+import {Navigator} from './navigator.js';
+import {SAChildNode, SARootNode} from './nodes/switch_access_node.js';
+import {SwitchAccess} from './switch_access.js';
+import {SAConstants, SwitchAccessMenuAction} from './switch_access_constants.js';
 
 /**
  * Class to handle performing actions with Switch Access, including determining
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/auto_scan_manager.js b/chrome/browser/resources/chromeos/accessibility/switch_access/auto_scan_manager.js
index fa0fe20..24f46851 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/auto_scan_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/auto_scan_manager.js
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {Navigator} from '/switch_access/navigator.js';
-import {SwitchAccess} from '/switch_access/switch_access.js';
-import {SAConstants} from '/switch_access/switch_access_constants.js';
+import {Navigator} from './navigator.js';
+import {SwitchAccess} from './switch_access.js';
+import {SAConstants} from './switch_access_constants.js';
 
 /**
  * Class to handle auto-scan behavior.
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/background.js b/chrome/browser/resources/chromeos/accessibility/switch_access/background.js
index 33f2271..78491a2 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/background.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/background.js
@@ -2,8 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {InstanceChecker} from '/common/instance_checker.js';
-import {SwitchAccess} from '/switch_access/switch_access.js';
+import {InstanceChecker} from '../common/instance_checker.js';
+
+import {SwitchAccess} from './switch_access.js';
 
 InstanceChecker.closeExtraInstances();
 SwitchAccess.initialize();
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/commands.js b/chrome/browser/resources/chromeos/accessibility/switch_access/commands.js
index 21630c36..9cdc081 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/commands.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/commands.js
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {ActionManager} from '/switch_access/action_manager.js';
-import {AutoScanManager} from '/switch_access/auto_scan_manager.js';
-import {Navigator} from '/switch_access/navigator.js';
+import {ActionManager} from './action_manager.js';
+import {AutoScanManager} from './auto_scan_manager.js';
+import {Navigator} from './navigator.js';
 
 const SwitchAccessCommand = chrome.accessibilityPrivate.SwitchAccessCommand;
 
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/focus_ring_manager.js b/chrome/browser/resources/chromeos/accessibility/switch_access/focus_ring_manager.js
index 2f413e6..2b75fb12 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/focus_ring_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/focus_ring_manager.js
@@ -2,11 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {RectUtil} from '/common/rect_util.js';
-import {MenuManager} from '/switch_access/menu_manager.js';
-import {SAChildNode, SARootNode} from '/switch_access/nodes/switch_access_node.js';
-import {SwitchAccess} from '/switch_access/switch_access.js';
-import {SAConstants} from '/switch_access/switch_access_constants.js';
+import {RectUtil} from '../common/rect_util.js';
+
+import {MenuManager} from './menu_manager.js';
+import {SAChildNode, SARootNode} from './nodes/switch_access_node.js';
+import {SwitchAccess} from './switch_access.js';
+import {SAConstants} from './switch_access_constants.js';
 
 /**
  * Class to handle focus rings.
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/history.js b/chrome/browser/resources/chromeos/accessibility/switch_access/history.js
index cfca462..13fab6e1 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/history.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/history.js
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {SACache} from '/switch_access/cache.js';
-import {Navigator} from '/switch_access/navigator.js';
-import {DesktopNode} from '/switch_access/nodes/desktop_node.js';
-import {SAChildNode, SARootNode} from '/switch_access/nodes/switch_access_node.js';
-import {SwitchAccessPredicate} from '/switch_access/switch_access_predicate.js';
+import {SACache} from './cache.js';
+import {Navigator} from './navigator.js';
+import {DesktopNode} from './nodes/desktop_node.js';
+import {SAChildNode, SARootNode} from './nodes/switch_access_node.js';
+import {SwitchAccessPredicate} from './switch_access_predicate.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
 
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager.js b/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager.js
index 7d647f2d..f1801d38 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager.js
@@ -2,27 +2,28 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {EventGenerator} from '/common/event_generator.js';
-import {RectUtil} from '/common/rect_util.js';
-import {ActionManager} from '/switch_access/action_manager.js';
-import {AutoScanManager} from '/switch_access/auto_scan_manager.js';
-import {FocusRingManager} from '/switch_access/focus_ring_manager.js';
-import {FocusData, FocusHistory} from '/switch_access/history.js';
-import {MenuManager} from '/switch_access/menu_manager.js';
-import {Navigator} from '/switch_access/navigator.js';
-import {ItemNavigatorInterface} from '/switch_access/navigator_interface.js';
-import {BackButtonNode} from '/switch_access/nodes/back_button_node.js';
-import {BasicNode, BasicRootNode} from '/switch_access/nodes/basic_node.js';
-import {DesktopNode} from '/switch_access/nodes/desktop_node.js';
-import {EditableTextNode} from '/switch_access/nodes/editable_text_node.js';
-import {KeyboardRootNode} from '/switch_access/nodes/keyboard_node.js';
-import {ModalDialogRootNode} from '/switch_access/nodes/modal_dialog_node.js';
-import {SliderNode} from '/switch_access/nodes/slider_node.js';
-import {SAChildNode, SARootNode} from '/switch_access/nodes/switch_access_node.js';
-import {TabNode} from '/switch_access/nodes/tab_node.js';
-import {SwitchAccess} from '/switch_access/switch_access.js';
-import {SAConstants} from '/switch_access/switch_access_constants.js';
-import {SwitchAccessPredicate} from '/switch_access/switch_access_predicate.js';
+import {EventGenerator} from '../common/event_generator.js';
+import {RectUtil} from '../common/rect_util.js';
+
+import {ActionManager} from './action_manager.js';
+import {AutoScanManager} from './auto_scan_manager.js';
+import {FocusRingManager} from './focus_ring_manager.js';
+import {FocusData, FocusHistory} from './history.js';
+import {MenuManager} from './menu_manager.js';
+import {Navigator} from './navigator.js';
+import {ItemNavigatorInterface} from './navigator_interface.js';
+import {BackButtonNode} from './nodes/back_button_node.js';
+import {BasicNode, BasicRootNode} from './nodes/basic_node.js';
+import {DesktopNode} from './nodes/desktop_node.js';
+import {EditableTextNode} from './nodes/editable_text_node.js';
+import {KeyboardRootNode} from './nodes/keyboard_node.js';
+import {ModalDialogRootNode} from './nodes/modal_dialog_node.js';
+import {SliderNode} from './nodes/slider_node.js';
+import {SAChildNode, SARootNode} from './nodes/switch_access_node.js';
+import {TabNode} from './nodes/tab_node.js';
+import {SwitchAccess} from './switch_access.js';
+import {SAConstants} from './switch_access_constants.js';
+import {SwitchAccessPredicate} from './switch_access_predicate.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
 
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/menu_manager.js b/chrome/browser/resources/chromeos/accessibility/switch_access/menu_manager.js
index 6a45015..087f4139 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/menu_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/menu_manager.js
@@ -2,11 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {ArrayUtil} from '/common/array_util.js';
-import {ActionManager} from '/switch_access/action_manager.js';
-import {Navigator} from '/switch_access/navigator.js';
-import {SwitchAccess} from '/switch_access/switch_access.js';
-import {SwitchAccessMenuAction} from '/switch_access/switch_access_constants.js';
+import {ArrayUtil} from '../common/array_util.js';
+
+import {ActionManager} from './action_manager.js';
+import {Navigator} from './navigator.js';
+import {SwitchAccess} from './switch_access.js';
+import {SwitchAccessMenuAction} from './switch_access_constants.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
 
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/navigator.js b/chrome/browser/resources/chromeos/accessibility/switch_access/navigator.js
index f3086310..e0cfe53 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/navigator.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/navigator.js
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {ItemScanManager} from '/switch_access/item_scan_manager.js';
-import {ItemNavigatorInterface, PointNavigatorInterface} from '/switch_access/navigator_interface.js';
-import {PointScanManager} from '/switch_access/point_scan_manager.js';
+import {ItemScanManager} from './item_scan_manager.js';
+import {ItemNavigatorInterface, PointNavigatorInterface} from './navigator_interface.js';
+import {PointScanManager} from './point_scan_manager.js';
 
 export class Navigator {
   /** @param {!chrome.automation.AutomationNode} desktop */
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/navigator_interface.js b/chrome/browser/resources/chromeos/accessibility/switch_access/navigator_interface.js
index 1d2cbf6..5574900 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/navigator_interface.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/navigator_interface.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 {SAChildNode, SARootNode} from '/switch_access/nodes/switch_access_node.js';
+import {SAChildNode, SARootNode} from './nodes/switch_access_node.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
 
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/back_button_node.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/back_button_node.js
index 8d6b1f7a..adcdb19e 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/back_button_node.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/back_button_node.js
@@ -2,13 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {ActionManager} from '/switch_access/action_manager.js';
-import {FocusRingManager} from '/switch_access/focus_ring_manager.js';
-import {MenuManager} from '/switch_access/menu_manager.js';
-import {Navigator} from '/switch_access/navigator.js';
-import {SAChildNode, SARootNode} from '/switch_access/nodes/switch_access_node.js';
-import {SwitchAccess} from '/switch_access/switch_access.js';
-import {SAConstants, SwitchAccessMenuAction} from '/switch_access/switch_access_constants.js';
+import {ActionManager} from '../action_manager.js';
+import {FocusRingManager} from '../focus_ring_manager.js';
+import {MenuManager} from '../menu_manager.js';
+import {Navigator} from '../navigator.js';
+import {SwitchAccess} from '../switch_access.js';
+import {SAConstants, SwitchAccessMenuAction} from '../switch_access_constants.js';
+
+import {SAChildNode, SARootNode} from './switch_access_node.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
 
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/basic_node.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/basic_node.js
index 55db5c6..ebd62c4c 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/basic_node.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/basic_node.js
@@ -2,14 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {SACache} from '/switch_access/cache.js';
-import {FocusRingManager} from '/switch_access/focus_ring_manager.js';
-import {Navigator} from '/switch_access/navigator.js';
-import {BackButtonNode} from '/switch_access/nodes/back_button_node.js';
-import {SAChildNode, SARootNode} from '/switch_access/nodes/switch_access_node.js';
-import {SwitchAccess} from '/switch_access/switch_access.js';
-import {SAConstants, SwitchAccessMenuAction} from '/switch_access/switch_access_constants.js';
-import {SwitchAccessPredicate} from '/switch_access/switch_access_predicate.js';
+import {SACache} from '../cache.js';
+import {FocusRingManager} from '../focus_ring_manager.js';
+import {Navigator} from '../navigator.js';
+import {SwitchAccess} from '../switch_access.js';
+import {SAConstants, SwitchAccessMenuAction} from '../switch_access_constants.js';
+import {SwitchAccessPredicate} from '../switch_access_predicate.js';
+
+import {BackButtonNode} from './back_button_node.js';
+import {SAChildNode, SARootNode} from './switch_access_node.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
 
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/combo_box_node.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/combo_box_node.js
index 37b6b8a3..d4d6eeb 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/combo_box_node.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/combo_box_node.js
@@ -2,11 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {EventGenerator} from '/common/event_generator.js';
-import {Navigator} from '/switch_access/navigator.js';
-import {BasicNode} from '/switch_access/nodes/basic_node.js';
-import {SAChildNode, SARootNode} from '/switch_access/nodes/switch_access_node.js';
-import {SAConstants, SwitchAccessMenuAction} from '/switch_access/switch_access_constants.js';
+import {EventGenerator} from '../../common/event_generator.js';
+import {Navigator} from '../navigator.js';
+import {SAConstants, SwitchAccessMenuAction} from '../switch_access_constants.js';
+
+import {BasicNode} from './basic_node.js';
+import {SAChildNode, SARootNode} from './switch_access_node.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
 
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/desktop_node.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/desktop_node.js
index 8407fcd..4de834ec 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/desktop_node.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/desktop_node.js
@@ -2,11 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {Navigator} from '/switch_access/navigator.js';
-import {BasicNode, BasicRootNode} from '/switch_access/nodes/basic_node.js';
-import {SwitchAccess} from '/switch_access/switch_access.js';
-import {SAConstants} from '/switch_access/switch_access_constants.js';
-import {SwitchAccessPredicate} from '/switch_access/switch_access_predicate.js';
+import {Navigator} from '../navigator.js';
+import {SwitchAccess} from '../switch_access.js';
+import {SAConstants} from '../switch_access_constants.js';
+import {SwitchAccessPredicate} from '../switch_access_predicate.js';
+
+import {BasicNode, BasicRootNode} from './basic_node.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
 
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/editable_text_node.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/editable_text_node.js
index 2dbd04692..20d3cd4 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/editable_text_node.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/editable_text_node.js
@@ -2,14 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {EventGenerator} from '/common/event_generator.js';
-import {Navigator} from '/switch_access/navigator.js';
-import {BasicNode} from '/switch_access/nodes/basic_node.js';
-import {SAChildNode, SARootNode} from '/switch_access/nodes/switch_access_node.js';
-import {SwitchAccess} from '/switch_access/switch_access.js';
-import {SAConstants, SwitchAccessMenuAction} from '/switch_access/switch_access_constants.js';
-import {SwitchAccessPredicate} from '/switch_access/switch_access_predicate.js';
-import {TextNavigationManager} from '/switch_access/text_navigation_manager.js';
+import {EventGenerator} from '../../common/event_generator.js';
+import {Navigator} from '../navigator.js';
+import {SwitchAccess} from '../switch_access.js';
+import {SAConstants, SwitchAccessMenuAction} from '../switch_access_constants.js';
+import {SwitchAccessPredicate} from '../switch_access_predicate.js';
+import {TextNavigationManager} from '../text_navigation_manager.js';
+
+import {BasicNode} from './basic_node.js';
+import {SAChildNode, SARootNode} from './switch_access_node.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
 
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/group_node.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/group_node.js
index 4d3ed19d..4222d64 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/group_node.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/group_node.js
@@ -2,11 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {RectUtil} from '/common/rect_util.js';
-import {Navigator} from '/switch_access/navigator.js';
-import {BackButtonNode} from '/switch_access/nodes/back_button_node.js';
-import {SAChildNode, SARootNode} from '/switch_access/nodes/switch_access_node.js';
-import {SAConstants, SwitchAccessMenuAction} from '/switch_access/switch_access_constants.js';
+import {RectUtil} from '../../common/rect_util.js';
+import {Navigator} from '../navigator.js';
+import {SAConstants, SwitchAccessMenuAction} from '../switch_access_constants.js';
+
+import {BackButtonNode} from './back_button_node.js';
+import {SAChildNode, SARootNode} from './switch_access_node.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
 
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/keyboard_node.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/keyboard_node.js
index 37c11eda..b0a74eb3 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/keyboard_node.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/keyboard_node.js
@@ -2,17 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {EventGenerator} from '/common/event_generator.js';
-import {RectUtil} from '/common/rect_util.js';
-import {AutoScanManager} from '/switch_access/auto_scan_manager.js';
-import {Navigator} from '/switch_access/navigator.js';
-import {BackButtonNode} from '/switch_access/nodes/back_button_node.js';
-import {BasicNode, BasicRootNode} from '/switch_access/nodes/basic_node.js';
-import {GroupNode} from '/switch_access/nodes/group_node.js';
-import {SAChildNode, SARootNode} from '/switch_access/nodes/switch_access_node.js';
-import {SwitchAccess} from '/switch_access/switch_access.js';
-import {SAConstants, SwitchAccessMenuAction} from '/switch_access/switch_access_constants.js';
-import {SwitchAccessPredicate} from '/switch_access/switch_access_predicate.js';
+import {EventGenerator} from '../../common/event_generator.js';
+import {RectUtil} from '../../common/rect_util.js';
+import {AutoScanManager} from '../auto_scan_manager.js';
+import {Navigator} from '../navigator.js';
+import {SwitchAccess} from '../switch_access.js';
+import {SAConstants, SwitchAccessMenuAction} from '../switch_access_constants.js';
+import {SwitchAccessPredicate} from '../switch_access_predicate.js';
+
+import {BackButtonNode} from './back_button_node.js';
+import {BasicNode, BasicRootNode} from './basic_node.js';
+import {GroupNode} from './group_node.js';
+import {SAChildNode, SARootNode} from './switch_access_node.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
 
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/modal_dialog_node.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/modal_dialog_node.js
index 475e2ad..aba92878 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/modal_dialog_node.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/modal_dialog_node.js
@@ -2,8 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {EventGenerator} from '/common/event_generator.js';
-import {BasicNode, BasicRootNode} from '/switch_access/nodes/basic_node.js';
+import {EventGenerator} from '../../common/event_generator.js';
+
+import {BasicNode, BasicRootNode} from './basic_node.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
 
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/slider_node.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/slider_node.js
index d1ebed8..09c846f 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/slider_node.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/slider_node.js
@@ -2,10 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {EventGenerator} from '/common/event_generator.js';
-import {BasicNode} from '/switch_access/nodes/basic_node.js';
-import {SAChildNode, SARootNode} from '/switch_access/nodes/switch_access_node.js';
-import {SAConstants, SwitchAccessMenuAction} from '/switch_access/switch_access_constants.js';
+import {EventGenerator} from '../../common/event_generator.js';
+import {SAConstants, SwitchAccessMenuAction} from '../switch_access_constants.js';
+
+import {BasicNode} from './basic_node.js';
+import {SAChildNode, SARootNode} from './switch_access_node.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
 
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/switch_access_node.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/switch_access_node.js
index cd3851d5..0ba16163 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/switch_access_node.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/switch_access_node.js
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {RectUtil} from '/common/rect_util.js';
-import {FocusRingManager} from '/switch_access/focus_ring_manager.js';
-import {SwitchAccess} from '/switch_access/switch_access.js';
-import {SAConstants, SwitchAccessMenuAction} from '/switch_access/switch_access_constants.js';
+import {RectUtil} from '../../common/rect_util.js';
+import {FocusRingManager} from '../focus_ring_manager.js';
+import {SwitchAccess} from '../switch_access.js';
+import {SAConstants, SwitchAccessMenuAction} from '../switch_access_constants.js';
 
 
 const AutomationNode = chrome.automation.AutomationNode;
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/tab_node.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/tab_node.js
index 0e65d13..0d40436c 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/tab_node.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/tab_node.js
@@ -2,12 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {RectUtil} from '/common/rect_util.js';
-import {Navigator} from '/switch_access/navigator.js';
-import {BackButtonNode} from '/switch_access/nodes/back_button_node.js';
-import {BasicNode, BasicRootNode} from '/switch_access/nodes/basic_node.js';
-import {SAChildNode, SARootNode} from '/switch_access/nodes/switch_access_node.js';
-import {SAConstants, SwitchAccessMenuAction} from '/switch_access/switch_access_constants.js';
+import {RectUtil} from '../../common/rect_util.js';
+import {Navigator} from '../navigator.js';
+import {SAConstants, SwitchAccessMenuAction} from '../switch_access_constants.js';
+
+import {BackButtonNode} from './back_button_node.js';
+import {BasicNode, BasicRootNode} from './basic_node.js';
+import {SAChildNode, SARootNode} from './switch_access_node.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
 
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/window_node.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/window_node.js
index 1bf8e05..4b66072a 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/window_node.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/window_node.js
@@ -2,8 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BasicNode, BasicRootNode} from '/switch_access/nodes/basic_node.js';
-import {SwitchAccessPredicate} from '/switch_access/switch_access_predicate.js';
+import {SwitchAccessPredicate} from '../switch_access_predicate.js';
+
+import {BasicNode, BasicRootNode} from './basic_node.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
 
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/point_scan_manager.js b/chrome/browser/resources/chromeos/accessibility/switch_access/point_scan_manager.js
index d2b47b8..02da8665 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/point_scan_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/point_scan_manager.js
@@ -2,12 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {EventGenerator} from '/common/event_generator.js';
-import {ActionManager} from '/switch_access/action_manager.js';
-import {FocusRingManager} from '/switch_access/focus_ring_manager.js';
-import {PointNavigatorInterface} from '/switch_access/navigator_interface.js';
-import {SwitchAccess} from '/switch_access/switch_access.js';
-import {SAConstants, SwitchAccessMenuAction} from '/switch_access/switch_access_constants.js';
+import {EventGenerator} from '../common/event_generator.js';
+
+import {ActionManager} from './action_manager.js';
+import {FocusRingManager} from './focus_ring_manager.js';
+import {PointNavigatorInterface} from './navigator_interface.js';
+import {SwitchAccess} from './switch_access.js';
+import {SAConstants, SwitchAccessMenuAction} from './switch_access_constants.js';
 
 const PointScanState = chrome.accessibilityPrivate.PointScanState;
 
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/preference_manager.js b/chrome/browser/resources/chromeos/accessibility/switch_access/preference_manager.js
index 9ebdce5c..d2c2fe0f 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/preference_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/preference_manager.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 {AutoScanManager} from '/switch_access/auto_scan_manager.js';
-import {SAConstants} from '/switch_access/switch_access_constants.js';
+import {AutoScanManager} from './auto_scan_manager.js';
+import {SAConstants} from './switch_access_constants.js';
 
 /**
  * Class to manage user preferences.
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access.js b/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access.js
index 157921e..6af4832 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access.js
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {Commands} from '/switch_access/commands.js';
-import {Navigator} from '/switch_access/navigator.js';
-import {KeyboardRootNode} from '/switch_access/nodes/keyboard_node.js';
-import {PreferenceManager} from '/switch_access/preference_manager.js';
-import {SAConstants} from '/switch_access/switch_access_constants.js';
+import {Commands} from './commands.js';
+import {Navigator} from './navigator.js';
+import {KeyboardRootNode} from './nodes/keyboard_node.js';
+import {PreferenceManager} from './preference_manager.js';
+import {SAConstants} from './switch_access_constants.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
 
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_predicate.js b/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_predicate.js
index 6f1636ca..1db8ec6 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_predicate.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_predicate.js
@@ -2,9 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {RectUtil} from '/common/rect_util.js';
-import {SACache} from '/switch_access/cache.js';
-import {SAChildNode, SARootNode} from '/switch_access/nodes/switch_access_node.js';
+import {RectUtil} from '../common/rect_util.js';
+
+import {SACache} from './cache.js';
+import {SAChildNode, SARootNode} from './nodes/switch_access_node.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
 const StateType = chrome.automation.StateType;
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/text_navigation_manager.js b/chrome/browser/resources/chromeos/accessibility/switch_access/text_navigation_manager.js
index 322752c..337b6c95 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/text_navigation_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/text_navigation_manager.js
@@ -2,11 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {EventGenerator} from '/common/event_generator.js';
-import {ActionManager} from '/switch_access/action_manager.js';
-import {Navigator} from '/switch_access/navigator.js';
-import {SwitchAccess} from '/switch_access/switch_access.js';
-import {SAConstants, SwitchAccessMenuAction} from '/switch_access/switch_access_constants.js';
+import {EventGenerator} from '../common/event_generator.js';
+
+import {ActionManager} from './action_manager.js';
+import {Navigator} from './navigator.js';
+import {SwitchAccess} from './switch_access.js';
+import {SAConstants, SwitchAccessMenuAction} from './switch_access_constants.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
 
diff --git a/chrome/browser/resources/chromeos/contact_center_insights/background.js b/chrome/browser/resources/chromeos/contact_center_insights/background.js
index 121ff3b..dd75cec 100644
--- a/chrome/browser/resources/chromeos/contact_center_insights/background.js
+++ b/chrome/browser/resources/chromeos/contact_center_insights/background.js
@@ -6,46 +6,18 @@
 // that follow
 importScripts('ccaas_deps.js');
 
-goog.require('proto.reporting.BandwidthData');
 goog.require('proto.reporting.Record');
 goog.require('proto.reporting.Destination');
 goog.require('proto.reporting.Priority');
-goog.require('proto.reporting.NetworksTelemetry');
 goog.require('proto.reporting.MetricData');
 goog.require('proto.reporting.TelemetryData');
 goog.require('proto.reporting.UserStatusTelemetry');
 goog.require('proto.reporting.UserStatusTelemetry.DeviceActivityState');
 
-const NETWORK_BANDWIDTH_ALARM = 'NetworkBandwidth';
-const REPORT_NETWORK_BANDWIDTH_PERIOD_MINUTES = 15;
-
 const DEVICE_ACTIVITY_STATE_ALARM = 'DeviceActivityState';
 const REPORT_DEVICE_ACTIVITY_STATE_PERIOD_MINUTES = 15;
 const IDLE_THRESHOLD_SECONDS = 5 /** minutes **/ * 60;
 
-function reportBandwidthData() {
-  // Extract bandwidth data
-  const networkInfo = navigator.connection;
-  if (!networkInfo) {
-    // No data
-    console.error('Network info unavailable');
-    return;
-  }
-
-  // Prepare telemetry proto message with network bandwidth information
-  const bandwidth = new proto.reporting.BandwidthData();
-  const downloadSpeedKbps = networkInfo.downlink /** mbps **/ * 1000;
-  bandwidth.setDownloadSpeedKbps(downloadSpeedKbps);
-
-  const networksTelemetry = new proto.reporting.NetworksTelemetry();
-  networksTelemetry.setBandwidthData(bandwidth);
-
-  const telemetryData = new proto.reporting.TelemetryData();
-  telemetryData.setNetworksTelemetry(networksTelemetry);
-
-  reportTelemetryData(telemetryData);
-}
-
 function reportDeviceActivityState() {
   chrome.idle.queryState(IDLE_THRESHOLD_SECONDS, (state) => {
     const userStatusTelemetry = new proto.reporting.UserStatusTelemetry();
@@ -124,10 +96,6 @@
 
 // Global listener for all alarms
 chrome.alarms.onAlarm.addListener((alarm) => {
-  if (alarm.name === NETWORK_BANDWIDTH_ALARM) {
-    reportBandwidthData();
-  }
-
   if (alarm.name === DEVICE_ACTIVITY_STATE_ALARM) {
     reportDeviceActivityState();
   }
@@ -135,7 +103,6 @@
 
 // Register alarms for periodically reporting telemetry data.
 chrome.runtime.onInstalled.addListener(() => {
-  createAlarm(NETWORK_BANDWIDTH_ALARM, REPORT_NETWORK_BANDWIDTH_PERIOD_MINUTES);
   createAlarm(
       DEVICE_ACTIVITY_STATE_ALARM, REPORT_DEVICE_ACTIVITY_STATE_PERIOD_MINUTES);
 });
diff --git a/chrome/browser/resources/chromeos/contact_center_insights/manifest.json b/chrome/browser/resources/chromeos/contact_center_insights/manifest.json
index 8cdf89ce..286aafe 100644
--- a/chrome/browser/resources/chromeos/contact_center_insights/manifest.json
+++ b/chrome/browser/resources/chromeos/contact_center_insights/manifest.json
@@ -2,7 +2,7 @@
   // chrome-extension://oebfonohdfogiaaaelfmjlkjbgdbaahf
   "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxOnBHZ/Nxvc2WeeAxeSNXiOAmOPSeyjC70+3tfpbySO8Pslt/zWJBT76dhM/Gm+OwVoFdB5/C/pXfJGMqPTGFNyMq5MgJuo4giqWA542sKgn4y8lwtN4Z/2XhZPo5BXizyyxRq1lZdV21ZyImmW+3ODkC35CZ/bTXdpHzV0I5hJ14wVRzZ/fS047R5Dx+/JUJCp7uspL8nt00hPuwFmW/PLZjAnmMq3ULW216YP2VhF9ROUwRwvZqgJ5nWmfQj7dlVKstwa1PtQgqe/2p0oVif4NP/hg3+zpz7y9f0kXFRB/QVjg6R9hLGqDuqu5uh5LfTarVMH35zvaiZGhtB4rLQIDAQAB",
   "name": "Contact Center Insights",
-  "version": "1.0.0",
+  "version": "1.0.1",
   "manifest_version": 3,
   "background": {
     "service_worker": "background_wrapper.js"
diff --git a/chrome/browser/resources/management/management_ui.ts b/chrome/browser/resources/management/management_ui.ts
index 02004b5c..14cec01 100644
--- a/chrome/browser/resources/management/management_ui.ts
+++ b/chrome/browser/resources/management/management_ui.ts
@@ -171,13 +171,13 @@
       return info;
     }, {} as {[k: string]: {icon: string, messageIds: string[]}});
 
-    const reportingTypeOrder = {
+    const reportingTypeOrder: {[k: string]: number} = {
       [ReportingType.SECURITY]: 1,
       [ReportingType.EXTENSIONS]: 2,
       [ReportingType.USER]: 3,
       [ReportingType.USER_ACTIVITY]: 4,
       [ReportingType.DEVICE]: 5,
-    } as {[k: string]: number};
+    };
 
     this.browserReportingInfo_ =
         Object.keys(reportingInfoMap)
diff --git a/chrome/browser/resources/new_tab_page/modules/modules.ts b/chrome/browser/resources/new_tab_page/modules/modules.ts
index 21b78ef..a03be5fe 100644
--- a/chrome/browser/resources/new_tab_page/modules/modules.ts
+++ b/chrome/browser/resources/new_tab_page/modules/modules.ts
@@ -64,16 +64,24 @@
 
   static get properties() {
     return {
+      disabledModules_: {
+        type: Object,
+        value: () => ({all: true, ids: []}),
+      },
+
       dismissedModules_: {
         type: Array,
         value: () => [],
       },
 
-      disabledModules_: {
-        type: Object,
-        value: () => ({all: true, ids: []}),
+      dragEnabled_: {
+        type: Boolean,
+        value: () => loadTimeData.getBoolean('modulesDragAndDropEnabled'),
+        reflectToAttribute: true,
       },
 
+      moduleImpressionDetected_: Boolean,
+
       modulesFreRemoved_: {
         type: Boolean,
         value: false,
@@ -97,18 +105,8 @@
         value: false,
       },
 
-      /** Data about the most recently removed module. */
-      removedModuleData_: {
-        type: Object,
-        value: null,
-      },
-
-      moduleImpressionDetected_: Boolean,
-
       modulesLoaded_: Boolean,
 
-      modulesVisibilityDetermined_: Boolean,
-
       modulesLoadedAndVisibilityDetermined_: {
         type: Boolean,
         computed: `computeModulesLoadedAndVisibilityDetermined_(
@@ -117,22 +115,23 @@
         observer: 'onModulesLoadedAndVisibilityDeterminedChange_',
       },
 
-      modulesShownToUser: {
-        type: Boolean,
-        notify: true,
-      },
-
-      /** @private {boolean} */
       modulesRedesignedLayoutEnabled_: {
         type: Boolean,
         value: () => loadTimeData.getBoolean('modulesRedesignedLayoutEnabled'),
         reflectToAttribute: true,
       },
 
-      dragEnabled_: {
+      modulesShownToUser: {
         type: Boolean,
-        value: () => loadTimeData.getBoolean('modulesDragAndDropEnabled'),
-        reflectToAttribute: true,
+        notify: true,
+      },
+
+      modulesVisibilityDetermined_: Boolean,
+
+      /** Data about the most recently removed module. */
+      removedModuleData_: {
+        type: Object,
+        value: null,
       },
     };
   }
@@ -143,16 +142,17 @@
 
   private dismissedModules_: string[];
   private disabledModules_: {all: boolean, ids: string[]};
-  private removedModuleData_: {message: string, undo: () => void}|null;
+  private dragEnabled_: boolean;
+  private moduleImpressionDetected_: boolean;
   private modulesFreRemoved_: boolean;
   private modulesFreShown: boolean;
   private modulesFreVisible_: boolean;
-  private moduleImpressionDetected_: boolean;
   private modulesLoaded_: boolean;
-  private modulesVisibilityDetermined_: boolean;
   private modulesLoadedAndVisibilityDetermined_: boolean;
+  private modulesRedesignedLayoutEnabled_: boolean;
   private modulesShownToUser: boolean;
-  private dragEnabled_: boolean;
+  private modulesVisibilityDetermined_: boolean;
+  private removedModuleData_: {message: string, undo: () => void}|null;
 
   private setDisabledModulesListenerId_: number|null = null;
   private setModulesFreVisibilityListenerId_: number|null = null;
@@ -201,7 +201,7 @@
       if (!moduleContainer.hidden) {
         this.modulesShownToUser = !moduleContainer.hidden;
       }
-      if (loadTimeData.getBoolean('modulesRedesignedLayoutEnabled')) {
+      if (this.modulesRedesignedLayoutEnabled_) {
         // Remove short module sibling container class name from short modules
         // that were in a sibling container before.
         moduleContainer.classList.toggle(SHORT_MODULE_SIBLING_1, false);
@@ -284,7 +284,7 @@
         });
         const moduleContainer = this.ownerDocument.createElement('div');
         moduleContainer.classList.add('module-container');
-        if (loadTimeData.getBoolean('modulesRedesignedLayoutEnabled')) {
+        if (this.modulesRedesignedLayoutEnabled_) {
           if (module.descriptor.height === ModuleHeight.SHORT) {
             moduleContainer.classList.add(SHORT_CLASS_NAME);
           }
diff --git a/chrome/browser/resources/settings/chromeos/.eslintrc.js b/chrome/browser/resources/settings/chromeos/.eslintrc.js
new file mode 100644
index 0000000..39744917
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/.eslintrc.js
@@ -0,0 +1,9 @@
+// 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.
+
+module.exports = {
+  'rules': {
+    'comma-dangle' : ['error', 'always-multiline'],
+  },
+};
diff --git a/chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_page.js b/chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_page.js
index 7dd31e3..6c45aad 100644
--- a/chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_page.js
+++ b/chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_page.js
@@ -41,8 +41,11 @@
  */
 const SettingsAmbientModePageElementBase = mixinBehaviors(
     [
-      DeepLinkingBehavior, I18nBehavior, PrefsBehavior, RouteObserverBehavior,
-      WebUIListenerBehavior
+      DeepLinkingBehavior,
+      I18nBehavior,
+      PrefsBehavior,
+      RouteObserverBehavior,
+      WebUIListenerBehavior,
     ],
     PolymerElement);
 
@@ -86,7 +89,7 @@
         type: Array,
         value: [
           AmbientModeTopicSource.GOOGLE_PHOTOS,
-          AmbientModeTopicSource.ART_GALLERY
+          AmbientModeTopicSource.ART_GALLERY,
         ],
       },
 
@@ -103,7 +106,7 @@
       selectedTemperatureUnit_: {
         type: AmbientModeTemperatureUnit,
         value: AmbientModeTemperatureUnit.UNKNOWN,
-        observer: 'onSelectedTemperatureUnitChanged_'
+        observer: 'onSelectedTemperatureUnitChanged_',
       },
 
       /**
@@ -129,7 +132,7 @@
       disableSettings_: {
         type: Boolean,
         computed: 'computeDisableSettings_(prefs.settings.ambient_mode.*)',
-      }
+      },
     };
   }
 
diff --git a/chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_photos_page.js b/chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_photos_page.js
index 89b960e..1cb9c299 100644
--- a/chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_photos_page.js
+++ b/chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_photos_page.js
@@ -75,7 +75,7 @@
       showArtAlbumDialog_: {
         type: Boolean,
         value: false,
-      }
+      },
     };
   }
 
diff --git a/chrome/browser/resources/settings/chromeos/bluetooth_page/bluetooth_subpage.js b/chrome/browser/resources/settings/chromeos/bluetooth_page/bluetooth_subpage.js
index deb35d9..b6927178 100644
--- a/chrome/browser/resources/settings/chromeos/bluetooth_page/bluetooth_subpage.js
+++ b/chrome/browser/resources/settings/chromeos/bluetooth_page/bluetooth_subpage.js
@@ -48,8 +48,11 @@
  */
 const SettingsBluetoothSubpageElementBase = mixinBehaviors(
     [
-      I18nBehavior, CrScrollableBehavior, DeepLinkingBehavior,
-      ListPropertyUpdateBehavior, RouteObserverBehavior
+      I18nBehavior,
+      CrScrollableBehavior,
+      DeepLinkingBehavior,
+      ListPropertyUpdateBehavior,
+      RouteObserverBehavior,
     ],
     PolymerElement);
 
@@ -673,7 +676,7 @@
   refreshBluetoothList_() {
     const filter = {
       filterType: chrome.bluetooth.FilterType.KNOWN,
-      limit: MAX_NUMBER_DEVICE_SHOWN
+      limit: MAX_NUMBER_DEVICE_SHOWN,
     };
     this.bluetooth.getDevices(filter, devices => {
       this.deviceList_ = this.sortDevices_(devices);
diff --git a/chrome/browser/resources/settings/chromeos/combined_search_handler.js b/chrome/browser/resources/settings/chromeos/combined_search_handler.js
index 717ad34c..4eaa6f6 100644
--- a/chrome/browser/resources/settings/chromeos/combined_search_handler.js
+++ b/chrome/browser/resources/settings/chromeos/combined_search_handler.js
@@ -48,6 +48,6 @@
   return {
     results: mergeResults(
         settingsResponse.results, personalizationResponse.results,
-        maxNumResults)
+        maxNumResults),
   };
 }
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_arc_adb.js b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_arc_adb.js
index 8826355..c1ff6fa 100644
--- a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_arc_adb.js
+++ b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_arc_adb.js
@@ -37,8 +37,10 @@
  */
 const SettingsCrostiniArcAdbElementBase = mixinBehaviors(
     [
-      DeepLinkingBehavior, I18nBehavior, RouteObserverBehavior,
-      WebUIListenerBehavior
+      DeepLinkingBehavior,
+      I18nBehavior,
+      RouteObserverBehavior,
+      WebUIListenerBehavior,
     ],
     PolymerElement);
 
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_confirmation_dialog.js b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_confirmation_dialog.js
index d0dcc991..a8ff9d0c 100644
--- a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_confirmation_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_confirmation_dialog.js
@@ -35,7 +35,7 @@
       cancelButtonText: {
         type: String,
         value: loadTimeData.getString('cancel'),
-      }
+      },
     };
   }
 
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_page.js b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_page.js
index 49feaad5..60d12b7 100644
--- a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_page.js
+++ b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_page.js
@@ -50,8 +50,11 @@
  */
 const SettingsCrostiniPageElementBase = mixinBehaviors(
     [
-      DeepLinkingBehavior, I18nBehavior, PrefsBehavior, RouteObserverBehavior,
-      WebUIListenerBehavior
+      DeepLinkingBehavior,
+      I18nBehavior,
+      PrefsBehavior,
+      RouteObserverBehavior,
+      WebUIListenerBehavior,
     ],
     PolymerElement);
 
@@ -140,7 +143,7 @@
       enableBruschetta_: {
         type: Boolean,
         value: loadTimeData.getBoolean('enableBruschetta'),
-      }
+      },
     };
   }
 
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding.js b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding.js
index e1282bd..d742592 100644
--- a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding.js
+++ b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding.js
@@ -90,7 +90,7 @@
 
   static get observers() {
     return [
-      'onCrostiniPortsChanged_(prefs.crostini.port_forwarding.ports.value)'
+      'onCrostiniPortsChanged_(prefs.crostini.port_forwarding.ports.value)',
     ];
   }
 
@@ -153,7 +153,7 @@
               activePort.protocol_type === port.protocol_type);
       port.container_id = port.container_id || {
         vm_name: port['vm_name'] || DEFAULT_CROSTINI_VM,
-        container_name: port['container_name'] || DEFAULT_CROSTINI_CONTAINER
+        container_name: port['container_name'] || DEFAULT_CROSTINI_CONTAINER,
       };
       this.push('allPorts_', port);
     }
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_subpage.js b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_subpage.js
index 679af81..e890ad0 100644
--- a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_subpage.js
+++ b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_subpage.js
@@ -51,8 +51,10 @@
  */
 const SettingsCrostiniSubpageElementBase = mixinBehaviors(
     [
-      DeepLinkingBehavior, PrefsBehavior, RouteOriginBehavior,
-      WebUIListenerBehavior
+      DeepLinkingBehavior,
+      PrefsBehavior,
+      RouteOriginBehavior,
+      WebUIListenerBehavior,
     ],
     PolymerElement);
 
@@ -243,7 +245,7 @@
   static get observers() {
     return [
       'onCrostiniEnabledChanged_(prefs.crostini.enabled.value)',
-      'onArcEnabledChanged_(prefs.arc.enabled.value)'
+      'onArcEnabledChanged_(prefs.arc.enabled.value)',
     ];
   }
 
diff --git a/chrome/browser/resources/settings/chromeos/date_time_page/date_time_page.js b/chrome/browser/resources/settings/chromeos/date_time_page/date_time_page.js
index a1bd702..79d3a870 100644
--- a/chrome/browser/resources/settings/chromeos/date_time_page/date_time_page.js
+++ b/chrome/browser/resources/settings/chromeos/date_time_page/date_time_page.js
@@ -43,8 +43,11 @@
  */
 const SettingsDateTimePageElementBase = mixinBehaviors(
     [
-      DeepLinkingBehavior, I18nBehavior, PrefsBehavior, RouteObserverBehavior,
-      WebUIListenerBehavior
+      DeepLinkingBehavior,
+      I18nBehavior,
+      PrefsBehavior,
+      RouteObserverBehavior,
+      WebUIListenerBehavior,
     ],
     PolymerElement);
 
@@ -100,7 +103,7 @@
         computed: `computeTimeZoneSettingSubLabel_(
             activeTimeZoneDisplayName,
             prefs.generated.resolve_timezone_by_geolocation_on_off.value,
-            prefs.generated.resolve_timezone_by_geolocation_method_short.value)`
+            prefs.generated.resolve_timezone_by_geolocation_method_short.value)`,
       },
 
       /**
diff --git a/chrome/browser/resources/settings/chromeos/date_time_page/date_time_types.js b/chrome/browser/resources/settings/chromeos/date_time_page/date_time_types.js
index 1f2affc..54ed546 100644
--- a/chrome/browser/resources/settings/chromeos/date_time_page/date_time_types.js
+++ b/chrome/browser/resources/settings/chromeos/date_time_page/date_time_types.js
@@ -12,5 +12,5 @@
   DISABLED: 0,
   IP_ONLY: 1,
   SEND_WIFI_ACCESS_POINTS: 2,
-  SEND_ALL_LOCATION_INFO: 3
+  SEND_ALL_LOCATION_INFO: 3,
 };
diff --git a/chrome/browser/resources/settings/chromeos/date_time_page/timezone_subpage.js b/chrome/browser/resources/settings/chromeos/date_time_page/timezone_subpage.js
index 0fd5709..41dea75 100644
--- a/chrome/browser/resources/settings/chromeos/date_time_page/timezone_subpage.js
+++ b/chrome/browser/resources/settings/chromeos/date_time_page/timezone_subpage.js
@@ -36,8 +36,10 @@
  */
 const TimezoneSubpageElementBase = mixinBehaviors(
     [
-      DeepLinkingBehavior, PrefsBehavior, RouteObserverBehavior,
-      WebUIListenerBehavior
+      DeepLinkingBehavior,
+      PrefsBehavior,
+      RouteObserverBehavior,
+      WebUIListenerBehavior,
     ],
     PolymerElement);
 
@@ -130,20 +132,20 @@
     }
     result.push({
       value: TimeZoneAutoDetectMethod.IP_ONLY,
-      name: loadTimeData.getString('setTimeZoneAutomaticallyIpOnlyDefault')
+      name: loadTimeData.getString('setTimeZoneAutomaticallyIpOnlyDefault'),
     });
 
     if (pref.value === TimeZoneAutoDetectMethod.SEND_WIFI_ACCESS_POINTS) {
       result.push({
         value: TimeZoneAutoDetectMethod.SEND_WIFI_ACCESS_POINTS,
         name: loadTimeData.getString(
-            'setTimeZoneAutomaticallyWithWiFiAccessPointsData')
+            'setTimeZoneAutomaticallyWithWiFiAccessPointsData'),
       });
     }
     result.push({
       value: TimeZoneAutoDetectMethod.SEND_ALL_LOCATION_INFO,
       name:
-          loadTimeData.getString('setTimeZoneAutomaticallyWithAllLocationInfo')
+          loadTimeData.getString('setTimeZoneAutomaticallyWithAllLocationInfo'),
     });
     return result;
   }
diff --git a/chrome/browser/resources/settings/chromeos/device_page/audio.js b/chrome/browser/resources/settings/chromeos/device_page/audio.js
index 88ac4ca7..faad436 100644
--- a/chrome/browser/resources/settings/chromeos/device_page/audio.js
+++ b/chrome/browser/resources/settings/chromeos/device_page/audio.js
@@ -43,7 +43,7 @@
       showAudioInfo: {
         type: Boolean,
         value: false,
-      }
+      },
     };
   }
 
diff --git a/chrome/browser/resources/settings/chromeos/device_page/device_page_browser_proxy.js b/chrome/browser/resources/settings/chromeos/device_page/device_page_browser_proxy.js
index 2bb11cdb..351ef86 100644
--- a/chrome/browser/resources/settings/chromeos/device_page/device_page_browser_proxy.js
+++ b/chrome/browser/resources/settings/chromeos/device_page/device_page_browser_proxy.js
@@ -13,7 +13,7 @@
 export const StorageSpaceState = {
   NORMAL: 0,
   LOW: 1,
-  CRITICALLY_LOW: 2
+  CRITICALLY_LOW: 2,
 };
 
 let systemDisplayApi = null;
@@ -97,7 +97,7 @@
   NOT_SUPPORTED: 0,
   NOT_ALLOWED_BY_POLICY: 1,
   SUPPORTED: 2,
-  ENABLED: 3
+  ENABLED: 3,
 };
 
 /**
diff --git a/chrome/browser/resources/settings/chromeos/device_page/display.js b/chrome/browser/resources/settings/chromeos/device_page/display.js
index aa4ec0dc..6ad01efc 100644
--- a/chrome/browser/resources/settings/chromeos/device_page/display.js
+++ b/chrome/browser/resources/settings/chromeos/device_page/display.js
@@ -176,7 +176,7 @@
         type: Boolean,
         value() {
           return loadTimeData.getBoolean('unifiedDesktopAvailable');
-        }
+        },
       },
 
       /** @private */
@@ -184,7 +184,7 @@
         type: Boolean,
         value() {
           return loadTimeData.getBoolean('deviceSupportsAmbientColor');
-        }
+        },
       },
 
       /** @private */
@@ -192,7 +192,7 @@
         type: Boolean,
         value() {
           return loadTimeData.getBoolean('listAllDisplayModes');
-        }
+        },
       },
 
       /** @private */
@@ -223,17 +223,17 @@
           return [
             {
               name: loadTimeData.getString('displayNightLightScheduleNever'),
-              value: NightLightScheduleType.NEVER
+              value: NightLightScheduleType.NEVER,
             },
             {
               name: loadTimeData.getString(
                   'displayNightLightScheduleSunsetToSunRise'),
-              value: NightLightScheduleType.SUNSET_TO_SUNRISE
+              value: NightLightScheduleType.SUNSET_TO_SUNRISE,
             },
             {
               name: loadTimeData.getString('displayNightLightScheduleCustom'),
-              value: NightLightScheduleType.CUSTOM
-            }
+              value: NightLightScheduleType.CUSTOM,
+            },
           ];
         },
       },
@@ -446,7 +446,7 @@
   /** @private */
   getDisplayInfo_() {
     /** @type {chrome.system.display.GetInfoFlags} */ const flags = {
-      singleUnified: true
+      singleUnified: true,
     };
     getDisplayApi().getInfo(
         flags, displays => this.displayInfoFetched_(displays));
@@ -829,7 +829,7 @@
       return {
         value,
         ariaValue,
-        label: this.i18n('displayZoomValue', ariaValue.toString())
+        label: this.i18n('displayZoomValue', ariaValue.toString()),
       };
     });
   }
@@ -1247,7 +1247,7 @@
     }
 
     /** @type {!chrome.system.display.DisplayProperties} */ const properties = {
-      isPrimary: true
+      isPrimary: true,
     };
     getDisplayApi().setDisplayProperties(
         this.selectedDisplay.id, properties,
@@ -1328,7 +1328,7 @@
     }
     /** @type {!chrome.system.display.DisplayProperties} */ const properties = {
       displayMode: this.selectedDisplay.modes[
-          /** @type {number} */ (this.selectedModePref_.value)]
+          /** @type {number} */ (this.selectedModePref_.value)],
     };
 
     this.refreshRateList_ = this.parentModeToRefreshRateMap_.get(
@@ -1351,7 +1351,7 @@
 
     /** @type {!chrome.system.display.DisplayProperties} */ const properties = {
       displayZoomFactor:
-          /** @type {number} */ (this.selectedZoomPref_.value)
+          /** @type {number} */ (this.selectedZoomPref_.value),
     };
 
     getDisplayApi().setDisplayProperties(
@@ -1381,7 +1381,7 @@
     assert(value !== -1 || this.selectedDisplay.isAutoRotationAllowed);
 
     /** @type {!chrome.system.display.DisplayProperties} */ const properties = {
-      rotation: value
+      rotation: value,
     };
     getDisplayApi().setDisplayProperties(
         this.selectedDisplay.id, properties,
@@ -1398,7 +1398,7 @@
     const mirrorModeInfo = {
       mode: this.isMirrored_(this.displays) ?
           chrome.system.display.MirrorMode.OFF :
-          chrome.system.display.MirrorMode.NORMAL
+          chrome.system.display.MirrorMode.NORMAL,
     };
     getDisplayApi().setMirrorMode(mirrorModeInfo, () => {
       const error = chrome.runtime.lastError;
diff --git a/chrome/browser/resources/settings/chromeos/device_page/display_layout.js b/chrome/browser/resources/settings/chromeos/device_page/display_layout.js
index 355edd93..878d31f 100644
--- a/chrome/browser/resources/settings/chromeos/device_page/display_layout.js
+++ b/chrome/browser/resources/settings/chromeos/device_page/display_layout.js
@@ -345,7 +345,7 @@
           this.hasDragStarted_ = true;
           this.lastDragCoordinates_ = {
             x: calculatedBounds.left,
-            y: calculatedBounds.top
+            y: calculatedBounds.top,
           };
         }
 
diff --git a/chrome/browser/resources/settings/chromeos/device_page/keyboard.js b/chrome/browser/resources/settings/chromeos/device_page/keyboard.js
index 3a5e25a..4c32d8c 100644
--- a/chrome/browser/resources/settings/chromeos/device_page/keyboard.js
+++ b/chrome/browser/resources/settings/chromeos/device_page/keyboard.js
@@ -187,32 +187,32 @@
       },
       {
         value: ModifierKey.CONTROL_KEY,
-        name: loadTimeData.getString('keyboardKeyCtrl')
+        name: loadTimeData.getString('keyboardKeyCtrl'),
       },
       {
         value: ModifierKey.ALT_KEY,
-        name: loadTimeData.getString('keyboardKeyAlt')
+        name: loadTimeData.getString('keyboardKeyAlt'),
       },
       {
         value: ModifierKey.CAPS_LOCK_KEY,
-        name: loadTimeData.getString('keyboardKeyCapsLock')
+        name: loadTimeData.getString('keyboardKeyCapsLock'),
       },
       {
         value: ModifierKey.ESCAPE_KEY,
-        name: loadTimeData.getString('keyboardKeyEscape')
+        name: loadTimeData.getString('keyboardKeyEscape'),
       },
       {
         value: ModifierKey.BACKSPACE_KEY,
-        name: loadTimeData.getString('keyboardKeyBackspace')
+        name: loadTimeData.getString('keyboardKeyBackspace'),
       },
       {
         value: ModifierKey.ASSISTANT_KEY,
-        name: loadTimeData.getString('keyboardKeyAssistant')
+        name: loadTimeData.getString('keyboardKeyAssistant'),
       },
       {
         value: ModifierKey.VOID_KEY,
-        name: loadTimeData.getString('keyboardKeyDisabled')
-      }
+        name: loadTimeData.getString('keyboardKeyDisabled'),
+      },
     ];
   }
 
diff --git a/chrome/browser/resources/settings/chromeos/device_page/layout_behavior.js b/chrome/browser/resources/settings/chromeos/device_page/layout_behavior.js
index 8a7c1d3b..1461e88 100644
--- a/chrome/browser/resources/settings/chromeos/device_page/layout_behavior.js
+++ b/chrome/browser/resources/settings/chromeos/device_page/layout_behavior.js
@@ -109,7 +109,7 @@
     const oldBounds = this.dragBounds_ || this.getCalculatedDisplayBounds(id);
     const deltaPos = {
       x: newBounds.left - oldBounds.left,
-      y: newBounds.top - oldBounds.top
+      y: newBounds.top - oldBounds.top,
     };
 
     // Check for collisions after snapping. This should not collide with the
@@ -277,7 +277,7 @@
     const desiredPos = this.snapBounds_(bounds, newParentId, layoutPosition);
     const deltaPos = {
       x: desiredPos.x - cornerBounds.left,
-      y: desiredPos.y - cornerBounds.top
+      y: desiredPos.y - cornerBounds.top,
     };
 
     // Check for collisions.
@@ -286,7 +286,7 @@
       left: cornerBounds.left + deltaPos.x,
       top: cornerBounds.top + deltaPos.y,
       width: bounds.width,
-      height: bounds.height
+      height: bounds.height,
     };
 
     this.updateOffsetAndPosition_(desiredBounds, layoutPosition, layout);
diff --git a/chrome/browser/resources/settings/chromeos/device_page/pointers.js b/chrome/browser/resources/settings/chromeos/device_page/pointers.js
index f4120997..f6f19f6 100644
--- a/chrome/browser/resources/settings/chromeos/device_page/pointers.js
+++ b/chrome/browser/resources/settings/chromeos/device_page/pointers.js
@@ -68,11 +68,11 @@
           return [
             {
               value: false,
-              name: loadTimeData.getString('primaryMouseButtonLeft')
+              name: loadTimeData.getString('primaryMouseButtonLeft'),
             },
             {
               value: true,
-              name: loadTimeData.getString('primaryMouseButtonRight')
+              name: loadTimeData.getString('primaryMouseButtonRight'),
             },
           ];
         },
diff --git a/chrome/browser/resources/settings/chromeos/device_page/power.js b/chrome/browser/resources/settings/chromeos/device_page/power.js
index 5b8fd81a..68f697f 100644
--- a/chrome/browser/resources/settings/chromeos/device_page/power.js
+++ b/chrome/browser/resources/settings/chromeos/device_page/power.js
@@ -39,8 +39,10 @@
  */
 const SettingsPowerElementBase = mixinBehaviors(
     [
-      DeepLinkingBehavior, I18nBehavior, RouteObserverBehavior,
-      WebUIListenerBehavior
+      DeepLinkingBehavior,
+      I18nBehavior,
+      RouteObserverBehavior,
+      WebUIListenerBehavior,
     ],
     PolymerElement);
 
@@ -337,7 +339,7 @@
     recordSettingChange(
         chromeos.settings.mojom.Setting.kAdaptiveCharging,
         /** @type {!chromeos.settings.mojom.SettingChangeValue} */ ({
-          boolValue: enabled
+          boolValue: enabled,
         }));
   }
 
@@ -405,31 +407,31 @@
         return {
           value: idleBehavior,
           name: loadTimeData.getString('powerIdleDisplayOffSleep'),
-          selected: selected
+          selected: selected,
         };
       case IdleBehavior.DISPLAY_OFF:
         return {
           value: idleBehavior,
           name: loadTimeData.getString('powerIdleDisplayOff'),
-          selected: selected
+          selected: selected,
         };
       case IdleBehavior.DISPLAY_ON:
         return {
           value: idleBehavior,
           name: loadTimeData.getString('powerIdleDisplayOn'),
-          selected: selected
+          selected: selected,
         };
       case IdleBehavior.SHUT_DOWN:
         return {
           value: idleBehavior,
           name: loadTimeData.getString('powerIdleDisplayShutDown'),
-          selected: selected
+          selected: selected,
         };
       case IdleBehavior.STOP_SESSION:
         return {
           value: idleBehavior,
           name: loadTimeData.getString('powerIdleDisplayStopSession'),
-          selected: selected
+          selected: selected,
         };
       default:
         assertNotReached('Unknown IdleBehavior type');
diff --git a/chrome/browser/resources/settings/chromeos/device_page/storage.js b/chrome/browser/resources/settings/chromeos/device_page/storage.js
index 256de2bc..db4e50d1 100644
--- a/chrome/browser/resources/settings/chromeos/device_page/storage.js
+++ b/chrome/browser/resources/settings/chromeos/device_page/storage.js
@@ -71,7 +71,7 @@
         type: Boolean,
         value() {
           return loadTimeData.getBoolean('isGuest');
-        }
+        },
       },
 
       /** @private */
@@ -80,7 +80,7 @@
         // Initialize showOtherUsers_ to false if the user is in guest mode.
         value() {
           return !loadTimeData.getBoolean('isGuest');
-        }
+        },
       },
 
       /** @private {StorageSizeStat} */
diff --git a/chrome/browser/resources/settings/chromeos/device_page/storage_external.js b/chrome/browser/resources/settings/chromeos/device_page/storage_external.js
index d6ec2819..aff3b4df 100644
--- a/chrome/browser/resources/settings/chromeos/device_page/storage_external.js
+++ b/chrome/browser/resources/settings/chromeos/device_page/storage_external.js
@@ -49,7 +49,7 @@
         type: Array,
         value() {
           return [];
-        }
+        },
       },
 
       /** @private {!chrome.settingsPrivate.PrefObject} */
diff --git a/chrome/browser/resources/settings/chromeos/device_page/stylus.js b/chrome/browser/resources/settings/chromeos/device_page/stylus.js
index 512b3e3..7f087d9e 100644
--- a/chrome/browser/resources/settings/chromeos/device_page/stylus.js
+++ b/chrome/browser/resources/settings/chromeos/device_page/stylus.js
@@ -79,7 +79,7 @@
         type: Array,
         value() {
           return [];
-        }
+        },
       },
 
       /**
diff --git a/chrome/browser/resources/settings/chromeos/google_assistant_page/google_assistant_page.js b/chrome/browser/resources/settings/chromeos/google_assistant_page/google_assistant_page.js
index 53fff8f..10cad0a 100644
--- a/chrome/browser/resources/settings/chromeos/google_assistant_page/google_assistant_page.js
+++ b/chrome/browser/resources/settings/chromeos/google_assistant_page/google_assistant_page.js
@@ -73,8 +73,11 @@
  */
 const SettingsGoogleAssistantPageElementBase = mixinBehaviors(
     [
-      DeepLinkingBehavior, I18nBehavior, PrefsBehavior, RouteObserverBehavior,
-      WebUIListenerBehavior
+      DeepLinkingBehavior,
+      I18nBehavior,
+      PrefsBehavior,
+      RouteObserverBehavior,
+      WebUIListenerBehavior,
     ],
     PolymerElement);
 
@@ -113,18 +116,18 @@
             {
               name: loadTimeData.getString(
                   'googleAssistantEnableHotwordWithoutDspRecommended'),
-              value: DspHotwordState.DEFAULT_ON
+              value: DspHotwordState.DEFAULT_ON,
             },
             {
               name: loadTimeData.getString(
                   'googleAssistantEnableHotwordWithoutDspAlwaysOn'),
-              value: DspHotwordState.ALWAYS_ON
+              value: DspHotwordState.ALWAYS_ON,
             },
             {
               name: loadTimeData.getString(
                   'googleAssistantEnableHotwordWithoutDspOff'),
-              value: DspHotwordState.OFF
-            }
+              value: DspHotwordState.OFF,
+            },
           ];
         },
       },
diff --git a/chrome/browser/resources/settings/chromeos/guest_os/guest_os_shared_paths.js b/chrome/browser/resources/settings/chromeos/guest_os/guest_os_shared_paths.js
index 461b3b91..bfc8df07 100644
--- a/chrome/browser/resources/settings/chromeos/guest_os/guest_os_shared_paths.js
+++ b/chrome/browser/resources/settings/chromeos/guest_os/guest_os_shared_paths.js
@@ -72,7 +72,7 @@
 
   static get observers() {
     return [
-      'onGuestOsSharedPathsChanged_(prefs.guest_os.paths_shared_to_vms.value)'
+      'onGuestOsSharedPathsChanged_(prefs.guest_os.paths_shared_to_vms.value)',
     ];
   }
 
diff --git a/chrome/browser/resources/settings/chromeos/guest_os/guest_os_shared_usb_devices.js b/chrome/browser/resources/settings/chromeos/guest_os/guest_os_shared_usb_devices.js
index 04cf6db..1dcff7a 100644
--- a/chrome/browser/resources/settings/chromeos/guest_os/guest_os_shared_usb_devices.js
+++ b/chrome/browser/resources/settings/chromeos/guest_os/guest_os_shared_usb_devices.js
@@ -130,7 +130,7 @@
       crostini: 'termina',
       pluginVm: 'PvmDefault',
       arcvm: 'arcvm',
-      bruschetta: 'bru'
+      bruschetta: 'bru',
     }[this.guestOsType];
   }
 
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/cellular_roaming_toggle_button.js b/chrome/browser/resources/settings/chromeos/internet_page/cellular_roaming_toggle_button.js
index 0f2c205..e84725d4d 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/cellular_roaming_toggle_button.js
+++ b/chrome/browser/resources/settings/chromeos/internet_page/cellular_roaming_toggle_button.js
@@ -111,7 +111,7 @@
     const config =
         OncMojo.getDefaultConfigProperties(this.managedProperties.type);
     config.typeConfig.cellular = {
-      roaming: {allowRoaming: this.isRoamingAllowedForNetwork_}
+      roaming: {allowRoaming: this.isRoamingAllowedForNetwork_},
     };
     this.networkConfig_.setProperties(this.managedProperties.guid, config)
         .then(response => {
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/internet_config.js b/chrome/browser/resources/settings/chromeos/internet_page/internet_config.js
index fa20774c..2207ba1 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/internet_config.js
+++ b/chrome/browser/resources/settings/chromeos/internet_page/internet_config.js
@@ -44,7 +44,7 @@
         type: Boolean,
         value() {
           return loadTimeData.getBoolean('shareNetworkAllowEnable');
-        }
+        },
       },
 
       /** @private */
@@ -52,7 +52,7 @@
         type: Boolean,
         value() {
           return loadTimeData.getBoolean('shareNetworkDefault');
-        }
+        },
       },
 
       /**
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/internet_detail_page.js b/chrome/browser/resources/settings/chromeos/internet_page/internet_detail_page.js
index 5e93de2..4e6d168a 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/internet_detail_page.js
+++ b/chrome/browser/resources/settings/chromeos/internet_page/internet_detail_page.js
@@ -68,8 +68,12 @@
  */
 const SettingsInternetDetailPageElementBase = mixinBehaviors(
     [
-      NetworkListenerBehavior, CrPolicyNetworkBehaviorMojo, DeepLinkingBehavior,
-      RouteObserverBehavior, I18nBehavior, WebUIListenerBehavior
+      NetworkListenerBehavior,
+      CrPolicyNetworkBehaviorMojo,
+      DeepLinkingBehavior,
+      RouteObserverBehavior,
+      I18nBehavior,
+      WebUIListenerBehavior,
     ],
     PolymerElement);
 
@@ -270,7 +274,7 @@
         value() {
           return loadTimeData.valueExists('showTechnologyBadge') &&
               loadTimeData.getBoolean('showTechnologyBadge');
-        }
+        },
       },
 
       /**
@@ -282,7 +286,7 @@
         value() {
           return loadTimeData.valueExists('showMeteredToggle') &&
               loadTimeData.getBoolean('showMeteredToggle');
-        }
+        },
       },
 
       /**
@@ -303,7 +307,7 @@
         value() {
           return loadTimeData.valueExists('trafficCountersEnabled') &&
               loadTimeData.getBoolean('trafficCountersEnabled');
-        }
+        },
       },
 
       /**
@@ -313,7 +317,7 @@
       disabled_: {
         type: Boolean,
         value: false,
-        computed: 'computeDisabled_(deviceState_.*)'
+        computed: 'computeDisabled_(deviceState_.*)',
       },
 
       /** @private */
@@ -1684,7 +1688,8 @@
     const networkConnectEvent = new CustomEvent('network-connect', {
       bubbles: true,
       composed: true,
-      detail: {networkState: networkState, bypassConnectionDialog: bypassDialog}
+      detail:
+          {networkState: networkState, bypassConnectionDialog: bypassDialog},
     });
     this.dispatchEvent(networkConnectEvent);
     recordSettingChange();
@@ -1804,7 +1809,7 @@
       detail: {
         guid: this.guid,
         type: OncMojo.getNetworkTypeString(this.managedProperties_.type),
-        name: OncMojo.getNetworkName(this.managedProperties_)
+        name: OncMojo.getNetworkName(this.managedProperties_),
       },
     });
     this.dispatchEvent(showConfigEvent);
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/internet_known_networks_page.js b/chrome/browser/resources/settings/chromeos/internet_page/internet_known_networks_page.js
index 48e41ee2..490182e1 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/internet_known_networks_page.js
+++ b/chrome/browser/resources/settings/chromeos/internet_page/internet_known_networks_page.js
@@ -40,8 +40,11 @@
  */
 const SettingsInternetKnownNetworksPageElementBase = mixinBehaviors(
     [
-      DeepLinkingBehavior, NetworkListenerBehavior, CrPolicyNetworkBehaviorMojo,
-      RouteObserverBehavior, I18nBehavior
+      DeepLinkingBehavior,
+      NetworkListenerBehavior,
+      CrPolicyNetworkBehaviorMojo,
+      RouteObserverBehavior,
+      I18nBehavior,
     ],
     PolymerElement);
 
@@ -75,7 +78,7 @@
         type: Array,
         value() {
           return [];
-        }
+        },
       },
 
       /** @private */
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/internet_page.js b/chrome/browser/resources/settings/chromeos/internet_page/internet_page.js
index 7e069f9..dd26e014 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/internet_page.js
+++ b/chrome/browser/resources/settings/chromeos/internet_page/internet_page.js
@@ -68,8 +68,11 @@
  */
 const SettingsInternetPageElementBase = mixinBehaviors(
     [
-      NetworkListenerBehavior, DeepLinkingBehavior, I18nBehavior,
-      RouteObserverBehavior, WebUIListenerBehavior
+      NetworkListenerBehavior,
+      DeepLinkingBehavior,
+      I18nBehavior,
+      RouteObserverBehavior,
+      WebUIListenerBehavior,
     ],
     PolymerElement);
 
@@ -171,7 +174,7 @@
         type: Array,
         value() {
           return [];
-        }
+        },
       },
 
       /** @private {boolean} */
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/internet_subpage.js b/chrome/browser/resources/settings/chromeos/internet_page/internet_subpage.js
index ca0dfa2..27504633 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/internet_subpage.js
+++ b/chrome/browser/resources/settings/chromeos/internet_page/internet_subpage.js
@@ -55,8 +55,12 @@
  */
 const SettingsInternetSubpageElementBase = mixinBehaviors(
     [
-      NetworkListenerBehavior, CrPolicyNetworkBehaviorMojo, DeepLinkingBehavior,
-      RouteObserverBehavior, RouteOriginBehavior, I18nBehavior
+      NetworkListenerBehavior,
+      CrPolicyNetworkBehaviorMojo,
+      DeepLinkingBehavior,
+      RouteObserverBehavior,
+      RouteOriginBehavior,
+      I18nBehavior,
     ],
     PolymerElement);
 
@@ -193,7 +197,7 @@
         value() {
           return loadTimeData.valueExists('showTechnologyBadge') &&
               loadTimeData.getBoolean('showTechnologyBadge');
-        }
+        },
       },
 
       /** @private */
@@ -589,7 +593,7 @@
         providerId: vpn.providerId,
         providerName: vpn.providerName || vpn.providerId,
         appId: '',
-        lastLaunchTime: {internalValue: 0}
+        lastLaunchTime: {internalValue: 0},
       };
       configuredProviders.push(provider);
     }
@@ -804,7 +808,7 @@
           composed: true,
           detail: {
             enabled: !this.deviceIsEnabled_(this.deviceState),
-            type: this.deviceState.type
+            type: this.deviceState.type,
           },
         });
     this.dispatchEvent(deviceEnabledToggledEvent);
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/network_proxy_section.js b/chrome/browser/resources/settings/chromeos/internet_page/network_proxy_section.js
index eb9e66e..141bc78 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/network_proxy_section.js
+++ b/chrome/browser/resources/settings/chromeos/internet_page/network_proxy_section.js
@@ -44,8 +44,10 @@
  */
 const NetworkProxySectionElementBase = mixinBehaviors(
     [
-      CrPolicyNetworkBehaviorMojo, I18nBehavior, PrefsBehavior,
-      RouteObserverBehavior
+      CrPolicyNetworkBehaviorMojo,
+      I18nBehavior,
+      PrefsBehavior,
+      RouteObserverBehavior,
     ],
     PolymerElement);
 
@@ -136,7 +138,7 @@
       this.extensionInfo_ = {
         id: pref.value['extension_id_key'],
         name: pref.value['extension_name_key'],
-        canBeDisabled: pref.value['can_be_disabled_key']
+        canBeDisabled: pref.value['can_be_disabled_key'],
       };
     }
   }
@@ -153,7 +155,7 @@
     this.extensionInfo_ = {
       id: this.prefs.proxy.extensionId,
       name: this.prefs.proxy.controlledByName,
-      canBeDisabled: this.prefs.proxy.extensionCanBeDisabled
+      canBeDisabled: this.prefs.proxy.extensionCanBeDisabled,
     };
     return true;
   }
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/network_summary_item.js b/chrome/browser/resources/settings/chromeos/internet_page/network_summary_item.js
index 5c95d97..8ebc555 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/network_summary_item.js
+++ b/chrome/browser/resources/settings/chromeos/internet_page/network_summary_item.js
@@ -97,7 +97,7 @@
         value() {
           return loadTimeData.valueExists('showTechnologyBadge') &&
               loadTimeData.getBoolean('showTechnologyBadge');
-        }
+        },
       },
 
       /** @private {!chromeos.networkConfig.mojom.GlobalPolicy|undefined} */
@@ -604,7 +604,7 @@
         new CustomEvent('device-enabled-toggled', {
           bubbles: true,
           composed: true,
-          detail: {enabled: !deviceIsEnabled, type: this.deviceState.type}
+          detail: {enabled: !deviceIsEnabled, type: this.deviceState.type},
         });
     this.dispatchEvent(deviceEnabledToggledEvent);
 
diff --git a/chrome/browser/resources/settings/chromeos/kerberos_page/kerberos_accounts.js b/chrome/browser/resources/settings/chromeos/kerberos_page/kerberos_accounts.js
index 3e0f66f1..497ec74 100644
--- a/chrome/browser/resources/settings/chromeos/kerberos_page/kerberos_accounts.js
+++ b/chrome/browser/resources/settings/chromeos/kerberos_page/kerberos_accounts.js
@@ -44,8 +44,10 @@
  */
 const SettingsKerberosAccountsElementBase = mixinBehaviors(
     [
-      DeepLinkingBehavior, I18nBehavior, RouteObserverBehavior,
-      WebUIListenerBehavior
+      DeepLinkingBehavior,
+      I18nBehavior,
+      RouteObserverBehavior,
+      WebUIListenerBehavior,
     ],
     PolymerElement);
 
diff --git a/chrome/browser/resources/settings/chromeos/keyboard_shortcut_banner/keyboard_shortcut_banner.js b/chrome/browser/resources/settings/chromeos/keyboard_shortcut_banner/keyboard_shortcut_banner.js
index 78f82812..00b05f6 100644
--- a/chrome/browser/resources/settings/chromeos/keyboard_shortcut_banner/keyboard_shortcut_banner.js
+++ b/chrome/browser/resources/settings/chromeos/keyboard_shortcut_banner/keyboard_shortcut_banner.js
@@ -45,7 +45,7 @@
       /** @type {!Array<string>} */
       body: {
         type: Array,
-      }
+      },
     };
   }
 
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_behavior.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_behavior.js
index 37f70c4e..bad257f 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_behavior.js
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_behavior.js
@@ -87,7 +87,8 @@
     return [
       MultiDeviceFeature.PHONE_HUB_CAMERA_ROLL,
       MultiDeviceFeature.PHONE_HUB_NOTIFICATIONS,
-      MultiDeviceFeature.PHONE_HUB_TASK_CONTINUATION, MultiDeviceFeature.ECHE
+      MultiDeviceFeature.PHONE_HUB_TASK_CONTINUATION,
+      MultiDeviceFeature.ECHE,
     ].includes(feature);
   },
 
@@ -182,7 +183,7 @@
 
     return [
       MultiDeviceFeatureState.DISABLED_BY_USER,
-      MultiDeviceFeatureState.ENABLED_BY_USER
+      MultiDeviceFeatureState.ENABLED_BY_USER,
     ].includes(this.getFeatureState(feature));
   },
 
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_item.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_item.js
index 895aad6..f7744e70 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_item.js
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_item.js
@@ -95,7 +95,7 @@
       isFeatureIconHidden: {
         type: Boolean,
         value: false,
-      }
+      },
     };
   }
 
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_toggle.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_toggle.js
index 2f50100..b467c50 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_toggle.js
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_toggle.js
@@ -80,7 +80,7 @@
         new CustomEvent('feature-toggle-clicked', {
           bubbles: true,
           composed: true,
-          detail: {feature: this.feature, enabled: !this.checked_}
+          detail: {feature: this.feature, enabled: !this.checked_},
         });
     this.dispatchEvent(featureToggleClickedEvent);
   }
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_metrics_logger.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_metrics_logger.js
index c1f49f23..66d61cbe 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_metrics_logger.js
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_metrics_logger.js
@@ -42,7 +42,7 @@
   chrome.send('metricsHandler:recordInHistogram', [
     SmartLockToggleHistogramName,
     getSmartLockToggleValue_(smartLockToggleLocation, enabled),
-    SmartLockToggle.MAX
+    SmartLockToggle.MAX,
   ]);
 }
 
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.js
index d634ce0..dd7cbdf 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.js
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.js
@@ -137,7 +137,7 @@
         type: Boolean,
         value: function() {
           return loadTimeData.getBoolean('isNearbyShareSupported');
-        }
+        },
       },
 
       /** @private */
@@ -190,7 +190,7 @@
         type: Boolean,
         value: function() {
           return loadTimeData.getBoolean('isChromeosScreenLockEnabled');
-        }
+        },
       },
 
       /** @private */
@@ -198,7 +198,7 @@
         type: Boolean,
         value: function() {
           return loadTimeData.getBoolean('isPhoneScreenLockEnabled');
-        }
+        },
       },
     };
   }
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_permissions_setup_dialog.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_permissions_setup_dialog.js
index bafe68d..09457ebd 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_permissions_setup_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_permissions_setup_dialog.js
@@ -609,7 +609,7 @@
     const pinNumberEvent = new CustomEvent('pin-number-selected', {
       bubbles: true,
       composed: true,
-      detail: {isPinNumberSelected: selected}
+      detail: {isPinNumberSelected: selected},
     });
     this.dispatchEvent(pinNumberEvent);
   }
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_radio_button.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_radio_button.js
index 82c66d95..40daea6 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_radio_button.js
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_radio_button.js
@@ -35,21 +35,21 @@
         type: String,
         notify: true,
         reflectToAttribute: true,
-        computed: 'getAriaChecked_(checked)'
+        computed: 'getAriaChecked_(checked)',
       },
       ariaDisabled: {
         type: String,
         notify: true,
         reflectToAttribute: true,
-        computed: 'getAriaDisabled_(disabled)'
+        computed: 'getAriaDisabled_(disabled)',
 
       },
       ariaLabel: {
         type: String,
         notify: true,
         reflectToAttribute: true,
-        computed: 'getLabel_(label)'
-      }
+        computed: 'getLabel_(label)',
+      },
     };
   }
 
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_screen_lock_subpage.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_screen_lock_subpage.js
index fdc962b..ae34c89 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_screen_lock_subpage.js
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_screen_lock_subpage.js
@@ -121,8 +121,8 @@
       bubbles: true,
       composed: true,
       detail: {
-        isPinNumberSelected: (selected === LockScreenUnlockType.PIN_PASSWORD)
-      }
+        isPinNumberSelected: (selected === LockScreenUnlockType.PIN_PASSWORD),
+      },
     });
     this.dispatchEvent(pinNumberEvent);
     if (selected === LockScreenUnlockType.PASSWORD && this.setModes_) {
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_smartlock_subpage.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_smartlock_subpage.js
index 456d621..2559ee1 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_smartlock_subpage.js
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_smartlock_subpage.js
@@ -34,8 +34,10 @@
  */
 const SettingsMultideviceSmartlockSubpageElementBase = mixinBehaviors(
     [
-      DeepLinkingBehavior, MultiDeviceFeatureBehavior, RouteObserverBehavior,
-      WebUIListenerBehavior
+      DeepLinkingBehavior,
+      MultiDeviceFeatureBehavior,
+      RouteObserverBehavior,
+      WebUIListenerBehavior,
     ],
     PolymerElement);
 
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_subpage.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_subpage.js
index f4f4023..4cf42b1 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_subpage.js
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_subpage.js
@@ -45,8 +45,10 @@
  */
 const SettingsMultideviceSubpageElementBase = mixinBehaviors(
     [
-      DeepLinkingBehavior, MultiDeviceFeatureBehavior, RouteObserverBehavior,
-      I18nBehavior
+      DeepLinkingBehavior,
+      MultiDeviceFeatureBehavior,
+      RouteObserverBehavior,
+      I18nBehavior,
     ],
     PolymerElement);
 
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_tether_item.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_tether_item.js
index 6c6c1f96..5fadf7f 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_tether_item.js
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_tether_item.js
@@ -85,7 +85,7 @@
         value() {
           return loadTimeData.valueExists('showTechnologyBadge') &&
               loadTimeData.getBoolean('showTechnologyBadge');
-        }
+        },
       },
     };
   }
diff --git a/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_high_visibility_page.js b/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_high_visibility_page.js
index bd7d14a..a7b6d5a 100644
--- a/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_high_visibility_page.js
+++ b/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_high_visibility_page.js
@@ -121,7 +121,7 @@
         value: null,
         computed:
             'computeErrorState_(shutoffTimestamp, remainingTimeInSeconds_,' +
-            'registerResult, nearbyProcessStopped, startAdvertisingFailed)'
+            'registerResult, nearbyProcessStopped, startAdvertisingFailed)',
       },
 
       /**
diff --git a/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_subpage.js b/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_subpage.js
index f8a1cf5..7475e0e 100644
--- a/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_subpage.js
+++ b/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_subpage.js
@@ -122,7 +122,7 @@
       /** @private */
       manageContactsUrl_: {
         type: String,
-        value: () => loadTimeData.getString('nearbyShareManageContactsUrl')
+        value: () => loadTimeData.getString('nearbyShareManageContactsUrl'),
       },
 
       /** @private {boolean} */
diff --git a/chrome/browser/resources/settings/chromeos/os_a11y_page/manage_a11y_page.js b/chrome/browser/resources/settings/chromeos/os_a11y_page/manage_a11y_page.js
index c8c8888..5a706b5 100644
--- a/chrome/browser/resources/settings/chromeos/os_a11y_page/manage_a11y_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_a11y_page/manage_a11y_page.js
@@ -47,8 +47,11 @@
  */
 const SettingsManageA11YPageElementBase = mixinBehaviors(
     [
-      DeepLinkingBehavior, I18nBehavior, RouteObserverBehavior,
-      RouteOriginBehavior, WebUIListenerBehavior
+      DeepLinkingBehavior,
+      I18nBehavior,
+      RouteObserverBehavior,
+      RouteOriginBehavior,
+      WebUIListenerBehavior,
     ],
     PolymerElement);
 
@@ -121,20 +124,20 @@
           return [
             {
               value: 600,
-              name: loadTimeData.getString('delayBeforeClickExtremelyShort')
+              name: loadTimeData.getString('delayBeforeClickExtremelyShort'),
             },
             {
               value: 800,
-              name: loadTimeData.getString('delayBeforeClickVeryShort')
+              name: loadTimeData.getString('delayBeforeClickVeryShort'),
             },
             {
               value: 1000,
-              name: loadTimeData.getString('delayBeforeClickShort')
+              name: loadTimeData.getString('delayBeforeClickShort'),
             },
             {value: 2000, name: loadTimeData.getString('delayBeforeClickLong')},
             {
               value: 4000,
-              name: loadTimeData.getString('delayBeforeClickVeryLong')
+              name: loadTimeData.getString('delayBeforeClickVeryLong'),
             },
           ];
         },
@@ -147,25 +150,25 @@
           return [
             {
               value: 5,
-              name:
-                  loadTimeData.getString('autoclickMovementThresholdExtraSmall')
+              name: loadTimeData.getString(
+                  'autoclickMovementThresholdExtraSmall'),
             },
             {
               value: 10,
-              name: loadTimeData.getString('autoclickMovementThresholdSmall')
+              name: loadTimeData.getString('autoclickMovementThresholdSmall'),
             },
             {
               value: 20,
-              name: loadTimeData.getString('autoclickMovementThresholdDefault')
+              name: loadTimeData.getString('autoclickMovementThresholdDefault'),
             },
             {
               value: 30,
-              name: loadTimeData.getString('autoclickMovementThresholdLarge')
+              name: loadTimeData.getString('autoclickMovementThresholdLarge'),
             },
             {
               value: 40,
-              name:
-                  loadTimeData.getString('autoclickMovementThresholdExtraLarge')
+              name: loadTimeData.getString(
+                  'autoclickMovementThresholdExtraLarge'),
             },
           ];
         },
@@ -240,7 +243,7 @@
         type: Boolean,
         value() {
           return loadTimeData.getBoolean('isKioskModeActive');
-        }
+        },
       },
 
       /**
@@ -259,7 +262,7 @@
         type: Boolean,
         value() {
           return loadTimeData.getBoolean('isGuest');
-        }
+        },
       },
 
       /** @private */
@@ -269,7 +272,7 @@
           return this.i18n(
               'screenMagnifierHintLabel',
               this.i18n('screenMagnifierHintSearchKey'));
-        }
+        },
       },
 
       /** @private */
@@ -277,7 +280,7 @@
         type: String,
         value() {
           return loadTimeData.getString('dictationDescription');
-        }
+        },
       },
 
       /** @private */
@@ -306,7 +309,7 @@
         type: Array,
         value() {
           return [];
-        }
+        },
       },
 
       /** @private */
@@ -314,7 +317,7 @@
         type: Array,
         value() {
           return [];
-        }
+        },
       },
 
       /** @private */
@@ -634,7 +637,7 @@
       return /** @type {!chrome.settingsPrivate.PrefObject}*/ ({
         value: true,
         type: chrome.settingsPrivate.PrefType.BOOLEAN,
-        key: ''
+        key: '',
       });
     }
 
diff --git a/chrome/browser/resources/settings/chromeos/os_a11y_page/os_a11y_page.js b/chrome/browser/resources/settings/chromeos/os_a11y_page/os_a11y_page.js
index 3363546..0e7ca48 100644
--- a/chrome/browser/resources/settings/chromeos/os_a11y_page/os_a11y_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_a11y_page/os_a11y_page.js
@@ -118,7 +118,7 @@
         type: Boolean,
         value: function() {
           return loadTimeData.getBoolean('isKioskModeActive');
-        }
+        },
       },
 
       /**
diff --git a/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_action_assignment_pane.js b/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_action_assignment_pane.js
index b444135..b5f43c5 100644
--- a/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_action_assignment_pane.js
+++ b/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_action_assignment_pane.js
@@ -90,7 +90,7 @@
  */
 export function getLabelForAssignment(assignment) {
   return I18nBehavior.i18nAdvanced('switchAndDeviceType', {
-    substitutions: [assignment.key, getLabelForDeviceType(assignment.device)]
+    substitutions: [assignment.key, getLabelForDeviceType(assignment.device)],
   });
 }
 
diff --git a/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_constants.js b/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_constants.js
index b0fec243..29939893 100644
--- a/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_constants.js
+++ b/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_constants.js
@@ -9,7 +9,7 @@
 export const actionToPref = {
   select: 'settings.a11y.switch_access.select.device_key_codes',
   next: 'settings.a11y.switch_access.next.device_key_codes',
-  previous: 'settings.a11y.switch_access.previous.device_key_codes'
+  previous: 'settings.a11y.switch_access.previous.device_key_codes',
 };
 
 /**
@@ -40,7 +40,7 @@
 export const AUTO_SCAN_SPEED_RANGE_MS = [
   4000, 3900, 3800, 3700, 3600, 3500, 3400, 3300, 3200, 3100, 3000, 2900,
   2800, 2700, 2600, 2500, 2400, 2300, 2200, 2100, 2000, 1900, 1800, 1700,
-  1600, 1500, 1400, 1300, 1200, 1100, 1000, 900,  800,  700
+  1600, 1500, 1400, 1300, 1200, 1100, 1000, 900,  800,  700,
 ];
 
 /**
@@ -55,7 +55,7 @@
  */
 export const AssignmentContext = {
   DIALOG: 'dialog',
-  SETUP_GUIDE: 'setupGuide'
+  SETUP_GUIDE: 'setupGuide',
 };
 
 /**
@@ -65,7 +65,7 @@
 export const SwitchAccessCommand = {
   NEXT: 'next',
   PREVIOUS: 'previous',
-  SELECT: 'select'
+  SELECT: 'select',
 };
 
 /**
@@ -76,5 +76,5 @@
   INTERNAL: 'internal',
   USB: 'usb',
   BLUETOOTH: 'bluetooth',
-  UNKNOWN: 'unknown'
+  UNKNOWN: 'unknown',
 };
diff --git a/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_setup_guide_dialog.js b/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_setup_guide_dialog.js
index 0bd7848..abde114 100644
--- a/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_setup_guide_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_setup_guide_dialog.js
@@ -74,58 +74,63 @@
 SASetupPageList[SASetupPageId.INTRO] = {
   titleId: 'switchAccessSetupIntroTitle',
   visibleElements: [
-    SASetupElement.BLUETOOTH_BUTTON, SASetupElement.NEXT_BUTTON,
-    SASetupElement.INTRO_CONTENT
-  ]
+    SASetupElement.BLUETOOTH_BUTTON,
+    SASetupElement.NEXT_BUTTON,
+    SASetupElement.INTRO_CONTENT,
+  ],
 };
 
 SASetupPageList[SASetupPageId.ASSIGN_SELECT] = {
   titleId: 'switchAccessSetupAssignSelectTitle',
-  visibleElements: [SASetupElement.ASSIGN_SWITCH_CONTENT]
+  visibleElements: [SASetupElement.ASSIGN_SWITCH_CONTENT],
 };
 
 SASetupPageList[SASetupPageId.AUTO_SCAN_ENABLED] = {
   titleId: 'switchAccessSetupAutoScanEnabledTitle',
   visibleElements: [
-    SASetupElement.NEXT_BUTTON, SASetupElement.PREVIOUS_BUTTON,
-    SASetupElement.AUTO_SCAN_ENABLED_CONTENT
-  ]
+    SASetupElement.NEXT_BUTTON,
+    SASetupElement.PREVIOUS_BUTTON,
+    SASetupElement.AUTO_SCAN_ENABLED_CONTENT,
+  ],
 };
 
 SASetupPageList[SASetupPageId.CHOOSE_SWITCH_COUNT] = {
   titleId: 'switchAccessSetupChooseSwitchCountTitle',
   visibleElements: [
-    SASetupElement.NEXT_BUTTON, SASetupElement.PREVIOUS_BUTTON,
-    SASetupElement.CHOOSE_SWITCH_COUNT_CONTENT
-  ]
+    SASetupElement.NEXT_BUTTON,
+    SASetupElement.PREVIOUS_BUTTON,
+    SASetupElement.CHOOSE_SWITCH_COUNT_CONTENT,
+  ],
 };
 
 SASetupPageList[SASetupPageId.AUTO_SCAN_SPEED] = {
   titleId: 'switchAccessSetupAutoScanSpeedTitle',
   visibleElements: [
-    SASetupElement.NEXT_BUTTON, SASetupElement.PREVIOUS_BUTTON,
-    SASetupElement.AUTO_SCAN_SPEED_CONTENT
-  ]
+    SASetupElement.NEXT_BUTTON,
+    SASetupElement.PREVIOUS_BUTTON,
+    SASetupElement.AUTO_SCAN_SPEED_CONTENT,
+  ],
 };
 
 SASetupPageList[SASetupPageId.ASSIGN_NEXT] = {
   titleId: 'switchAccessSetupAssignNextTitle',
   visibleElements:
-      [SASetupElement.PREVIOUS_BUTTON, SASetupElement.ASSIGN_SWITCH_CONTENT]
+      [SASetupElement.PREVIOUS_BUTTON, SASetupElement.ASSIGN_SWITCH_CONTENT],
 };
 
 SASetupPageList[SASetupPageId.ASSIGN_PREVIOUS] = {
   titleId: 'switchAccessSetupAssignPreviousTitle',
   visibleElements:
-      [SASetupElement.PREVIOUS_BUTTON, SASetupElement.ASSIGN_SWITCH_CONTENT]
+      [SASetupElement.PREVIOUS_BUTTON, SASetupElement.ASSIGN_SWITCH_CONTENT],
 };
 
 SASetupPageList[SASetupPageId.CLOSING] = {
   titleId: 'switchAccessSetupClosingTitle',
   visibleElements: [
-    SASetupElement.DONE_BUTTON, SASetupElement.START_OVER_BUTTON,
-    SASetupElement.CLOSING_CONTENT
-  ]
+    SASetupElement.DONE_BUTTON,
+    SASetupElement.START_OVER_BUTTON,
+    SASetupElement.CLOSING_CONTENT,
+  ],
 };
 
 /**
@@ -190,7 +195,7 @@
       maxScanSpeedMs_: {
         readOnly: true,
         type: Number,
-        value: AUTO_SCAN_SPEED_RANGE_MS[AUTO_SCAN_SPEED_RANGE_MS.length - 1]
+        value: AUTO_SCAN_SPEED_RANGE_MS[AUTO_SCAN_SPEED_RANGE_MS.length - 1],
       },
 
       /** @private */
diff --git a/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_subpage.js b/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_subpage.js
index 2166fda..53943564 100644
--- a/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_subpage.js
+++ b/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_subpage.js
@@ -68,8 +68,11 @@
  */
 const SettingsSwitchAccessSubpageElementBase = mixinBehaviors(
     [
-      DeepLinkingBehavior, I18nBehavior, PrefsBehavior, RouteObserverBehavior,
-      WebUIListenerBehavior
+      DeepLinkingBehavior,
+      I18nBehavior,
+      PrefsBehavior,
+      RouteObserverBehavior,
+      WebUIListenerBehavior,
     ],
     PolymerElement);
 
@@ -144,7 +147,7 @@
       maxScanSpeedMs_: {
         readOnly: true,
         type: Number,
-        value: AUTO_SCAN_SPEED_RANGE_MS[AUTO_SCAN_SPEED_RANGE_MS.length - 1]
+        value: AUTO_SCAN_SPEED_RANGE_MS[AUTO_SCAN_SPEED_RANGE_MS.length - 1],
       },
 
       /** @private {string} */
@@ -173,7 +176,7 @@
       maxPointScanSpeed_: {
         readOnly: true,
         type: Number,
-        value: POINT_SCAN_SPEED_RANGE_DIPS_PER_SECOND.length
+        value: POINT_SCAN_SPEED_RANGE_DIPS_PER_SECOND.length,
       },
 
       /** @private {number} */
diff --git a/chrome/browser/resources/settings/chromeos/os_a11y_page/text_to_speech_page.js b/chrome/browser/resources/settings/chromeos/os_a11y_page/text_to_speech_page.js
index 43283db..ea9b3bb9 100644
--- a/chrome/browser/resources/settings/chromeos/os_a11y_page/text_to_speech_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_a11y_page/text_to_speech_page.js
@@ -38,8 +38,11 @@
  */
 const SettingsTextToSpeechPageElementBase = mixinBehaviors(
     [
-      DeepLinkingBehavior, I18nBehavior, RouteObserverBehavior,
-      RouteOriginBehavior, WebUIListenerBehavior
+      DeepLinkingBehavior,
+      I18nBehavior,
+      RouteObserverBehavior,
+      RouteOriginBehavior,
+      WebUIListenerBehavior,
     ],
     PolymerElement);
 
diff --git a/chrome/browser/resources/settings/chromeos/os_a11y_page/tts_subpage.js b/chrome/browser/resources/settings/chromeos/os_a11y_page/tts_subpage.js
index d4dc798..1fe9ed1e 100644
--- a/chrome/browser/resources/settings/chromeos/os_a11y_page/tts_subpage.js
+++ b/chrome/browser/resources/settings/chromeos/os_a11y_page/tts_subpage.js
@@ -38,8 +38,10 @@
  */
 const SettingsTtsSubpageElementBase = mixinBehaviors(
     [
-      DeepLinkingBehavior, I18nBehavior, RouteObserverBehavior,
-      WebUIListenerBehavior
+      DeepLinkingBehavior,
+      I18nBehavior,
+      RouteObserverBehavior,
+      WebUIListenerBehavior,
     ],
     PolymerElement);
 
@@ -288,7 +290,7 @@
           language: voice.displayLanguage,
           code: voice.languageCode,
           preferred: false,
-          voices: []
+          voices: [],
         };
       }
       // Each voice gets a unique ID from its name and extension.
diff --git a/chrome/browser/resources/settings/chromeos/os_about_page/edit_hostname_dialog.js b/chrome/browser/resources/settings/chromeos/os_about_page/edit_hostname_dialog.js
index bcc94c03..5d161e8 100644
--- a/chrome/browser/resources/settings/chromeos/os_about_page/edit_hostname_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/os_about_page/edit_hostname_dialog.js
@@ -70,7 +70,7 @@
       inputCountString_: {
         type: String,
         computed: 'computeInputCountString_(deviceName_)',
-      }
+      },
 
     };
   }
diff --git a/chrome/browser/resources/settings/chromeos/os_about_page/os_about_page.js b/chrome/browser/resources/settings/chromeos/os_about_page/os_about_page.js
index dd9ffde..0345863e 100644
--- a/chrome/browser/resources/settings/chromeos/os_about_page/os_about_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_about_page/os_about_page.js
@@ -93,7 +93,7 @@
           progress: 0,
           rollback: false,
           powerwash: false,
-          status: UpdateStatus.UPDATED
+          status: UpdateStatus.UPDATED,
         },
       },
 
@@ -516,8 +516,8 @@
             substitutions: [
               this.i18nAdvanced(
                   browserChannelToI18nId(this.targetChannel_, this.isLts_)),
-              progressPercent
-            ]
+              progressPercent,
+            ],
           });
         }
         if (this.currentUpdateStatusEvent_.rollback) {
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/android_apps_subpage.js b/chrome/browser/resources/settings/chromeos/os_apps_page/android_apps_subpage.js
index 08ccc72..ef8914b 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/android_apps_subpage.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/android_apps_subpage.js
@@ -63,7 +63,7 @@
       playStoreEnabled_: {
         type: Boolean,
         computed: 'computePlayStoreEnabled_(androidAppsInfo)',
-        observer: 'onPlayStoreEnabledChanged_'
+        observer: 'onPlayStoreEnabledChanged_',
       },
 
       /** @private */
@@ -73,7 +73,7 @@
           return this.i18nAdvanced(
               'androidAppsDisableDialogMessage',
               {substitutions: [], tags: ['br']});
-        }
+        },
       },
 
       /** Whether Arc VM manage usb subpage should be shown. */
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_details_item.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_details_item.js
index 2cfdc4a..e2027031 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_details_item.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_details_item.js
@@ -202,8 +202,8 @@
             'appManagementAppDetailsTypeAndSourceCombined', {
               substitutions: [
                 String(this.getTypeString_(app)),
-                String(this.getInstallSourceString_(app))
-              ]
+                String(this.getInstallSourceString_(app)),
+              ],
             });
       case InstallSource.kBrowser:
         return this.i18n('appManagementAppDetailsInstallSourceBrowser');
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/borealis_page/borealis_detail_view.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/borealis_page/borealis_detail_view.js
index d8d1be5..339d958 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/borealis_page/borealis_detail_view.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/borealis_page/borealis_detail_view.js
@@ -41,7 +41,7 @@
       /** @private {App} */
       app_: {
         type: Object,
-      }
+      },
     };
   }
 
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/browser_proxy.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/browser_proxy.js
index 2cb638ee..801058e0 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/browser_proxy.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/browser_proxy.js
@@ -54,7 +54,7 @@
       };
       permissionOptions[PermissionType.kCamera] = {
         permissionValue: TriState.kBlock,
-        isManaged: true
+        isManaged: true,
       };
 
       const /** @type {!Array<App>}*/ appList = [
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/main_view.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/main_view.js
index 0351f49..938c64c 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/main_view.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/main_view.js
@@ -61,7 +61,7 @@
       appList_: {
         type: Array,
         value: () => [],
-        computed: 'computeAppList_(apps_, searchTerm)'
+        computed: 'computeAppList_(apps_, searchTerm)',
       },
     };
   }
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_notifications_page/app_notification_row.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_notifications_page/app_notification_row.js
index ccf2d6b..04d1f95 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_notifications_page/app_notification_row.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_notifications_page/app_notification_row.js
@@ -45,7 +45,7 @@
       checked_: {
         type: Boolean,
         value: false,
-      }
+      },
     };
   }
 
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/os_apps_page.js b/chrome/browser/resources/settings/chromeos/os_apps_page/os_apps_page.js
index 682371be..4cc9a2a 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/os_apps_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/os_apps_page.js
@@ -77,8 +77,11 @@
  */
 const OsSettingsAppsPageElementBase = mixinBehaviors(
     [
-      AppManagementStoreClient, DeepLinkingBehavior, I18nBehavior,
-      PrefsBehavior, RouteObserverBehavior
+      AppManagementStoreClient,
+      DeepLinkingBehavior,
+      I18nBehavior,
+      PrefsBehavior,
+      RouteObserverBehavior,
     ],
     PolymerElement);
 
diff --git a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_change_device_name_dialog.js b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_change_device_name_dialog.js
index 3b7b1283..0f690d96 100644
--- a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_change_device_name_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_change_device_name_dialog.js
@@ -66,7 +66,7 @@
         type: Boolean,
         value: false,
         reflectToAttribute: true,
-      }
+      },
     };
   }
 
diff --git a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_device_detail_subpage.js b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_device_detail_subpage.js
index c0a7380..7a134e5 100644
--- a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_device_detail_subpage.js
+++ b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_device_detail_subpage.js
@@ -103,7 +103,7 @@
       pageState_: {
         type: Object,
         value: PageState.DISCONNECTED,
-      }
+      },
     };
   }
 
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 c30b9d6e..43419a4 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
@@ -38,8 +38,10 @@
  */
 const SettingsBluetoothDevicesSubpageElementBase = mixinBehaviors(
     [
-      I18nBehavior, RouteObserverBehavior, DeepLinkingBehavior,
-      WebUIListenerBehavior
+      I18nBehavior,
+      RouteObserverBehavior,
+      DeepLinkingBehavior,
+      WebUIListenerBehavior,
     ],
     PolymerElement);
 
@@ -123,7 +125,7 @@
       unconnectedDevices_: {
         type: Array,
         value: [],
-      }
+      },
     };
   }
 
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 d2e1332..d3f98c12 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
@@ -32,7 +32,7 @@
  */
 const LabelType = {
   A11Y: 1,
-  DISPLAYED_TEXT: 2
+  DISPLAYED_TEXT: 2,
 };
 
 /**
diff --git a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_paired_bluetooth_list_item.js b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_paired_bluetooth_list_item.js
index 225841f..74f67cc 100644
--- a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_paired_bluetooth_list_item.js
+++ b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_paired_bluetooth_list_item.js
@@ -290,7 +290,7 @@
         show: showTooltip,
         element: showTooltip ? this.shadowRoot.getElementById('managedIcon') :
                                undefined,
-      }
+      },
     }));
   }
 }
diff --git a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/settings_fast_pair_constants.js b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/settings_fast_pair_constants.js
index cad1cfc..5115d61 100644
--- a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/settings_fast_pair_constants.js
+++ b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/settings_fast_pair_constants.js
@@ -12,7 +12,7 @@
 export const FastPairSavedDevicesOptInStatus = {
   STATUS_UKNOWN: 0,
   STATUS_OPTED_IN: 1,
-  STATUS_OPTED_OUT: 2
+  STATUS_OPTED_OUT: 2,
 };
 
 /**
diff --git a/chrome/browser/resources/settings/chromeos/os_languages_page/add_input_methods_dialog.js b/chrome/browser/resources/settings/chromeos/os_languages_page/add_input_methods_dialog.js
index 730ceb4..c446d6c 100644
--- a/chrome/browser/resources/settings/chromeos/os_languages_page/add_input_methods_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/os_languages_page/add_input_methods_dialog.js
@@ -47,7 +47,7 @@
   getSuggestedInputMethodIds_() {
     const languageCodes = [
       ...this.languageHelper.getEnabledLanguageCodes(),
-      this.languageHelper.getArcImeLanguageCode()
+      this.languageHelper.getArcImeLanguageCode(),
     ];
     let inputMethods =
         this.languageHelper.getInputMethodsForLanguages(languageCodes);
@@ -86,7 +86,7 @@
                id: inputMethod.id,
                name: inputMethod.displayName,
                searchTerms: inputMethod.tags,
-               disabledByPolicy: !!inputMethod.isProhibitedByPolicy
+               disabledByPolicy: !!inputMethod.isProhibitedByPolicy,
              }));
   }
 
diff --git a/chrome/browser/resources/settings/chromeos/os_languages_page/add_items_dialog.js b/chrome/browser/resources/settings/chromeos/os_languages_page/add_items_dialog.js
index e0bccdd..57f72fcf 100644
--- a/chrome/browser/resources/settings/chromeos/os_languages_page/add_items_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/os_languages_page/add_items_dialog.js
@@ -118,7 +118,7 @@
       suggestedItems_: {
         type: Array,
         computed: 'getSuggestedItems_(suggestedItemIds.*, itemIdsToItems_)',
-        value: []
+        value: [],
       },
 
       /** @private */
@@ -151,7 +151,7 @@
       `updateSuggestedListScrollOffset_(showSuggestedList_,
           suggestedItemsLabel)`,
       `updateFilteredListScrollOffset_(showSuggestedList_,
-          suggestedItemsLabel, suggestedItems_.length, showFilteredList_)`
+          suggestedItemsLabel, suggestedItems_.length, showFilteredList_)`,
     ];
   }
 
diff --git a/chrome/browser/resources/settings/chromeos/os_languages_page/add_spellcheck_languages_dialog.js b/chrome/browser/resources/settings/chromeos/os_languages_page/add_spellcheck_languages_dialog.js
index 96cf7ca..6d45e3c 100644
--- a/chrome/browser/resources/settings/chromeos/os_languages_page/add_spellcheck_languages_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/os_languages_page/add_spellcheck_languages_dialog.js
@@ -79,7 +79,7 @@
           name: this.getDisplayText_(spellCheckLang.language),
           searchTerms: [
             spellCheckLang.language.displayName,
-            spellCheckLang.language.nativeDisplayName
+            spellCheckLang.language.nativeDisplayName,
           ],
           disabledByPolicy: spellCheckLang.isManaged,
         }));
diff --git a/chrome/browser/resources/settings/chromeos/os_languages_page/input_method_options_page.js b/chrome/browser/resources/settings/chromeos/os_languages_page/input_method_options_page.js
index a3165c10..623516c9 100644
--- a/chrome/browser/resources/settings/chromeos/os_languages_page/input_method_options_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_languages_page/input_method_options_page.js
@@ -163,7 +163,7 @@
         url: getOptionUrl(name),
         dependentOptions: option.dependentOptions ?
             option.dependentOptions.map(t => makeOption({name: t})) :
-            []
+            [],
       };
     };
 
diff --git a/chrome/browser/resources/settings/chromeos/os_languages_page/input_method_util.js b/chrome/browser/resources/settings/chromeos/os_languages_page/input_method_util.js
index cc61d9ef..9dfac17d 100644
--- a/chrome/browser/resources/settings/chromeos/os_languages_page/input_method_util.js
+++ b/chrome/browser/resources/settings/chromeos/os_languages_page/input_method_util.js
@@ -39,7 +39,7 @@
   SET3_YET: '3 Set (Old Hangul) / 세벌식 (옛글)',
   XKB_US: 'US',
   XKB_DVORAK: 'Dvorak',
-  XKB_COLEMAK: 'Colemak'
+  XKB_COLEMAK: 'Colemak',
 };
 
 /**
@@ -134,7 +134,7 @@
     r_l: undefined,
     s_sh: undefined,
     uan_uang: undefined,
-    z_zh: undefined
+    z_zh: undefined,
   },
   // Options for zhuyin input method.
   [OptionType.ZHUYIN_KEYBOARD_LAYOUT]: KeyboardLayout.STANDARD,
@@ -185,17 +185,18 @@
     {
       title: SettingsHeaders.VIRTUAL_KEYBOARD,
       optionNames: [
-        {name: OptionType.ENABLE_SOUND_ON_KEYPRESS}, {
+        {name: OptionType.ENABLE_SOUND_ON_KEYPRESS},
+        {
           name: OptionType.VIRTUAL_KEYBOARD_AUTO_CORRECTION_LEVEL,
           dependentOptions: [
             OptionType.VIRTUAL_KEYBOARD_ENABLE_CAPITALIZATION,
-          ]
+          ],
         },
         {name: OptionType.ENABLE_GESTURE_TYPING},
         {name: OptionType.ENABLE_DOUBLE_SPACE_PERIOD},
-        {name: OptionType.EDIT_USER_DICT}
+        {name: OptionType.EDIT_USER_DICT},
       ],
-    }
+    },
   ],
   [SettingsType.ZHUYIN_SETTINGS]: [{
     title: SettingsHeaders.PHYSICAL_KEYBOARD,
@@ -203,7 +204,7 @@
       {name: OptionType.ZHUYIN_KEYBOARD_LAYOUT},
       {name: OptionType.ZHUYIN_SELECT_KEYS},
       {name: OptionType.ZHUYIN_PAGE_SIZE},
-    ]
+    ],
   }],
   [SettingsType.KOREAN_SETTINGS]: [{
     title: SettingsHeaders.BASIC,
@@ -229,7 +230,7 @@
         OptionType.PINYIN_L_N,
         OptionType.PINYIN_S_SH,
         OptionType.PINYIN_Z_ZH,
-      ]
+      ],
     }],
   }],
   [SettingsType.PINYIN_SETTINGS]: [
@@ -246,8 +247,8 @@
         {name: OptionType.PINYIN_DEFAULT_CHINESE},
         {name: OptionType.PINYIN_FULL_WIDTH_CHARACTER},
         {name: OptionType.PINYIN_CHINESE_PUNCTUATION},
-      ]
-    }
+      ],
+    },
   ],
   [SettingsType.BASIC_SETTINGS]: [{
     title: SettingsHeaders.VIRTUAL_KEYBOARD,
@@ -495,13 +496,13 @@
       return [
         {value: 0, name: 'inputMethodOptionsAutoCorrectionOff'},
         {value: 1, name: 'inputMethodOptionsAutoCorrectionModest'},
-        {value: 2, name: 'inputMethodOptionsAutoCorrectionAggressive'}
+        {value: 2, name: 'inputMethodOptionsAutoCorrectionAggressive'},
       ];
     case OptionType.XKB_LAYOUT:
       return [
         {value: 'US', name: 'inputMethodOptionsUsKeyboard'},
         {value: 'Dvorak', name: 'inputMethodOptionsDvorakKeyboard'},
-        {value: 'Colemak', name: 'inputMethodOptionsColemakKeyboard'}
+        {value: 'Colemak', name: 'inputMethodOptionsColemakKeyboard'},
       ];
     case OptionType.ZHUYIN_KEYBOARD_LAYOUT:
       return [
diff --git a/chrome/browser/resources/settings/chromeos/os_languages_page/languages.js b/chrome/browser/resources/settings/chromeos/os_languages_page/languages.js
index 080929f..d4031a0 100644
--- a/chrome/browser/resources/settings/chromeos/os_languages_page/languages.js
+++ b/chrome/browser/resources/settings/chromeos/os_languages_page/languages.js
@@ -177,7 +177,7 @@
         type: Object,
         value() {
           return new Set();
-        }
+        },
       },
 
       /** @private Prospective UI language when the page was loaded. */
@@ -522,7 +522,7 @@
           isManaged: blockedCodesSet.has(language.code),
           spellCheckEnabled: false,
           downloadDictionaryStatus: null,
-          downloadDictionaryFailureCount: 0
+          downloadDictionaryFailureCount: 0,
         });
       }
     }
diff --git a/chrome/browser/resources/settings/chromeos/os_languages_page/os_edit_dictionary_page.js b/chrome/browser/resources/settings/chromeos/os_languages_page/os_edit_dictionary_page.js
index b01d768..b1bfe0e 100644
--- a/chrome/browser/resources/settings/chromeos/os_languages_page/os_edit_dictionary_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_languages_page/os_edit_dictionary_page.js
@@ -100,7 +100,7 @@
         type: Number,
         value: NewWordState.NO_WORD,
         computed: 'updateNewWordState_(newWordValue_, words_.*)',
-      }
+      },
     };
   }
 
diff --git a/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_page_v2.js b/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_page_v2.js
index 924ce6d..4381f43 100644
--- a/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_page_v2.js
+++ b/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_page_v2.js
@@ -381,7 +381,7 @@
     this.detailLanguage_ =
         /** @type {{state: !LanguageState, index: number}} */ ({
           state: /** @type {!LanguageState} */ (e.model.item),
-          index: /** @type {number} */ (e.model.index)
+          index: /** @type {number} */ (e.model.index),
         });
 
     const menu = /** @type {!CrActionMenuElement} */ (this.$.menu.get());
diff --git a/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_section.js b/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_section.js
index 53b4f2c..f009354 100644
--- a/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_section.js
+++ b/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_section.js
@@ -101,7 +101,7 @@
           return loadTimeData.getBoolean('allowAssistivePersonalInfo') ||
               loadTimeData.getBoolean('allowEmojiSuggestion');
         },
-      }
+      },
 
     };
   }
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.js b/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.js
index 80fe737..461e9af 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.js
@@ -43,8 +43,10 @@
  */
 const SettingsAccountManagerElementBase = mixinBehaviors(
     [
-      DeepLinkingBehavior, I18nBehavior, WebUIListenerBehavior,
-      RouteObserverBehavior
+      DeepLinkingBehavior,
+      I18nBehavior,
+      WebUIListenerBehavior,
+      RouteObserverBehavior,
     ],
     PolymerElement);
 
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/fingerprint_list.js b/chrome/browser/resources/settings/chromeos/os_people_page/fingerprint_list.js
index bcbd442..113d97d 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/fingerprint_list.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/fingerprint_list.js
@@ -83,7 +83,7 @@
         type: Array,
         value() {
           return [];
-        }
+        },
       },
 
       /** @private */
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen.js b/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen.js
index c4508cd..2f5297f 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen.js
@@ -56,8 +56,11 @@
  */
 const SettingsLockScreenElementBase = mixinBehaviors(
     [
-      DeepLinkingBehavior, I18nBehavior, LockStateBehavior,
-      WebUIListenerBehavior, RouteObserverBehavior
+      DeepLinkingBehavior,
+      I18nBehavior,
+      LockStateBehavior,
+      WebUIListenerBehavior,
+      RouteObserverBehavior,
     ],
     PolymerElement);
 
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen_password_prompt_dialog.js b/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen_password_prompt_dialog.js
index 6c1241f2..f0767c5 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen_password_prompt_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen_password_prompt_dialog.js
@@ -55,7 +55,7 @@
         type: Object,
         value() {
           return recordLockScreenProgress;
-        }
+        },
       },
     };
   }
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/lock_state_behavior.js b/chrome/browser/resources/settings/chromeos/os_people_page/lock_state_behavior.js
index 8d28e26f..a65ab56 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/lock_state_behavior.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/lock_state_behavior.js
@@ -15,7 +15,7 @@
 export const LockScreenUnlockType = {
   VALUE_PENDING: 'value_pending',
   PASSWORD: 'password',
-  PIN_PASSWORD: 'pin+password'
+  PIN_PASSWORD: 'pin+password',
 };
 
 /**
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.js b/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.js
index d96cf9b7..ad7c1985 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.js
@@ -60,8 +60,11 @@
  */
 const OsSettingsPeoplePageElementBase = mixinBehaviors(
     [
-      DeepLinkingBehavior, RouteObserverBehavior, I18nBehavior,
-      WebUIListenerBehavior, LockStateBehavior
+      DeepLinkingBehavior,
+      RouteObserverBehavior,
+      I18nBehavior,
+      WebUIListenerBehavior,
+      LockStateBehavior,
     ],
     PolymerElement);
 
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/os_sync_controls.js b/chrome/browser/resources/settings/chromeos/os_people_page/os_sync_controls.js
index 6772529..5960dead 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/os_sync_controls.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/os_sync_controls.js
@@ -62,8 +62,10 @@
  */
 const OsSyncControlsElementBase = mixinBehaviors(
     [
-      DeepLinkingBehavior, I18nBehavior, RouteObserverBehavior,
-      WebUIListenerBehavior
+      DeepLinkingBehavior,
+      I18nBehavior,
+      RouteObserverBehavior,
+      WebUIListenerBehavior,
     ],
     PolymerElement);
 
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/setup_fingerprint_dialog.js b/chrome/browser/resources/settings/chromeos/os_people_page/setup_fingerprint_dialog.js
index d44efbf..f4b940e 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/setup_fingerprint_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/setup_fingerprint_dialog.js
@@ -24,7 +24,7 @@
 export const FingerprintSetupStep = {
   LOCATE_SCANNER: 1,  // The user needs to locate the scanner.
   MOVE_FINGER: 2,     // The user needs to move finger around the scanner.
-  READY: 3            // The scanner has read the fingerprint successfully.
+  READY: 3,           // The scanner has read the fingerprint successfully.
 };
 
 /**
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/setup_pin_dialog.js b/chrome/browser/resources/settings/chromeos/os_people_page/setup_pin_dialog.js
index 47542e00..03c430bc 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/setup_pin_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/setup_pin_dialog.js
@@ -78,7 +78,7 @@
         type: Object,
         value() {
           return () => {};
-        }
+        },
       },
     };
   }
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/user_list.js b/chrome/browser/resources/settings/chromeos/os_people_page/user_list.js
index 5cc4dda..3f52da03 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/user_list.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/user_list.js
@@ -61,7 +61,7 @@
         value() {
           return [];
         },
-        notify: true
+        notify: true,
       },
 
       /**
@@ -73,7 +73,7 @@
         type: Boolean,
         value: false,
         reflectToAttribute: true,
-      }
+      },
     };
   }
 
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_manually_dialog.js b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_manually_dialog.js
index bf6959f..71eba59e 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_manually_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_manually_dialog.js
@@ -108,7 +108,8 @@
         new CustomEvent('show-cups-printer-toast', {
           bubbles: true,
           composed: true,
-          detail: {resultCode: result, printerName: this.newPrinter.printerName}
+          detail:
+              {resultCode: result, printerName: this.newPrinter.printerName},
         });
     this.dispatchEvent(showCupsPrinterToastEvent);
     this.shadowRoot.querySelector('add-printer-dialog').close();
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_enterprise_printers.js b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_enterprise_printers.js
index 2f6262c..15218676 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_enterprise_printers.js
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_enterprise_printers.js
@@ -48,8 +48,9 @@
  */
 const SettingsCupsEnterprisePrintersElementBase = mixinBehaviors(
     [
-      CupsPrintersEntryListBehavior, ListPropertyUpdateBehavior,
-      WebUIListenerBehavior
+      CupsPrintersEntryListBehavior,
+      ListPropertyUpdateBehavior,
+      WebUIListenerBehavior,
     ],
     PolymerElement);
 
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_nearby_printers.js b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_nearby_printers.js
index b38f991..044cd4e 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_nearby_printers.js
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_nearby_printers.js
@@ -30,8 +30,9 @@
  */
 const SettingsCupsNearbyPrintersElementBase = mixinBehaviors(
     [
-      CupsPrintersEntryListBehavior, ListPropertyUpdateBehavior,
-      WebUIListenerBehavior
+      CupsPrintersEntryListBehavior,
+      ListPropertyUpdateBehavior,
+      WebUIListenerBehavior,
     ],
     PolymerElement);
 
@@ -251,7 +252,7 @@
       detail: {
         resultCode,
         printerName,
-      }
+      },
     });
     this.dispatchEvent(event);
   }
@@ -300,7 +301,7 @@
         'open-manufacturer-model-dialog-for-specified-printer', {
           bubbles: true,
           composed: true,
-          detail: {item: /** @type {CupsPrinterInfo} */ (printer)}
+          detail: {item: /** @type {CupsPrinterInfo} */ (printer)},
         });
     this.dispatchEvent(openManufacturerDialogEvent);
   }
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers.js b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers.js
index cee8afe..1c999fb 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers.js
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers.js
@@ -56,8 +56,10 @@
  */
 const SettingsCupsPrintersElementBase = mixinBehaviors(
     [
-      DeepLinkingBehavior, NetworkListenerBehavior, RouteObserverBehavior,
-      WebUIListenerBehavior
+      DeepLinkingBehavior,
+      NetworkListenerBehavior,
+      RouteObserverBehavior,
+      WebUIListenerBehavior,
     ],
     PolymerElement);
 
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers_entry.js b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers_entry.js
index 78bc3f9..781232f 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers_entry.js
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers_entry.js
@@ -60,7 +60,7 @@
       userPrintersAllowed: {
         type: Boolean,
         value: false,
-      }
+      },
     };
   }
 
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_saved_printers.js b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_saved_printers.js
index ed78bf2..ccad5f08 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_saved_printers.js
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_saved_printers.js
@@ -50,8 +50,9 @@
  */
 const SettingsCupsSavedPrintersElementBase = mixinBehaviors(
     [
-      CupsPrintersEntryListBehavior, ListPropertyUpdateBehavior,
-      WebUIListenerBehavior
+      CupsPrintersEntryListBehavior,
+      ListPropertyUpdateBehavior,
+      WebUIListenerBehavior,
     ],
     PolymerElement);
 
@@ -147,7 +148,7 @@
   static get observers() {
     return [
       'onSearchOrPrintersChanged_(savedPrinters.*, searchTerm,' +
-      'hasShowMoreBeenTapped_, newPrinters_.*)'
+          'hasShowMoreBeenTapped_, newPrinters_.*)',
     ];
   }
 
diff --git a/chrome/browser/resources/settings/chromeos/os_privacy_page/os_privacy_page.js b/chrome/browser/resources/settings/chromeos/os_privacy_page/os_privacy_page.js
index 7f54299..e36877c 100644
--- a/chrome/browser/resources/settings/chromeos/os_privacy_page/os_privacy_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_privacy_page/os_privacy_page.js
@@ -45,8 +45,10 @@
  */
 const OsSettingsPrivacyPageElementBase = mixinBehaviors(
     [
-      DeepLinkingBehavior, RouteObserverBehavior, LockStateBehavior,
-      PrefsBehavior
+      DeepLinkingBehavior,
+      RouteObserverBehavior,
+      LockStateBehavior,
+      PrefsBehavior,
     ],
     PolymerElement);
 
diff --git a/chrome/browser/resources/settings/chromeos/os_privacy_page/privacy_hub_page.js b/chrome/browser/resources/settings/chromeos/os_privacy_page/privacy_hub_page.js
index 6db1a1d..898c4a6 100644
--- a/chrome/browser/resources/settings/chromeos/os_privacy_page/privacy_hub_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_privacy_page/privacy_hub_page.js
@@ -33,8 +33,10 @@
  */
 const SettingsPrivacyHubPageBase = mixinBehaviors(
     [
-      DeepLinkingBehavior, I18nBehavior, RouteObserverBehavior,
-      WebUIListenerBehavior
+      DeepLinkingBehavior,
+      I18nBehavior,
+      RouteObserverBehavior,
+      WebUIListenerBehavior,
     ],
     PolymerElement);
 
diff --git a/chrome/browser/resources/settings/chromeos/os_search_page/os_search_selection_dialog.js b/chrome/browser/resources/settings/chromeos/os_search_page/os_search_selection_dialog.js
index ab3148cd..e7ca66e4 100644
--- a/chrome/browser/resources/settings/chromeos/os_search_page/os_search_selection_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/os_search_page/os_search_selection_dialog.js
@@ -46,7 +46,7 @@
         type: Array,
         value() {
           return [];
-        }
+        },
       },
     };
   }
diff --git a/chrome/browser/resources/settings/chromeos/os_search_page/search_engine.js b/chrome/browser/resources/settings/chromeos/os_search_page/search_engine.js
index 27b699c..0562464 100644
--- a/chrome/browser/resources/settings/chromeos/os_search_page/search_engine.js
+++ b/chrome/browser/resources/settings/chromeos/os_search_page/search_engine.js
@@ -110,7 +110,7 @@
     const event = new CustomEvent('refresh-pref', {
       bubbles: true,
       composed: true,
-      detail: 'default_search_provider.enabled'
+      detail: 'default_search_provider.enabled',
     });
     this.dispatchEvent(event);
   }
diff --git a/chrome/browser/resources/settings/chromeos/os_search_page/search_subpage.js b/chrome/browser/resources/settings/chromeos/os_search_page/search_subpage.js
index e7502dc..2b016ce 100644
--- a/chrome/browser/resources/settings/chromeos/os_search_page/search_subpage.js
+++ b/chrome/browser/resources/settings/chromeos/os_search_page/search_subpage.js
@@ -92,7 +92,7 @@
         value() {
           return this.getAriaLabelledSubLabel_(
               this.i18nAdvanced('quickAnswersEnableDescriptionWithLink'));
-        }
+        },
       },
 
       /** @private */
@@ -101,7 +101,7 @@
         value() {
           return this.getAriaLabelledSubLabel_(
               this.i18nAdvanced('quickAnswersTranslationEnableDescription'));
-        }
+        },
       },
     };
   }
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_menu/os_settings_menu.js b/chrome/browser/resources/settings/chromeos/os_settings_menu/os_settings_menu.js
index df77b80d..8d30b7a 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_menu/os_settings_menu.js
+++ b/chrome/browser/resources/settings/chromeos/os_settings_menu/os_settings_menu.js
@@ -67,7 +67,7 @@
         value() {
           return loadTimeData.getBoolean(
               'isAccessibilityOSSettingsVisibilityEnabled');
-        }
+        },
       },
 
       showCrostini: Boolean,
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_page/main_page_behavior.js b/chrome/browser/resources/settings/chromeos/os_settings_page/main_page_behavior.js
index 56fe1a8..19d7c24 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_page/main_page_behavior.js
+++ b/chrome/browser/resources/settings/chromeos/os_settings_page/main_page_behavior.js
@@ -74,11 +74,12 @@
     return new Map([
       [RouteState.INITIAL, allStates],
       [
-        RouteState.DIALOG, new Set([
+        RouteState.DIALOG,
+        new Set([
           RouteState.SECTION,
           RouteState.SUBPAGE,
           RouteState.TOP_LEVEL,
-        ])
+        ]),
       ],
       [RouteState.SECTION, allStates],
       [RouteState.SUBPAGE, allStates],
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.js b/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.js
index 650fa180..3b8bd56 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.js
@@ -111,7 +111,7 @@
         value() {
           return loadTimeData.getBoolean(
               'isAccessibilityOSSettingsVisibilityEnabled');
-        }
+        },
       },
 
       /**
@@ -171,7 +171,7 @@
         type: Boolean,
         value() {
           return loadTimeData.getBoolean('enableBluetoothRevamp');
-        }
+        },
       },
     };
   }
@@ -325,8 +325,8 @@
                   top: toggle.offsetTop,
                   callback: () => {
                     this.advancedTogglingInProgress_ = false;
-                  }
-                }
+                  },
+                },
               });
               this.dispatchEvent(event);
             });
@@ -340,8 +340,8 @@
           callback: () => {
             this.advancedToggleExpanded = false;
             this.advancedTogglingInProgress_ = false;
-          }
-        }
+          },
+        },
       });
       this.dispatchEvent(event);
     }
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_search_result_row.js b/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_search_result_row.js
index 042b9f38..3b2621a2 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_search_result_row.js
+++ b/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_search_result_row.js
@@ -86,7 +86,7 @@
  */
 const HYPHENS = [
   '-', '~', '֊', '־', '᠆', '‐',  '‑',  '‒',  '–',  '—',  '―', '⁓',
-  '⁻', '₋', '−', '⸺', '⸻', '〜', '〰', '゠', '﹘', '﹣', '-'
+  '⁻', '₋', '−', '⸺', '⸻', '〜', '〰', '゠', '﹘', '﹣', '-',
 ];
 
 /**
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.js b/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.js
index 57d1fcd49..0821019 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.js
+++ b/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.js
@@ -51,10 +51,11 @@
  */
 const OsSettingsUiElementBase = mixinBehaviors(
     [
-      CrContainerShadowBehavior, FindShortcutBehavior,
+      CrContainerShadowBehavior,
+      FindShortcutBehavior,
       // Calls currentRouteChanged() in attached(),so ensure other behaviors
       // run their attached() first.
-      RouteObserverBehavior
+      RouteObserverBehavior,
     ],
     PolymerElement);
 
diff --git a/chrome/browser/resources/settings/chromeos/parental_controls_page/parental_controls_page.js b/chrome/browser/resources/settings/chromeos/parental_controls_page/parental_controls_page.js
index 7dd5387b..b8ae4385 100644
--- a/chrome/browser/resources/settings/chromeos/parental_controls_page/parental_controls_page.js
+++ b/chrome/browser/resources/settings/chromeos/parental_controls_page/parental_controls_page.js
@@ -46,7 +46,7 @@
         type: Boolean,
         value() {
           return loadTimeData.getBoolean('isChild');
-        }
+        },
       },
 
       /** @private */
@@ -54,7 +54,7 @@
         type: Boolean,
         value() {
           return navigator.onLine;
-        }
+        },
       },
     };
   }
diff --git a/chrome/browser/resources/settings/chromeos/personalization_page/change_picture.js b/chrome/browser/resources/settings/chromeos/personalization_page/change_picture.js
index f06c702..0265161 100644
--- a/chrome/browser/resources/settings/chromeos/personalization_page/change_picture.js
+++ b/chrome/browser/resources/settings/chromeos/personalization_page/change_picture.js
@@ -38,8 +38,10 @@
  */
 const SettingsChangePictureElementBase = mixinBehaviors(
     [
-      DeepLinkingBehavior, RouteObserverBehavior, I18nBehavior,
-      WebUIListenerBehavior
+      DeepLinkingBehavior,
+      RouteObserverBehavior,
+      I18nBehavior,
+      WebUIListenerBehavior,
     ],
     PolymerElement);
 
@@ -374,7 +376,7 @@
     const event = new CustomEvent('iron-announce', {
       bubbles: true,
       composed: true,
-      detail: {text: this.i18n('photoDiscardAccessibleText')}
+      detail: {text: this.i18n('photoDiscardAccessibleText')},
     });
     this.dispatchEvent(event);
   }
diff --git a/chrome/browser/resources/settings/chromeos/personalization_page/personalization_page.js b/chrome/browser/resources/settings/chromeos/personalization_page/personalization_page.js
index 4acc3ef..2384e43f 100644
--- a/chrome/browser/resources/settings/chromeos/personalization_page/personalization_page.js
+++ b/chrome/browser/resources/settings/chromeos/personalization_page/personalization_page.js
@@ -91,7 +91,7 @@
           }
 
           return map;
-        }
+        },
       },
 
       /**
diff --git a/chrome/browser/resources/settings/chromeos/pref_to_setting_metric_converter.js b/chrome/browser/resources/settings/chromeos/pref_to_setting_metric_converter.js
index aabf1cf..6c43860 100644
--- a/chrome/browser/resources/settings/chromeos/pref_to_setting_metric_converter.js
+++ b/chrome/browser/resources/settings/chromeos/pref_to_setting_metric_converter.js
@@ -25,14 +25,14 @@
       case 'settings.language.send_function_keys':
         return {
           setting: chromeos.settings.mojom.Setting.kKeyboardFunctionKeys,
-          value: {boolValue: /** @type {boolean} */ (prefValue)}
+          value: {boolValue: /** @type {boolean} */ (prefValue)},
         };
 
       // device_page/pointers.js
       case 'settings.touchpad.sensitivity2':
         return {
           setting: chromeos.settings.mojom.Setting.kTouchpadSpeed,
-          value: {intValue: /** @type {number} */ (prefValue)}
+          value: {intValue: /** @type {number} */ (prefValue)},
         };
 
       // os_privacy_page/os_privacy_page.js
@@ -40,7 +40,7 @@
         return {
           setting:
               chromeos.settings.mojom.Setting.kPeripheralDataAccessProtection,
-          value: {boolValue: /** @type {boolean} */ (prefValue)}
+          value: {boolValue: /** @type {boolean} */ (prefValue)},
         };
 
       // pref to setting metric not implemented.
diff --git a/chrome/browser/resources/settings/route.ts b/chrome/browser/resources/settings/route.ts
index 644c515b3..a2e54078e 100644
--- a/chrome/browser/resources/settings/route.ts
+++ b/chrome/browser/resources/settings/route.ts
@@ -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 {assert} from 'chrome://resources/js/assert_ts.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 
 import {pageVisibility} from './page_visibility.js';
@@ -12,7 +13,8 @@
  * Add all of the child routes that originate from the privacy route,
  * regardless of whether the privacy section under basic or advanced.
  */
-function addPrivacyChildRoutes(r: SettingsRoutes) {
+function addPrivacyChildRoutes(r: Partial<SettingsRoutes>) {
+  assert(r.PRIVACY);
   r.CLEAR_BROWSER_DATA = r.PRIVACY.createChild('/clearBrowserData');
   r.CLEAR_BROWSER_DATA.isNavigableDialog = true;
 
@@ -102,8 +104,8 @@
 /**
  * Adds Route objects for each path.
  */
-function createBrowserSettingsRoutes(): SettingsRoutes {
-  const r = {} as SettingsRoutes;
+function createBrowserSettingsRoutes(): Partial<SettingsRoutes> {
+  const r: Partial<SettingsRoutes> = {};
 
   // Root pages.
   r.BASIC = new Route('/');
@@ -127,6 +129,7 @@
 
   // <if expr="not chromeos_ash">
   if (visibility.people !== false) {
+    assert(r.PEOPLE);
     r.MANAGE_PROFILE = r.PEOPLE.createChild('/manageProfile');
   }
   // </if>
@@ -221,7 +224,11 @@
  * @return A router with the browser settings routes.
  */
 export function buildRouter(): Router {
-  return new Router(createBrowserSettingsRoutes());
+  return new Router(createBrowserSettingsRoutes() as {
+    BASIC: Route,
+    ADVANCED: Route,
+    ABOUT: Route,
+  });
 }
 
 Router.setInstance(buildRouter());
diff --git a/chrome/browser/ssl/secure_origin_policy_handler.cc b/chrome/browser/ssl/secure_origin_policy_handler.cc
index 2ae4f4f8..b4dd6a0 100644
--- a/chrome/browser/ssl/secure_origin_policy_handler.cc
+++ b/chrome/browser/ssl/secure_origin_policy_handler.cc
@@ -20,8 +20,11 @@
     : SchemaValidatingPolicyHandler(policy_name,
                                     schema.GetKnownProperty(policy_name),
                                     SCHEMA_ALLOW_UNKNOWN) {
-  DCHECK(policy_name == key::kUnsafelyTreatInsecureOriginAsSecure ||
-         policy_name == key::kOverrideSecurityRestrictionsOnInsecureOrigin);
+  DCHECK(
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
+      policy_name == key::kUnsafelyTreatInsecureOriginAsSecure ||
+#endif
+      policy_name == key::kOverrideSecurityRestrictionsOnInsecureOrigin);
 }
 
 SecureOriginPolicyHandler::~SecureOriginPolicyHandler() = default;
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessor.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessor.java
index fca8573..025a03d 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessor.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessor.java
@@ -10,6 +10,7 @@
 import androidx.annotation.NonNull;
 import androidx.appcompat.content.res.AppCompatResources;
 
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
 import org.chromium.chrome.browser.omnibox.R;
 import org.chromium.chrome.browser.omnibox.suggestions.FaviconFetcher;
@@ -40,6 +41,7 @@
     private final @NonNull SuggestionHost mSuggestionHost;
     private final @NonNull FaviconFetcher mFaviconFetcher;
     private final int mMinCarouselItemViewHeight;
+    private boolean mShouldWrapTitleText;
 
     /**
      * Constructor.
@@ -79,6 +81,12 @@
     }
 
     @Override
+    public void onNativeInitialized() {
+        mShouldWrapTitleText = ChromeFeatureList.isEnabled(
+                ChromeFeatureList.OMNIBOX_MOST_VISITED_TILES_TITLE_WRAP_AROUND);
+    }
+
+    @Override
     public void populateModel(AutocompleteMatch suggestion, PropertyModel model, int matchIndex) {
         final List<AutocompleteMatch.SuggestTile> tiles = suggestion.getSuggestTiles();
         final int tilesCount = tiles.size();
@@ -93,7 +101,7 @@
             final int itemIndex = elementIndex;
 
             tileModel.set(TileViewProperties.TITLE, title);
-            tileModel.set(TileViewProperties.TITLE_LINES, 1);
+            tileModel.set(TileViewProperties.TITLE_LINES, mShouldWrapTitleText ? 2 : 1);
             tileModel.set(TileViewProperties.ON_FOCUS_VIA_SELECTION,
                     () -> mSuggestionHost.setOmniboxEditingText(url.getSpec()));
             tileModel.set(TileViewProperties.ON_CLICK, v -> {
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessorUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessorUnitTest.java
index 6064264..aac16034 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessorUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessorUnitTest.java
@@ -25,6 +25,7 @@
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.InOrder;
@@ -38,11 +39,15 @@
 import org.chromium.base.metrics.test.ShadowRecordHistogram;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.omnibox.suggestions.FaviconFetcher;
 import org.chromium.chrome.browser.omnibox.suggestions.FaviconFetcher.FaviconFetchCompleteListener;
 import org.chromium.chrome.browser.omnibox.suggestions.SuggestionHost;
 import org.chromium.chrome.browser.omnibox.suggestions.carousel.BaseCarouselSuggestionItemViewBuilder;
 import org.chromium.chrome.browser.omnibox.suggestions.carousel.BaseCarouselSuggestionViewProperties;
+import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
+import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
 import org.chromium.components.browser_ui.widget.tile.TileViewProperties;
 import org.chromium.components.omnibox.AutocompleteMatch;
 import org.chromium.components.omnibox.AutocompleteMatch.SuggestTile;
@@ -68,6 +73,7 @@
     private static final int DESIRED_FAVICON_SIZE_PX = 100;
 
     public @Rule MockitoRule mockitoRule = MockitoJUnit.rule();
+    public @Rule TestRule mFeatures = new Features.JUnitProcessor();
 
     private Activity mActivity;
     private PropertyModel mPropertyModel;
@@ -272,4 +278,30 @@
                         "title", NAV_URL.getHost());
         assertEquals(expectedDescription, tileModel.get(TileViewProperties.CONTENT_DESCRIPTION));
     }
+
+    @DisableFeatures(ChromeFeatureList.OMNIBOX_MOST_VISITED_TILES_TITLE_WRAP_AROUND)
+    @Test
+    public void testDescriptionWrapping_singleLine() {
+        mProcessor.onNativeInitialized();
+        List<ListItem> tileList =
+                populateTilePropertiesForTiles(0, new SuggestTile("title", NAV_URL, false));
+
+        assertEquals(1, tileList.size());
+        ListItem tileItem = tileList.get(0);
+        PropertyModel tileModel = tileItem.model;
+        assertEquals(1, tileModel.get(TileViewProperties.TITLE_LINES));
+    }
+
+    @EnableFeatures(ChromeFeatureList.OMNIBOX_MOST_VISITED_TILES_TITLE_WRAP_AROUND)
+    @Test
+    public void testDescriptionWrapping_wrappingLine() {
+        mProcessor.onNativeInitialized();
+        List<ListItem> tileList =
+                populateTilePropertiesForTiles(0, new SuggestTile("title", NAV_URL, false));
+
+        assertEquals(1, tileList.size());
+        ListItem tileItem = tileList.get(0);
+        PropertyModel tileModel = tileItem.model;
+        assertEquals(2, tileModel.get(TileViewProperties.TITLE_LINES));
+    }
 }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java
index c8ee3b73..334ed75 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java
@@ -686,6 +686,40 @@
         final @BrandedColorScheme int brandedColorScheme =
                 OmniboxResourceProvider.getBrandedColorScheme(mContext, isIncognito(), color);
 
+        // Assign red color to security icon if the page shows security warning.
+        return getSecurityIconColorWithSecurityLevel(
+                getSecurityLevel(), brandedColorScheme, isIncognito());
+    }
+
+    /**
+     * Get the color for the security icon for different security levels.
+     * If we are using dark background (dark mode or incognito mode), we should return light red.
+     * If we are using light background (light mode, but not LIGHT_BRANDED_THEME), we should return
+     * dark red. The default brand color will be returned if no change is needed.
+     *
+     * @param connectionSecurityLevel The connection security level for the current website.
+     * @param brandedColorScheme The branded color scheme for the omnibox.
+     * @param isIncognito Whether the tab is in Incognito mode.
+     * @return The color resource for the security icon, returns -1 if doe snot need to change
+     *         color.
+     */
+    @VisibleForTesting
+    protected @ColorRes int getSecurityIconColorWithSecurityLevel(
+            @ConnectionSecurityLevel int connectionSecurityLevel,
+            @BrandedColorScheme int brandedColorScheme, boolean isIncognito) {
+        // Return regular color scheme if the website does not show warning.
+        if (connectionSecurityLevel == ConnectionSecurityLevel.DANGEROUS) {
+            // Assign red color only on light or dark background including Incognito mode.
+            // We will not change the security icon to red when BrandedColorScheme is
+            // LIGHT_BRANDED_THEME for the purpose of improving contrast.
+            if (isIncognito) {
+                // Use light red for Incognito mode.
+                return R.color.baseline_error_200;
+            } else if (brandedColorScheme == BrandedColorScheme.APP_DEFAULT) {
+                // Use adaptive red for light and dark background.
+                return R.color.default_red;
+            }
+        }
         return ThemeUtils.getThemedToolbarIconTintRes(brandedColorScheme);
     }
 
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainerTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainerTest.java
index 042ba5fc..9d0c379 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainerTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainerTest.java
@@ -48,6 +48,8 @@
     public void testIsDirty() {
         ToolbarViewResourceAdapter adapter =
                 new ToolbarViewResourceAdapter(mToolbarContainer, false);
+        adapter.setOnResourceReadyCallback((resource) -> {});
+
         Assert.assertEquals(0,
                 ShadowRecordHistogram.getHistogramTotalCountForTesting(
                         "Android.TopToolbar.BlockCaptureReason"));
@@ -79,7 +81,7 @@
                         "Android.TopToolbar.AllowCaptureReason",
                         TopToolbarBlockCaptureReason.UNKNOWN));
 
-        adapter.getBitmap();
+        adapter.triggerBitmapCapture();
         Assert.assertFalse(adapter.isDirty());
         Assert.assertEquals(1,
                 ShadowRecordHistogram.getHistogramValueCountForTesting(
@@ -88,19 +90,14 @@
         Assert.assertEquals(0,
                 ShadowRecordHistogram.getHistogramTotalCountForTesting(
                         "Android.TopToolbar.SnapshotDifference"));
-
-        // Need to be careful here. #getBitmap() in debug builds will call isDirty. Reset histogram
-        // tracking to avoid being needing to depend on build type.
-        ShadowRecordHistogram.reset();
-
-        Assert.assertEquals(0,
+        Assert.assertEquals(1,
                 ShadowRecordHistogram.getHistogramValueCountForTesting(
                         "Android.TopToolbar.AllowCaptureReason",
                         TopToolbarBlockCaptureReason.UNKNOWN));
 
         adapter.forceInvalidate();
         Assert.assertTrue(adapter.isDirty());
-        Assert.assertEquals(1,
+        Assert.assertEquals(2,
                 ShadowRecordHistogram.getHistogramValueCountForTesting(
                         "Android.TopToolbar.AllowCaptureReason",
                         TopToolbarBlockCaptureReason.UNKNOWN));
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
index 3cfec56..73c5aa9 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
@@ -1338,8 +1338,6 @@
                     mToolbarButtonsContainer, canvas, rgbAlpha);
         }
 
-        mToolbarSnapshotState = generateToolbarSnapshotState();
-
         canvas.restore();
     }
 
@@ -1793,6 +1791,11 @@
             setVisibility(mPreTextureCaptureVisibility);
             updateShadowVisibility();
             mPreTextureCaptureAlpha = 1f;
+
+            // When texture mode is turned off, we know a capture has just been completed. Update
+            // our snapshot so that we can suppress correctly on the next
+            // #isReadyForTextureCapture() call.
+            mToolbarSnapshotState = generateToolbarSnapshotState();
         }
     }
 
diff --git a/chrome/browser/ui/ash/clipboard_history_browsertest.cc b/chrome/browser/ui/ash/clipboard_history_browsertest.cc
index a9f44d1e..40b6da0 100644
--- a/chrome/browser/ui/ash/clipboard_history_browsertest.cc
+++ b/chrome/browser/ui/ash/clipboard_history_browsertest.cc
@@ -10,11 +10,10 @@
 #include "ash/clipboard/clipboard_history_controller_impl.h"
 #include "ash/clipboard/clipboard_history_item.h"
 #include "ash/clipboard/clipboard_history_menu_model_adapter.h"
-#include "ash/clipboard/views/clipboard_history_delete_button.h"
+#include "ash/clipboard/clipboard_history_metrics.h"
 #include "ash/clipboard/views/clipboard_history_item_view.h"
 #include "ash/constants/ash_features.h"
 #include "ash/public/cpp/clipboard_history_controller.h"
-#include "ash/public/cpp/clipboard_image_model_factory.h"
 #include "ash/shell.h"
 #include "base/bind.h"
 #include "base/path_service.h"
@@ -22,7 +21,6 @@
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece_forward.h"
-#include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/repeating_test_future.h"
 #include "base/test/scoped_feature_list.h"
@@ -34,13 +32,10 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/ash/clipboard_image_model_request.h"
 #include "chrome/browser/ui/browser.h"
-#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 "chromeos/ash/components/dbus/session_manager/session_manager_client.h"
-#include "chromeos/crosapi/mojom/clipboard_history.mojom.h"
 #include "components/user_manager/user_manager.h"
-#include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
@@ -57,7 +52,6 @@
 #include "ui/views/controls/menu/menu_item_view.h"
 #include "ui/views/controls/textfield/textfield.h"
 #include "ui/views/widget/widget.h"
-#include "url/origin.h"
 
 namespace {
 
@@ -991,11 +985,66 @@
         /*clipboard_history_reorder_enabled=*/::testing::Bool(),
         /*paste_plain_text=*/::testing::Bool()));
 
-IN_PROC_BROWSER_TEST_P(ClipboardHistoryReorderBrowserTest, Reorder) {
+IN_PROC_BROWSER_TEST_P(ClipboardHistoryReorderBrowserTest, OnCopy) {
+  // Confirm initial state.
+  const auto& clipboard_history_items = GetClipboardItems();
+  base::HistogramTester histogram_tester;
+  ASSERT_TRUE(clipboard_history_items.empty());
+  histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.ReorderType",
+                                    /*count=*/0);
+
+  const auto* const clipboard = ui::ClipboardNonBacked::GetForCurrentThread();
+  ui::DataTransferEndpoint data_dst(ui::EndpointType::kClipboardHistory);
+
+  // Write some data to the clipboard.
+  {
+    // Start listening for changes to the item list. We must wait for the item
+    // list to update before checking verifying the clipboard history state.
+    ScopedClipboardHistoryListUpdateWaiter scoped_waiter;
+    SetClipboardTextAndHtml("A", "<span>A</span>");
+  }
+  ui::ClipboardData clipboard_data_a(*clipboard->GetClipboardData(&data_dst));
+  ASSERT_EQ(clipboard_history_items.size(), 1);
+  ASSERT_EQ(clipboard_history_items.front().data(), clipboard_data_a);
+  histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.ReorderType",
+                                    /*count=*/0);
+
+  // Write different data to the clipboard.
+  {
+    // Start listening for changes to the item list. We must wait for the item
+    // list to update before checking verifying the clipboard history state.
+    ScopedClipboardHistoryListUpdateWaiter scoped_waiter;
+    SetClipboardTextAndHtml("B", "<span>B</span>");
+  }
+  ui::ClipboardData clipboard_data_b(*clipboard->GetClipboardData(&data_dst));
+  ASSERT_EQ(clipboard_history_items.size(), 2);
+  ASSERT_EQ(clipboard_history_items.front().data(), clipboard_data_b);
+  histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.ReorderType",
+                                    /*count=*/0);
+
+  // Write the original data to the clipboard again. Instead of creating a new
+  // clipboard history item, this should bump the original item to the top slot.
+  {
+    // Start listening for changes to the item list. We must wait for the item
+    // list to update before checking verifying the clipboard history state.
+    ScopedClipboardHistoryListUpdateWaiter scoped_waiter;
+    SetClipboardTextAndHtml("A", "<span>A</span>");
+  }
+  ASSERT_EQ(clipboard_history_items.size(), 2);
+  ASSERT_EQ(clipboard_history_items.front().data(), clipboard_data_a);
+  histogram_tester.ExpectBucketCount(
+      "Ash.ClipboardHistory.ReorderType",
+      /*sample=*/ash::ClipboardHistoryReorderType::kOnCopy,
+      /*expected_count=*/1);
+}
+
+IN_PROC_BROWSER_TEST_P(ClipboardHistoryReorderBrowserTest, OnPaste) {
   // Confirm initial state.
   base::HistogramTester histogram_tester;
   histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.ConsecutivePastes",
                                     /*count=*/0);
+  histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.ReorderType",
+                                    /*count=*/0);
 
   // Write some things to the clipboard. Pasting may result in temporary
   // modification of the clipboard buffer. Cache the clipboard data for each
@@ -1026,6 +1075,8 @@
   // Wait for the clipboard buffer to be restored before performing another
   // paste.
   ClipboardDataWaiter().WaitFor(&clipboard_data_b);
+  histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.ReorderType",
+                                    /*count=*/0);
 
   // Open clipboard history and paste the last history item.
   ShowContextMenuViaAccelerator(/*wait_for_selection=*/true);
@@ -1065,6 +1116,11 @@
     // item of the clipboard history item list.
     ClipboardDataWaiter().WaitFor(expected_clipboard_data);
   }
+  histogram_tester.ExpectBucketCount(
+      "Ash.ClipboardHistory.ReorderType",
+      /*sample=*/ash::ClipboardHistoryReorderType::kOnPaste,
+      /*expected_count=*/ClipboardHistoryReorderEnabled() ? 1 : 0);
+
   const auto& clipboard_history_items = GetClipboardItems();
   ASSERT_EQ(clipboard_history_items.size(), 2);
   ASSERT_EQ(clipboard_history_items.front().data(), *expected_clipboard_data);
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.cc b/chrome/browser/ui/autofill/chrome_autofill_client.cc
index 7f5ce7b4..eddf691 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.cc
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.cc
@@ -993,8 +993,7 @@
 #endif
 }
 
-void ChromeAutofillClient::OnPromoCodeSuggestionsFooterSelected(
-    const GURL& url) {
+void ChromeAutofillClient::OpenPromoCodeOfferDetailsURL(const GURL& url) {
   web_contents()->OpenURL(content::OpenURLParams(
       url, content::Referrer(), WindowOpenDisposition::NEW_FOREGROUND_TAB,
       ui::PageTransition::PAGE_TRANSITION_AUTO_TOPLEVEL,
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.h b/chrome/browser/ui/autofill/chrome_autofill_client.h
index 1fce98d8..87fc459 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.h
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.h
@@ -207,7 +207,7 @@
   bool ShouldShowSigninPromo() override;
   bool AreServerCardsSupported() const override;
   void ExecuteCommand(int id) override;
-  void OnPromoCodeSuggestionsFooterSelected(const GURL& url) override;
+  void OpenPromoCodeOfferDetailsURL(const GURL& url) override;
   LogManager* GetLogManager() const override;
 
   // RiskDataLoader:
diff --git a/chrome/browser/ui/managed_ui.cc b/chrome/browser/ui/managed_ui.cc
index cea7a17..63b40b1 100644
--- a/chrome/browser/ui/managed_ui.cc
+++ b/chrome/browser/ui/managed_ui.cc
@@ -15,9 +15,11 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/management/management_ui_handler.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/policy/core/browser/webui/policy_data_utils.h"
 #include "components/policy/core/common/cloud/machine_level_user_cloud_policy_manager.h"
 #include "components/policy/core/common/management/management_service.h"
 #include "components/policy/proto/device_management_backend.pb.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/base/l10n/l10n_util.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -39,17 +41,6 @@
 
 namespace {
 
-std::string GetManagedBy(const policy::CloudPolicyManager* manager) {
-  if (manager) {
-    const enterprise_management::PolicyData* policy =
-        manager->core()->store()->policy();
-    if (policy && policy->has_managed_by()) {
-      return policy->managed_by();
-    }
-  }
-  return std::string();
-}
-
 const policy::CloudPolicyManager* GetUserCloudPolicyManager(Profile* profile) {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   return profile->GetUserCloudPolicyManagerAsh();
@@ -144,8 +135,8 @@
              ? connector->GetRealm()
              : connector->GetEnterpriseDomainManager();
 #else
-  return GetManagedBy(g_browser_process->browser_policy_connector()
-                          ->machine_level_user_cloud_policy_manager());
+  return policy::GetManagedBy(g_browser_process->browser_policy_connector()
+                                  ->machine_level_user_cloud_policy_manager());
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 }
 
@@ -161,10 +152,10 @@
   if (!policy::ManagementServiceFactory::GetForProfile(profile)->IsManaged())
     return absl::nullopt;
 
-  const std::string managed_by =
-      GetManagedBy(GetUserCloudPolicyManager(profile));
-  if (!managed_by.empty())
-    return managed_by;
+  const absl::optional<std::string> managed_by =
+      policy::GetManagedBy(GetUserCloudPolicyManager(profile));
+  if (managed_by)
+    return *managed_by;
 
   return GetEnterpriseAccountDomain(profile);
 }
diff --git a/chrome/browser/ui/media_router/media_router_ui_unittest.cc b/chrome/browser/ui/media_router/media_router_ui_unittest.cc
index b38ed9db..d41d1436 100644
--- a/chrome/browser/ui/media_router/media_router_ui_unittest.cc
+++ b/chrome/browser/ui/media_router/media_router_ui_unittest.cc
@@ -444,6 +444,7 @@
   NotifyUiOnRoutesUpdated({});
 }
 
+#if !BUILDFLAG(IS_ANDROID)
 TEST_F(MediaRouterViewsUITest, AddAndRemoveIssue) {
   MediaSink sink1{CreateCastSink("sink_id1", "Sink 1")};
   MediaSink sink2{CreateCastSink("sink_id2", "Sink 2")};
@@ -481,6 +482,7 @@
       })));
   mock_router_->GetIssueManager()->ClearIssue(issue_id);
 }
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 TEST_F(MediaRouterViewsUITest, RouteCreationTimeoutForTab) {
   StartCastingAndExpectTimeout(
diff --git a/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view.cc b/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view.cc
index f300262b..b5a66b0 100644
--- a/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view.cc
+++ b/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view.cc
@@ -366,6 +366,7 @@
 
   MaybeCreateAndInsertSiteAccessItem(action_id);
   UpdateSiteAccessTab();
+  SizeToContents();
 
   ConsistencyCheck();
 }
@@ -385,6 +386,7 @@
               GetSiteAccessMenuItem(has_access_.items, action_id));
 
   UpdateSiteAccessTab();
+  SizeToContents();
 
   ConsistencyCheck();
 }
@@ -395,6 +397,7 @@
   UpdateSiteAccessMenuItems({action_id});
 
   UpdateSiteAccessTab();
+  SizeToContents();
 
   ConsistencyCheck();
 }
@@ -404,6 +407,7 @@
   DCHECK(requests_access_.items->children().empty());
   DCHECK(has_access_.items->children().empty());
   Populate();
+  SizeToContents();
 }
 
 void ExtensionsTabbedMenuView::OnToolbarPinnedActionsChanged() {
@@ -459,6 +463,7 @@
   UpdateSiteAccessMenuItems(action_ids);
 
   UpdateSiteAccessTab();
+  SizeToContents();
 
   ConsistencyCheck();
 }
@@ -764,12 +769,6 @@
         break;
     }
   }
-
-  // Site access tab updates can happen during the menu construction, and
-  // afterwards. The dialog bubble is created after constructing the menu,
-  // therefore we can only resize the contents when the dialog exists.
-  if (g_extensions_dialog)
-    SizeToContents();
 }
 
 void ExtensionsTabbedMenuView::UpdateSiteAccessSectionsVisibility(
diff --git a/chrome/browser/ui/views/side_panel/user_note/user_note_ui_coordinator_unittest.cc b/chrome/browser/ui/views/side_panel/user_note/user_note_ui_coordinator_unittest.cc
index ff32334..c7db8f8 100644
--- a/chrome/browser/ui/views/side_panel/user_note/user_note_ui_coordinator_unittest.cc
+++ b/chrome/browser/ui/views/side_panel/user_note/user_note_ui_coordinator_unittest.cc
@@ -29,6 +29,8 @@
 #include "ui/views/interaction/element_tracker_views.h"
 #include "ui/views/view_utils.h"
 
+using user_notes::UserNoteInstance;
+
 namespace {
 
 // Mock the note storage to prevent side effects.
@@ -133,8 +135,14 @@
     auto note = CreateUserNote(note_ids_[index]);
     auto safe_ref = note->GetSafeRef();
     service_->model_map_.emplace(note_ids_[index], std::move(note));
-    manager->AddNoteInstance(std::make_unique<user_notes::UserNoteInstance>(
-        safe_ref, manager, note_rects_[index]));
+
+    std::unique_ptr<UserNoteInstance> note_instance =
+        UserNoteInstance::Create(safe_ref, manager);
+    auto* instance_raw = note_instance.get();
+
+    manager->AddNoteInstance(std::move(note_instance));
+
+    instance_raw->DidFinishAttachment(note_rects_[index]);
   }
 
   views::ScrollView* GetUserNoteScrollView() {
diff --git a/chrome/browser/ui/views/webid/account_selection_bubble_view.cc b/chrome/browser/ui/views/webid/account_selection_bubble_view.cc
index 839c896..d69bfa49 100644
--- a/chrome/browser/ui/views/webid/account_selection_bubble_view.cc
+++ b/chrome/browser/ui/views/webid/account_selection_bubble_view.cc
@@ -37,6 +37,7 @@
 #include "ui/views/controls/highlight_path_generator.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/label.h"
+#include "ui/views/controls/scroll_view.h"
 #include "ui/views/controls/separator.h"
 #include "ui/views/controls/styled_label.h"
 #include "ui/views/layout/box_layout.h"
@@ -72,6 +73,10 @@
 // The size of the space between the top boundary of the WebContents and the top
 // boundary of the bubble.
 constexpr int kTopMargin = 16;
+// The maximum height that the multi-account-picker can have. This value was
+// chosen so that if there are more than two accounts, the picker will show up
+// as a scrollbar.
+constexpr int kMaxScrollViewHeight = 150;
 
 constexpr char kImageFetcherUmaClient[] = "FedCMAccountChooser";
 
@@ -550,13 +555,17 @@
 std::unique_ptr<views::View>
 AccountSelectionBubbleView::CreateMultipleAccountChooser(
     base::span<const content::IdentityRequestAccount> accounts) {
-  auto row = std::make_unique<views::View>();
+  auto scroll_view = std::make_unique<views::ScrollView>();
+  scroll_view->SetHorizontalScrollBarMode(
+      views::ScrollView::ScrollBarMode::kDisabled);
+  scroll_view->ClipHeightTo(0, kMaxScrollViewHeight);
+  views::View* row = scroll_view->SetContents(std::make_unique<views::View>());
   row->SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::Orientation::kVertical));
   for (const auto& account : accounts) {
     row->AddChildView(CreateAccountRow(account, /*should_hover=*/true));
   }
-  return row;
+  return scroll_view;
 }
 
 std::unique_ptr<views::View> AccountSelectionBubbleView::CreateAccountRow(
diff --git a/chrome/browser/ui/views/webid/account_selection_bubble_view_unittest.cc b/chrome/browser/ui/views/webid/account_selection_bubble_view_unittest.cc
index 16f10838..e6376956 100644
--- a/chrome/browser/ui/views/webid/account_selection_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/webid/account_selection_bubble_view_unittest.cc
@@ -29,6 +29,7 @@
 #include "ui/views/controls/button/md_text_button.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/label.h"
+#include "ui/views/controls/scroll_view.h"
 #include "ui/views/controls/separator.h"
 #include "ui/views/controls/styled_label.h"
 #include "ui/views/layout/box_layout.h"
@@ -199,7 +200,12 @@
 
     PerformHeaderChecks(children[0], kTitleSignIn);
 
-    views::View* multiple_account_chooser = children[2];
+    views::ScrollView* scroller = static_cast<views::ScrollView*>(children[2]);
+    ASSERT_FALSE(scroller->children().empty());
+    views::View* wrapper = scroller->children()[0];
+    ASSERT_FALSE(wrapper->children().empty());
+    views::View* multiple_account_chooser = wrapper->children()[0];
+
     std::vector<views::View*> accounts = multiple_account_chooser->children();
     ASSERT_EQ(accounts.size(), num_expected_accounts);
     for (views::View* account : accounts)
@@ -328,13 +334,18 @@
   ASSERT_EQ(children.size(), 3u);
   PerformHeaderChecks(children[0], kTitleSignIn);
 
-  views::View* multiple_account_chooser = children[2];
-  views::BoxLayout* layout_manager = static_cast<views::BoxLayout*>(
-      multiple_account_chooser->GetLayoutManager());
+  views::ScrollView* scroller = static_cast<views::ScrollView*>(children[2]);
+  ASSERT_FALSE(scroller->children().empty());
+  views::View* wrapper = scroller->children()[0];
+  ASSERT_FALSE(wrapper->children().empty());
+  views::View* contents = wrapper->children()[0];
+
+  views::BoxLayout* layout_manager =
+      static_cast<views::BoxLayout*>(contents->GetLayoutManager());
   EXPECT_TRUE(layout_manager);
   EXPECT_EQ(layout_manager->GetOrientation(),
             views::BoxLayout::Orientation::kVertical);
-  std::vector<views::View*> accounts = multiple_account_chooser->children();
+  std::vector<views::View*> accounts = contents->children();
   ASSERT_EQ(accounts.size(), 3u);
 
   // Check the text shown.
diff --git a/chrome/browser/ui/webui/policy/policy_ui_browsertest.cc b/chrome/browser/ui/webui/policy/policy_ui_browsertest.cc
index 05f0b7cc..88f8d575 100644
--- a/chrome/browser/ui/webui/policy/policy_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/policy/policy_ui_browsertest.cc
@@ -497,12 +497,13 @@
   // contents).
   VerifyExportingPolicies(expected_values);
 
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
   // This also checks that we do not bypass the policy that blocks file
   // selection dialogs. This is a desktop only policy.
   values.Set(policy::key::kAllowFileSelectionDialogs,
              policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_MACHINE,
              policy::POLICY_SOURCE_PLATFORM, base::Value(false), nullptr);
+
   popups_blocked_for_urls.Append("eeeeee");
   values.Set(policy::key::kPopupsBlockedForUrls, policy::POLICY_LEVEL_MANDATORY,
              policy::POLICY_SCOPE_MACHINE, policy::POLICY_SOURCE_PLATFORM,
diff --git a/chrome/browser/ui/webui/settings/chromeos/search_section.cc b/chrome/browser/ui/webui/settings/chromeos/search_section.cc
index 10675913..eb84ed0c 100644
--- a/chrome/browser/ui/webui/settings/chromeos/search_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/search_section.cc
@@ -41,16 +41,27 @@
 }
 
 const std::vector<SearchConcept>& GetSearchPageSearchConcepts() {
-  static const base::NoDestructor<std::vector<SearchConcept>> tags({
-      {IDS_OS_SETTINGS_TAG_PREFERRED_SEARCH_ENGINE,
-       ShouldShowQuickAnswersSettings() ? mojom::kSearchSubpagePath
-                                        : mojom::kSearchAndAssistantSectionPath,
-       mojom::SearchResultIcon::kMagnifyingGlass,
-       mojom::SearchResultDefaultRank::kMedium,
-       mojom::SearchResultType::kSetting,
-       {.setting = mojom::Setting::kPreferredSearchEngine}},
-  });
-  return *tags;
+  if (ShouldShowQuickAnswersSettings()) {
+    static const base::NoDestructor<std::vector<SearchConcept>> tags({
+        {IDS_OS_SETTINGS_TAG_PREFERRED_SEARCH_ENGINE,
+         mojom::kSearchSubpagePath,
+         mojom::SearchResultIcon::kMagnifyingGlass,
+         mojom::SearchResultDefaultRank::kMedium,
+         mojom::SearchResultType::kSetting,
+         {.setting = mojom::Setting::kPreferredSearchEngine}},
+    });
+    return *tags;
+  } else {
+    static const base::NoDestructor<std::vector<SearchConcept>> tags({
+        {IDS_OS_SETTINGS_TAG_PREFERRED_SEARCH_ENGINE,
+         mojom::kSearchAndAssistantSectionPath,
+         mojom::SearchResultIcon::kMagnifyingGlass,
+         mojom::SearchResultDefaultRank::kMedium,
+         mojom::SearchResultType::kSetting,
+         {.setting = mojom::Setting::kPreferredSearchEngine}},
+    });
+    return *tags;
+  }
 }
 
 const std::vector<SearchConcept>& GetQuickAnswersSearchConcepts() {
@@ -242,25 +253,6 @@
   html_source->AddBoolean("voiceMatchDisabled", !IsVoiceMatchAllowed());
 }
 
-const std::vector<mojom::Setting>& GetSearchSettings() {
-  static const base::NoDestructor<std::vector<mojom::Setting>> settings([] {
-    std::vector<mojom::Setting> base_settings{
-        mojom::Setting::kQuickAnswersOnOff,
-        mojom::Setting::kQuickAnswersDefinition,
-        mojom::Setting::kQuickAnswersTranslation,
-        mojom::Setting::kQuickAnswersUnitConversion,
-    };
-
-    if (ShouldShowQuickAnswersSettings()) {
-      base_settings.insert(base_settings.end(),
-                           mojom::Setting::kPreferredSearchEngine);
-    }
-
-    return base_settings;
-  }());
-  return *settings;
-}
-
 }  // namespace
 
 SearchSection::SearchSection(Profile* profile,
@@ -354,6 +346,8 @@
 }
 
 void SearchSection::RegisterHierarchy(HierarchyGenerator* generator) const {
+  // Register Preferred search engine as top level settings if Quick answers is
+  // not available.
   if (!ShouldShowQuickAnswersSettings())
     generator->RegisterTopLevelSetting(mojom::Setting::kPreferredSearchEngine);
 
@@ -362,8 +356,32 @@
       IDS_SETTINGS_SEARCH_SUBPAGE_TITLE, mojom::Subpage::kSearch,
       mojom::SearchResultIcon::kMagnifyingGlass,
       mojom::SearchResultDefaultRank::kMedium, mojom::kSearchSubpagePath);
-  RegisterNestedSettingBulk(mojom::Subpage::kSearch, GetSearchSettings(),
-                            generator);
+  // Register Preferred search engine under Search subpage if Quick answers is
+  // available.
+  if (ShouldShowQuickAnswersSettings()) {
+    static constexpr mojom::Setting kSearchSettingsWithPreferredSearchEngine[] =
+        {
+            mojom::Setting::kQuickAnswersOnOff,
+            mojom::Setting::kQuickAnswersDefinition,
+            mojom::Setting::kQuickAnswersTranslation,
+            mojom::Setting::kQuickAnswersUnitConversion,
+            mojom::Setting::kPreferredSearchEngine,
+        };
+    RegisterNestedSettingBulk(mojom::Subpage::kSearch,
+                              kSearchSettingsWithPreferredSearchEngine,
+                              generator);
+  } else {
+    static constexpr mojom::Setting
+        kSearchSettingsWithoutPreferredSearchEngine[] = {
+            mojom::Setting::kQuickAnswersOnOff,
+            mojom::Setting::kQuickAnswersDefinition,
+            mojom::Setting::kQuickAnswersTranslation,
+            mojom::Setting::kQuickAnswersUnitConversion,
+        };
+    RegisterNestedSettingBulk(mojom::Subpage::kSearch,
+                              kSearchSettingsWithoutPreferredSearchEngine,
+                              generator);
+  }
 
   // Assistant.
   generator->RegisterTopLevelSubpage(
diff --git a/chrome/browser/ui/webui/settings/search_engines_handler.cc b/chrome/browser/ui/webui/settings/search_engines_handler.cc
index c0c4421..02aa1fd8 100644
--- a/chrome/browser/ui/webui/settings/search_engines_handler.cc
+++ b/chrome/browser/ui/webui/settings/search_engines_handler.cc
@@ -299,17 +299,16 @@
   CHECK_EQ(1U, args.size());
   int index = args[0].GetInt();
 
-  // Allow -1, which means we are adding a new engine.
-  if (index < kNewSearchEngineIndex ||
-      static_cast<size_t>(index) >=
-          list_controller_.table_model()->RowCount()) {
+  TemplateURL* engine = nullptr;
+  if (index >= 0 && static_cast<size_t>(index) >=
+                        list_controller_.table_model()->RowCount()) {
+    engine = list_controller_.GetTemplateURL(index);
+  } else if (index != kNewSearchEngineIndex) {
     return;
   }
 
   edit_controller_ = std::make_unique<EditSearchEngineController>(
-      index == kNewSearchEngineIndex ? nullptr
-                                     : list_controller_.GetTemplateURL(index),
-      this, Profile::FromWebUI(web_ui()));
+      engine, this, Profile::FromWebUI(web_ui()));
 }
 
 void SearchEnginesHandler::OnEditedKeyword(TemplateURL* template_url,
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 613b5fd..199fde0 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1657713163-aa3d57577b5eeb5ddf572849893ea7aaf8cdd026.profdata
+chrome-linux-main-1657735157-5ef5c3c4b64001ef53f3255e8063180c07c0ee00.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index c00643d..89d0af7a 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1657713163-5e0885118a90021ce3f66c756e589307abbe5e36.profdata
+chrome-mac-main-1657735157-24149420d5e13aaa54f49289fcde70bc41e91f52.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 56107dec..3e7d33e 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1657713163-c94debed123988015ff59644030156f01250f329.profdata
+chrome-win32-main-1657735157-a82a59157d2d333403f87494de82054a276a4fda.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 0423e31..26b8929c 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1657713163-00d3f38380d538700042cb1ee28587b5a2e62f7e.profdata
+chrome-win64-main-1657735157-2777a9eacae64067aeacca9c778b8215d4d8799f.profdata
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index e2071a72..20b82925 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -282,6 +282,8 @@
       "extensions/api/notifications/notification_style.h",
       "extensions/api/omnibox/omnibox_handler.cc",
       "extensions/api/omnibox/omnibox_handler.h",
+      "extensions/api/side_panel/side_panel_info.cc",
+      "extensions/api/side_panel/side_panel_info.h",
       "extensions/api/speech/tts_engine_manifest_handler.cc",
       "extensions/api/speech/tts_engine_manifest_handler.h",
       "extensions/api/storage/storage_schema_manifest_handler.cc",
diff --git a/chrome/common/extensions/api/_api_features.json b/chrome/common/extensions/api/_api_features.json
index d8c0b1c..fa0b9eb 100644
--- a/chrome/common/extensions/api/_api_features.json
+++ b/chrome/common/extensions/api/_api_features.json
@@ -404,6 +404,15 @@
     "dependencies": ["permission:enterprise.reportingPrivate"],
     "contexts": ["blessed_extension"]
   },
+  "enterprise.reportingPrivate.getFileSystemInfo": {
+    "platforms": ["win", "mac", "linux"]
+  },
+  "enterprise.reportingPrivate.getAvInfo": {
+    "platforms": ["win"]
+  },
+  "enterprise.reportingPrivate.getHotfixes": {
+    "platforms": ["win"]
+  },
   "experimental.devtools.audits": {
     "nocompile": true,
     "dependencies": ["permission:experimental", "manifest:devtools_page"],
@@ -764,6 +773,10 @@
       "chrome://settings/*"
     ]
   }],
+  "sidePanel": {
+    "dependencies": ["permission:sidePanel"],
+    "contexts": ["blessed_extension"]
+  },
   "speechRecognitionPrivate": {
     "dependencies": ["permission:speechRecognitionPrivate"],
     "contexts": ["blessed_extension"]
diff --git a/chrome/common/extensions/api/_manifest_features.json b/chrome/common/extensions/api/_manifest_features.json
index 0791093d..ca2de7ce 100644
--- a/chrome/common/extensions/api/_manifest_features.json
+++ b/chrome/common/extensions/api/_manifest_features.json
@@ -210,6 +210,11 @@
     "channel": "stable",
     "extension_types": "all"
   },
+  "side_panel": {
+    "channel": "canary",
+    "extension_types": ["extension"],
+    "min_manifest_version": 3
+  },
   "storage": {
     "channel": "stable",
     "extension_types": [
diff --git a/chrome/common/extensions/api/_permission_features.json b/chrome/common/extensions/api/_permission_features.json
index fd1ae714..a2c0e75d 100644
--- a/chrome/common/extensions/api/_permission_features.json
+++ b/chrome/common/extensions/api/_permission_features.json
@@ -551,7 +551,7 @@
   }, {
     "channel": "stable",
     "extension_types": ["platform_app"],
-    "platforms": ["chromeos"],
+    "platforms": ["chromeos", "lacros"],
     "allowlist": [
       "EC3DE21E048B67319893889529354DFBFA96FD23"  // Smart Card Connector
     ]
@@ -829,6 +829,11 @@
       "A9A9FC0228ADF541F0334F22BEFB8F9C245B21D7"   // https://crbug.com/839189
     ]
   }],
+  "sidePanel": {
+    "channel": "canary",
+    "extension_types": ["extension"],
+    "min_manifest_version": 3
+  },
   "speechRecognitionPrivate": {
     "channel": "stable",
     "extension_types": ["extension"],
diff --git a/chrome/common/extensions/api/api_sources.gni b/chrome/common/extensions/api/api_sources.gni
index 9e96693..3511ad13 100644
--- a/chrome/common/extensions/api/api_sources.gni
+++ b/chrome/common/extensions/api/api_sources.gni
@@ -50,6 +50,7 @@
   "search.idl",
   "sessions.json",
   "settings_private.idl",
+  "side_panel.idl",
   "system_private.json",
   "tab_capture.idl",
   "tab_groups.json",
diff --git a/chrome/common/extensions/api/enterprise_reporting_private.idl b/chrome/common/extensions/api/enterprise_reporting_private.idl
index 4a4fc4c..f00b2ebf 100644
--- a/chrome/common/extensions/api/enterprise_reporting_private.idl
+++ b/chrome/common/extensions/api/enterprise_reporting_private.idl
@@ -162,6 +162,82 @@
   [platforms = ("win")]
   callback HotfixesCallback = void(HotfixSignal[] hotfixSignals);
 
+  // Used to indicate whether a given signal was correctly found or not, or
+  // indicate a reason for not being able to find it.
+  [platforms = ("win", "mac", "linux")]
+  enum PresenceValue {
+    // Was unable to determine whether the signal source exists or not. This
+    // typically indicates that a failure occurred before even trying to assess
+    // its presence.
+    UNSPECIFIED,
+
+    // Current user does not have access to the signal's source.
+    ACCESS_DENIED,
+
+    // The resource was not found.
+    NOT_FOUND,
+
+    // The resource was found.
+    FOUND
+  };
+
+  // Parameter used to collect information about a specific file system
+  // resource.
+  [platforms = ("win", "mac", "linux")]
+  dictionary GetFileSystemInfoOptions {
+    DOMString path;
+    boolean computeSha256;
+    boolean computeIsExecutable;
+  };
+
+  [platforms = ("win", "mac", "linux")]
+  dictionary GetFileSystemInfoRequest {
+    // Information about the for whom the signal collection request is for.
+    UserContext userContext;
+
+    // Collection of parameters used to conduct signals collection about
+    // specific file system resources.
+    GetFileSystemInfoOptions[] options;
+  };
+
+  [platforms = ("win", "mac", "linux")]
+  dictionary GetFileSystemInfoResponse {
+    // Path to the file system object for whom those signals were collected.
+    DOMString path;
+
+    // Value indicating whether the specific resource could be found or not.
+    PresenceValue presence;
+
+    // Sha256 hash of a file's bytes. Ignored when path points to a
+    // directory. Collected only when computeSha256 is set to true in the
+    // given signals collection parameters.
+    DOMString? sha256Hash;
+
+    // Set of properties only relevant for executable files. Will only be
+    // collected if computeIsExecutable is set to true in the given signals
+    // collection parameters and if path points to an executable file.
+
+    // Represents whether path points to an executable file or not.
+    boolean? isExecutable;
+
+    // Is true if a currently running process was spawned from this file.
+    boolean? isRunning;
+
+    // Sha256 hash of the public key of the certificate used to sign the
+    // executable.
+    DOMString? publicKeySha256;
+
+    // Product name of this executable.
+    DOMString? productName;
+
+    // Version of this executable.
+    DOMString? version;
+  };
+
+  [platforms = ("win", "mac", "linux")]
+  callback FileSystemInfoCallback =
+      void(GetFileSystemInfoResponse[] fileSystemSignals);
+
   interface Functions {
     // Gets the identity of device that Chrome browser is running on. The ID is
     // retrieved from the local device and used by the Google admin console.
@@ -214,6 +290,17 @@
         EnqueueRecordRequest request,
         optional DoneCallback callback);
 
+    // Gets information about file system resources, specified by the contents
+    // of <code>request</code>, on the current device. <code>request</code> must
+    // hold a user context to be used to verify the affiliation between the
+    // user's organization and the organization managing the browser. If the
+    // management or affiliation states are not suitable, no results will be
+    // returned.
+    [platforms = ("win", "mac", "linux"), supportsPromises]
+    static void getFileSystemInfo(
+        GetFileSystemInfoRequest request,
+        FileSystemInfoCallback callback);
+
     // Gets information about AntiVirus software installed on the current
     // device. <code>userContext</code> is used to verify the affiliation
     // between the user's organization and the organization managing the
diff --git a/chrome/common/extensions/api/side_panel.idl b/chrome/common/extensions/api/side_panel.idl
new file mode 100644
index 0000000..dfdc8e8
--- /dev/null
+++ b/chrome/common/extensions/api/side_panel.idl
@@ -0,0 +1,54 @@
+// 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.
+
+// chrome.sidePanel API
+namespace sidePanel {
+  dictionary SidePanel {
+    // Developer specified path for side panel display.
+    DOMString default_path;
+  };
+
+  dictionary ManifestKeys {
+    SidePanel side_panel;
+  };
+
+  // The options used when setting a side panel. Omitted properties are
+  // unchanged.
+  dictionary PanelOptions {
+    // If specified, the side panel options will only apply to the tab with
+    // this id. If omitted, these options set the default behavior (used for any
+    // tab that doesn't have specific settings).
+    long? tabId;
+    // The path to the side panel HTML file to use. This must be a local
+    // resource within the extension package.
+    DOMString? path;
+    // Whether the side panel should be enabled.
+    boolean? enabled;
+  };
+
+  dictionary GetPanelOptions {
+    // If specified, the side panel options for the given tab will be returned.
+    // Otherwise, returns the default side panel options (used for any tab that
+    // doesn't have specific settings).
+    long? tabId;
+  };
+
+  callback VoidCallback = void();
+  callback PanelOptionsCallback = void(PanelOptions options);
+
+  interface Functions {
+    // Configures the side panel.
+    // |options|: The configuration options to apply to the panel.
+    // |callback|: Invoked when the options have been set.
+    [supportsPromises] static void setOptions(PanelOptions options,
+                                              optional VoidCallback callback);
+
+    // Returns the active panel configuration.
+    // |options|: Specifies the context to return the configuration for.
+    // |callback|: Called with the active panel configuration.
+    [supportsPromises] static void getOptions(
+      GetPanelOptions options,
+      PanelOptionsCallback callback);
+  };
+};
diff --git a/chrome/common/extensions/api/side_panel/side_panel_info.cc b/chrome/common/extensions/api/side_panel/side_panel_info.cc
new file mode 100644
index 0000000..6f99971b
--- /dev/null
+++ b/chrome/common/extensions/api/side_panel/side_panel_info.cc
@@ -0,0 +1,94 @@
+// 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/common/extensions/api/side_panel/side_panel_info.h"
+
+#include "base/files/file_util.h"
+#include "chrome/common/extensions/api/side_panel.h"
+#include "extensions/common/manifest.h"
+#include "extensions/common/manifest_constants.h"
+
+namespace extensions {
+
+namespace keys = manifest_keys;
+namespace errors = manifest_errors;
+
+using SidePanelManifestKeys = api::side_panel::ManifestKeys;
+
+namespace {
+
+const SidePanelInfo* GetResourcesInfo(const Extension* extension) {
+  return static_cast<SidePanelInfo*>(
+      extension->GetManifestData(SidePanelManifestKeys::kSidePanel));
+}
+
+std::unique_ptr<SidePanelInfo> ParseFromDictionary(const Extension& extension,
+                                                   std::u16string* error) {
+  SidePanelManifestKeys manifest_keys;
+  if (!SidePanelManifestKeys::ParseFromDictionary(
+          extension.manifest()->available_values(), &manifest_keys, error)) {
+    return nullptr;
+  }
+  auto info = std::make_unique<SidePanelInfo>();
+  info->default_path = std::move(manifest_keys.side_panel.default_path);
+  return info;
+}
+
+bool ExtensionResourceExists(const Extension* extension,
+                             const std::string& path) {
+  auto resource_path = extension->GetResource(path).GetFilePath();
+  return !resource_path.empty() && base::PathExists(resource_path);
+}
+
+}  // namespace
+
+SidePanelInfo::SidePanelInfo() = default;
+
+SidePanelInfo::~SidePanelInfo() = default;
+
+// static
+bool SidePanelInfo::HasSidePanel(const Extension* extension) {
+  const SidePanelInfo* info = GetResourcesInfo(extension);
+  return info != nullptr;
+}
+
+// static
+std::string SidePanelInfo::GetDefaultPath(const Extension* extension) {
+  const SidePanelInfo* info = GetResourcesInfo(extension);
+  return info ? info->default_path : "";
+}
+
+SidePanelManifestHandler::SidePanelManifestHandler() = default;
+
+SidePanelManifestHandler::~SidePanelManifestHandler() = default;
+
+bool SidePanelManifestHandler::Parse(Extension* extension,
+                                     std::u16string* error) {
+  auto info = ParseFromDictionary(*extension, error);
+  if (!info) {
+    return false;
+  }
+  extension->SetManifestData(SidePanelManifestKeys::kSidePanel,
+                             std::move(info));
+  return true;
+}
+
+base::span<const char* const> SidePanelManifestHandler::Keys() const {
+  static constexpr const char* kKeys[] = {SidePanelManifestKeys::kSidePanel};
+  return kKeys;
+}
+
+bool SidePanelManifestHandler::Validate(
+    const Extension* extension,
+    std::string* error,
+    std::vector<InstallWarning>* warnings) const {
+  std::string path = SidePanelInfo::GetDefaultPath(extension);
+  if (!ExtensionResourceExists(extension, path)) {
+    *error = errors::kSidePanelManifestDefaultPathError;
+    return false;
+  }
+  return true;
+}
+
+}  // namespace extensions
diff --git a/chrome/common/extensions/api/side_panel/side_panel_info.h b/chrome/common/extensions/api/side_panel/side_panel_info.h
new file mode 100644
index 0000000..7d7f07d
--- /dev/null
+++ b/chrome/common/extensions/api/side_panel/side_panel_info.h
@@ -0,0 +1,52 @@
+// 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_COMMON_EXTENSIONS_API_SIDE_PANEL_SIDE_PANEL_INFO_H_
+#define CHROME_COMMON_EXTENSIONS_API_SIDE_PANEL_SIDE_PANEL_INFO_H_
+
+#include <string>
+#include <vector>
+
+#include "extensions/common/extension.h"
+#include "extensions/common/manifest_handler.h"
+
+namespace extensions {
+
+// Structured contents of the "side_panel" key.
+struct SidePanelInfo : public Extension::ManifestData {
+  SidePanelInfo();
+  ~SidePanelInfo() override;
+
+  // Returns true when 'side_panel' is defined for the extension.
+  static bool HasSidePanel(const Extension* extension);
+
+  // Get default_path.
+  static std::string GetDefaultPath(const Extension* extension);
+
+  // SidePanelService relies on this local extension path if it's not set using
+  // the companion API. SidePanelService source of truth.
+  std::string default_path;
+};
+
+// Parses the "side_panel" manifest key.
+class SidePanelManifestHandler : public ManifestHandler {
+ public:
+  SidePanelManifestHandler();
+  SidePanelManifestHandler(const SidePanelManifestHandler&) = delete;
+  SidePanelManifestHandler& operator=(const SidePanelManifestHandler&) = delete;
+  ~SidePanelManifestHandler() override;
+
+  bool Parse(Extension* extension, std::u16string* error) override;
+
+  bool Validate(const Extension* extension,
+                std::string* error,
+                std::vector<InstallWarning>* warnings) const override;
+
+ private:
+  base::span<const char* const> Keys() const override;
+};
+
+}  // namespace extensions
+
+#endif  // CHROME_COMMON_EXTENSIONS_API_SIDE_PANEL_SIDE_PANEL_INFO_H_
diff --git a/chrome/common/extensions/chrome_manifest_handlers.cc b/chrome/common/extensions/chrome_manifest_handlers.cc
index 49c78e9..7a6bc26 100644
--- a/chrome/common/extensions/chrome_manifest_handlers.cc
+++ b/chrome/common/extensions/chrome_manifest_handlers.cc
@@ -9,6 +9,7 @@
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/common/extensions/api/omnibox/omnibox_handler.h"
+#include "chrome/common/extensions/api/side_panel/side_panel_info.h"
 #include "chrome/common/extensions/api/speech/tts_engine_manifest_handler.h"
 #include "chrome/common/extensions/api/storage/storage_schema_manifest_handler.h"
 #include "chrome/common/extensions/api/system_indicator/system_indicator_handler.h"
@@ -52,6 +53,7 @@
   registry->RegisterHandler(std::make_unique<OmniboxHandler>());
   registry->RegisterHandler(std::make_unique<OptionsPageManifestHandler>());
   registry->RegisterHandler(std::make_unique<SettingsOverridesHandler>());
+  registry->RegisterHandler(std::make_unique<SidePanelManifestHandler>());
   registry->RegisterHandler(std::make_unique<StorageSchemaManifestHandler>());
   registry->RegisterHandler(std::make_unique<SystemIndicatorHandler>());
   registry->RegisterHandler(std::make_unique<ThemeHandler>());
diff --git a/chrome/common/extensions/manifest_tests/extension_manifests_side_panel_unittest.cc b/chrome/common/extensions/manifest_tests/extension_manifests_side_panel_unittest.cc
new file mode 100644
index 0000000..1620b1d9
--- /dev/null
+++ b/chrome/common/extensions/manifest_tests/extension_manifests_side_panel_unittest.cc
@@ -0,0 +1,102 @@
+// 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/common/extensions/api/side_panel/side_panel_info.h"
+
+#include "base/strings/stringprintf.h"
+#include "base/test/values_test_util.h"
+#include "chrome/common/extensions/manifest_tests/chrome_manifest_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "extensions/common/manifest.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace extensions {
+
+class SidePanelManifestTest : public ChromeManifestTest {
+ protected:
+  ManifestData GetManifestData(const std::string& side_panel,
+                               int manifest_version = 3) {
+    constexpr char kManifestStub[] =
+        R"({
+        "name": "Test",
+        "version": "1.0",
+        "manifest_version": %d,
+        "side_panel": %s
+      })";
+    base::Value manifest_value = base::test::ParseJson(base::StringPrintf(
+        kManifestStub, manifest_version, side_panel.c_str()));
+    EXPECT_EQ(base::Value::Type::DICTIONARY, manifest_value.type());
+    return ManifestData(std::move(manifest_value), "test");
+  }
+};
+
+// Test presence of side_panel key in manifest.json.
+TEST_F(SidePanelManifestTest, All) {
+  // Succeed when side_panel.path is defined.
+  {
+    scoped_refptr<Extension> extension(LoadAndExpectSuccess(
+        GetManifestData(R"({"default_path": "panel.html"})")));
+    EXPECT_TRUE(SidePanelInfo::HasSidePanel(extension.get()));
+  }
+
+  // Error when side_panel.default_path type doesn't match.
+  {
+    std::string error =
+        "Error at key 'side_panel.default_path'. Type is invalid. Expected "
+        "string, found dictionary.";
+    LoadAndExpectError(GetManifestData(R"({"default_path": {}})"), error);
+  }
+
+  // Error when side_panel type doesn't match.
+  {
+    std::string error =
+        "Error at key 'side_panel'. Type is invalid. Expected dictionary, found"
+        " string.";
+    LoadAndExpectError(GetManifestData(R"("")"), error);
+  }
+}
+
+class SidePanelExtensionsTest : public testing::Test {
+ public:
+  void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); }
+
+ protected:
+  // Empty filepath doesn't exist test coverage.
+  scoped_refptr<Extension> CreateExtension(const base::Value::Dict& manifest) {
+    base::Value::Dict manifest_base;
+    manifest_base.Set("name", "test");
+    manifest_base.Set("version", "1.0");
+    manifest_base.Set("manifest_version", 3);
+    manifest_base.Merge(manifest.Clone());
+    auto value = base::Value(std::move(manifest_base));
+    auto& manifest_dict = std::move(base::Value::AsDictionaryValue(value));
+    std::string error;
+    scoped_refptr<Extension> extension = Extension::Create(
+        temp_dir_.GetPath(), mojom::ManifestLocation::kUnpacked, manifest_dict,
+        Extension::NO_FLAGS, "", &error);
+    if (!extension.get())
+      return nullptr;
+    return extension;
+  }
+
+ private:
+  base::ScopedTempDir temp_dir_;
+};
+
+// Error loading extension when filepath doesn't exist or is empty.
+TEST_F(SidePanelExtensionsTest, FileDoesntExist) {
+  for (const auto* default_path : {"", "error"}) {
+    std::string error;
+    std::vector<InstallWarning> warnings;
+    base::Value::Dict manifest;
+    base::Value::Dict side_panel;
+    side_panel.Set("default_path", default_path);
+    manifest.Set("side_panel", base::Value(std::move(side_panel)));
+    auto extension = CreateExtension(manifest);
+    ManifestHandler::ValidateExtension(extension.get(), &error, &warnings);
+    ASSERT_EQ("Side panel file path must exist.", error);
+  }
+}
+
+}  // namespace extensions
diff --git a/chrome/common/extensions/permissions/chrome_api_permissions.cc b/chrome/common/extensions/permissions/chrome_api_permissions.cc
index 42cdffa..e254f9c 100644
--- a/chrome/common/extensions/permissions/chrome_api_permissions.cc
+++ b/chrome/common/extensions/permissions/chrome_api_permissions.cc
@@ -109,6 +109,7 @@
     {APIPermissionID::kScripting, "scripting",
      APIPermissionInfo::kFlagRequiresManagementUIWarning},
     {APIPermissionID::kSessions, "sessions"},
+    {APIPermissionID::kSidePanel, "sidePanel"},
     {APIPermissionID::kTabGroups, "tabGroups",
      APIPermissionInfo::kFlagRequiresManagementUIWarning},
     {APIPermissionID::kTab, "tabs",
diff --git a/chrome/common/extensions/permissions/permission_set_unittest.cc b/chrome/common/extensions/permissions/permission_set_unittest.cc
index bf420c05..6fd4653f 100644
--- a/chrome/common/extensions/permissions/permission_set_unittest.cc
+++ b/chrome/common/extensions/permissions/permission_set_unittest.cc
@@ -766,6 +766,7 @@
   skip.insert(APIPermissionID::kPrinterProvider);
   skip.insert(APIPermissionID::kSearch);
   skip.insert(APIPermissionID::kSessions);
+  skip.insert(APIPermissionID::kSidePanel);
   skip.insert(APIPermissionID::kStorage);
   skip.insert(APIPermissionID::kSystemCpu);
   skip.insert(APIPermissionID::kSystemDisplay);
diff --git a/chrome/common/profiler/thread_profiler_platform_configuration_unittest.cc b/chrome/common/profiler/thread_profiler_platform_configuration_unittest.cc
index eb43539..dcf9e43 100644
--- a/chrome/common/profiler/thread_profiler_platform_configuration_unittest.cc
+++ b/chrome/common/profiler/thread_profiler_platform_configuration_unittest.cc
@@ -69,14 +69,6 @@
   EXPECT_FALSE(config()->IsSupported(version_info::Channel::STABLE));
 
   EXPECT_FALSE(config()->IsSupported(absl::nullopt));
-#elif BUILDFLAG(IS_ANDROID)
-  EXPECT_FALSE(config()->IsSupported(version_info::Channel::UNKNOWN));
-  EXPECT_TRUE(config()->IsSupported(version_info::Channel::CANARY));
-  EXPECT_TRUE(config()->IsSupported(version_info::Channel::DEV));
-  EXPECT_FALSE(config()->IsSupported(version_info::Channel::BETA));
-  EXPECT_FALSE(config()->IsSupported(version_info::Channel::STABLE));
-
-  EXPECT_TRUE(config()->IsSupported(absl::nullopt));
 #else
   EXPECT_FALSE(config()->IsSupported(version_info::Channel::UNKNOWN));
   EXPECT_TRUE(config()->IsSupported(version_info::Channel::CANARY));
@@ -150,6 +142,10 @@
 
 MAYBE_PLATFORM_CONFIG_TEST_F(ThreadProfilerPlatformConfigurationTest,
                              GetChildProcessEnableFraction) {
+  EXPECT_EQ(0.0, config()->GetChildProcessEnableFraction(
+                     metrics::CallStackProfileParams::Process::kUtility));
+  EXPECT_EQ(0.0, config()->GetChildProcessEnableFraction(
+                     metrics::CallStackProfileParams::Process::kUnknown));
 #if BUILDFLAG(IS_ANDROID)
   EXPECT_EQ(0.0, config()->GetChildProcessEnableFraction(
                      metrics::CallStackProfileParams::Process::kGpu));
@@ -158,10 +154,6 @@
   EXPECT_EQ(0.0,
             config()->GetChildProcessEnableFraction(
                 metrics::CallStackProfileParams::Process::kNetworkService));
-  EXPECT_EQ(0.0, config()->GetChildProcessEnableFraction(
-                     metrics::CallStackProfileParams::Process::kUtility));
-  EXPECT_EQ(0.0, config()->GetChildProcessEnableFraction(
-                     metrics::CallStackProfileParams::Process::kUnknown));
 #else
   EXPECT_EQ(1.0, config()->GetChildProcessEnableFraction(
                      metrics::CallStackProfileParams::Process::kGpu));
@@ -170,10 +162,6 @@
   EXPECT_EQ(1.0,
             config()->GetChildProcessEnableFraction(
                 metrics::CallStackProfileParams::Process::kNetworkService));
-  EXPECT_EQ(0.0, config()->GetChildProcessEnableFraction(
-                     metrics::CallStackProfileParams::Process::kUtility));
-  EXPECT_EQ(0.0, config()->GetChildProcessEnableFraction(
-                     metrics::CallStackProfileParams::Process::kUnknown));
 #endif
 }
 
diff --git a/chrome/installer/mini_installer/chrome.release b/chrome/installer/mini_installer/chrome.release
index 5cdaad0..50e7498 100644
--- a/chrome/installer/mini_installer/chrome.release
+++ b/chrome/installer/mini_installer/chrome.release
@@ -23,6 +23,7 @@
 chrome_child.dll: %(VersionDir)s\
 chrome_elf.dll: %(VersionDir)s\
 chrome_pwa_launcher.exe: %(VersionDir)s\
+chrome_wer.dll: %(VersionDir)s\
 d3dcompiler_47.dll: %(VersionDir)s\
 eventlog_provider.dll: %(VersionDir)s\
 icudtl.dat: %(VersionDir)s\
diff --git a/chrome/installer/setup/install_worker.cc b/chrome/installer/setup/install_worker.cc
index 883d032..3dca9cd 100644
--- a/chrome/installer/setup/install_worker.cc
+++ b/chrome/installer/setup/install_worker.cc
@@ -920,6 +920,22 @@
                                notification_helper_path.value(), true);
 }
 
+void AddWerHelperRegistration(HKEY root,
+                              const base::FilePath& wer_helper_path,
+                              WorkItemList* list) {
+  DCHECK(!wer_helper_path.empty());
+
+  std::wstring wer_registry_path = GetWerHelperRegistryPath();
+
+  list->AddCreateRegKeyWorkItem(root, wer_registry_path,
+                                WorkItem::kWow64Default);
+
+  // The DWORD value is not important.
+  list->AddSetRegValueWorkItem(root, wer_registry_path, WorkItem::kWow64Default,
+                               wer_helper_path.value().c_str(), DWORD{0},
+                               /*overwrite=*/true);
+}
+
 void AddSetMsiMarkerWorkItem(const InstallerState& installer_state,
                              bool set,
                              WorkItemList* work_item_list) {
@@ -1078,6 +1094,10 @@
   // overwriting any of the following post-install tasks.
   AddDowngradeCleanupItems(new_version, list);
 
+  AddWerHelperRegistration(
+      installer_state.root_key(),
+      GetWerHelperPath(installer_state.target_path(), new_version), list);
+
   const std::wstring client_state_key = install_static::GetClientStateKeyPath();
 
   // Adds the command that needs to be used in order to cleanup any breaking
diff --git a/chrome/installer/setup/install_worker.h b/chrome/installer/setup/install_worker.h
index 5e3968f2..2b26c94 100644
--- a/chrome/installer/setup/install_worker.h
+++ b/chrome/installer/setup/install_worker.h
@@ -65,13 +65,19 @@
 void AddInstallWorkItems(const InstallParams& install_params,
                          WorkItemList* install_list);
 
-// Adds work items to |list| to register a COM server with the OS after deleting
+// Adds work items to `list` to register a COM server with the OS after deleting
 // the old ones, which is used to handle the toast notification activation.
 void AddNativeNotificationWorkItems(
     HKEY root,
     const base::FilePath& notification_helper_path,
     WorkItemList* list);
 
+// Adds work items to `list` to register a WER runtime exception helper module
+// in the registry. The wer module should be located at `wer_helper_path`.
+void AddWerHelperRegistration(HKEY root,
+                              const base::FilePath& wer_helper_path,
+                              WorkItemList* list);
+
 void AddSetMsiMarkerWorkItem(const InstallerState& installer_state,
                              bool set,
                              WorkItemList* work_item_list);
@@ -92,7 +98,7 @@
 
 // Adds work items to add or remove the "on-os-upgrade" command to Chrome's
 // version key on the basis of the current operation (represented in
-// |installer_state|).  |new_version| is the version currently being installed
+// `installer_state`).  `new_version` is the version currently being installed
 // -- can be empty on uninstall.
 void AddOsUpgradeWorkItems(const InstallerState& installer_state,
                            const base::FilePath& setup_path,
diff --git a/chrome/installer/setup/setup_util.cc b/chrome/installer/setup/setup_util.cc
index 8de7df39..1cd55a8 100644
--- a/chrome/installer/setup/setup_util.cc
+++ b/chrome/installer/setup/setup_util.cc
@@ -799,6 +799,16 @@
       .Append(kNotificationHelperExe);
 }
 
+base::FilePath GetWerHelperPath(const base::FilePath& target_path,
+                                const base::Version& version) {
+  return target_path.AppendASCII(version.GetString()).Append(kWerDll);
+}
+
+std::wstring GetWerHelperRegistryPath() {
+  return L"Software\\Microsoft\\Windows\\Windows Error Reporting"
+         L"\\RuntimeExceptionHelperModules";
+}
+
 base::FilePath GetElevationServicePath(const base::FilePath& target_path,
                                        const base::Version& version) {
   return target_path.AppendASCII(version.GetString())
diff --git a/chrome/installer/setup/setup_util.h b/chrome/installer/setup/setup_util.h
index 57f3ea0..6971844 100644
--- a/chrome/installer/setup/setup_util.h
+++ b/chrome/installer/setup/setup_util.h
@@ -162,6 +162,13 @@
 base::FilePath GetNotificationHelperPath(const base::FilePath& target_path,
                                          const base::Version& version);
 
+// Returns the file path to chrome_wer.dll (in `version` directory).
+base::FilePath GetWerHelperPath(const base::FilePath& target_path,
+                                const base::Version& version);
+
+// Returns the WER runtime exception helper module registry path.
+std::wstring GetWerHelperRegistryPath();
+
 // Returns the file path to elevation_service.exe (in |version| directory).
 base::FilePath GetElevationServicePath(const base::FilePath& target_path,
                                        const base::Version& version);
diff --git a/chrome/installer/setup/setup_util_unittest.cc b/chrome/installer/setup/setup_util_unittest.cc
index db1308f..d6e9ad9b 100644
--- a/chrome/installer/setup/setup_util_unittest.cc
+++ b/chrome/installer/setup/setup_util_unittest.cc
@@ -734,6 +734,11 @@
   ASSERT_FALSE(key.Valid());
 }
 
+TEST(SetupUtilTest, WerHelperRegPath) {
+  // Must return a valid regpath, never an empty string.
+  ASSERT_FALSE(installer::GetWerHelperRegistryPath().empty());
+}
+
 namespace installer {
 
 class DeleteRegistryKeyPartialTest : public ::testing::Test {
diff --git a/chrome/installer/setup/uninstall.cc b/chrome/installer/setup/uninstall.cc
index db6e49a..b12e74b 100644
--- a/chrome/installer/setup/uninstall.cc
+++ b/chrome/installer/setup/uninstall.cc
@@ -569,6 +569,16 @@
   return result;
 }
 
+void DeleteWerRegistryKey(const installer::InstallerState& installer_state,
+                          const base::Version& version) {
+  // Delete WER runtime exception helper module dll registry entry.
+  std::wstring wer_helper_reg_path = GetWerHelperRegistryPath();
+  base::FilePath wer_path =
+      GetWerHelperPath(installer_state.target_path(), version);
+  DeleteRegistryValue(installer_state.root_key(), wer_helper_reg_path,
+                      WorkItem::kWow64Default, wer_path.value());
+}
+
 bool DeleteChromeRegistrationKeys(const InstallerState& installer_state,
                                   HKEY root,
                                   const std::wstring& browser_entry_suffix,
@@ -943,6 +953,8 @@
                                  &ret);
   }
 
+  DeleteWerRegistryKey(installer_state, product_state->version());
+
   ProcessChromeWorkItems(installer_state);
 
   UninstallActiveSetupEntries(installer_state);
diff --git a/chrome/installer/util/util_constants.cc b/chrome/installer/util/util_constants.cc
index 8e553ed2..fef9775 100644
--- a/chrome/installer/util/util_constants.cc
+++ b/chrome/installer/util/util_constants.cc
@@ -234,6 +234,7 @@
 const wchar_t kLnkExt[] = L".lnk";
 const wchar_t kNaClExe[] = L"nacl64.exe";
 const wchar_t kNotificationHelperExe[] = L"notification_helper.exe";
+const wchar_t kWerDll[] = L"chrome_wer.dll";
 
 // DowngradeVersion holds the version from which Chrome was downgraded. In case
 // of multiple downgrades (e.g., 75->74->73), it retains the highest version
diff --git a/chrome/installer/util/util_constants.h b/chrome/installer/util/util_constants.h
index f2a0223..94f03c82 100644
--- a/chrome/installer/util/util_constants.h
+++ b/chrome/installer/util/util_constants.h
@@ -243,6 +243,7 @@
 extern const wchar_t kUninstallDisplayNameField[];
 extern const wchar_t kUninstallInstallationDate[];
 extern const wchar_t kUninstallStringField[];
+extern const wchar_t kWerDll[];
 
 // Elevation Service constants.
 extern const base::FilePath::CharType kElevationServiceExe[];
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 9195a128..dd8d1365 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2900,6 +2900,7 @@
         "../browser/extensions/api/sessions/sessions_apitest.cc",
         "../browser/extensions/api/settings_overrides/settings_overrides_browsertest.cc",
         "../browser/extensions/api/settings_private/settings_private_apitest.cc",
+        "../browser/extensions/api/side_panel/side_panel_apitest.cc",
         "../browser/extensions/api/socket/socket_apitest.cc",
         "../browser/extensions/api/storage/settings_apitest.cc",
         "../browser/extensions/api/system_display/system_display_extension_apitest.cc",
@@ -4498,14 +4499,9 @@
       sources -= [
         "../../apps/app_restore_service_browsertest.cc",
         "../../apps/load_and_launch_browsertest.cc",
-        "../browser/apps/platform_apps/api/browser/browser_apitest.cc",
-        "../browser/apps/platform_apps/api/media_galleries/media_galleries_watch_apitest.cc",
-        "../browser/apps/platform_apps/api/sync_file_system/sync_file_system_apitest.cc",
-        "../browser/apps/platform_apps/api/sync_file_system/sync_file_system_browsertest.cc",
         "../browser/apps/platform_apps/app_browsertest.cc",
         "../browser/apps/platform_apps/app_speech_recognition_browsertest.cc",
         "../browser/apps/platform_apps/app_window_browsertest.cc",
-        "../browser/apps/platform_apps/audio_focus_web_contents_observer_browsertest.cc",
         "../browser/apps/platform_apps/event_page_browsertest.cc",
         "../browser/apps/platform_apps/platform_app_navigation_redirector_browsertest.cc",
         "../browser/apps/platform_apps/service_worker_browsertest.cc",
@@ -4515,21 +4511,29 @@
         "../browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc",
         "../browser/ui/views/web_apps/frame_toolbar/web_app_minimal_ui_test.cc",
         "../browser/ui/views/web_apps/protocol_handler_launch_dialog_browsertest.cc",
-        "../browser/ui/views/web_apps/pwa_confirmation_bubble_view_browsertest.cc",
-        "../browser/ui/views/web_apps/web_app_confirmation_view_browsertest.cc",
-        "../browser/ui/views/web_apps/web_app_identity_update_confirmation_view_browsertest.cc",
         "../browser/ui/views/web_apps/web_app_integration_browsertest.cc",
         "../browser/ui/views/web_apps/web_app_tab_strip_browsertest.cc",
         "../browser/web_applications/alternative_error_page_override_info_browsertest.cc",
       ]
+      if (enable_extensions) {
+        sources -= [
+          # TODO(crbug.com/1278123): Enable once PWAs are supported.
+          "../browser/apps/platform_apps/api/browser/browser_apitest.cc",
+          "../browser/apps/platform_apps/api/media_galleries/media_galleries_watch_apitest.cc",
+          "../browser/apps/platform_apps/api/sync_file_system/sync_file_system_apitest.cc",
+          "../browser/apps/platform_apps/api/sync_file_system/sync_file_system_browsertest.cc",
+          "../browser/apps/platform_apps/audio_focus_web_contents_observer_browsertest.cc",
+          "../browser/ui/views/web_apps/pwa_confirmation_bubble_view_browsertest.cc",
+          "../browser/ui/views/web_apps/web_app_confirmation_view_browsertest.cc",
+          "../browser/ui/views/web_apps/web_app_identity_update_confirmation_view_browsertest.cc",
 
-      # Exclude tests which depend on native messaging.
-      # TODO(crbug.com/1293504): Reintroduce tests when native messaging
-      # enabled, or remove TODO if the lack of native messaging is WAI.
-      sources -= [
-        "../browser/extensions/api/messaging/native_messaging_apitest.cc",
-        "../browser/extensions/api/messaging/service_worker_messaging_apitest.cc",
-      ]
+          # Exclude tests that depend on native messaging.
+          # TODO(crbug.com/1293504): Reintroduce tests when native messaging
+          # enabled, or remove TODO if the lack of native messaging is WAI.
+          "../browser/extensions/api/messaging/native_messaging_apitest.cc",
+          "../browser/extensions/api/messaging/service_worker_messaging_apitest.cc",
+        ]
+      }
 
       # TODO(crbug.com/1226159): Complete crash reporting integration on Fuchsia.
       deps -= [ "//components/crash/content/browser/error_reporting:mock_crash_endpoint" ]
@@ -4622,6 +4626,7 @@
     ]
 
     data_deps = [
+      "//build:version_metadata",
       "//chrome:packed_resources",
       "//testing/buildbot/filters:lacros_chrome_browsertests_filters",
     ]
@@ -4736,6 +4741,7 @@
     ]
 
     data_deps = [
+      "//build:version_metadata",
       "//chrome:packed_resources",
       "//testing/buildbot/filters:lacros_chrome_browsertests_filters",
     ]
@@ -7793,6 +7799,7 @@
       "../common/extensions/manifest_tests/extension_manifests_platformapp_unittest.cc",
       "../common/extensions/manifest_tests/extension_manifests_portsinpermissions_unittest.cc",
       "../common/extensions/manifest_tests/extension_manifests_requirements_unittest.cc",
+      "../common/extensions/manifest_tests/extension_manifests_side_panel_unittest.cc",
       "../common/extensions/manifest_tests/extension_manifests_ui_unittest.cc",
       "../common/extensions/manifest_tests/extension_manifests_update_unittest.cc",
       "../common/extensions/manifest_tests/extension_manifests_validapp_unittest.cc",
@@ -9652,6 +9659,11 @@
         "chrome/test/data/perf/frame_rate/content/googleblog/images/bse%e2%80%99s+solar+energy+development+center..jpg",
       ]
     }
+
+    if (is_chromeos_lacros && !is_chromeos_device) {
+      # For support version skew testing.
+      data_deps += [ "//build:version_metadata" ]
+    }
   }
 }
 
diff --git a/chrome/test/data/extensions/api_test/side_panel/extension/default_path.html b/chrome/test/data/extensions/api_test/side_panel/extension/default_path.html
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/side_panel/extension/default_path.html
diff --git a/chrome/test/data/extensions/api_test/side_panel/extension/manifest.json b/chrome/test/data/extensions/api_test/side_panel/extension/manifest.json
new file mode 100644
index 0000000..8c014fb6
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/side_panel/extension/manifest.json
@@ -0,0 +1,8 @@
+{
+  "name": "Extension",
+  "version": "1.0",
+  "manifest_version": 3,
+  "permissions": ["sidePanel"],
+  "side_panel": {"default_path": "default_path.html"},
+  "background": {"service_worker": "service_worker.js"}
+}
diff --git a/chrome/test/data/extensions/api_test/side_panel/extension/panel.html b/chrome/test/data/extensions/api_test/side_panel/extension/panel.html
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/side_panel/extension/panel.html
diff --git a/chrome/test/data/extensions/api_test/side_panel/extension/service_worker.js b/chrome/test/data/extensions/api_test/side_panel/extension/service_worker.js
new file mode 100644
index 0000000..e72e8a42
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/side_panel/extension/service_worker.js
@@ -0,0 +1,66 @@
+// 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.
+
+var tabId;
+
+chrome.test.runTests([
+  async function init() {
+    const tabs = await chrome.tabs.query({active: true, currentWindow: true});
+    tabId = tabs[0].id;
+    chrome.test.succeed();
+  },
+
+  // Get the manifest panel if setPanel() has never been called.
+  async function getPanel() {
+    const expected = {enabled: true, path: 'default_path.html'};
+    const result = await chrome.sidePanel.getOptions({tabId: tabId});
+    chrome.test.assertEq(expected, result);
+    chrome.test.succeed();
+  },
+
+  // Set panel options by tabId.
+  async function setAndGetPanel() {
+    const expected = {tabId: tabId, path: 'tab_specific.html', enabled: false};
+    await chrome.sidePanel.setOptions(expected);
+    const result = await chrome.sidePanel.getOptions({tabId: tabId});
+    chrome.test.assertEq(expected, result);
+    chrome.test.succeed();
+  },
+
+  // Set panel options by tabId, for upsert-update test coverage.
+  async function setAndGetPanelAgain() {
+    const expected = {tabId: tabId, path: 'tab_specific.html', enabled: true};
+    await chrome.sidePanel.setOptions(expected);
+    const result = await chrome.sidePanel.getOptions({tabId: tabId});
+    chrome.test.assertEq(expected, result);
+    chrome.test.succeed();
+  },
+
+  // Set (to add) panel options without a tabId.
+  async function defaultSetAndGetPanel() {
+    const expected = {path: 'new_default.html', enabled: true};
+    await chrome.sidePanel.setOptions(expected);
+    const result = await chrome.sidePanel.getOptions({});
+    chrome.test.assertEq(expected, result);
+    chrome.test.succeed();
+  },
+
+  // Retrieve the newly-set default when we try to fetch the options for a tab
+  // ID that doesn't have a dedicated entry.
+  async function defaultSetAndGetPanel() {
+    const newTabId = tabId + 100;
+    const expected = {path: 'new_default.html', enabled: true};
+    const result = await chrome.sidePanel.getOptions({tabId: tabId + 100});
+    chrome.test.assertEq(expected, result);
+    chrome.test.succeed();
+  },
+
+  // Retrieve options for `tabId` after adding default options.
+  async function defaultGetPanelForSpecificTab() {
+    const expected = {tabId: tabId, path: 'tab_specific.html', enabled: true};
+    const result = await chrome.sidePanel.getOptions({tabId: tabId});
+    chrome.test.assertEq(expected, result);
+    chrome.test.succeed();
+  },
+]);
diff --git a/chrome/test/data/extensions/api_test/side_panel/missing_manifest_key/manifest.json b/chrome/test/data/extensions/api_test/side_panel/missing_manifest_key/manifest.json
new file mode 100644
index 0000000..4096e5b
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/side_panel/missing_manifest_key/manifest.json
@@ -0,0 +1,7 @@
+{
+  "name": "Extension",
+  "version": "1.0",
+  "manifest_version": 3,
+  "permissions": ["sidePanel"],
+  "background": {"service_worker": "service_worker.js"}
+}
diff --git a/chrome/test/data/extensions/api_test/side_panel/missing_manifest_key/service_worker.js b/chrome/test/data/extensions/api_test/side_panel/missing_manifest_key/service_worker.js
new file mode 100644
index 0000000..b793994
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/side_panel/missing_manifest_key/service_worker.js
@@ -0,0 +1,21 @@
+// 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.
+
+var tabId;
+
+chrome.test.runTests([
+  async function init() {
+    const tabs = await chrome.tabs.query({active: true, currentWindow: true});
+    tabId = tabs[0].id;
+    chrome.test.succeed();
+  },
+
+  // Get the manifest panel if setPanel() has never been called.
+  async function getPanel() {
+    const expected = {};
+    const result = await chrome.sidePanel.getOptions({tabId: tabId});
+    chrome.test.assertEq(expected, result);
+    chrome.test.succeed();
+  }
+]);
diff --git a/chrome/test/data/extensions/api_test/side_panel/permission_missing/manifest.json b/chrome/test/data/extensions/api_test/side_panel/permission_missing/manifest.json
new file mode 100644
index 0000000..32224c0
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/side_panel/permission_missing/manifest.json
@@ -0,0 +1,6 @@
+{
+  "name": "Test",
+  "version": "1.0",
+  "manifest_version": 3,
+  "background": {"service_worker": "service_worker.js"}
+}
diff --git a/chrome/test/data/extensions/api_test/side_panel/permission_missing/service_worker.js b/chrome/test/data/extensions/api_test/side_panel/permission_missing/service_worker.js
new file mode 100644
index 0000000..4b04082
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/side_panel/permission_missing/service_worker.js
@@ -0,0 +1,10 @@
+// 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.
+
+chrome.test.runTests([
+  async function all() {
+    chrome.test.assertEq(undefined, chrome.sidePanel);
+    chrome.test.succeed();
+  },
+]);
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index 59f55fc..9c383317 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -6287,6 +6287,8 @@
   },
   "MaxConnectionsPerProxy": {
     "os": [
+      "chromeos_ash",
+      "chromeos_lacros",
       "win",
       "linux",
       "mac"
@@ -16949,7 +16951,9 @@
     "os": [
       "win",
       "linux",
-      "mac"
+      "mac",
+      "chromeos_ash",
+      "chromeos_lacros"
     ],
     "can_be_recommended": false,
     "policy_pref_mapping_tests": [
diff --git a/chrome/test/data/webui/chromeos/.eslintrc.js b/chrome/test/data/webui/chromeos/.eslintrc.js
new file mode 100644
index 0000000..2ca8df37
--- /dev/null
+++ b/chrome/test/data/webui/chromeos/.eslintrc.js
@@ -0,0 +1,9 @@
+// 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.
+
+module.exports = {
+  'rules' : {
+    'comma-dangle' : ['error', 'always-multiline'],
+  },
+};
diff --git a/chrome/test/data/webui/chromeos/arc_account_picker/arc_account_picker_test.js b/chrome/test/data/webui/chromeos/arc_account_picker/arc_account_picker_test.js
index 1460e87c..c17cade 100644
--- a/chrome/test/data/webui/chromeos/arc_account_picker/arc_account_picker_test.js
+++ b/chrome/test/data/webui/chromeos/arc_account_picker/arc_account_picker_test.js
@@ -54,7 +54,7 @@
     assertFalse(accountsFound);
     flush();
     const uiAccounts = [
-      ...arcAccountPickerComponent.shadowRoot.querySelectorAll('.account-item')
+      ...arcAccountPickerComponent.shadowRoot.querySelectorAll('.account-item'),
     ].filter(item => item.id !== 'addAccountButton');
     assertEquals(0, uiAccounts.length);
   });
@@ -68,7 +68,7 @@
     assertTrue(accountsFound);
     flush();
     const uiAccounts = [
-      ...arcAccountPickerComponent.shadowRoot.querySelectorAll('.account-item')
+      ...arcAccountPickerComponent.shadowRoot.querySelectorAll('.account-item'),
     ].filter(item => item.id !== 'addAccountButton');
     assertEquals(fakeAccounts.length, uiAccounts.length);
   });
diff --git a/chrome/test/data/webui/chromeos/arc_account_picker/test_util.js b/chrome/test/data/webui/chromeos/arc_account_picker/test_util.js
index 74e31c8..dd0c389 100644
--- a/chrome/test/data/webui/chromeos/arc_account_picker/test_util.js
+++ b/chrome/test/data/webui/chromeos/arc_account_picker/test_util.js
@@ -20,7 +20,7 @@
       id: '1',
       email: 'test@gmail.com',
       fullName: 'Test User',
-      image: 'data:image/png;base64,abc123'
+      image: 'data:image/png;base64,abc123',
     },
     {id: '2', email: 'test2@gmail.com', fullName: 'Test2 User', image: ''},
     {id: '3', email: 'test3@gmail.com', fullName: 'Test3 User', image: ''},
diff --git a/chrome/test/data/webui/chromeos/ash_common/navigation_view_panel_test.js b/chrome/test/data/webui/chromeos/ash_common/navigation_view_panel_test.js
index 416059e..4b58224b 100644
--- a/chrome/test/data/webui/chromeos/ash_common/navigation_view_panel_test.js
+++ b/chrome/test/data/webui/chromeos/ash_common/navigation_view_panel_test.js
@@ -181,7 +181,7 @@
 
     await addNavigationSections([
       viewElement.createSelectorItem('dummyPage1', 'dummy-page1', '', 'page1'),
-      viewElement.createSelectorItem('dummyPage1', 'dummy-page2', '', 'page2')
+      viewElement.createSelectorItem('dummyPage1', 'dummy-page2', '', 'page2'),
     ]);
 
 
diff --git a/chrome/test/data/webui/chromeos/crostini_installer_app_test.js b/chrome/test/data/webui/chromeos/crostini_installer_app_test.js
index 677d359..90fde808 100644
--- a/chrome/test/data/webui/chromeos/crostini_installer_app_test.js
+++ b/chrome/test/data/webui/chromeos/crostini_installer_app_test.js
@@ -14,8 +14,11 @@
 class FakePageHandler extends TestBrowserProxy {
   constructor() {
     super([
-      'install', 'cancel', 'cancelBeforeStart', 'onPageClosed',
-      'requestAmountOfFreeDiskSpace'
+      'install',
+      'cancel',
+      'cancelBeforeStart',
+      'onPageClosed',
+      'requestAmountOfFreeDiskSpace',
     ]);
 
     this.requestAmountOfFreeDiskSpaceResult_ = new Promise((resolve) => {
@@ -133,7 +136,7 @@
 
   const diskTicks = [
     {value: 1000, ariaValue: '1', label: '1'},
-    {value: 2000, ariaValue: '2', label: '2'}
+    {value: 2000, ariaValue: '2', label: '2'},
   ];
 
   test('installFlow', async () => {
diff --git a/chrome/test/data/webui/chromeos/crostini_upgrader_app_test.js b/chrome/test/data/webui/chromeos/crostini_upgrader_app_test.js
index 5593ad9..149361d 100644
--- a/chrome/test/data/webui/chromeos/crostini_upgrader_app_test.js
+++ b/chrome/test/data/webui/chromeos/crostini_upgrader_app_test.js
@@ -11,8 +11,14 @@
 class FakePageHandler extends TestBrowserProxy {
   constructor() {
     super([
-      'backup', 'startPrechecks', 'upgrade', 'restore', 'cancel',
-      'cancelBeforeStart', 'onPageClosed', 'launch'
+      'backup',
+      'startPrechecks',
+      'upgrade',
+      'restore',
+      'cancel',
+      'cancelBeforeStart',
+      'onPageClosed',
+      'launch',
     ]);
   }
 
diff --git a/chrome/test/data/webui/chromeos/diagnostics/ethernet_info_test.js b/chrome/test/data/webui/chromeos/diagnostics/ethernet_info_test.js
index cb186c8..6aa2586 100644
--- a/chrome/test/data/webui/chromeos/diagnostics/ethernet_info_test.js
+++ b/chrome/test/data/webui/chromeos/diagnostics/ethernet_info_test.js
@@ -54,7 +54,7 @@
     return Object.assign({}, fakeEthernetNetwork, {
       typeProperties: {
         ethernet: ethernetTypeProperies,
-      }
+      },
     });
   }
 
diff --git a/chrome/test/data/webui/chromeos/diagnostics/fake_network_health_provider_test.js b/chrome/test/data/webui/chromeos/diagnostics/fake_network_health_provider_test.js
index f2c6b2d..044da52d 100644
--- a/chrome/test/data/webui/chromeos/diagnostics/fake_network_health_provider_test.js
+++ b/chrome/test/data/webui/chromeos/diagnostics/fake_network_health_provider_test.js
@@ -49,7 +49,7 @@
               completeResolver.resolve();
             }
             whichSample++;
-          }
+          },
         });
 
     provider.observeNetworkList(networkListObserverRemote);
@@ -73,7 +73,7 @@
           onNetworkStateChanged: (network) => {
             assertDeepEquals(fakeCellularNetwork, network);
             resolver.resolve();
-          }
+          },
         });
 
     provider.observeNetwork(networkStateObserverRemote, 'cellularGuid');
@@ -93,7 +93,7 @@
           onNetworkStateChanged: (network) => {
             assertDeepEquals(fakeWifiNetwork, network);
             wifiResolver.resolve();
-          }
+          },
         });
 
     const ethernetNetworkStateObserverRemote =
@@ -101,7 +101,7 @@
           onNetworkStateChanged: (network) => {
             assertDeepEquals(fakeEthernetNetwork, network);
             ethernetResolver.resolve();
-          }
+          },
         });
 
     provider.observeNetwork(wifiNetworkStateObserverRemote, 'wifiGuid');
diff --git a/chrome/test/data/webui/chromeos/diagnostics/fake_system_data_provider_test.js b/chrome/test/data/webui/chromeos/diagnostics/fake_system_data_provider_test.js
index 86c13cd..f6653c6 100644
--- a/chrome/test/data/webui/chromeos/diagnostics/fake_system_data_provider_test.js
+++ b/chrome/test/data/webui/chromeos/diagnostics/fake_system_data_provider_test.js
@@ -65,7 +65,7 @@
         /** @type {!BatteryHealthObserverRemote} */ ({
           onBatteryHealthUpdated: (batteryHealth) => {
             assertDeepEquals(fakeBatteryHealth[0], batteryHealth);
-          }
+          },
         });
 
     provider.observeBatteryHealth(batteryHealthObserverRemote);
@@ -79,7 +79,7 @@
         /** @type {!BatteryChargeStatusObserverRemote} */ ({
           onBatteryChargeStatusUpdated: (batteryChargeStatus) => {
             assertDeepEquals(fakeBatteryChargeStatus[0], batteryChargeStatus);
-          }
+          },
         });
 
     provider.observeBatteryChargeStatus(batteryChargeStatusObserverRemote);
@@ -92,7 +92,7 @@
     const cpuObserverRemote = /** @type {!CpuUsageObserverRemote} */ ({
       onCpuUsageUpdated: (cpuUsage) => {
         assertDeepEquals(fakeCpuUsage[0], cpuUsage);
-      }
+      },
     });
 
     provider.observeCpuUsage(cpuObserverRemote);
@@ -106,7 +106,7 @@
         /** @type {!MemoryUsageObserverRemote} */ ({
           onMemoryUsageUpdated: (memoryUsage) => {
             assertDeepEquals(fakeMemoryUsage[0], memoryUsage);
-          }
+          },
         });
 
     provider.observeMemoryUsage(memoryUsageObserverRemote);
@@ -179,7 +179,7 @@
           completeResolver.resolve();
         }
         whichSample++;
-      }
+      },
     });
 
     provider.observeCpuUsage(cpuObserverRemote);
@@ -218,7 +218,7 @@
           completeResolver.resolve();
         }
         whichSample++;
-      }
+      },
     });
 
     provider.observeMemoryUsage(memoryObserverRemote);
@@ -258,7 +258,7 @@
               completeResolver.resolve();
             }
             whichSample++;
-          }
+          },
         });
 
     provider.observeBatteryHealth(batteryHealthObserverRemote);
@@ -299,7 +299,7 @@
               completeResolver.resolve();
             }
             whichSample++;
-          }
+          },
         });
 
     provider.observeBatteryChargeStatus(batteryChargeStatusObserverRemote);
@@ -341,7 +341,7 @@
           completeResolver.resolve();
         }
         whichSample++;
-      }
+      },
     });
 
     provider.observeCpuUsage(cpuObserverRemote);
diff --git a/chrome/test/data/webui/chromeos/diagnostics/fake_system_routine_controller_test.js b/chrome/test/data/webui/chromeos/diagnostics/fake_system_routine_controller_test.js
index e7b874f..b81719994 100644
--- a/chrome/test/data/webui/chromeos/diagnostics/fake_system_routine_controller_test.js
+++ b/chrome/test/data/webui/chromeos/diagnostics/fake_system_routine_controller_test.js
@@ -51,7 +51,7 @@
         }
 
         resolver.resolve();
-      }
+      },
     });
     controller.runRoutine(expectedType, routineRunnerRemote);
     return controller.getRunRoutinePromiseForTesting().then(() => {
@@ -89,7 +89,7 @@
         // Mark that the test completed.
         wasRun = true;
         resolver.resolve();
-      }
+      },
     });
 
     controller.runRoutine(expectedType, routineRunnerRemote);
diff --git a/chrome/test/data/webui/chromeos/diagnostics/input_card_test.js b/chrome/test/data/webui/chromeos/diagnostics/input_card_test.js
index 76f794a..d18303f3 100644
--- a/chrome/test/data/webui/chromeos/diagnostics/input_card_test.js
+++ b/chrome/test/data/webui/chromeos/diagnostics/input_card_test.js
@@ -18,10 +18,16 @@
     hasAssistantKey: true,
     numberPadPresent: NumberPadPresence.kPresent,
     topRowKeys: [
-      TopRowKey.kBack, TopRowKey.kForward, TopRowKey.kRefresh,
-      TopRowKey.kFullscreen, TopRowKey.kOverview,
-      TopRowKey.kScreenBrightnessDown, TopRowKey.kScreenBrightnessUp,
-      TopRowKey.kVolumeMute, TopRowKey.kVolumeDown, TopRowKey.kVolumeUp
+      TopRowKey.kBack,
+      TopRowKey.kForward,
+      TopRowKey.kRefresh,
+      TopRowKey.kFullscreen,
+      TopRowKey.kOverview,
+      TopRowKey.kScreenBrightnessDown,
+      TopRowKey.kScreenBrightnessUp,
+      TopRowKey.kVolumeMute,
+      TopRowKey.kVolumeDown,
+      TopRowKey.kVolumeUp,
     ],
     topRightKey: TopRightKey.kLock,
   },
diff --git a/chrome/test/data/webui/chromeos/diagnostics/input_list_test.js b/chrome/test/data/webui/chromeos/diagnostics/input_list_test.js
index 43d812b..92f497c 100644
--- a/chrome/test/data/webui/chromeos/diagnostics/input_list_test.js
+++ b/chrome/test/data/webui/chromeos/diagnostics/input_list_test.js
@@ -86,10 +86,16 @@
       hasAssistantKey: false,
       numberPadPresent: NumberPadPresence.kUnknown,
       topRowKeys: [
-        TopRowKey.kBack, TopRowKey.kForward, TopRowKey.kRefresh,
-        TopRowKey.kFullscreen, TopRowKey.kOverview,
-        TopRowKey.kScreenBrightnessDown, TopRowKey.kScreenBrightnessUp,
-        TopRowKey.kVolumeMute, TopRowKey.kVolumeDown, TopRowKey.kVolumeUp
+        TopRowKey.kBack,
+        TopRowKey.kForward,
+        TopRowKey.kRefresh,
+        TopRowKey.kFullscreen,
+        TopRowKey.kOverview,
+        TopRowKey.kScreenBrightnessDown,
+        TopRowKey.kScreenBrightnessUp,
+        TopRowKey.kVolumeMute,
+        TopRowKey.kVolumeDown,
+        TopRowKey.kVolumeUp,
       ],
       topRightKey: TopRightKey.kUnknown,
     };
diff --git a/chrome/test/data/webui/chromeos/diagnostics/routine_list_executor_test.js b/chrome/test/data/webui/chromeos/diagnostics/routine_list_executor_test.js
index e8c71df..52a643d0 100644
--- a/chrome/test/data/webui/chromeos/diagnostics/routine_list_executor_test.js
+++ b/chrome/test/data/webui/chromeos/diagnostics/routine_list_executor_test.js
@@ -106,7 +106,7 @@
     /** @type {!Array<!RoutineResultInfo>} */
     const routines = [{
       type: RoutineType.kCpuStress,
-      result: {simpleResult: StandardRoutineResult.kTestFailed}
+      result: {simpleResult: StandardRoutineResult.kTestFailed},
     }];
     return runRoutinesAndAssertResults(routines);
   });
@@ -116,19 +116,19 @@
     const routines = [
       {
         type: RoutineType.kCpuStress,
-        result: {simpleResult: StandardRoutineResult.kTestPassed}
+        result: {simpleResult: StandardRoutineResult.kTestPassed},
       },
       {
         type: RoutineType.kCpuCache,
-        result: {simpleResult: StandardRoutineResult.kTestFailed}
+        result: {simpleResult: StandardRoutineResult.kTestFailed},
       },
       {
         type: RoutineType.kCpuFloatingPoint,
-        result: {simpleResult: StandardRoutineResult.kTestPassed}
+        result: {simpleResult: StandardRoutineResult.kTestPassed},
       },
       {
         type: RoutineType.kCpuPrime,
-        result: {simpleResult: StandardRoutineResult.kTestFailed}
+        result: {simpleResult: StandardRoutineResult.kTestFailed},
       },
       {
         type: RoutineType.kBatteryCharge,
@@ -137,9 +137,9 @@
             simpleResult: StandardRoutineResult.kTestFailed,
             isCharging: true,
             percentDelta: 10,
-            timeDeltaSeconds: 10
-          }
-        }
+            timeDeltaSeconds: 10,
+          },
+        },
       },
     ];
 
diff --git a/chrome/test/data/webui/chromeos/diagnostics/routine_result_entry_test.js b/chrome/test/data/webui/chromeos/diagnostics/routine_result_entry_test.js
index dac3e61f..715057ef 100644
--- a/chrome/test/data/webui/chromeos/diagnostics/routine_result_entry_test.js
+++ b/chrome/test/data/webui/chromeos/diagnostics/routine_result_entry_test.js
@@ -167,7 +167,7 @@
     const item = createCompletedStatus(
         RoutineType.kCpuStress,
         /** @type {!RoutineResult} */ ({
-          simpleResult: StandardRoutineResult.kTestPassed
+          simpleResult: StandardRoutineResult.kTestPassed,
         }));
     return initializeEntryWithItem(item).then(() => {
       assertEquals(
@@ -186,7 +186,7 @@
     const item = createCompletedStatus(
         RoutineType.kCpuStress,
         /** @type {!RoutineResult} */ ({
-          simpleResult: StandardRoutineResult.kTestFailed
+          simpleResult: StandardRoutineResult.kTestFailed,
         }));
     return initializeEntryWithItem(item).then(() => {
       assertEquals(
@@ -227,8 +227,8 @@
             simpleResult: StandardRoutineResult.kTestPassed,
             isCharging: true,
             percentDelta: 10,
-            timeDeltaSeconds: 10
-          }
+            timeDeltaSeconds: 10,
+          },
         }));
     return initializeEntryWithItem(item).then(() => {
       assertEquals(
@@ -267,7 +267,7 @@
 
           item = createCompletedStatus(
               routine, /* @type {!RoutineResult} */ ({
-                simpleResult: StandardRoutineResult.kTestPassed
+                simpleResult: StandardRoutineResult.kTestPassed,
               }));
 
           return updateItem(item);
@@ -306,7 +306,7 @@
 
           item = createCompletedStatus(
               routine, /* @type {!RoutineResult} */ ({
-                simpleResult: StandardRoutineResult.kTestFailed
+                simpleResult: StandardRoutineResult.kTestFailed,
               }));
           expectedAnnounceText = 'Lan Connectivity test - FAILED';
 
diff --git a/chrome/test/data/webui/chromeos/diagnostics/routine_section_test.js b/chrome/test/data/webui/chromeos/diagnostics/routine_section_test.js
index 403eb1d..cb035486 100644
--- a/chrome/test/data/webui/chromeos/diagnostics/routine_section_test.js
+++ b/chrome/test/data/webui/chromeos/diagnostics/routine_section_test.js
@@ -72,7 +72,8 @@
     routineSectionElement.routineRuntime = runtime;
 
     if (!(routines[0] instanceof RoutineGroup) && routines.length === 1 && [
-          RoutineType.kBatteryDischarge, RoutineType.kBatteryCharge
+          RoutineType.kBatteryDischarge,
+          RoutineType.kBatteryCharge,
         ].includes(routines[0])) {
       routineSectionElement.isPowerRoutine = true;
     }
@@ -986,7 +987,7 @@
     const localNetworkGroup = new RoutineGroup(
         [
           createRoutine(RoutineType.kGatewayCanBePinged, true),
-          createRoutine(RoutineType.kLanConnectivity, true)
+          createRoutine(RoutineType.kLanConnectivity, true),
         ],
         'localNetworkGroupLabel');
 
@@ -1295,7 +1296,7 @@
           [
             createRoutine(RoutineType.kDnsResolverPresent, true),
           ],
-          'wifiGroupLabel')
+          'wifiGroupLabel'),
     ];
     routineController.setFakeStandardRoutineResult(
         RoutineType.kCaptivePortal, StandardRoutineResult.kTestFailed);
diff --git a/chrome/test/data/webui/chromeos/diagnostics/wifi_info_test.js b/chrome/test/data/webui/chromeos/diagnostics/wifi_info_test.js
index f87207ec..681887b 100644
--- a/chrome/test/data/webui/chromeos/diagnostics/wifi_info_test.js
+++ b/chrome/test/data/webui/chromeos/diagnostics/wifi_info_test.js
@@ -53,7 +53,7 @@
     return /** @type {!Network} */ (Object.assign({}, fakeWifiNetwork, {
       typeProperties: {
         wifi: wifiTypeProperties,
-      }
+      },
     }));
   }
 
diff --git a/chrome/test/data/webui/chromeos/edu_coexistence/edu_coexistence_browsertest.js b/chrome/test/data/webui/chromeos/edu_coexistence/edu_coexistence_browsertest.js
index 9bcbdf9..8f9f53f 100644
--- a/chrome/test/data/webui/chromeos/edu_coexistence/edu_coexistence_browsertest.js
+++ b/chrome/test/data/webui/chromeos/edu_coexistence/edu_coexistence_browsertest.js
@@ -80,8 +80,8 @@
     return {
       enabled: [
         'chromeos::features::kArcAccountRestrictions',
-        'chromeos::features::kLacrosSupport'
-      ]
+        'chromeos::features::kLacrosSupport',
+      ],
     };
   }
 };
diff --git a/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_browsertest.js b/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_browsertest.js
index 372b4f62..ca4e09e 100644
--- a/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_browsertest.js
+++ b/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_browsertest.js
@@ -92,7 +92,7 @@
   /** @override */
   get featureList() {
     return {
-      enabled: ['chromeos::features::kImeSystemEmojiPickerSearchExtension']
+      enabled: ['chromeos::features::kImeSystemEmojiPickerSearchExtension'],
     };
   }
   /** @override */
diff --git a/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_extension_test.js b/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_extension_test.js
index 3802404..5a044ae 100644
--- a/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_extension_test.js
+++ b/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_extension_test.js
@@ -42,8 +42,8 @@
     EmojiPicker.configs = () => ({
       'dataUrls': {
         'emoji': [
-            '/emoji_test_ordering_start.json',
-            '/emoji_test_ordering_remaining.json'
+          '/emoji_test_ordering_start.json',
+          '/emoji_test_ordering_remaining.json',
         ],
         'emoticon': ['/emoticon_test_ordering.json'],
       },
@@ -251,8 +251,10 @@
         assert(recentEmoticonButton);
 
         const recentlyUsedEmoticons =
-            findInEmojiPicker(emoticonHistoryGroupSelector
-                ).shadowRoot.querySelectorAll('.emoji-button');
+            findInEmojiPicker(
+                emoticonHistoryGroupSelector,
+                )
+                .shadowRoot.querySelectorAll('.emoji-button');
         assertEquals(1, recentlyUsedEmoticons.length);
       });
 
diff --git a/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_prefix_search_test.js b/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_prefix_search_test.js
index ba803aa1..d1dcea0 100644
--- a/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_prefix_search_test.js
+++ b/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_prefix_search_test.js
@@ -9,7 +9,7 @@
 const mockCollection1 = [
   {base: {string: '😹', name: 'cat with tears of joy'}},
   {base: {string: '🤠', name: 'cowboy hat face'}},
-  {base: {string: '🥲', name: 'smiling face with tear'}}
+  {base: {string: '🥲', name: 'smiling face with tear'}},
 ];
 
 const mockCollection2 = [
diff --git a/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_search_test.js b/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_search_test.js
index bd85464..93db507 100644
--- a/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_search_test.js
+++ b/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_search_test.js
@@ -33,7 +33,7 @@
       'dataUrls': {
         'emoji': [
           '/emoji_test_ordering_start.json',
-          '/emoji_test_ordering_remaining.json'
+          '/emoji_test_ordering_remaining.json',
         ],
         'emoticon': ['/emoticon_test_ordering.json'],
       },
diff --git a/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_test_util.js b/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_test_util.js
index af2e77e..bc06991 100644
--- a/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_test_util.js
+++ b/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_test_util.js
@@ -120,7 +120,7 @@
     button: button,
     buttons: 0,
     clientX: element.getBoundingClientRect().x,
-    clientY: element.getBoundingClientRect().y
+    clientY: element.getBoundingClientRect().y,
   }));
 }
 
diff --git a/chrome/test/data/webui/chromeos/fake_network_config_mojom.js b/chrome/test/data/webui/chromeos/fake_network_config_mojom.js
index f7638b6..c0274e04 100644
--- a/chrome/test/data/webui/chromeos/fake_network_config_mojom.js
+++ b/chrome/test/data/webui/chromeos/fake_network_config_mojom.js
@@ -100,7 +100,7 @@
           deviceState:
               chromeos.networkConfig.mojom.DeviceStateType.kUninitialized,
           inhibitReason:
-              chromeos.networkConfig.mojom.InhibitReason.kNotInhibited
+              chromeos.networkConfig.mojom.InhibitReason.kNotInhibited,
         });
     this.deviceStates_.set(type, deviceState);
     return deviceState;
@@ -681,9 +681,13 @@
       this.methodCalled('getSupportedVpnTypes');
       resolve({
         vpnTypes: [
-          'ikev2', 'l2tpipsec', 'openvpn', 'thirdpartyvpn', 'arcvpn',
-          'wireguard'
-        ]
+          'ikev2',
+          'l2tpipsec',
+          'openvpn',
+          'thirdpartyvpn',
+          'arcvpn',
+          'wireguard',
+        ],
       });
     });
   }
diff --git a/chrome/test/data/webui/chromeos/firmware_update/fake_update_provider_test.js b/chrome/test/data/webui/chromeos/firmware_update/fake_update_provider_test.js
index c27010e01..76ca26e 100644
--- a/chrome/test/data/webui/chromeos/firmware_update/fake_update_provider_test.js
+++ b/chrome/test/data/webui/chromeos/firmware_update/fake_update_provider_test.js
@@ -25,7 +25,7 @@
     const updateObserverRemote = /** @type {!UpdateObserverRemote} */ ({
       onUpdateListChanged: (firmwareUpdates) => {
         assertDeepEquals(fakeFirmwareUpdates[0], firmwareUpdates);
-      }
+      },
     });
 
     provider.observePeripheralUpdates(updateObserverRemote);
diff --git a/chrome/test/data/webui/chromeos/firmware_update/firmware_update_dialog_test.js b/chrome/test/data/webui/chromeos/firmware_update/firmware_update_dialog_test.js
index 38e4785..ae4e9e94 100644
--- a/chrome/test/data/webui/chromeos/firmware_update/firmware_update_dialog_test.js
+++ b/chrome/test/data/webui/chromeos/firmware_update/firmware_update_dialog_test.js
@@ -21,7 +21,7 @@
     updateDialogElement.update = fakeFirmwareUpdate;
     updateDialogElement.installationProgress = {
       percentage: 0,
-      state: UpdateState.kIdle
+      state: UpdateState.kIdle,
     };
     document.body.appendChild(updateDialogElement);
   });
diff --git a/chrome/test/data/webui/chromeos/mock_controller.js b/chrome/test/data/webui/chromeos/mock_controller.js
index df50a8c7..e624e69 100644
--- a/chrome/test/data/webui/chromeos/mock_controller.js
+++ b/chrome/test/data/webui/chromeos/mock_controller.js
@@ -161,7 +161,7 @@
       this.overrides_.push({
         parent: opt_parent,
         functionName: opt_functionName,
-        originalFunction: opt_parent[opt_functionName]
+        originalFunction: opt_parent[opt_functionName],
       });
       opt_parent[opt_functionName] = fn;
       fn.functionName = opt_functionName;
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/help_content_test.js b/chrome/test/data/webui/chromeos/os_feedback_ui/help_content_test.js
index 959994e..18133fa2 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/help_content_test.js
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/help_content_test.js
@@ -39,7 +39,7 @@
     helpContentElement.searchResult = {
       contentList: contentList,
       isQueryEmpty: isQueryEmpty,
-      isPopularContent: isPopularContent
+      isPopularContent: isPopularContent,
     };
 
     document.body.appendChild(helpContentElement);
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/search_page_test.js b/chrome/test/data/webui/chromeos/os_feedback_ui/search_page_test.js
index d5c9004..7efb793d 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/search_page_test.js
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/search_page_test.js
@@ -176,7 +176,7 @@
     const data = {
       contentList: fakeSearchResponse.results,
       isQueryEmpty: true,
-      isPopularContent: true
+      isPopularContent: true,
     };
     iframe.contentWindow.postMessage(data, OS_FEEDBACK_UNTRUSTED_ORIGIN);
 
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/share_data_page_test.js b/chrome/test/data/webui/chromeos/os_feedback_ui/share_data_page_test.js
index ea6b63c..e694e38 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/share_data_page_test.js
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/share_data_page_test.js
@@ -406,7 +406,7 @@
         fileName: stringToMojoString16('fake.zip'),
         fileData: {
           bytes: fakeFileData,
-        }
+        },
       };
     };
 
diff --git a/chrome/test/data/webui/chromeos/parent_access/parent_access_controller_test.js b/chrome/test/data/webui/chromeos/parent_access/parent_access_controller_test.js
index 79c7ae53..cd6617f63 100644
--- a/chrome/test/data/webui/chromeos/parent_access/parent_access_controller_test.js
+++ b/chrome/test/data/webui/chromeos/parent_access/parent_access_controller_test.js
@@ -41,7 +41,7 @@
 
         const parentAccessResult = await Promise.race([
           parentAccessController.whenParentAccessResult(),
-          parentAccessController.whenInitializationError()
+          parentAccessController.whenInitializationError(),
         ]);
 
         // Verify that the result received is the one set in test_content.html
diff --git a/chrome/test/data/webui/chromeos/personalization_app/ambient_preview_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/ambient_preview_element_test.ts
index 52e1493..906718a0 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/ambient_preview_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/ambient_preview_element_test.ts
@@ -90,7 +90,7 @@
               goToRoute(path: Paths, queryParams: Object = {}) {
                 resolve([path, queryParams]);
                 PersonalizationRouter.instance = original;
-              }
+              },
             } as PersonalizationRouter;
           };
         });
diff --git a/chrome/test/data/webui/chromeos/personalization_app/ambient_subpage_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/ambient_subpage_element_test.ts
index 8e1d076..7bca1995 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/ambient_subpage_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/ambient_subpage_element_test.ts
@@ -438,7 +438,7 @@
   test('has albums subpage visible with path ambient albums', async () => {
     ambientSubpageElement = initElement(AmbientSubpage, {
       path: Paths.AMBIENT_ALBUMS,
-      queryParams: {topicSource: TopicSource.kArtGallery}
+      queryParams: {topicSource: TopicSource.kArtGallery},
     });
     personalizationStore.data.ambient.ambientModeEnabled = true;
     personalizationStore.notifyObservers();
@@ -472,7 +472,7 @@
   test('show placeholders when no albums on albums subpage', async () => {
     ambientSubpageElement = initElement(AmbientSubpage, {
       path: Paths.AMBIENT_ALBUMS,
-      queryParams: {topicSource: TopicSource.kGooglePhotos}
+      queryParams: {topicSource: TopicSource.kGooglePhotos},
     });
     personalizationStore.data.ambient.ambientModeEnabled = true;
     personalizationStore.notifyObservers();
@@ -511,7 +511,7 @@
   test('has correct albums on Google Photos albums subpage', async () => {
     ambientSubpageElement = initElement(AmbientSubpage, {
       path: Paths.AMBIENT_ALBUMS,
-      queryParams: {topicSource: TopicSource.kGooglePhotos}
+      queryParams: {topicSource: TopicSource.kGooglePhotos},
     });
     personalizationStore.data.ambient.ambientModeEnabled = true;
     personalizationStore.data.ambient.albums = ambientProvider.albums;
@@ -535,7 +535,7 @@
   test('has correct albums on Art albums subpage', async () => {
     ambientSubpageElement = initElement(AmbientSubpage, {
       path: Paths.AMBIENT_ALBUMS,
-      queryParams: {topicSource: TopicSource.kArtGallery}
+      queryParams: {topicSource: TopicSource.kArtGallery},
     });
     personalizationStore.data.ambient.ambientModeEnabled = true;
     personalizationStore.data.ambient.albums = ambientProvider.albums;
@@ -564,7 +564,7 @@
     personalizationStore.expectAction(AmbientActionName.SET_ALBUMS);
     ambientSubpageElement = initElement(AmbientSubpage, {
       path: Paths.AMBIENT_ALBUMS,
-      queryParams: {topicSource: TopicSource.kArtGallery}
+      queryParams: {topicSource: TopicSource.kArtGallery},
     });
 
     await ambientProvider.whenCalled('setAmbientObserver');
@@ -622,7 +622,7 @@
     personalizationStore.expectAction(AmbientActionName.SET_ALBUMS);
     ambientSubpageElement = initElement(AmbientSubpage, {
       path: Paths.AMBIENT_ALBUMS,
-      queryParams: {topicSource: TopicSource.kArtGallery}
+      queryParams: {topicSource: TopicSource.kArtGallery},
     });
 
     await ambientProvider.whenCalled('setAmbientObserver');
@@ -729,7 +729,7 @@
             /*ambientModeEnabled=*/ true);
         personalizationStore.data.ambient.googlePhotosAlbumsPreviews = [
           ambientProvider.googlePhotosAlbumsPreviews[0],
-          ambientProvider.googlePhotosAlbumsPreviews[1]
+          ambientProvider.googlePhotosAlbumsPreviews[1],
         ];
         personalizationStore.notifyObservers();
         await waitAfterNextRender(ambientSubpageElement);
diff --git a/chrome/test/data/webui/chromeos/personalization_app/google_photos_albums_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/google_photos_albums_element_test.ts
index bd2812a..87003e3 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/google_photos_albums_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/google_photos_albums_element_test.ts
@@ -53,20 +53,20 @@
         id: '9bd1d7a3-f995-4445-be47-53c5b58ce1cb',
         title: 'Album 0',
         photoCount: 0,
-        preview: {url: 'foo.com'}
+        preview: {url: 'foo.com'},
       },
       {
         id: '0ec40478-9712-42e1-b5bf-3e75870ca042',
         title: 'Album 1',
         photoCount: 1,
-        preview: {url: 'bar.com'}
+        preview: {url: 'bar.com'},
       },
       {
         id: '0a268a37-877a-4936-81d4-38cc84b0f596',
         title: 'Album 2',
         photoCount: 2,
-        preview: {url: 'baz.com'}
-      }
+        preview: {url: 'baz.com'},
+      },
     ];
 
     // Set values returned by |wallpaperProvider|.
@@ -245,7 +245,7 @@
             id: `id-${nextAlbumId}`,
             title: `title-${nextAlbumId}`,
             photoCount: 1,
-            preview: {url: `url-${nextAlbumId++}`}
+            preview: {url: `url-${nextAlbumId++}`},
           };
         }));
 
@@ -271,7 +271,7 @@
             id: `id-${nextAlbumId}`,
             title: `title-${nextAlbumId}`,
             photoCount: 1,
-            preview: {url: `url-${nextAlbumId++}`}
+            preview: {url: `url-${nextAlbumId++}`},
           };
         }));
 
diff --git a/chrome/test/data/webui/chromeos/personalization_app/google_photos_collection_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/google_photos_collection_element_test.ts
index c1622c1..41a0869a 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/google_photos_collection_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/google_photos_collection_element_test.ts
@@ -48,7 +48,7 @@
       name: 'foo',
       date: {data: []},
       url: {url: 'foo.com'},
-      location: 'home'
+      location: 'home',
     }]);
 
     googlePhotosCollectionElement =
@@ -144,7 +144,7 @@
       id: '9bd1d7a3-f995-4445-be47-53c5b58ce1cb',
       title: 'Album 0',
       photoCount: 1,
-      preview: {url: 'foo.com'}
+      preview: {url: 'foo.com'},
     }];
     wallpaperProvider.setGooglePhotosAlbums(albums);
     wallpaperProvider.setGooglePhotosPhotos([{
@@ -153,7 +153,7 @@
       name: 'foo',
       date: {data: []},
       url: {url: 'foo.com'},
-      location: 'home'
+      location: 'home',
     }]);
 
     googlePhotosCollectionElement =
diff --git a/chrome/test/data/webui/chromeos/personalization_app/google_photos_photos_by_album_id_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/google_photos_photos_by_album_id_element_test.ts
index 3067613..67a5cd5 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/google_photos_photos_by_album_id_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/google_photos_photos_by_album_id_element_test.ts
@@ -137,7 +137,7 @@
           name: 'foo',
           date: {data: []},
           url: {url: 'foo.com'},
-          location: 'home1'
+          location: 'home1',
         },
         {
           id: '0ec40478-9712-42e1-b5bf-3e75870ca042',
@@ -145,7 +145,7 @@
           name: 'bar',
           date: {data: []},
           url: {url: 'bar.com'},
-          location: 'home2'
+          location: 'home2',
         },
       ],
       [otherAlbum.id]: [
@@ -155,7 +155,7 @@
           name: 'baz',
           date: {data: []},
           url: {url: 'baz.com'},
-          location: 'home3'
+          location: 'home3',
         },
       ],
     };
@@ -263,7 +263,7 @@
       name: 'foo',
       date: {data: []},
       url: {url: 'foo.com'},
-      location: 'home1'
+      location: 'home1',
     };
 
     const anotherPhoto: GooglePhotosPhoto = {
@@ -272,7 +272,7 @@
       name: 'bar',
       date: {data: []},
       url: {url: 'bar.com'},
-      location: 'home2'
+      location: 'home2',
     };
 
     const yetAnotherPhoto: GooglePhotosPhoto = {
@@ -281,7 +281,7 @@
       name: 'baz',
       date: {data: []},
       url: {url: 'baz.com'},
-      location: 'home3'
+      location: 'home3',
     };
 
     // Set values returned by |wallpaperProvider|.
@@ -333,7 +333,7 @@
       attribution: [],
       layout: WallpaperLayout.kCenter,
       type: WallpaperType.kOnceGooglePhotos,
-      key: photo.id
+      key: photo.id,
     };
     personalizationStore.notifyObservers();
     await waitAfterNextRender(googlePhotosPhotosByAlbumIdElement);
@@ -358,7 +358,7 @@
       attribution: [],
       layout: WallpaperLayout.kCenter,
       type: WallpaperType.kOnceGooglePhotos,
-      key: anotherPhoto.id
+      key: anotherPhoto.id,
     };
     personalizationStore.notifyObservers();
     await waitAfterNextRender(googlePhotosPhotosByAlbumIdElement);
@@ -383,7 +383,7 @@
       attribution: [],
       layout: WallpaperLayout.kCenter,
       type: WallpaperType.kOnceGooglePhotos,
-      key: yetAnotherPhoto.dedupKey
+      key: yetAnotherPhoto.dedupKey,
     };
     personalizationStore.notifyObservers();
     await waitAfterNextRender(googlePhotosPhotosByAlbumIdElement);
@@ -408,7 +408,7 @@
       attribution: [],
       layout: WallpaperLayout.kCenter,
       type: WallpaperType.kCustomized,
-      key: '//foo'
+      key: '//foo',
     };
     personalizationStore.notifyObservers();
     await waitAfterNextRender(googlePhotosPhotosByAlbumIdElement);
@@ -674,7 +674,7 @@
       name: 'foo',
       date: {data: []},
       url: {url: 'foo.com'},
-      location: 'home'
+      location: 'home',
     };
 
     // Initialize Google Photos data in the |personalizationStore|.
diff --git a/chrome/test/data/webui/chromeos/personalization_app/google_photos_photos_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/google_photos_photos_element_test.ts
index e8492cb..c5cd557a 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/google_photos_photos_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/google_photos_photos_element_test.ts
@@ -154,7 +154,7 @@
         name: '1',
         date: toString16('First row'),
         url: {url: '1'},
-        location: '1'
+        location: '1',
       },
       // Second row.
       {
@@ -163,7 +163,7 @@
         name: '2',
         date: toString16('Second row'),
         url: {url: '2'},
-        location: '2'
+        location: '2',
       },
       {
         id: '3',
@@ -171,7 +171,7 @@
         name: '3',
         date: toString16('Second row'),
         url: {url: '3'},
-        location: '3'
+        location: '3',
       },
       // Third row.
       {
@@ -180,8 +180,8 @@
         name: '4',
         date: toString16('Third row'),
         url: {url: '4'},
-        location: '4'
-      }
+        location: '4',
+      },
     ];
 
     // Set values returned by |wallpaperProvider|.
@@ -313,7 +313,7 @@
         name: 'foo',
         date: toString16('Wednesday, February 16, 2022'),
         url: {url: 'foo.com'},
-        location: undefined
+        location: undefined,
       },
       // Section of photos with one location.
       {
@@ -322,7 +322,7 @@
         name: 'bar',
         date: toString16('Friday, November 12, 2021'),
         url: {url: 'bar.com'},
-        location: 'home1'
+        location: 'home1',
       },
       {
         id: '0a268a37-877a-4936-81d4-38cc84b0f596',
@@ -330,7 +330,7 @@
         name: 'baz',
         date: toString16('Friday, November 12, 2021'),
         url: {url: 'baz.com'},
-        location: 'home1'
+        location: 'home1',
       },
       // Section of photos with different locations.
       {
@@ -339,7 +339,7 @@
         name: 'bare',
         date: toString16('Friday, July 16, 2021'),
         url: {url: 'bare.com'},
-        location: 'home2'
+        location: 'home2',
       },
       {
         id: '0a268a11-877a-4936-81d4-38cc8s9dn396',
@@ -347,8 +347,8 @@
         name: 'baze',
         date: toString16('Friday, July 16, 2021'),
         url: {url: 'baze.com'},
-        location: 'home3'
-      }
+        location: 'home3',
+      },
     ];
 
     const sections =
@@ -432,7 +432,7 @@
       name: 'foo',
       date: {data: []},
       url: {url: 'foo.com'},
-      location: 'home1'
+      location: 'home1',
     };
 
     const anotherPhoto: GooglePhotosPhoto = {
@@ -441,7 +441,7 @@
       name: 'bar',
       date: {data: []},
       url: {url: 'bar.com'},
-      location: 'home2'
+      location: 'home2',
     };
 
     const yetAnotherPhoto: GooglePhotosPhoto = {
@@ -450,7 +450,7 @@
       name: 'baz',
       date: {data: []},
       url: {url: 'baz.com'},
-      location: 'home3'
+      location: 'home3',
     };
 
     // Set values returned by |wallpaperProvider|.
@@ -498,7 +498,7 @@
       attribution: [],
       layout: WallpaperLayout.kCenter,
       type: WallpaperType.kOnceGooglePhotos,
-      key: photo.id
+      key: photo.id,
     };
     personalizationStore.notifyObservers();
     await waitAfterNextRender(googlePhotosPhotosElement);
@@ -524,7 +524,7 @@
       attribution: [],
       layout: WallpaperLayout.kCenter,
       type: WallpaperType.kOnceGooglePhotos,
-      key: anotherPhoto.dedupKey
+      key: anotherPhoto.dedupKey,
     };
     personalizationStore.notifyObservers();
     await waitAfterNextRender(googlePhotosPhotosElement);
@@ -551,7 +551,7 @@
       attribution: [],
       layout: WallpaperLayout.kCenter,
       type: WallpaperType.kCustomized,
-      key: '//foo'
+      key: '//foo',
     };
     personalizationStore.notifyObservers();
     await waitAfterNextRender(googlePhotosPhotosElement);
@@ -800,7 +800,7 @@
       name: 'foo',
       date: {data: []},
       url: {url: 'foo.com'},
-      location: 'home'
+      location: 'home',
     };
 
     // Set values returned by |wallpaperProvider|.
diff --git a/chrome/test/data/webui/chromeos/personalization_app/local_images_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/local_images_element_test.ts
index 00addd20..ec9b5be9 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/local_images_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/local_images_element_test.ts
@@ -59,7 +59,7 @@
     };
     personalizationStore.data.wallpaper.loading.local = {
       images: false,
-      data: {}
+      data: {},
     };
 
     localImagesElement = initElement(LocalImages, {hidden: false});
@@ -104,7 +104,7 @@
         };
         personalizationStore.data.wallpaper.loading.local = {
           images: false,
-          data: {}
+          data: {},
         };
 
         localImagesElement = initElement(LocalImages, {hidden: false});
@@ -120,7 +120,7 @@
 
         // Set loading finished for first thumbnail.
         personalizationStore.data.wallpaper.loading.local.data = {
-          'LocalImage0.png': false
+          'LocalImage0.png': false,
         };
         personalizationStore.notifyObservers();
         await waitAfterNextRender(localImagesElement);
@@ -132,11 +132,11 @@
         // Set loading failed for second thumbnail.
         personalizationStore.data.wallpaper.loading.local.data = {
           'LocalImage0.png': false,
-          'LocalImage1.png': false
+          'LocalImage1.png': false,
         };
         personalizationStore.data.wallpaper.local.data = {
           'LocalImage0.png': 'data://localimage0data',
-          'LocalImage1.png': null
+          'LocalImage1.png': null,
         };
         personalizationStore.notifyObservers();
         await waitAfterNextRender(localImagesElement);
@@ -151,7 +151,8 @@
       async () => {
         personalizationStore.data.wallpaper.local = {
           images: [
-            {path: '/test/LocalImage0.png'}, {path: '/test/LocalImage1.png'}
+            {path: '/test/LocalImage0.png'},
+            {path: '/test/LocalImage1.png'},
           ],
           data: {
             '/test/LocalImage0.png': 'data://localimage0data',
@@ -179,7 +180,7 @@
             image => image.getAttribute('aria-selected') === 'false'));
 
         personalizationStore.data.wallpaper.currentSelected = {
-          key: '/test/LocalImage1.png'
+          key: '/test/LocalImage1.png',
         };
         personalizationStore.notifyObservers();
 
@@ -225,7 +226,7 @@
       images: [kDefaultImageSymbol, ...wallpaperProvider.localImages!],
       data: {
         [kDefaultImageSymbol]: wallpaperProvider.defaultImageThumbnail,
-        ...wallpaperProvider.localImageData
+        ...wallpaperProvider.localImageData,
       },
     };
 
diff --git a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_component_browsertest.js b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_component_browsertest.js
index 6e5c0c4..9e71591 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_component_browsertest.js
+++ b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_component_browsertest.js
@@ -40,13 +40,14 @@
  ['GooglePhotosCollectionTest', 'google_photos_collection_element_test.js'],
  [
    'GooglePhotosPhotosByAlbumIdTest',
-   'google_photos_photos_by_album_id_element_test.js'
+   'google_photos_photos_by_album_id_element_test.js',
  ],
  ['GooglePhotosPhotosTest', 'google_photos_photos_element_test.js'],
  ['KeyboardBacklightTest', 'keyboard_backlight_element_test.js'],
  ['LocalImagesTest', 'local_images_element_test.js'],
  [
-   'PersonalizationBreadcrumbTest', 'personalization_breadcrumb_element_test.js'
+   'PersonalizationBreadcrumbTest',
+   'personalization_breadcrumb_element_test.js',
  ],
  ['PersonalizationMainTest', 'personalization_main_element_test.js'],
  ['PersonalizationRouterTest', 'personalization_router_element_test.js'],
diff --git a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_controller_test.ts b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_controller_test.ts
index e2abe57e..0158d03f 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_controller_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_controller_test.ts
@@ -123,7 +123,7 @@
       name: 'foo',
       date: {data: []},
       url: {url: 'foo.com'},
-      location: 'home'
+      location: 'home',
     }];
 
     wallpaperProvider.setGooglePhotosAlbums([album]);
@@ -248,7 +248,7 @@
           // Begin loading local image list.
           {
             'wallpaper.loading.local': {images: true, data: {}},
-            'wallpaper.local': {images: null, data: {}}
+            'wallpaper.local': {images: null, data: {}},
           },
           // Done loading local image data.
           {
@@ -258,8 +258,8 @@
                 {path: 'LocalImage0.png'},
                 {path: 'LocalImage1.png'},
               ],
-              data: {}
-            }
+              data: {},
+            },
           },
           // Mark image 0 as loading.
           {
@@ -284,7 +284,7 @@
                 {path: 'LocalImage1.png'},
               ],
               data: {},
-            }
+            },
           },
           // Finish loading image 0.
           {
@@ -298,7 +298,7 @@
                 {path: 'LocalImage1.png'},
               ],
               data: {'LocalImage0.png': 'data://localimage0data'},
-            }
+            },
           },
           // Finish loading image 1.
           {
@@ -315,8 +315,8 @@
                 'LocalImage0.png': 'data://localimage0data',
                 'LocalImage1.png': 'data://localimage1data',
               },
-            }
-          }
+            },
+          },
         ],
         personalizationStore.states.map(filterAndFlattenState(
             ['wallpaper.local', 'wallpaper.loading.local'])));
@@ -411,7 +411,7 @@
             name: 'set_local_image_data',
             id: 'NewPath.png',
             data: 'data://newpath',
-          }
+          },
         ],
         personalizationStore.actions,
     );
@@ -470,7 +470,7 @@
                 'NewPath.png': 'data://newpath',
               },
             },
-          }
+          },
         ],
         personalizationStore.states.map(filterAndFlattenState(
             ['wallpaper.local', 'wallpaper.loading.local'])));
@@ -867,7 +867,7 @@
           name: 'foo',
           date: {data: []},
           url: {url: 'foo.com'},
-          location: 'home'
+          location: 'home',
         };
         // Reset the history of actions and prior states, but keep the current
         // state.
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 682065c..40dccff4 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
@@ -69,7 +69,7 @@
     themeProvider,
     userProvider,
     wallpaperProvider,
-    personalizationStore
+    personalizationStore,
   };
 }
 
diff --git a/chrome/test/data/webui/chromeos/personalization_app/personalization_breadcrumb_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/personalization_breadcrumb_element_test.ts
index 9dfbdd7..332f843 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/personalization_breadcrumb_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/personalization_breadcrumb_element_test.ts
@@ -106,7 +106,7 @@
           goToRoute(path: Paths, queryParams: Object = {}) {
             resolve([path, queryParams]);
             PersonalizationRouter.instance = original;
-          }
+          },
         } as PersonalizationRouter;
       };
     });
@@ -169,7 +169,7 @@
           goToRoute(path: Paths, queryParams: Object = {}) {
             resolve([path, queryParams]);
             PersonalizationRouter.instance = original;
-          }
+          },
         } as PersonalizationRouter;
       };
     });
@@ -199,7 +199,7 @@
 
     breadcrumbElement = initElement(PersonalizationBreadcrumb, {
       'path': Paths.GOOGLE_PHOTOS_COLLECTION,
-      'googlePhotosAlbumId': googlePhotosAlbum.id
+      'googlePhotosAlbumId': googlePhotosAlbum.id,
     });
 
     const breadcrumbContainer =
@@ -207,7 +207,8 @@
     assertTrue(!!breadcrumbContainer && !breadcrumbContainer.hidden);
     assertBreadcrumbs(breadcrumbContainer, [
       breadcrumbElement.i18n('wallpaperLabel'),
-      breadcrumbElement.i18n('googlePhotosLabel'), googlePhotosAlbum.title
+      breadcrumbElement.i18n('googlePhotosLabel'),
+      googlePhotosAlbum.title,
     ]);
 
     const original = PersonalizationRouter.instance;
@@ -217,7 +218,7 @@
           goToRoute(path: Paths, queryParams: Object = {}) {
             resolve([path, queryParams]);
             PersonalizationRouter.instance = original;
-          }
+          },
         } as PersonalizationRouter;
       };
     });
@@ -246,7 +247,7 @@
     assertTrue(!!breadcrumbContainer && !breadcrumbContainer.hidden);
     assertBreadcrumbs(breadcrumbContainer, [
       breadcrumbElement.i18n('wallpaperLabel'),
-      breadcrumbElement.i18n('googlePhotosLabel')
+      breadcrumbElement.i18n('googlePhotosLabel'),
     ]);
 
     const original = PersonalizationRouter.instance;
@@ -256,7 +257,7 @@
           goToRoute(path: Paths, queryParams: Object = {}) {
             resolve([path, queryParams]);
             PersonalizationRouter.instance = original;
-          }
+          },
         } as PersonalizationRouter;
       };
     });
@@ -286,7 +287,7 @@
     assertTrue(!!breadcrumbContainer && !breadcrumbContainer.hidden);
     assertBreadcrumbs(breadcrumbContainer, [
       breadcrumbElement.i18n('wallpaperLabel'),
-      breadcrumbElement.i18n('myImagesLabel')
+      breadcrumbElement.i18n('myImagesLabel'),
     ]);
 
     const original = PersonalizationRouter.instance;
@@ -296,7 +297,7 @@
           goToRoute(path: Paths, queryParams: Object = {}) {
             resolve([path, queryParams]);
             PersonalizationRouter.instance = original;
-          }
+          },
         } as PersonalizationRouter;
       };
     });
@@ -344,7 +345,7 @@
 
         breadcrumbElement = initElement(PersonalizationBreadcrumb, {
           'path': Paths.AMBIENT_ALBUMS,
-          'topicSource': TopicSource.kGooglePhotos
+          'topicSource': TopicSource.kGooglePhotos,
         });
 
         const breadcrumbContainer =
@@ -352,7 +353,7 @@
         assertTrue(!!breadcrumbContainer && !breadcrumbContainer.hidden);
         assertBreadcrumbs(breadcrumbContainer, [
           breadcrumbElement.i18n('screensaverLabel'),
-          breadcrumbElement.i18n('ambientModeTopicSourceGooglePhotos')
+          breadcrumbElement.i18n('ambientModeTopicSourceGooglePhotos'),
         ]);
 
         const original = PersonalizationRouter.instance;
@@ -362,7 +363,7 @@
               goToRoute(path: Paths, queryParams: Object = {}) {
                 resolve([path, queryParams]);
                 PersonalizationRouter.instance = original;
-              }
+              },
             } as PersonalizationRouter;
           };
         });
@@ -386,7 +387,7 @@
 
         breadcrumbElement = initElement(PersonalizationBreadcrumb, {
           'path': Paths.AMBIENT_ALBUMS,
-          'topicSource': TopicSource.kArtGallery
+          'topicSource': TopicSource.kArtGallery,
         });
 
         const breadcrumbContainer =
@@ -394,7 +395,7 @@
         assertTrue(!!breadcrumbContainer && !breadcrumbContainer.hidden);
         assertBreadcrumbs(breadcrumbContainer, [
           breadcrumbElement.i18n('screensaverLabel'),
-          breadcrumbElement.i18n('ambientModeTopicSourceArtGallery')
+          breadcrumbElement.i18n('ambientModeTopicSourceArtGallery'),
         ]);
 
         const original = PersonalizationRouter.instance;
@@ -404,7 +405,7 @@
               goToRoute(path: Paths, queryParams: Object = {}) {
                 resolve([path, queryParams]);
                 PersonalizationRouter.instance = original;
-              }
+              },
             } as PersonalizationRouter;
           };
         });
diff --git a/chrome/test/data/webui/chromeos/personalization_app/personalization_main_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/personalization_main_element_test.ts
index 3fb58637..9e56fb6e 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/personalization_main_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/personalization_main_element_test.ts
@@ -33,7 +33,7 @@
           goToRoute(path: Paths, queryParams: Object = {}) {
             resolve([path, queryParams]);
             PersonalizationRouter.instance = original;
-          }
+          },
         } as PersonalizationRouter;
       };
     });
diff --git a/chrome/test/data/webui/chromeos/personalization_app/personalization_toast_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/personalization_toast_element_test.ts
index 934dc0d0..6434acf9 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/personalization_toast_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/personalization_toast_element_test.ts
@@ -74,7 +74,7 @@
 
     personalizationStore.data.error = {
       message: 'There was an error',
-      dismiss: {callback: dismissCallback}
+      dismiss: {callback: dismissCallback},
     };
     personalizationStore.notifyObservers();
     await waitAfterNextRender(personalizationToastElement);
diff --git a/chrome/test/data/webui/chromeos/personalization_app/test_ambient_interface_provider.ts b/chrome/test/data/webui/chromeos/personalization_app/test_ambient_interface_provider.ts
index e3e224e..ccb7252 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/test_ambient_interface_provider.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/test_ambient_interface_provider.ts
@@ -16,7 +16,7 @@
       description: '0',
       numberOfPhotos: 0,
       topicSource: TopicSource.kArtGallery,
-      url: {url: 'http://test_url0'}
+      url: {url: 'http://test_url0'},
     },
     {
       id: '1',
@@ -25,7 +25,7 @@
       description: '1',
       numberOfPhotos: 0,
       topicSource: TopicSource.kArtGallery,
-      url: {url: 'http://test_url1'}
+      url: {url: 'http://test_url1'},
     },
     {
       id: '2',
@@ -34,7 +34,7 @@
       description: '2',
       numberOfPhotos: 0,
       topicSource: TopicSource.kArtGallery,
-      url: {url: 'http://test_url2'}
+      url: {url: 'http://test_url2'},
     },
     {
       id: '3',
@@ -43,8 +43,8 @@
       description: '3',
       numberOfPhotos: 1,
       topicSource: TopicSource.kGooglePhotos,
-      url: {url: 'http://test_url3'}
-    }
+      url: {url: 'http://test_url3'},
+    },
   ];
 
   public googlePhotosAlbumsPreviews: Url[] = [
diff --git a/chrome/test/data/webui/chromeos/personalization_app/test_wallpaper_interface_provider.ts b/chrome/test/data/webui/chromeos/personalization_app/test_wallpaper_interface_provider.ts
index 72103e85..571705b8 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/test_wallpaper_interface_provider.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/test_wallpaper_interface_provider.ts
@@ -46,20 +46,20 @@
       {
         id: 'id_0',
         name: 'zero',
-        previews: [{url: 'https://collections.googleusercontent.com/0'}]
+        previews: [{url: 'https://collections.googleusercontent.com/0'}],
       },
       {
         id: 'id_1',
         name: 'one',
-        previews: [{url: 'https://collections.googleusercontent.com/1'}]
+        previews: [{url: 'https://collections.googleusercontent.com/1'}],
       },
       {
         id: 'id_2',
         name: 'dark-light',
         previews: [
           {url: 'https://collections.googleusercontent.com/2'},
-          {url: 'https://collections.googleusercontent.com/3'}
-        ]
+          {url: 'https://collections.googleusercontent.com/3'},
+        ],
       },
     ];
 
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 c833824..2fdc60b 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
@@ -39,7 +39,7 @@
 
     personalizationStore.data.wallpaper.loading = {
       ...personalizationStore.data.wallpaper.loading,
-      collections: false
+      collections: false,
     };
     personalizationStore.data.wallpaper.backdrop.collections =
         wallpaperProvider.collections;
@@ -135,7 +135,7 @@
 
     testProxy.resetResolver('sendImageCounts');
     personalizationStore.data.wallpaper.backdrop.images = {
-      'id_0': [wallpaperProvider.images![0]]
+      'id_0': [wallpaperProvider.images![0]],
     };
     personalizationStore.data.wallpaper.loading.images = {'id_0': false};
     personalizationStore.notifyObservers();
@@ -179,7 +179,7 @@
     personalizationStore.data.wallpaper.loading = {
       ...personalizationStore.data.wallpaper.loading,
       collections: false,
-      local: {images: false, data: {[kDefaultImageSymbol]: false}}
+      local: {images: false, data: {[kDefaultImageSymbol]: false}},
     };
     personalizationStore.data.wallpaper.local.images =
         wallpaperProvider.localImages;
@@ -368,10 +368,10 @@
 
         // First thumbnail loads in.
         personalizationStore.data.wallpaper.loading.local.data = {
-          'LocalImage0.png': false
+          'LocalImage0.png': false,
         };
         personalizationStore.data.wallpaper.local.data = {
-          'LocalImage0.png': 'local_data_0'
+          'LocalImage0.png': 'local_data_0',
         };
         personalizationStore.notifyObservers();
 
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 e735535..3c80baa 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
@@ -118,7 +118,7 @@
         wallpaperProvider.collections;
     personalizationStore.data.wallpaper.loading.images = {
       'id_0': false,
-      'id_1': false
+      'id_1': false,
     };
     personalizationStore.data.wallpaper.loading.collections = false;
 
diff --git a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_preview_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_preview_element_test.ts
index 6fac054..3ba110b3 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_preview_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_preview_element_test.ts
@@ -134,7 +134,7 @@
 
     personalizationStore.data.wallpaper.currentSelected = {
       ...personalizationStore.data.wallpaper.currentSelected,
-      type: WallpaperType.kPolicy
+      type: WallpaperType.kPolicy,
     };
     personalizationStore.notifyObservers();
     await waitAfterNextRender(wallpaperPreviewElement);
diff --git a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_selected_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_selected_element_test.ts
index ac0a6ed0..1886e57 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_selected_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_selected_element_test.ts
@@ -247,7 +247,7 @@
 
         wallpaperSelectedElement = initElement(WallpaperSelected, {
           'path': Paths.GOOGLE_PHOTOS_COLLECTION,
-          'googlePhotosAlbumId': ''
+          'googlePhotosAlbumId': '',
         });
         await waitAfterNextRender(wallpaperSelectedElement);
 
@@ -307,7 +307,7 @@
 
         wallpaperSelectedElement = initElement(WallpaperSelected, {
           'path': Paths.GOOGLE_PHOTOS_COLLECTION,
-          'googlePhotosAlbumId': album_id
+          'googlePhotosAlbumId': album_id,
         });
         personalizationStore.notifyObservers();
 
diff --git a/chrome/test/data/webui/chromeos/print_management/print_management_test.js b/chrome/test/data/webui/chromeos/print_management/print_management_test.js
index dc0566f..ca7824b 100644
--- a/chrome/test/data/webui/chromeos/print_management/print_management_test.js
+++ b/chrome/test/data/webui/chromeos/print_management/print_management_test.js
@@ -586,7 +586,7 @@
       createJobEntry(
           'oldest', 'titleC',
           convertToMojoTime(new Date(Date.UTC(2020, 1, 1, 1, 1, 1))),
-          PrinterErrorCode.NO_ERROR, completedInfo, /*activeInfo=*/ null)
+          PrinterErrorCode.NO_ERROR, completedInfo, /*activeInfo=*/ null),
     ];
 
     // Initialize with a reversed array of |expectedArr|, since we expect the
@@ -1061,7 +1061,7 @@
 
     // Change date and assert it shows the correct date (Feb 5, 2020);
     jobEntryTestElement.set('jobEntry.creationTime', {
-      internalValue: convertToMojoTime(new Date('February 5, 2020 03:24:00'))
+      internalValue: convertToMojoTime(new Date('February 5, 2020 03:24:00')),
     });
     assertEquals(
         'Feb 5, 2020',
diff --git a/chrome/test/data/webui/chromeos/scanning/page_size_select_test.js b/chrome/test/data/webui/chromeos/scanning/page_size_select_test.js
index e101db5..a5d9ef213 100644
--- a/chrome/test/data/webui/chromeos/scanning/page_size_select_test.js
+++ b/chrome/test/data/webui/chromeos/scanning/page_size_select_test.js
@@ -72,8 +72,13 @@
   // Verify the pages sizes are sorted correctly.
   test('pageSizesSortedCorrectly', () => {
     pageSizeSelect.options = [
-      PageSize.Tabloid, PageSize.Letter, PageSize.A3, PageSize.Max,
-      PageSize.Legal, PageSize.B4, PageSize.A4
+      PageSize.Tabloid,
+      PageSize.Letter,
+      PageSize.A3,
+      PageSize.Max,
+      PageSize.Legal,
+      PageSize.B4,
+      PageSize.A4,
     ];
     flush();
 
diff --git a/chrome/test/data/webui/chromeos/scanning/scanner_select_test.js b/chrome/test/data/webui/chromeos/scanning/scanner_select_test.js
index 11e1970c..013d2aa 100644
--- a/chrome/test/data/webui/chromeos/scanning/scanner_select_test.js
+++ b/chrome/test/data/webui/chromeos/scanning/scanner_select_test.js
@@ -48,7 +48,7 @@
 
     const scannerArr = [
       createScanner(firstScannerId, firstScannerName),
-      createScanner(secondScannerId, secondScannerName)
+      createScanner(secondScannerId, secondScannerName),
     ];
     scannerSelect.scanners = scannerArr;
     flush();
@@ -64,7 +64,7 @@
   test('scannersSortedAlphabetically', () => {
     const scanners = [
       createScanner(secondScannerId, secondScannerName),
-      createScanner(firstScannerId, firstScannerName)
+      createScanner(firstScannerId, firstScannerName),
     ];
     scannerSelect.scanners = scanners;
     flush();
diff --git a/chrome/test/data/webui/chromeos/scanning/scanning_app_browsertest.js b/chrome/test/data/webui/chromeos/scanning/scanning_app_browsertest.js
index 14053ecf..1ebbbc1 100644
--- a/chrome/test/data/webui/chromeos/scanning/scanning_app_browsertest.js
+++ b/chrome/test/data/webui/chromeos/scanning/scanning_app_browsertest.js
@@ -38,10 +38,20 @@
 // You must register all suites in unified test here as well for consistency,
 // although technically is not necessary.
 const debug_suites_list = [
-  'ActionToolbar', 'ColorModeSelect', 'FileTypeSelect', 'LoadingPage',
-  'MultiPageCheckbox', 'MultiPageScan', 'PageSizeSelect', 'ResolutionSelect',
-  'ScanApp', 'ScanDoneSection', 'ScannerSelect', 'ScanPreview', 'ScanToSelect',
-  'SourceSelect'
+  'ActionToolbar',
+  'ColorModeSelect',
+  'FileTypeSelect',
+  'LoadingPage',
+  'MultiPageCheckbox',
+  'MultiPageScan',
+  'PageSizeSelect',
+  'ResolutionSelect',
+  'ScanApp',
+  'ScanDoneSection',
+  'ScannerSelect',
+  'ScanPreview',
+  'ScanToSelect',
+  'SourceSelect',
 ];
 
 // Flaky. See crbug.com/1334465
diff --git a/chrome/test/data/webui/chromeos/scanning/scanning_app_test.js b/chrome/test/data/webui/chromeos/scanning/scanning_app_test.js
index 01772df0..a89612e 100644
--- a/chrome/test/data/webui/chromeos/scanning/scanning_app_test.js
+++ b/chrome/test/data/webui/chromeos/scanning/scanning_app_test.js
@@ -79,7 +79,7 @@
     createScannerSource(
         SourceType.FLATBED, PLATEN, secondPageSizes, firstColorModes,
         firstResolutions),
-  ]
+  ],
 };
 
 const secondCapabilities = {
@@ -90,7 +90,7 @@
     createScannerSource(
         SourceType.ADF_SIMPLEX, ADF_SIMPLEX, secondPageSizes, secondColorModes,
         secondResolutions),
-  ]
+  ],
 };
 
 /** @implements {ash.scanning.mojom.ScanServiceInterface} */
@@ -291,7 +291,7 @@
       this.scanJobObserverRemote_ = remote;
       this.methodCalled('startMultiPageScan');
       resolve({
-        controller: this.failStartScan_ ? null : this.multiPageScanController_
+        controller: this.failStartScan_ ? null : this.multiPageScanController_,
       });
     });
   }
@@ -469,7 +469,7 @@
   /** @type {!ScannerArr} */
   const expectedScanners = [
     createScanner(firstScannerId, firstScannerName),
-    createScanner(secondScannerId, secondScannerName)
+    createScanner(secondScannerId, secondScannerName),
   ];
 
   suiteSetup(() => {
diff --git a/chrome/test/data/webui/chromeos/scanning/test_scanning_browser_proxy.js b/chrome/test/data/webui/chromeos/scanning/test_scanning_browser_proxy.js
index c6a48ed..0404a7f 100644
--- a/chrome/test/data/webui/chromeos/scanning/test_scanning_browser_proxy.js
+++ b/chrome/test/data/webui/chromeos/scanning/test_scanning_browser_proxy.js
@@ -9,7 +9,7 @@
 
 const EMPTY_SELECTED_PATH = {
   baseName: '',
-  filePath: ''
+  filePath: '',
 };
 
 /**
diff --git a/chrome/test/data/webui/chromeos/shimless_rma/fake_shimless_rma_service_test.js b/chrome/test/data/webui/chromeos/shimless_rma/fake_shimless_rma_service_test.js
index cb4e1bdc..a5573e78 100644
--- a/chrome/test/data/webui/chromeos/shimless_rma/fake_shimless_rma_service_test.js
+++ b/chrome/test/data/webui/chromeos/shimless_rma/fake_shimless_rma_service_test.js
@@ -33,7 +33,7 @@
         state: State.kWelcomeScreen,
         canExit: true,
         canGoBack: false,
-        error: RmadErrorCode.kOk
+        error: RmadErrorCode.kOk,
       },
     ];
     service.setStates(states);
@@ -333,11 +333,11 @@
     const expectedComponents = [
       {
         component: ComponentType.kKeyboard,
-        state: ComponentRepairStatus.kOriginal
+        state: ComponentRepairStatus.kOriginal,
       },
       {
         component: ComponentType.kTouchpad,
-        state: ComponentRepairStatus.kMissing
+        state: ComponentRepairStatus.kMissing,
       },
     ];
     service.setGetComponentListResult(expectedComponents);
@@ -350,7 +350,7 @@
     const components = [
       {
         component: ComponentType.kKeyboard,
-        state: ComponentRepairStatus.kOriginal
+        state: ComponentRepairStatus.kOriginal,
       },
     ];
     const states = [
@@ -370,7 +370,7 @@
     const components = [
       {
         component: ComponentType.kKeyboard,
-        state: ComponentRepairStatus.kOriginal
+        state: ComponentRepairStatus.kOriginal,
       },
     ];
     const states = [
@@ -594,13 +594,13 @@
       ({
         component: ComponentType.kLidAccelerometer,
         status: CalibrationStatus.kCalibrationInProgress,
-        progress: 0.5
+        progress: 0.5,
       }),
       /** @type {!CalibrationComponentStatus} */
       ({
         component: ComponentType.kBaseAccelerometer,
         status: CalibrationStatus.kCalibrationComplete,
-        progress: 1.0
+        progress: 1.0,
       }),
     ];
     service.setGetCalibrationComponentListResult(expectedCalibrationComponents);
@@ -746,7 +746,7 @@
        */
       onError(error) {
         assertEquals(error, RmadErrorCode.kRequestInvalid);
-      }
+      },
     });
     service.observeError(errorObserver);
     return service.triggerErrorObserver(RmadErrorCode.kRequestInvalid, 0);
@@ -763,7 +763,7 @@
       onOsUpdateProgressUpdated(operation, progress) {
         assertEquals(operation, OsUpdateOperation.kDownloading);
         assertEquals(progress, 0.75);
-      }
+      },
     });
     service.observeOsUpdateProgress(osUpdateObserver);
     return service.triggerOsUpdateObserver(
@@ -781,7 +781,7 @@
            */
           onUpdateRoFirmwareStatusChanged(status) {
             assertEquals(UpdateRoFirmwareStatus.kDownloading, status);
-          }
+          },
         });
     service.observeRoFirmwareUpdateProgress(roFirmwareUpdateObserver);
     return service.triggerUpdateRoFirmwareObserver(
@@ -801,7 +801,7 @@
         assertEquals(
             calibrationStatus.status, CalibrationStatus.kCalibrationComplete);
         assertEquals(calibrationStatus.progress, 0.5);
-      }
+      },
     });
     service.observeCalibrationProgress(calibrationObserver);
     return service.triggerCalibrationObserver(
@@ -809,7 +809,7 @@
         ({
           component: ComponentType.kBaseAccelerometer,
           status: CalibrationStatus.kCalibrationComplete,
-          progress: 0.5
+          progress: 0.5,
         }),
         0);
   });
@@ -824,7 +824,7 @@
       onCalibrationStepComplete(status) {
         assertEquals(
             status, CalibrationOverallStatus.kCalibrationOverallComplete);
-      }
+      },
     });
     service.observeCalibrationProgress(calibrationObserver);
     return service.triggerCalibrationOverallObserver(
@@ -842,7 +842,7 @@
       onProvisioningUpdated(status, progress) {
         assertEquals(status, ProvisioningStatus.kInProgress);
         assertEquals(progress, 0.25);
-      }
+      },
     });
     service.observeProvisioningProgress(provisioningObserver);
     return service.triggerProvisioningObserver(
@@ -861,7 +861,7 @@
            */
           onHardwareWriteProtectionStateChanged(enable) {
             assertEquals(enable, true);
-          }
+          },
         });
     service.observeHardwareWriteProtectionState(
         hardwareWriteProtectionStateObserver);
@@ -878,7 +878,7 @@
            */
           onPowerCableStateChanged(enable) {
             assertEquals(enable, true);
-          }
+          },
         });
     service.observePowerCableState(powerCableStateObserver);
     return service.triggerPowerCableObserver(true, 0);
@@ -898,7 +898,7 @@
           onHardwareVerificationResult(isCompliant, errorMessage) {
             assertEquals(true, isCompliant);
             assertEquals('ok', errorMessage);
-          }
+          },
         });
     service.observeHardwareVerificationStatus(observer);
     return service.triggerHardwareVerificationStatusObserver(true, 'ok', 0);
@@ -917,7 +917,7 @@
           onFinalizationUpdated(status, progress) {
             assertEquals(FinalizationStatus.kInProgress, status);
             assertEquals(0.5, progress);
-          }
+          },
         });
     service.observeFinalizationStatus(finalizationObserver);
     return service.triggerFinalizationObserver(
diff --git a/chrome/test/data/webui/chromeos/shimless_rma/shimless_rma_app_test.js b/chrome/test/data/webui/chromeos/shimless_rma/shimless_rma_app_test.js
index 68828fc0..13376bad 100644
--- a/chrome/test/data/webui/chromeos/shimless_rma/shimless_rma_app_test.js
+++ b/chrome/test/data/webui/chromeos/shimless_rma/shimless_rma_app_test.js
@@ -263,7 +263,7 @@
           state: State.kSelectComponents,
           canExit: true,
           canGoBack: true,
-          error: RmadErrorCode.kOk
+          error: RmadErrorCode.kOk,
         }],
         fakeChromeVersion[0]);
 
@@ -287,7 +287,7 @@
           state: State.kSelectComponents,
           canExit: true,
           canGoBack: true,
-          error: RmadErrorCode.kOk
+          error: RmadErrorCode.kOk,
         }],
         fakeChromeVersion[0]);
 
@@ -328,7 +328,7 @@
           state: State.kSelectComponents,
           canExit: true,
           canGoBack: true,
-          error: RmadErrorCode.kOk
+          error: RmadErrorCode.kOk,
         }],
         fakeChromeVersion[0]);
 
@@ -367,7 +367,7 @@
           state: State.kSelectComponents,
           canExit: true,
           canGoBack: true,
-          error: RmadErrorCode.kOk
+          error: RmadErrorCode.kOk,
         }],
         fakeChromeVersion[0]);
 
@@ -444,7 +444,7 @@
           state: State.kWelcomeScreen,
           canExit: true,
           canGoBack: true,
-          error: RmadErrorCode.kOk
+          error: RmadErrorCode.kOk,
         }],
         fakeChromeVersion[0]);
 
@@ -479,8 +479,9 @@
         {
           bubbles: true,
           composed: true,
-          detail: () => Promise.resolve(
-              {stateResult: {state: State.kUpdateOs, error: RmadErrorCode.kOk}})
+          detail: () => Promise.resolve({
+            stateResult: {state: State.kUpdateOs, error: RmadErrorCode.kOk},
+          }),
         },
         ));
     await flushTasks();
@@ -509,9 +510,9 @@
               state: State.kUpdateOs,
               error: RmadErrorCode.kOk,
               canExit: false,
-              canGoBack: false
-            }
-          })
+              canGoBack: false,
+            },
+          }),
         },
         ));
     await flushTasks();
@@ -542,9 +543,9 @@
               state: State.kCheckCalibration,
               error: RmadErrorCode.kOk,
               canExit: false,
-              canGoBack: false
-            }
-          })
+              canGoBack: false,
+            },
+          }),
         },
         ));
     await flushTasks();
diff --git a/chrome/test/data/webui/chromeos/shimless_rma/shimless_rma_browsertest.js b/chrome/test/data/webui/chromeos/shimless_rma/shimless_rma_browsertest.js
index f811001..953760d 100644
--- a/chrome/test/data/webui/chromeos/shimless_rma/shimless_rma_browsertest.js
+++ b/chrome/test/data/webui/chromeos/shimless_rma/shimless_rma_browsertest.js
@@ -36,8 +36,8 @@
       enabled: [
         'chromeos::features::kShimlessRMAFlow',
         'chromeos::features::kShimlessRMAEnableStandalone',
-        'chromeos::features::kShimlessRMAOsUpdate'
-      ]
+        'chromeos::features::kShimlessRMAOsUpdate',
+      ],
     };
   }
 };
diff --git a/chrome/test/data/webui/chromeos/shortcut_customization/shortcut_customization_test.js b/chrome/test/data/webui/chromeos/shortcut_customization/shortcut_customization_test.js
index c7a4163..c4ed268 100644
--- a/chrome/test/data/webui/chromeos/shortcut_customization/shortcut_customization_test.js
+++ b/chrome/test/data/webui/chromeos/shortcut_customization/shortcut_customization_test.js
@@ -162,7 +162,7 @@
       bubbles: true,
       composed: true,
       detail: /**@type {!Object}*/ (
-          {description: 'test', accelerators: [acceleratorInfo]})
+          {description: 'test', accelerators: [acceleratorInfo]}),
     }));
     await flushTasks();
 
diff --git a/chrome/test/data/webui/print_preview/pdf_toolbar_manager_test.ts b/chrome/test/data/webui/print_preview/pdf_toolbar_manager_test.ts
index 8b2b9fd..5d9f48da 100644
--- a/chrome/test/data/webui/print_preview/pdf_toolbar_manager_test.ts
+++ b/chrome/test/data/webui/print_preview/pdf_toolbar_manager_test.ts
@@ -101,7 +101,7 @@
     movementX: 0,
     movementY: 0,
     sourceCapabilities: new InputDeviceCapabilities({firesTouchEvents: true}),
-  } as MouseEventInit);
+  });
   return e;
 }
 
diff --git a/chrome/test/data/webui/tab_search/fuzzy_search_test.ts b/chrome/test/data/webui/tab_search/fuzzy_search_test.ts
index 24964688..4a6f2d9 100644
--- a/chrome/test/data/webui/tab_search/fuzzy_search_test.ts
+++ b/chrome/test/data/webui/tab_search/fuzzy_search_test.ts
@@ -7,7 +7,7 @@
 import {fuzzySearch, FuzzySearchOptions, TabData, TabGroup, TabItemType} from 'chrome://tab-search.top-chrome/tab_search.js';
 import {assertDeepEquals, assertEquals} from 'chrome://webui-test/chai_assert.js';
 
-import {createTab} from './tab_search_test_data.js';
+import {createTab, sampleToken} from './tab_search_test_data.js';
 
 /**
  * Assert search results return in specific order.
@@ -97,7 +97,13 @@
     const tabDataWithGroup = new TabData(
         createTab({title: 'Meet the cast'}), TabItemType.OPEN_TAB,
         'meet the cast');
-    tabDataWithGroup.tabGroup = {title: 'Glee TV show'} as TabGroup;
+
+    const tabGroup: TabGroup = {
+      title: 'Glee TV show',
+      color: 0,
+      id: sampleToken(1n, 1n),
+    };
+    tabDataWithGroup.tabGroup = tabGroup;
 
     const records = [
       new TabData(
diff --git a/chrome/test/mini_installer/config/chrome_beta_installed.prop b/chrome/test/mini_installer/config/chrome_beta_installed.prop
index 57dbd39..a3b28a10 100644
--- a/chrome/test/mini_installer/config/chrome_beta_installed.prop
+++ b/chrome/test/mini_installer/config/chrome_beta_installed.prop
@@ -8,6 +8,8 @@
         {"exists": true},
     "$LOCAL_APPDATA\\$CHROME_DIR_BETA\\Application\\$MINI_INSTALLER_FILE_VERSION\\chrome_elf.dll":
         {"exists": true},
+    "$LOCAL_APPDATA\\$CHROME_DIR_BETA\\Application\\$MINI_INSTALLER_FILE_VERSION\\chrome_wer.dll":
+        {"exists": true},
     "$LOCAL_APPDATA\\$CHROME_DIR_BETA\\Application\\$MINI_INSTALLER_FILE_VERSION\\Installer\\chrome.7z":
         {"exists": true},
     "$LOCAL_APPDATA\\$CHROME_DIR_BETA\\Application\\$MINI_INSTALLER_FILE_VERSION\\Installer\\setup.exe":
@@ -58,6 +60,14 @@
       },
       "wow_key": "KEY_WOW64_32KEY"
     },
+    "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\RuntimeExceptionHelperModules": {
+      "exists": "required",
+      "values": {
+        "$LOCAL_APPDATA\\$CHROME_DIR_BETA\\Application\\$MINI_INSTALLER_FILE_VERSION\\chrome_wer.dll": {
+          "type": "DWORD"
+        }
+      }
+    },
     "HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\$CHROME_TOAST_ACTIVATOR_CLSID_BETA\\LocalServer32": {
       "exists": "required",
       "values": {
diff --git a/chrome/test/mini_installer/config/chrome_beta_not_installed.prop b/chrome/test/mini_installer/config/chrome_beta_not_installed.prop
index 6078923..8590610 100644
--- a/chrome/test/mini_installer/config/chrome_beta_not_installed.prop
+++ b/chrome/test/mini_installer/config/chrome_beta_not_installed.prop
@@ -14,6 +14,12 @@
       "exists": "forbidden",
       "wow_key": "KEY_WOW64_32KEY"
     },
+    "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\RuntimeExceptionHelperModules": {
+      "exists": "optional",
+      "values": {
+        "$LOCAL_APPDATA\\$CHROME_DIR_BETA\\Application\\$MINI_INSTALLER_FILE_VERSION\\chrome_wer.dll": {}
+      }
+    },
     "HKEY_CURRENT_USER\\Software\\Classes\\$CHROME_HTML_PROG_ID_BETA$USER_SPECIFIC_REGISTRY_SUFFIX": {
       "exists": "forbidden"
     },
diff --git a/chrome/test/mini_installer/config/chrome_canary_installed.prop b/chrome/test/mini_installer/config/chrome_canary_installed.prop
index c73472a..66f58554 100644
--- a/chrome/test/mini_installer/config/chrome_canary_installed.prop
+++ b/chrome/test/mini_installer/config/chrome_canary_installed.prop
@@ -8,6 +8,8 @@
         {"exists": true},
     "$LOCAL_APPDATA\\$CHROME_DIR_SXS\\Application\\$MINI_INSTALLER_FILE_VERSION\\chrome_elf.dll":
         {"exists": true},
+    "$LOCAL_APPDATA\\$CHROME_DIR_SXS\\Application\\$MINI_INSTALLER_FILE_VERSION\\chrome_wer.dll":
+        {"exists": true},
     "$LOCAL_APPDATA\\$CHROME_DIR_SXS\\Application\\$MINI_INSTALLER_FILE_VERSION\\Installer\\chrome.7z":
         {"exists": true},
     "$LOCAL_APPDATA\\$CHROME_DIR_SXS\\Application\\$MINI_INSTALLER_FILE_VERSION\\Installer\\setup.exe":
@@ -58,6 +60,14 @@
       },
       "wow_key": "KEY_WOW64_32KEY"
     },
+    "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\RuntimeExceptionHelperModules": {
+      "exists": "required",
+      "values": {
+        "$LOCAL_APPDATA\\$CHROME_DIR_SXS\\Application\\$MINI_INSTALLER_FILE_VERSION\\chrome_wer.dll": {
+          "type": "DWORD"
+        }
+      }
+    },
     "HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\$CHROME_TOAST_ACTIVATOR_CLSID_SXS\\LocalServer32": {
       "exists": "required",
       "values": {
diff --git a/chrome/test/mini_installer/config/chrome_canary_not_installed.prop b/chrome/test/mini_installer/config/chrome_canary_not_installed.prop
index d8a391f..83b0c686 100644
--- a/chrome/test/mini_installer/config/chrome_canary_not_installed.prop
+++ b/chrome/test/mini_installer/config/chrome_canary_not_installed.prop
@@ -14,6 +14,12 @@
       "exists": "forbidden",
       "wow_key": "KEY_WOW64_32KEY"
     },
+    "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\RuntimeExceptionHelperModules": {
+      "exists": "optional",
+      "values": {
+        "$LOCAL_APPDATA\\$CHROME_DIR_SXS\\Application\\$MINI_INSTALLER_FILE_VERSION\\chrome_wer.dll": {}
+      }
+    },
     "HKEY_CURRENT_USER\\Software\\Classes\\$CHROME_HTML_PROG_ID_SXS$USER_SPECIFIC_REGISTRY_SUFFIX": {
       "exists": "forbidden"
     },
diff --git a/chrome/test/mini_installer/config/chrome_dev_installed.prop b/chrome/test/mini_installer/config/chrome_dev_installed.prop
index 9719186..0dcef672 100644
--- a/chrome/test/mini_installer/config/chrome_dev_installed.prop
+++ b/chrome/test/mini_installer/config/chrome_dev_installed.prop
@@ -8,6 +8,8 @@
         {"exists": true},
     "$LOCAL_APPDATA\\$CHROME_DIR_DEV\\Application\\$MINI_INSTALLER_FILE_VERSION\\chrome_elf.dll":
         {"exists": true},
+    "$LOCAL_APPDATA\\$CHROME_DIR_DEV\\Application\\$MINI_INSTALLER_FILE_VERSION\\chrome_wer.dll":
+        {"exists": true},
     "$LOCAL_APPDATA\\$CHROME_DIR_DEV\\Application\\$MINI_INSTALLER_FILE_VERSION\\Installer\\chrome.7z":
         {"exists": true},
     "$LOCAL_APPDATA\\$CHROME_DIR_DEV\\Application\\$MINI_INSTALLER_FILE_VERSION\\Installer\\setup.exe":
@@ -58,6 +60,14 @@
       },
       "wow_key": "KEY_WOW64_32KEY"
     },
+    "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\RuntimeExceptionHelperModules": {
+      "exists": "required",
+      "values": {
+        "$LOCAL_APPDATA\\$CHROME_DIR_DEV\\Application\\$MINI_INSTALLER_FILE_VERSION\\chrome_wer.dll": {
+          "type": "DWORD"
+        }
+      }
+    },
     "HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\$CHROME_TOAST_ACTIVATOR_CLSID_DEV\\LocalServer32": {
       "exists": "required",
       "values": {
diff --git a/chrome/test/mini_installer/config/chrome_dev_not_installed.prop b/chrome/test/mini_installer/config/chrome_dev_not_installed.prop
index fe975fb..85ff71a 100644
--- a/chrome/test/mini_installer/config/chrome_dev_not_installed.prop
+++ b/chrome/test/mini_installer/config/chrome_dev_not_installed.prop
@@ -14,6 +14,12 @@
       "exists": "forbidden",
       "wow_key": "KEY_WOW64_32KEY"
     },
+    "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\RuntimeExceptionHelperModules": {
+      "exists": "optional",
+      "values": {
+        "$LOCAL_APPDATA\\$CHROME_DIR_DEV\\Application\\$MINI_INSTALLER_FILE_VERSION\\chrome_wer.dll": {}
+      }
+    },
     "HKEY_CURRENT_USER\\Software\\Classes\\$CHROME_HTML_PROG_ID_DEV$USER_SPECIFIC_REGISTRY_SUFFIX": {
       "exists": "forbidden"
     },
diff --git a/chrome/test/mini_installer/config/chrome_system_installed.prop b/chrome/test/mini_installer/config/chrome_system_installed.prop
index 716a77c..78c4793 100644
--- a/chrome/test/mini_installer/config/chrome_system_installed.prop
+++ b/chrome/test/mini_installer/config/chrome_system_installed.prop
@@ -7,6 +7,8 @@
         {"exists": true},
     "$PROGRAM_FILES\\$CHROME_DIR\\Application\\$MINI_INSTALLER_FILE_VERSION\\chrome_elf.dll":
         {"exists": true},
+    "$PROGRAM_FILES\\$CHROME_DIR\\Application\\$MINI_INSTALLER_FILE_VERSION\\chrome_wer.dll":
+        {"exists": true},
     "$PROGRAM_FILES\\$CHROME_DIR\\Application\\$MINI_INSTALLER_FILE_VERSION\\elevation_service.exe": {
       "condition": "'$CHROME_SHORT_NAME' == 'Chrome'",
       "exists": true
@@ -119,6 +121,14 @@
       },
       "wow_key": "KEY_WOW64_32KEY"
     },
+    "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\RuntimeExceptionHelperModules": {
+      "exists": "required",
+      "values": {
+        "$PROGRAM_FILES\\$CHROME_DIR\\Application\\$MINI_INSTALLER_FILE_VERSION\\chrome_wer.dll": {
+          "type": "DWORD"
+        }
+      }
+    },
     "HKEY_LOCAL_MACHINE\\Software\\Classes\\CLSID\\$CHROME_TOAST_ACTIVATOR_CLSID\\LocalServer32": {
       "exists": "required",
       "values": {
diff --git a/chrome/test/mini_installer/config/chrome_system_not_installed.prop b/chrome/test/mini_installer/config/chrome_system_not_installed.prop
index d602d89..3c3e06f21 100644
--- a/chrome/test/mini_installer/config/chrome_system_not_installed.prop
+++ b/chrome/test/mini_installer/config/chrome_system_not_installed.prop
@@ -20,6 +20,12 @@
       "exists": "forbidden",
       "wow_key": "KEY_WOW64_32KEY"
     },
+    "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\RuntimeExceptionHelperModules": {
+      "exists": "optional",
+      "values": {
+        "$PROGRAM_FILES\\$CHROME_DIR\\Application\\$MINI_INSTALLER_FILE_VERSION\\chrome_wer.dll": {}
+      }
+    },
     "HKEY_LOCAL_MACHINE\\Software\\Classes\\$CHROME_HTML_PROG_ID": {
       "exists": "forbidden"
     },
diff --git a/chrome/test/mini_installer/config/chrome_user_installed.prop b/chrome/test/mini_installer/config/chrome_user_installed.prop
index c8d0693..4f68e21 100644
--- a/chrome/test/mini_installer/config/chrome_user_installed.prop
+++ b/chrome/test/mini_installer/config/chrome_user_installed.prop
@@ -7,6 +7,8 @@
         {"exists": true},
     "$LOCAL_APPDATA\\$CHROME_DIR\\Application\\$MINI_INSTALLER_FILE_VERSION\\chrome_elf.dll":
         {"exists": true},
+    "$LOCAL_APPDATA\\$CHROME_DIR\\Application\\$MINI_INSTALLER_FILE_VERSION\\chrome_wer.dll":
+        {"exists": true},
     "$LOCAL_APPDATA\\$CHROME_DIR\\Application\\$MINI_INSTALLER_FILE_VERSION\\Installer\\chrome.7z":
         {"exists": true},
     "$LOCAL_APPDATA\\$CHROME_DIR\\Application\\$MINI_INSTALLER_FILE_VERSION\\Installer\\setup.exe":
@@ -66,6 +68,14 @@
       },
       "wow_key": "KEY_WOW64_32KEY"
     },
+    "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\RuntimeExceptionHelperModules": {
+      "exists": "required",
+      "values": {
+        "$LOCAL_APPDATA\\$CHROME_DIR\\Application\\$MINI_INSTALLER_FILE_VERSION\\chrome_wer.dll": {
+          "type": "DWORD"
+        }
+      }
+    },
     "HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\$CHROME_TOAST_ACTIVATOR_CLSID\\LocalServer32": {
       "exists": "required",
       "values": {
diff --git a/chrome/test/mini_installer/config/chrome_user_not_installed.prop b/chrome/test/mini_installer/config/chrome_user_not_installed.prop
index 95c1b7a..1694421 100644
--- a/chrome/test/mini_installer/config/chrome_user_not_installed.prop
+++ b/chrome/test/mini_installer/config/chrome_user_not_installed.prop
@@ -27,6 +27,12 @@
       "exists": "forbidden",
       "wow_key": "KEY_WOW64_32KEY"
     },
+    "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\RuntimeExceptionHelperModules": {
+      "exists": "optional",
+      "values": {
+        "$LOCAL_APPDATA\\$CHROME_DIR\\Application\\$MINI_INSTALLER_FILE_VERSION\\chrome_wer.dll": {}
+      }
+    },
     "HKEY_CURRENT_USER\\Software\\Classes\\$CHROME_HTML_PROG_ID$USER_SPECIFIC_REGISTRY_SUFFIX": {
       "exists": "forbidden"
     },
diff --git a/chrome/test/mini_installer/config/previous_chrome_canary_installed.prop b/chrome/test/mini_installer/config/previous_chrome_canary_installed.prop
index df512f6..3ec7cb6 100644
--- a/chrome/test/mini_installer/config/previous_chrome_canary_installed.prop
+++ b/chrome/test/mini_installer/config/previous_chrome_canary_installed.prop
@@ -6,6 +6,7 @@
     "$LOCAL_APPDATA\\$CHROME_DIR_SXS\\Application\\chrome_proxy.exe": {"exists": true},
     "$LOCAL_APPDATA\\$CHROME_DIR_SXS\\Application\\$PREVIOUS_VERSION_MINI_INSTALLER_FILE_VERSION\\chrome.dll": {"exists": true},
     "$LOCAL_APPDATA\\$CHROME_DIR_SXS\\Application\\$PREVIOUS_VERSION_MINI_INSTALLER_FILE_VERSION\\chrome_elf.dll": {"exists": true},
+    "$LOCAL_APPDATA\\$CHROME_DIR_SXS\\Application\\$PREVIOUS_VERSION_MINI_INSTALLER_FILE_VERSION\\chrome_wer.dll": {"exists": true},
     "$LOCAL_APPDATA\\$CHROME_DIR_SXS\\Application\\$PREVIOUS_VERSION_MINI_INSTALLER_FILE_VERSION\\Installer\\chrome.7z": {"exists": true},
     "$LOCAL_APPDATA\\$CHROME_DIR_SXS\\Application\\$PREVIOUS_VERSION_MINI_INSTALLER_FILE_VERSION\\Installer\\setup.exe": {"exists": true},
     "$LOCAL_APPDATA\\$CHROME_DIR_SXS\\Application\\$PREVIOUS_VERSION_MINI_INSTALLER_FILE_VERSION\\$PREVIOUS_VERSION_MINI_INSTALLER_FILE_VERSION.manifest": {"exists": true},
@@ -50,6 +51,14 @@
       },
       "wow_key": "KEY_WOW64_32KEY"
     },
+    "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\RuntimeExceptionHelperModules": {
+      "exists": "required",
+      "values": {
+        "$LOCAL_APPDATA\\$CHROME_DIR_SXS\\Application\\$PREVIOUS_VERSION_MINI_INSTALLER_FILE_VERSION\\chrome_wer.dll": {
+          "type": "DWORD"
+        }
+      }
+    },
     "HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\$CHROME_TOAST_ACTIVATOR_CLSID_SXS\\LocalServer32": {
       "exists": "required",
       "values": {
diff --git a/chrome/test/mini_installer/config/previous_chrome_system_installed.prop b/chrome/test/mini_installer/config/previous_chrome_system_installed.prop
index a4e10550..6a616d5 100644
--- a/chrome/test/mini_installer/config/previous_chrome_system_installed.prop
+++ b/chrome/test/mini_installer/config/previous_chrome_system_installed.prop
@@ -7,6 +7,8 @@
         {"exists": true},
     "$PROGRAM_FILES\\$CHROME_DIR\\Application\\$PREVIOUS_VERSION_MINI_INSTALLER_FILE_VERSION\\chrome_elf.dll":
         {"exists": true},
+    "$PROGRAM_FILES\\$CHROME_DIR\\Application\\$PREVIOUS_VERSION_MINI_INSTALLER_FILE_VERSION\\chrome_wer.dll":
+        {"exists": true},
     "$PROGRAM_FILES\\$CHROME_DIR\\Application\\$PREVIOUS_VERSION_MINI_INSTALLER_FILE_VERSION\\elevation_service.exe": {
       "condition": "'$CHROME_SHORT_NAME' == 'Chrome'",
       "exists": true
@@ -117,6 +119,14 @@
       },
       "wow_key": "KEY_WOW64_32KEY"
     },
+    "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\RuntimeExceptionHelperModules": {
+      "exists": "required",
+      "values": {
+        "$PROGRAM_FILES\\$CHROME_DIR\\Application\\$PREVIOUS_VERSION_MINI_INSTALLER_FILE_VERSION\\chrome_wer.dll": {
+          "type": "DWORD"
+        }
+      }
+    },
     "HKEY_LOCAL_MACHINE\\Software\\Classes\\CLSID\\$CHROME_TOAST_ACTIVATOR_CLSID\\LocalServer32": {
       "exists": "required",
       "values": {
diff --git a/chrome/test/mini_installer/config/previous_chrome_user_installed.prop b/chrome/test/mini_installer/config/previous_chrome_user_installed.prop
index c0cbf40d..49837b5 100644
--- a/chrome/test/mini_installer/config/previous_chrome_user_installed.prop
+++ b/chrome/test/mini_installer/config/previous_chrome_user_installed.prop
@@ -7,6 +7,8 @@
         {"exists": true},
     "$LOCAL_APPDATA\\$CHROME_DIR\\Application\\$PREVIOUS_VERSION_MINI_INSTALLER_FILE_VERSION\\chrome_elf.dll":
         {"exists": true},
+    "$LOCAL_APPDATA\\$CHROME_DIR\\Application\\$PREVIOUS_VERSION_MINI_INSTALLER_FILE_VERSION\\chrome_wer.dll":
+        {"exists": true},
     "$LOCAL_APPDATA\\$CHROME_DIR\\Application\\$PREVIOUS_VERSION_MINI_INSTALLER_FILE_VERSION\\Installer\\chrome.7z":
         {"exists": true},
     "$LOCAL_APPDATA\\$CHROME_DIR\\Application\\$PREVIOUS_VERSION_MINI_INSTALLER_FILE_VERSION\\Installer\\setup.exe":
@@ -64,6 +66,14 @@
       },
       "wow_key": "KEY_WOW64_32KEY"
     },
+    "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\RuntimeExceptionHelperModules": {
+      "exists": "required",
+      "values": {
+        "$LOCAL_APPDATA\\$CHROME_DIR\\Application\\$PREVIOUS_VERSION_MINI_INSTALLER_FILE_VERSION\\chrome_wer.dll": {
+          "type": "DWORD"
+        }
+      }
+    },
     "HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\$CHROME_TOAST_ACTIVATOR_CLSID\\LocalServer32": {
       "exists": "required",
       "values": {
diff --git a/chrome/tools/build/win/FILES.cfg b/chrome/tools/build/win/FILES.cfg
index b7b4f6ac..f9f91440 100644
--- a/chrome/tools/build/win/FILES.cfg
+++ b/chrome/tools/build/win/FILES.cfg
@@ -75,6 +75,11 @@
     'filegroup': ['default', 'symsrc'],
   },
   {
+    'filename': 'chrome_wer.dll',
+    'buildtype': ['official'],
+    'filegroup': ['default', 'symsrc'],
+  },
+  {
     'filename': 'eventlog_provider.dll',
     'buildtype': ['official'],
     'filegroup': ['default'],
@@ -627,6 +632,11 @@
     'archive': 'chrome-win32-syms.zip',
   },
   {
+    'filename': 'chrome_wer.dll.pdb',
+    'buildtype': ['official'],
+    'archive': 'chrome-win32-syms.zip',
+  },
+  {
     'filename': 'chrome.exe.pdb',
     'buildtype': ['official'],
     'archive': 'chrome-win32-syms.zip',
diff --git a/chromecast/browser/android/apk/AndroidManifest.xml.jinja2 b/chromecast/browser/android/apk/AndroidManifest.xml.jinja2
index abd334d6..4056146 100644
--- a/chromecast/browser/android/apk/AndroidManifest.xml.jinja2
+++ b/chromecast/browser/android/apk/AndroidManifest.xml.jinja2
@@ -15,12 +15,17 @@
     <uses-permission android:name="android.permission.INTERNET"/>
     <uses-permission android:name="android.permission.WAKE_LOCK"/>
 
+    <!-- Needed because CastWebContentsActivity sets orientation to landscape. -->
+    <uses-feature android:name="android.hardware.screen.landscape" android:required="false" />
+
     <application android:icon="@drawable/ic_settings_cast">
+        <service android:name="org.chromium.chromecast.shell.CastWebContentsService"/>
+
         <activity android:name="org.chromium.chromecast.shell.CastWebContentsActivity"
                   android:theme="@style/CastShellTheme"
                   android:exported="true"
                   android:hardwareAccelerated="true"
-                  android:launchMode="standard"
+                  android:launchMode="singleInstance"
                   android:screenOrientation="landscape"
                   android:taskAffinity=".CastWebContentsActivity"
                   android:supportsPictureInPicture="true"
diff --git a/chromecast/browser/android/apk/res-values/values-v17/styles.xml b/chromecast/browser/android/apk/res-values/values-v17/styles.xml
index d652a61..644feac 100644
--- a/chromecast/browser/android/apk/res-values/values-v17/styles.xml
+++ b/chromecast/browser/android/apk/res-values/values-v17/styles.xml
@@ -6,7 +6,7 @@
 -->
 
 <resources xmlns:tools="http://schemas.android.com/tools">
-    <style name="CastShellTheme" parent="@android:style/Theme.Holo.Light.NoActionBar" tools:ignore="UnusedResources">
+    <style name="CastShellTheme" parent="@android:style/Theme.Holo.Light.NoActionBar">
         <item name="android:windowBackground">@android:color/black</item>
     </style>
 </resources>
diff --git a/chromeos/dbus/cec_service/cec_service_client.cc b/chromeos/dbus/cec_service/cec_service_client.cc
index ff77378..aa394d4 100644
--- a/chromeos/dbus/cec_service/cec_service_client.cc
+++ b/chromeos/dbus/cec_service/cec_service_client.cc
@@ -8,17 +8,18 @@
 #include "base/callback_helpers.h"
 #include "base/logging.h"
 #include "base/memory/scoped_refptr.h"
-#include "base/memory/weak_ptr.h"
+#include "chromeos/dbus/cec_service/fake_cec_service_client.h"
 #include "dbus/bus.h"
 #include "dbus/message.h"
 #include "dbus/object_proxy.h"
-
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
 namespace chromeos {
 
 namespace {
 
+CecServiceClient* g_instance = nullptr;
+
 // Translates a power state from a D-Bus response to the types exposed by
 // CecServiceClient.
 CecServiceClient::PowerState ConvertDBusPowerState(int32_t power_state) {
@@ -111,7 +112,6 @@
         base::BindOnce(&OnGetTvsPowerStatus, std::move(callback)));
   }
 
- protected:
   void Init(dbus::Bus* bus) override {
     cec_service_proxy_ =
         bus->GetObjectProxy(cecservice::kCecServiceName,
@@ -127,13 +127,36 @@
 ////////////////////////////////////////////////////////////////////////////////
 // CecServiceClient
 
-CecServiceClient::CecServiceClient() = default;
-
-CecServiceClient::~CecServiceClient() = default;
+// static
+CecServiceClient* CecServiceClient::Get() {
+  return g_instance;
+}
 
 // static
-std::unique_ptr<CecServiceClient> CecServiceClient::Create() {
-  return std::make_unique<CecServiceClientImpl>();
+void CecServiceClient::Initialize(dbus::Bus* bus) {
+  CHECK(bus);
+  (new CecServiceClientImpl())->Init(bus);
+}
+
+// static
+void CecServiceClient::InitializeFake() {
+  (new FakeCecServiceClient())->Init(nullptr);
+}
+
+// static
+void CecServiceClient::Shutdown() {
+  CHECK(g_instance);
+  delete g_instance;
+}
+
+CecServiceClient::CecServiceClient() {
+  CHECK(!g_instance);
+  g_instance = this;
+}
+
+CecServiceClient::~CecServiceClient() {
+  CHECK_EQ(g_instance, this);
+  g_instance = nullptr;
 }
 
 }  // namespace chromeos
diff --git a/chromeos/dbus/cec_service/cec_service_client.h b/chromeos/dbus/cec_service/cec_service_client.h
index cefca21..afd6e69 100644
--- a/chromeos/dbus/cec_service/cec_service_client.h
+++ b/chromeos/dbus/cec_service/cec_service_client.h
@@ -23,11 +23,21 @@
 class COMPONENT_EXPORT(CHROMEOS_DBUS_CEC_SERVICE) CecServiceClient
     : public DBusClient {
  public:
+  // Returns the global instance if initialized. May return null.
+  static CecServiceClient* Get();
+
+  // Creates and initializes the global instance. |bus| must not be null.
+  static void Initialize(dbus::Bus* bus);
+
+  // Creates and initializes a fake global instance.
+  static void InitializeFake();
+
+  // Destroys the global instance if it has been initialized.
+  static void Shutdown();
+
   CecServiceClient(const CecServiceClient&) = delete;
   CecServiceClient& operator=(const CecServiceClient&) = delete;
 
-  ~CecServiceClient() override;
-
   enum class PowerState {
     // There was an error when querying the display.
     kError,
@@ -51,9 +61,6 @@
   using PowerStateCallback =
       base::OnceCallback<void(const std::vector<PowerState>&)>;
 
-  // For normal usage, access the singleton via DBusThreadManager::Get().
-  static std::unique_ptr<CecServiceClient> Create();
-
   // Puts all connected HDMI CEC capable displays into stand-by mode. The effect
   // of calling this method is on a best effort basis, no guarantees of displays
   // going into stand-by is made.
@@ -74,6 +81,7 @@
   friend class CecServiceClientTest;
 
   CecServiceClient();
+  ~CecServiceClient() override;
 };
 
 }  // namespace chromeos
diff --git a/chromeos/dbus/cec_service/cec_service_client_unittest.cc b/chromeos/dbus/cec_service/cec_service_client_unittest.cc
index 4d11e067..e338e4c7 100644
--- a/chromeos/dbus/cec_service/cec_service_client_unittest.cc
+++ b/chromeos/dbus/cec_service/cec_service_client_unittest.cc
@@ -95,16 +95,18 @@
         .WillByDefault(Return(mock_proxy_.get()));
 
     // Create a client with the mock bus.
-    client_ = CecServiceClient::Create();
-    client_->Init(mock_bus_.get());
+    CecServiceClient::Initialize(mock_bus_.get());
+    client_ = CecServiceClient::Get();
 
     // Run the message loop to run the signal connection result callback.
     base::RunLoop().RunUntilIdle();
   }
 
+  ~CecServiceClientTest() override { CecServiceClient::Shutdown(); }
+
  protected:
   base::test::SingleThreadTaskEnvironment task_environment_;
-  std::unique_ptr<CecServiceClient> client_;
+  CecServiceClient* client_ = nullptr;
   scoped_refptr<dbus::MockBus> mock_bus_;
   scoped_refptr<dbus::MockObjectProxy> mock_proxy_;
 };
diff --git a/chromeos/dbus/cec_service/fake_cec_service_client.h b/chromeos/dbus/cec_service/fake_cec_service_client.h
index ea0b7a11..31605455 100644
--- a/chromeos/dbus/cec_service/fake_cec_service_client.h
+++ b/chromeos/dbus/cec_service/fake_cec_service_client.h
@@ -27,6 +27,7 @@
   void SendWakeUp() override;
   void QueryDisplayCecPowerState(
       CecServiceClient::PowerStateCallback callback) override;
+  void Init(dbus::Bus* bus) override;
 
   int stand_by_call_count() const { return stand_by_call_count_; }
   int wake_up_call_count() const { return wake_up_call_count_; }
@@ -38,9 +39,6 @@
     return tv_power_states_;
   }
 
- protected:
-  void Init(dbus::Bus* bus) override;
-
  private:
   void SetDisplayPowerState(PowerState new_state);
 
diff --git a/chromeos/dbus/dbus_clients_browser.cc b/chromeos/dbus/dbus_clients_browser.cc
index d03e03a..1e201a2 100644
--- a/chromeos/dbus/dbus_clients_browser.cc
+++ b/chromeos/dbus/dbus_clients_browser.cc
@@ -5,8 +5,6 @@
 #include "chromeos/dbus/dbus_clients_browser.h"
 
 #include "base/check.h"
-#include "chromeos/dbus/cec_service/cec_service_client.h"
-#include "chromeos/dbus/cec_service/fake_cec_service_client.h"
 #include "chromeos/dbus/cros_disks/cros_disks_client.h"
 #include "chromeos/dbus/cros_disks/fake_cros_disks_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
@@ -32,7 +30,6 @@
 #endif  // USE_REAL_DBUS_CLIENTS
 
 DBusClientsBrowser::DBusClientsBrowser(bool use_real_clients) {
-  cec_service_client_ = CREATE_DBUS_CLIENT(CecServiceClient, use_real_clients);
   cros_disks_client_ = CREATE_DBUS_CLIENT(CrosDisksClient, use_real_clients);
   debug_daemon_client_ =
       CREATE_DBUS_CLIENT(DebugDaemonClient, use_real_clients);
@@ -45,7 +42,6 @@
 void DBusClientsBrowser::Initialize(dbus::Bus* system_bus) {
   DCHECK(DBusThreadManager::IsInitialized());
 
-  cec_service_client_->Init(system_bus);
   cros_disks_client_->Init(system_bus);
   debug_daemon_client_->Init(system_bus);
   easy_unlock_client_->Init(system_bus);
diff --git a/chromeos/dbus/dbus_clients_browser.h b/chromeos/dbus/dbus_clients_browser.h
index 7703cf2..d1e1b5e 100644
--- a/chromeos/dbus/dbus_clients_browser.h
+++ b/chromeos/dbus/dbus_clients_browser.h
@@ -15,7 +15,6 @@
 
 namespace chromeos {
 
-class CecServiceClient;
 class CrosDisksClient;
 class DebugDaemonClient;
 class EasyUnlockClient;
@@ -42,7 +41,6 @@
   friend class DBusThreadManager;
   friend class DBusThreadManagerSetter;
 
-  std::unique_ptr<CecServiceClient> cec_service_client_;
   std::unique_ptr<CrosDisksClient> cros_disks_client_;
   std::unique_ptr<DebugDaemonClient> debug_daemon_client_;
   std::unique_ptr<EasyUnlockClient> easy_unlock_client_;
diff --git a/chromeos/dbus/dbus_thread_manager.cc b/chromeos/dbus/dbus_thread_manager.cc
index a0680c8..d6a789b7 100644
--- a/chromeos/dbus/dbus_thread_manager.cc
+++ b/chromeos/dbus/dbus_thread_manager.cc
@@ -10,7 +10,6 @@
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/message_loop/message_pump_type.h"
-#include "chromeos/dbus/cec_service/cec_service_client.h"
 #include "chromeos/dbus/common/dbus_client.h"
 #include "chromeos/dbus/cros_disks/cros_disks_client.h"
 #include "chromeos/dbus/dbus_clients_browser.h"
@@ -38,11 +37,6 @@
              ? g_setter->name.get()   \
              : (clients_browser_ ? clients_browser_->name.get() : nullptr)
 
-CecServiceClient* DBusThreadManager::GetCecServiceClient() {
-  return clients_browser_ ? clients_browser_->cec_service_client_.get()
-                          : nullptr;
-}
-
 CrosDisksClient* DBusThreadManager::GetCrosDisksClient() {
   RETURN_DBUS_CLIENT(cros_disks_client_);
 }
diff --git a/chromeos/dbus/dbus_thread_manager.h b/chromeos/dbus/dbus_thread_manager.h
index 7601121..10a82663 100644
--- a/chromeos/dbus/dbus_thread_manager.h
+++ b/chromeos/dbus/dbus_thread_manager.h
@@ -15,7 +15,6 @@
 namespace chromeos {
 
 // Style Note: Clients are sorted by names.
-class CecServiceClient;
 class CrosDisksClient;
 class DBusClientsBrowser;
 class DBusThreadManagerSetter;
@@ -57,7 +56,6 @@
   // pointers after DBusThreadManager has been shut down.
   // TODO(jamescook): Replace this with calls to FooClient::Get().
   // http://crbug.com/647367
-  CecServiceClient* GetCecServiceClient();
   CrosDisksClient* GetCrosDisksClient();
   DebugDaemonClient* GetDebugDaemonClient();
   EasyUnlockClient* GetEasyUnlockClient();
diff --git a/components/autofill/core/browser/autofill_client.h b/components/autofill/core/browser/autofill_client.h
index 1c331c9..b16ee2a 100644
--- a/components/autofill/core/browser/autofill_client.h
+++ b/components/autofill/core/browser/autofill_client.h
@@ -712,8 +712,10 @@
   virtual bool IsQueryIDRelevant(int query_id) = 0;
 #endif
 
-  // Opens a new tab and navigates to the given |url|.
-  virtual void OnPromoCodeSuggestionsFooterSelected(const GURL& url) = 0;
+  // Navigates to |url| in a new tab. |url| links to the promo code offer
+  // details page for the offers in a promo code suggestions popup. Every offer
+  // in a promo code suggestions popup links to the same offer details page.
+  virtual void OpenPromoCodeOfferDetailsURL(const GURL& url) = 0;
 };
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/autofill_external_delegate.cc b/components/autofill/core/browser/autofill_external_delegate.cc
index 345dace..1423b990 100644
--- a/components/autofill/core/browser/autofill_external_delegate.cc
+++ b/components/autofill/core/browser/autofill_external_delegate.cc
@@ -284,8 +284,8 @@
         query_id_, query_form_, query_field_);
   } else if (frontend_id == POPUP_ITEM_ID_SEE_PROMO_CODE_DETAILS) {
     DCHECK(absl::holds_alternative<GURL>(payload));
-    manager_->client()->OnPromoCodeSuggestionsFooterSelected(
-        absl::get<GURL>(payload));
+    manager_->OnSeePromoCodeOfferDetailsSelected(absl::get<GURL>(payload),
+                                                 value, frontend_id);
   } else {
     if (frontend_id > 0) {  // Denotes an Autofill suggestion.
       AutofillMetrics::LogAutofillSuggestionAcceptedIndex(
diff --git a/components/autofill/core/browser/autofill_external_delegate_unittest.cc b/components/autofill/core/browser/autofill_external_delegate_unittest.cc
index 573d475..d9e2ca13 100644
--- a/components/autofill/core/browser/autofill_external_delegate_unittest.cc
+++ b/components/autofill/core/browser/autofill_external_delegate_unittest.cc
@@ -95,7 +95,7 @@
   MOCK_METHOD(void, HideAutofillPopup, (PopupHidingReason), (override));
   MOCK_METHOD(void, ExecuteCommand, (int), (override));
   MOCK_METHOD(void,
-              OnPromoCodeSuggestionsFooterSelected,
+              OpenPromoCodeOfferDetailsURL,
               (const GURL& url),
               (override));
 
@@ -665,7 +665,7 @@
        ExternalDelegateMerchantPromoCodeSuggestionsFooter) {
   const GURL gurl{"https://example.com/"};
   absl::variant<std::string, GURL> payload(absl::in_place_type<GURL>, gurl);
-  EXPECT_CALL(autofill_client_, OnPromoCodeSuggestionsFooterSelected(gurl));
+  EXPECT_CALL(autofill_client_, OpenPromoCodeOfferDetailsURL(gurl));
   external_delegate_->DidAcceptSuggestion(
       u"baz foo", POPUP_ITEM_ID_SEE_PROMO_CODE_DETAILS, payload, 0);
 }
diff --git a/components/autofill/core/browser/autofill_suggestion_generator.cc b/components/autofill/core/browser/autofill_suggestion_generator.cc
index 0dbe739c..248118f 100644
--- a/components/autofill/core/browser/autofill_suggestion_generator.cc
+++ b/components/autofill/core/browser/autofill_suggestion_generator.cc
@@ -416,15 +416,6 @@
 bool AutofillSuggestionGenerator::ShouldShowVirtualCardOption(
     const CreditCard* candidate_card,
     const FormStructure& form_structure) const {
-  // If the form is an incomplete form and the incomplete form experiment is
-  // disabled, do not offer a virtual card option. We will likely not be able to
-  // fill in all information, and the user doesn't have the info either.
-  if (!IsCompleteCreditCardFormIncludingCvcField(form_structure) &&
-      !base::FeatureList::IsEnabled(
-          features::kAutofillSuggestVirtualCardsOnIncompleteForm)) {
-    return false;
-  }
-
   switch (candidate_card->record_type()) {
     case CreditCard::MASKED_SERVER_CARD:
       return candidate_card->virtual_card_enrollment_state() ==
diff --git a/components/autofill/core/browser/browser_autofill_manager.cc b/components/autofill/core/browser/browser_autofill_manager.cc
index 6bffb80..eef567a 100644
--- a/components/autofill/core/browser/browser_autofill_manager.cc
+++ b/components/autofill/core/browser/browser_autofill_manager.cc
@@ -2897,4 +2897,12 @@
       static_cast<PhoneCollectionMetricState>(phone_collection_metric_state_));
 }
 
+void BrowserAutofillManager::OnSeePromoCodeOfferDetailsSelected(
+    const GURL& offer_details_url,
+    const std::u16string& value,
+    int frontend_id) {
+  client()->OpenPromoCodeOfferDetailsURL(offer_details_url);
+  OnSingleFieldSuggestionSelected(value, frontend_id);
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/browser_autofill_manager.h b/components/autofill/core/browser/browser_autofill_manager.h
index 2291194..a76d0e9ae 100644
--- a/components/autofill/core/browser/browser_autofill_manager.h
+++ b/components/autofill/core/browser/browser_autofill_manager.h
@@ -306,6 +306,13 @@
   //   2. there is no form and WebOTP is not used
   void ReportAutofillWebOTPMetrics(bool used_web_otp) override;
 
+  // Handles the logic for when the user selects to see promo code offer
+  // details. It opens a new tab and navigates to the offer details page, and
+  // then logs that the promo code suggestions footer was selected.
+  void OnSeePromoCodeOfferDetailsSelected(const GURL& offer_details_url,
+                                          const std::u16string& value,
+                                          int frontend_id);
+
 #if defined(UNIT_TEST)
   void SetExternalDelegateForTest(
       std::unique_ptr<AutofillExternalDelegate> external_delegate) {
diff --git a/components/autofill/core/browser/browser_autofill_manager_unittest.cc b/components/autofill/core/browser/browser_autofill_manager_unittest.cc
index 111a14a..91b3a50 100644
--- a/components/autofill/core/browser/browser_autofill_manager_unittest.cc
+++ b/components/autofill/core/browser/browser_autofill_manager_unittest.cc
@@ -8239,10 +8239,6 @@
 
 TEST_P(BrowserAutofillManagerStructuredProfileTest,
        GetCreditCardSuggestions_VirtualCard) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndDisableFeature(
-      features::kAutofillSuggestVirtualCardsOnIncompleteForm);
-
   personal_data().ClearCreditCards();
   CreditCard masked_server_card(CreditCard::MASKED_SERVER_CARD,
                                 /*server_id=*/"a123");
@@ -8311,7 +8307,7 @@
   GetAutofillSuggestions(form, field);
 
   CheckSuggestions(
-      kDefaultPageID,
+      kDefaultPageID, virtual_card_suggestion,
       Suggestion("Elvis Presley", label, kVisaCard,
                  browser_autofill_manager_->GetPackedCreditCardID(7)));
 }
diff --git a/components/autofill/core/browser/merchant_promo_code_manager.cc b/components/autofill/core/browser/merchant_promo_code_manager.cc
index 5910d3a..0c78f120 100644
--- a/components/autofill/core/browser/merchant_promo_code_manager.cc
+++ b/components/autofill/core/browser/merchant_promo_code_manager.cc
@@ -7,8 +7,10 @@
 #include "components/autofill/core/browser/autofill_suggestion_generator.h"
 #include "components/autofill/core/browser/browser_autofill_manager.h"
 #include "components/autofill/core/browser/data_model/autofill_offer_data.h"
+#include "components/autofill/core/browser/metrics/payments/offers_metrics.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/browser/suggestions_context.h"
+#include "components/autofill/core/browser/ui/popup_item_ids.h"
 
 namespace autofill {
 
@@ -35,6 +37,7 @@
       SendPromoCodeSuggestions(
           promo_code_offers,
           QueryHandler(query_id, autoselect_first_suggestion, prefix, handler));
+      uma_recorder_.OnOffersSuggestionsShown(name, promo_code_offers);
       return;
     }
   }
@@ -55,7 +58,7 @@
 void MerchantPromoCodeManager::OnSingleFieldSuggestionSelected(
     const std::u16string& value,
     int frontend_id) {
-  // TODO(crbug.com/1190334): Add promo code suggestion accepted metrics here.
+  uma_recorder_.OnOfferSuggestionSelected(frontend_id);
 }
 
 void MerchantPromoCodeManager::Init(PersonalDataManager* personal_data_manager,
@@ -68,6 +71,78 @@
   return weak_ptr_factory_.GetWeakPtr();
 }
 
+void MerchantPromoCodeManager::UMARecorder::OnOffersSuggestionsShown(
+    const std::u16string& name,
+    std::vector<const AutofillOfferData*>& offers) {
+  // Log metrics related to the showing of overall offers suggestions popup.
+  autofill_metrics::LogOffersSuggestionsPopupShown(
+      /*first_time_being_logged=*/most_recent_suggestions_shown_field_name_ !=
+      name);
+
+  // Log metrics related to the showing of individual offers in the offers
+  // suggestions popup.
+  for (const AutofillOfferData* offer : offers) {
+    // We log every time an individual offer suggestion is shown, regardless if
+    // the user is repeatedly clicking the same field.
+    autofill_metrics::LogIndividualOfferSuggestionEvent(
+        autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionShown,
+        offer->GetOfferType());
+
+    // We log that this individual offer suggestion was shown once for this
+    // field while autofilling if it is the first time being logged.
+    if (most_recent_suggestions_shown_field_name_ != name) {
+      autofill_metrics::LogIndividualOfferSuggestionEvent(
+          autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionShownOnce,
+          offer->GetOfferType());
+    }
+  }
+
+  most_recent_suggestions_shown_field_name_ = name;
+}
+
+void MerchantPromoCodeManager::UMARecorder::OnOfferSuggestionSelected(
+    int frontend_id) {
+  if (frontend_id == PopupItemId::POPUP_ITEM_ID_MERCHANT_PROMO_CODE_ENTRY) {
+    // We log every time an individual offer suggestion is selected, regardless
+    // if the user is repeatedly autofilling the same field.
+    autofill_metrics::LogIndividualOfferSuggestionEvent(
+        autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionSelected,
+        AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER);
+
+    // We log that this individual offer suggestion was selected once for this
+    // field while autofilling if it is the first time being logged.
+    if (most_recent_suggestion_selected_field_name_ !=
+        most_recent_suggestions_shown_field_name_) {
+      autofill_metrics::LogIndividualOfferSuggestionEvent(
+          autofill_metrics::OffersSuggestionsEvent::
+              kOfferSuggestionSelectedOnce,
+          AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER);
+    }
+  } else if (frontend_id == PopupItemId::POPUP_ITEM_ID_SEE_PROMO_CODE_DETAILS) {
+    // We log every time the see offer details suggestion in the footer is
+    // selected, regardless if the user is repeatedly autofilling the same
+    // field.
+    autofill_metrics::LogIndividualOfferSuggestionEvent(
+        autofill_metrics::OffersSuggestionsEvent::
+            kOfferSuggestionSeeOfferDetailsSelected,
+        AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER);
+
+    // We log that this individual see offer details suggestion in the footer
+    // was selected once for this field while autofilling if it is the first
+    // time being logged.
+    if (most_recent_suggestion_selected_field_name_ !=
+        most_recent_suggestions_shown_field_name_) {
+      autofill_metrics::LogIndividualOfferSuggestionEvent(
+          autofill_metrics::OffersSuggestionsEvent::
+              kOfferSuggestionSeeOfferDetailsSelectedOnce,
+          AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER);
+    }
+  }
+
+  most_recent_suggestion_selected_field_name_ =
+      most_recent_suggestions_shown_field_name_;
+}
+
 void MerchantPromoCodeManager::SendPromoCodeSuggestions(
     const std::vector<const AutofillOfferData*>& promo_code_offers,
     const QueryHandler& query_handler) {
diff --git a/components/autofill/core/browser/merchant_promo_code_manager.h b/components/autofill/core/browser/merchant_promo_code_manager.h
index e32cf22..d461ae9c 100644
--- a/components/autofill/core/browser/merchant_promo_code_manager.h
+++ b/components/autofill/core/browser/merchant_promo_code_manager.h
@@ -68,6 +68,29 @@
       MerchantPromoCodeManagerTest,
       DoesNotShowPromoCodeOffersIfPersonalDataManagerDoesNotExist);
 
+  // Records metrics related to the offers suggestions popup.
+  class UMARecorder {
+   public:
+    UMARecorder() = default;
+
+    UMARecorder(const UMARecorder&) = delete;
+    UMARecorder& operator=(const UMARecorder&) = delete;
+
+    ~UMARecorder() = default;
+
+    void OnOffersSuggestionsShown(
+        const std::u16string& name,
+        std::vector<const AutofillOfferData*>& offers);
+    void OnOfferSuggestionSelected(int frontend_id);
+
+   private:
+    // The name of the field that most recently had suggestions shown.
+    std::u16string most_recent_suggestions_shown_field_name_;
+
+    // The name of the field that most recently had a suggestion selected.
+    std::u16string most_recent_suggestion_selected_field_name_;
+  };
+
   // Sends suggestions for |promo_code_offers| to the |query_handler|'s handler
   // for display in the associated Autofill popup.
   void SendPromoCodeSuggestions(
@@ -78,6 +101,8 @@
 
   bool is_off_the_record_ = false;
 
+  UMARecorder uma_recorder_;
+
   base::WeakPtrFactory<MerchantPromoCodeManager> weak_ptr_factory_{this};
 };
 
diff --git a/components/autofill/core/browser/merchant_promo_code_manager_unittest.cc b/components/autofill/core/browser/merchant_promo_code_manager_unittest.cc
index 3ebc03ae..b654380 100644
--- a/components/autofill/core/browser/merchant_promo_code_manager_unittest.cc
+++ b/components/autofill/core/browser/merchant_promo_code_manager_unittest.cc
@@ -3,8 +3,10 @@
 // found in the LICENSE file.
 
 #include "components/autofill/core/browser/merchant_promo_code_manager.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/browser/metrics/payments/offers_metrics.h"
 #include "components/autofill/core/browser/suggestions_context.h"
 #include "components/autofill/core/browser/test_form_structure.h"
 #include "components/autofill/core/browser/test_personal_data_manager.h"
@@ -75,11 +77,14 @@
 };
 
 TEST_F(MerchantPromoCodeManagerTest, ShowsPromoCodeSuggestions) {
+  base::HistogramTester histogram_tester;
   auto suggestions_handler = std::make_unique<MockSuggestionsHandler>();
   int test_query_id = 2;
   std::u16string test_name = u"Some Field Name";
   std::u16string test_prefix = u"SomePrefix";
   bool autoselect_first_suggestion = false;
+  bool is_autocomplete_enabled = true;
+  std::string form_control_type = "Some Type";
   std::string last_committed_origin_url = "https://www.example.com";
   FormData form_data;
   form_data.main_frame_origin =
@@ -102,21 +107,55 @@
           UnorderedElementsAre(
               Field(&Suggestion::main_text, promo_code_suggestion.main_text),
               Field(&Suggestion::main_text, footer_suggestion.main_text))))
-      .Times(1);
+      .Times(3);
 
   // Simulate request for suggestions.
   // Because all criteria are met, active promo code suggestions for the given
   // merchant site will be displayed instead of requesting Autocomplete
   // suggestions.
   merchant_promo_code_manager_->OnGetSingleFieldSuggestions(
-      test_query_id, /*is_autocomplete_enabled=*/true,
-      autoselect_first_suggestion, test_name, test_prefix, "Some Type",
+      test_query_id, is_autocomplete_enabled, autoselect_first_suggestion,
+      test_name, test_prefix, form_control_type,
       suggestions_handler->GetWeakPtr(),
       /*context=*/context);
+
+  // Trigger offers suggestions popup again to be able to test that we do not
+  // log metrics twice for the same field.
+  merchant_promo_code_manager_->OnGetSingleFieldSuggestions(
+      test_query_id, is_autocomplete_enabled, autoselect_first_suggestion,
+      test_name, test_prefix, form_control_type,
+      suggestions_handler->GetWeakPtr(),
+      /*context=*/context);
+
+  // Trigger offers suggestions popup again to be able to test that we log
+  // metrics more than once if it is a different field.
+  merchant_promo_code_manager_->OnGetSingleFieldSuggestions(
+      test_query_id, is_autocomplete_enabled, autoselect_first_suggestion,
+      u"other_name", test_prefix, form_control_type,
+      suggestions_handler->GetWeakPtr(),
+      /*context=*/context);
+
+  histogram_tester.ExpectBucketCount(
+      "Autofill.Offer.SuggestionsPopupShown",
+      autofill_metrics::OffersSuggestionsPopupEvent::
+          kOffersSuggestionsPopupShownOnce,
+      2);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.Offer.SuggestionsPopupShown",
+      autofill_metrics::OffersSuggestionsPopupEvent::
+          kOffersSuggestionsPopupShown,
+      3);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+      autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionShownOnce, 2);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+      autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionShown, 3);
 }
 
 TEST_F(MerchantPromoCodeManagerTest,
        DoesNotShowPromoCodeOffersForOffTheRecord) {
+  base::HistogramTester histogram_tester;
   auto suggestions_handler = std::make_unique<MockSuggestionsHandler>();
   std::string last_committed_origin_url = "https://www.example.com";
   std::string promo_code = SetUpPromoCodeOffer(
@@ -139,10 +178,29 @@
       /*autoselect_first_suggestion=*/false, /*name=*/u"Some Field Name",
       /*prefix=*/u"SomePrefix", "Some Type", suggestions_handler->GetWeakPtr(),
       /*context=*/context);
+
+  // Ensure that no metrics were logged.
+  histogram_tester.ExpectBucketCount(
+      "Autofill.Offer.SuggestionsPopupShown",
+      autofill_metrics::OffersSuggestionsPopupEvent::
+          kOffersSuggestionsPopupShownOnce,
+      0);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.Offer.SuggestionsPopupShown",
+      autofill_metrics::OffersSuggestionsPopupEvent::
+          kOffersSuggestionsPopupShown,
+      0);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+      autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionShownOnce, 0);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+      autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionShown, 0);
 }
 
 TEST_F(MerchantPromoCodeManagerTest,
        DoesNotShowPromoCodeOffersIfPersonalDataManagerDoesNotExist) {
+  base::HistogramTester histogram_tester;
   auto suggestions_handler = std::make_unique<MockSuggestionsHandler>();
   std::string last_committed_origin_url = "https://www.example.com";
   FormData form_data;
@@ -163,9 +221,28 @@
       /*autoselect_first_suggestion=*/false, /*name=*/u"Some Field Name",
       /*prefix=*/u"SomePrefix", "Some Type", suggestions_handler->GetWeakPtr(),
       /*context=*/context);
+
+  // Ensure that no metrics were logged.
+  histogram_tester.ExpectBucketCount(
+      "Autofill.Offer.SuggestionsPopupShown",
+      autofill_metrics::OffersSuggestionsPopupEvent::
+          kOffersSuggestionsPopupShownOnce,
+      0);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.Offer.SuggestionsPopupShown",
+      autofill_metrics::OffersSuggestionsPopupEvent::
+          kOffersSuggestionsPopupShown,
+      0);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+      autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionShownOnce, 0);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+      autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionShown, 0);
 }
 
 TEST_F(MerchantPromoCodeManagerTest, NoPromoCodeOffers) {
+  base::HistogramTester histogram_tester;
   auto suggestions_handler = std::make_unique<MockSuggestionsHandler>();
   std::string last_committed_origin_url = "https://www.example.com";
   personal_data_manager_.get()->SetAutofillWalletImportEnabled(true);
@@ -186,6 +263,174 @@
       /*autoselect_first_suggestion=*/false, /*name=*/u"Some Field Name",
       /*prefix=*/u"SomePrefix", "Some Type", suggestions_handler->GetWeakPtr(),
       /*context=*/context);
+
+  // Ensure that no metrics were logged.
+  histogram_tester.ExpectBucketCount(
+      "Autofill.Offer.SuggestionsPopupShown",
+      autofill_metrics::OffersSuggestionsPopupEvent::
+          kOffersSuggestionsPopupShownOnce,
+      0);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.Offer.SuggestionsPopupShown",
+      autofill_metrics::OffersSuggestionsPopupEvent::
+          kOffersSuggestionsPopupShown,
+      0);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+      autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionShownOnce, 0);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+      autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionShown, 0);
+}
+
+TEST_F(MerchantPromoCodeManagerTest,
+       OnSingleFieldSuggestion_GPayPromoCodeOfferSuggestion) {
+  // Set up the test.
+  base::HistogramTester histogram_tester;
+  auto suggestions_handler = std::make_unique<MockSuggestionsHandler>();
+  int test_query_id = 2;
+  std::u16string test_name = u"Some Field Name";
+  std::u16string test_prefix = u"SomePrefix";
+  std::u16string test_promo_code = u"test_promo_code";
+  bool autoselect_first_suggestion = false;
+  bool is_autocomplete_enabled = true;
+  std::string form_control_type = "Some Type";
+  std::string last_committed_origin_url = "https://www.example.com";
+  FormData form_data;
+  form_data.main_frame_origin =
+      url::Origin::Create(GURL(last_committed_origin_url));
+  TestFormStructure form_structure{form_data};
+  SuggestionsContext context;
+  context.form_structure = &form_structure;
+  SetUpPromoCodeOffer(last_committed_origin_url,
+                      GURL("https://offer-details-url.com/"));
+
+  // Check that non promo code frontend id's do not log as offer suggestion
+  // selected.
+  merchant_promo_code_manager_->OnSingleFieldSuggestionSelected(
+      test_promo_code, POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+      autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionSelected, 0);
+
+  // Simulate showing the promo code offers suggestions popup.
+  merchant_promo_code_manager_->OnGetSingleFieldSuggestions(
+      test_query_id, is_autocomplete_enabled, autoselect_first_suggestion,
+      test_name, test_prefix, form_control_type,
+      suggestions_handler->GetWeakPtr(),
+      /*context=*/context);
+
+  // Simulate selecting a promo code offer suggestion.
+  merchant_promo_code_manager_->OnSingleFieldSuggestionSelected(
+      test_promo_code, POPUP_ITEM_ID_MERCHANT_PROMO_CODE_ENTRY);
+
+  // Check that the histograms logged correctly.
+  histogram_tester.ExpectBucketCount(
+      "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+      autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionSelected, 1);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+      autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionSelectedOnce,
+      1);
+
+  // Simulate showing the promo code offers suggestions popup.
+  merchant_promo_code_manager_->OnGetSingleFieldSuggestions(
+      test_query_id, is_autocomplete_enabled, autoselect_first_suggestion,
+      test_name, test_prefix, form_control_type,
+      suggestions_handler->GetWeakPtr(),
+      /*context=*/context);
+
+  // Simulate selecting a promo code offer suggestion.
+  merchant_promo_code_manager_->OnSingleFieldSuggestionSelected(
+      test_promo_code, POPUP_ITEM_ID_MERCHANT_PROMO_CODE_ENTRY);
+
+  // Check that the histograms logged correctly.
+  histogram_tester.ExpectBucketCount(
+      "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+      autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionSelected, 2);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+      autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionSelectedOnce,
+      1);
+}
+
+TEST_F(MerchantPromoCodeManagerTest,
+       OnSingleFieldSuggestion_GPayPromoCodeOfferFooter) {
+  // Set up the test.
+  base::HistogramTester histogram_tester;
+  auto suggestions_handler = std::make_unique<MockSuggestionsHandler>();
+  int test_query_id = 2;
+  std::u16string test_name = u"Some Field Name";
+  std::u16string test_prefix = u"SomePrefix";
+  std::u16string test_promo_code = u"test_promo_code";
+  bool autoselect_first_suggestion = false;
+  bool is_autocomplete_enabled = true;
+  std::string form_control_type = "Some Type";
+  std::string last_committed_origin_url = "https://www.example.com";
+  FormData form_data;
+  form_data.main_frame_origin =
+      url::Origin::Create(GURL(last_committed_origin_url));
+  TestFormStructure form_structure{form_data};
+  SuggestionsContext context;
+  context.form_structure = &form_structure;
+  SetUpPromoCodeOffer(last_committed_origin_url,
+                      GURL("https://offer-details-url.com/"));
+
+  // Check that non promo code footer frontend id's do not log as offer
+  // suggestions footer selected.
+  merchant_promo_code_manager_->OnSingleFieldSuggestionSelected(
+      test_promo_code, POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+      autofill_metrics::OffersSuggestionsEvent::
+          kOfferSuggestionSeeOfferDetailsSelected,
+      0);
+
+  // Simulate showing the promo code offers suggestions popup.
+  merchant_promo_code_manager_->OnGetSingleFieldSuggestions(
+      test_query_id, is_autocomplete_enabled, autoselect_first_suggestion,
+      test_name, test_prefix, form_control_type,
+      suggestions_handler->GetWeakPtr(),
+      /*context=*/context);
+
+  // Simulate selecting a promo code offer suggestion.
+  merchant_promo_code_manager_->OnSingleFieldSuggestionSelected(
+      test_promo_code, POPUP_ITEM_ID_SEE_PROMO_CODE_DETAILS);
+
+  // Check that the histograms logged correctly.
+  histogram_tester.ExpectBucketCount(
+      "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+      autofill_metrics::OffersSuggestionsEvent::
+          kOfferSuggestionSeeOfferDetailsSelected,
+      1);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+      autofill_metrics::OffersSuggestionsEvent::
+          kOfferSuggestionSeeOfferDetailsSelectedOnce,
+      1);
+
+  // Simulate showing the promo code offers suggestions popup.
+  merchant_promo_code_manager_->OnGetSingleFieldSuggestions(
+      test_query_id, is_autocomplete_enabled, autoselect_first_suggestion,
+      test_name, test_prefix, form_control_type,
+      suggestions_handler->GetWeakPtr(),
+      /*context=*/context);
+
+  // Simulate selecting a promo code offer suggestion.
+  merchant_promo_code_manager_->OnSingleFieldSuggestionSelected(
+      test_promo_code, POPUP_ITEM_ID_SEE_PROMO_CODE_DETAILS);
+
+  // Check that the histograms logged correctly.
+  histogram_tester.ExpectBucketCount(
+      "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+      autofill_metrics::OffersSuggestionsEvent::
+          kOfferSuggestionSeeOfferDetailsSelected,
+      2);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.Offer.Suggestion.GPayPromoCodeOffer",
+      autofill_metrics::OffersSuggestionsEvent::
+          kOfferSuggestionSeeOfferDetailsSelectedOnce,
+      1);
 }
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/metrics/payments/offers_metrics.cc b/components/autofill/core/browser/metrics/payments/offers_metrics.cc
index 28e7d2e..4a7b997 100644
--- a/components/autofill/core/browser/metrics/payments/offers_metrics.cc
+++ b/components/autofill/core/browser/metrics/payments/offers_metrics.cc
@@ -7,6 +7,7 @@
 #include <unordered_map>
 
 #include "base/metrics/histogram_functions.h"
+#include "base/notreached.h"
 #include "components/autofill/core/browser/data_model/autofill_offer_data.h"
 
 namespace autofill::autofill_metrics {
@@ -47,4 +48,42 @@
   }
 }
 
+void LogOffersSuggestionsPopupShown(bool first_time_being_logged) {
+  if (first_time_being_logged) {
+    // We log that the offers suggestions popup was shown once for this field
+    // while autofilling if it is the first time being logged.
+    base::UmaHistogramEnumeration(
+        "Autofill.Offer.SuggestionsPopupShown",
+        autofill::autofill_metrics::OffersSuggestionsPopupEvent::
+            kOffersSuggestionsPopupShownOnce);
+  }
+
+  // We log every time the offers suggestions popup is shown, regardless if the
+  // user is repeatedly clicking the same field.
+  base::UmaHistogramEnumeration(
+      "Autofill.Offer.SuggestionsPopupShown",
+      autofill::autofill_metrics::OffersSuggestionsPopupEvent::
+          kOffersSuggestionsPopupShown);
+}
+
+void LogIndividualOfferSuggestionEvent(
+    OffersSuggestionsEvent event,
+    AutofillOfferData::OfferType offer_type) {
+  std::string histogram_name = "Autofill.Offer.Suggestion";
+
+  // Switch to different sub-histogram depending on offer type being displayed.
+  switch (offer_type) {
+    case AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER:
+      histogram_name += ".GPayPromoCodeOffer";
+      break;
+    case AutofillOfferData::OfferType::GPAY_CARD_LINKED_OFFER:
+    case AutofillOfferData::OfferType::FREE_LISTING_COUPON_OFFER:
+    case AutofillOfferData::OfferType::UNKNOWN:
+      NOTREACHED();
+      return;
+  }
+
+  base::UmaHistogramEnumeration(histogram_name, event);
+}
+
 }  // namespace autofill::autofill_metrics
diff --git a/components/autofill/core/browser/metrics/payments/offers_metrics.h b/components/autofill/core/browser/metrics/payments/offers_metrics.h
index 34c7c6f5..38cc516 100644
--- a/components/autofill/core/browser/metrics/payments/offers_metrics.h
+++ b/components/autofill/core/browser/metrics/payments/offers_metrics.h
@@ -17,6 +17,62 @@
 void LogStoredOfferMetrics(
     const std::vector<std::unique_ptr<AutofillOfferData>>& offers);
 
+// Metrics to track events related to the offers suggestions popup.
+enum class OffersSuggestionsPopupEvent {
+  // These values are persisted to logs. Entries should not be renumbered and
+  // numeric values should never be reused.
+
+  // Default value, should never be used.
+  kUndefined = 0,
+  // The offer suggestions popup was shown. One event is logged in this metric
+  // bucket per time that the overall popup is shown.
+  kOffersSuggestionsPopupShown = 1,
+  // The offers suggestions popup was shown. Logged once if the user repeatedly
+  // displays suggestions for the same field.
+  kOffersSuggestionsPopupShownOnce = 2,
+  kMaxValue = kOffersSuggestionsPopupShownOnce,
+};
+
+// Metrics to track events related to individual offer suggestions in the
+// offers suggestions popup.
+enum class OffersSuggestionsEvent {
+  // These values are persisted to logs. Entries should not be renumbered and
+  // numeric values should never be reused.
+
+  // Default value, should never be used.
+  kUndefined = 0,
+  // An individual offer suggestion was shown. One event is logged in this
+  // metric bucket for each individual offer suggestion in the suggestions
+  // popup. For instance, if there are three offers suggested, this bucket would
+  // log three times.
+  kOfferSuggestionShown = 1,
+  // An individual offer suggestion was shown. Logged once if the user
+  // repeatedly displays suggestions for the same field.
+  kOfferSuggestionShownOnce = 2,
+  // An individual offer suggestion was selected.
+  kOfferSuggestionSelected = 3,
+  // An individual offer suggestion was selected. Logged once if the user
+  // repeatedly selects suggestions for the same field.
+  kOfferSuggestionSelectedOnce = 4,
+  // The user selected to see the offer details page in the footer.
+  kOfferSuggestionSeeOfferDetailsSelected = 5,
+  // The user selected to see the offer details page in the footer. Logged once
+  // if the user repeatedly selects to see the offer details page in the footer
+  // for the same field.
+  kOfferSuggestionSeeOfferDetailsSelectedOnce = 6,
+  kMaxValue = kOfferSuggestionSeeOfferDetailsSelectedOnce,
+};
+
+// Log that the offers suggestions popup was shown. If |first_time_being_logged|
+// is true, it represents that it has not been logged yet for the promo code
+// offer field that the user is on, so additional logging is needed for the
+// histogram that denotes showing the offers suggestions popup once for a field.
+void LogOffersSuggestionsPopupShown(bool first_time_being_logged);
+
+// Log the offers suggestions popup |event| for the corresponding |offer_type|.
+void LogIndividualOfferSuggestionEvent(OffersSuggestionsEvent event,
+                                       AutofillOfferData::OfferType offer_type);
+
 }  // namespace autofill::autofill_metrics
 
 #endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_METRICS_PAYMENTS_OFFERS_METRICS_H_
diff --git a/components/autofill/core/browser/single_field_form_fill_router.cc b/components/autofill/core/browser/single_field_form_fill_router.cc
index 44de401..9bbc838 100644
--- a/components/autofill/core/browser/single_field_form_fill_router.cc
+++ b/components/autofill/core/browser/single_field_form_fill_router.cc
@@ -104,7 +104,8 @@
     const std::u16string& value,
     int frontend_id) {
   if (merchant_promo_code_manager_ &&
-      frontend_id == POPUP_ITEM_ID_MERCHANT_PROMO_CODE_ENTRY) {
+      (frontend_id == POPUP_ITEM_ID_MERCHANT_PROMO_CODE_ENTRY ||
+       frontend_id == POPUP_ITEM_ID_SEE_PROMO_CODE_DETAILS)) {
     merchant_promo_code_manager_->OnSingleFieldSuggestionSelected(value,
                                                                   frontend_id);
   } else {
diff --git a/components/autofill/core/browser/test_autofill_client.cc b/components/autofill/core/browser/test_autofill_client.cc
index f974ae13..344d482 100644
--- a/components/autofill/core/browser/test_autofill_client.cc
+++ b/components/autofill/core/browser/test_autofill_client.cc
@@ -324,8 +324,7 @@
 
 void TestAutofillClient::ExecuteCommand(int id) {}
 
-void TestAutofillClient::OnPromoCodeSuggestionsFooterSelected(const GURL& url) {
-}
+void TestAutofillClient::OpenPromoCodeOfferDetailsURL(const GURL& url) {}
 
 void TestAutofillClient::LoadRiskData(
     base::OnceCallback<void(const std::string&)> callback) {
diff --git a/components/autofill/core/browser/test_autofill_client.h b/components/autofill/core/browser/test_autofill_client.h
index a7d75e1..72acfd3 100644
--- a/components/autofill/core/browser/test_autofill_client.h
+++ b/components/autofill/core/browser/test_autofill_client.h
@@ -171,7 +171,7 @@
   bool ShouldShowSigninPromo() override;
   bool AreServerCardsSupported() const override;
   void ExecuteCommand(int id) override;
-  void OnPromoCodeSuggestionsFooterSelected(const GURL& url) override;
+  void OpenPromoCodeOfferDetailsURL(const GURL& url) override;
 
   // RiskDataLoader:
   void LoadRiskData(
diff --git a/components/autofill/core/common/autofill_payments_features.cc b/components/autofill/core/common/autofill_payments_features.cc
index 10df939f4..92ee536 100644
--- a/components/autofill/core/common/autofill_payments_features.cc
+++ b/components/autofill/core/common/autofill_payments_features.cc
@@ -85,6 +85,10 @@
     "AutofillEnableOffersInClankKeyboardAccessory",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
+// When enabled, some extra metrics logging for Autofill Downstream will start.
+const base::Feature kAutofillEnableRemadeDownstreamMetrics{
+    "AutofillEnableRemadeDownstreamMetrics", base::FEATURE_ENABLED_BY_DEFAULT};
+
 // Controls whether we send billing customer number in GetUploadDetails
 // preflight call.
 const base::Feature kAutofillEnableSendingBcnInGetUploadDetails{
@@ -198,12 +202,6 @@
     "AutofillShowUnmaskedCachedCardInManualFillingView",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
-// When enabled, merchant bound virtual cards will be suggested even if we don't
-// detect all of the card number, exp date and CVC fields in the payment form.
-const base::Feature kAutofillSuggestVirtualCardsOnIncompleteForm{
-    "AutofillSuggestVirtualCardsOnIncompleteForm",
-    base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Controls offering credit card upload to Google Payments. Cannot ever be
 // ENABLED_BY_DEFAULT because the feature state depends on the user's country.
 // The set of launched countries is listed in autofill_experiments.cc, and this
diff --git a/components/autofill/core/common/autofill_payments_features.h b/components/autofill/core/common/autofill_payments_features.h
index 9423072..8818c06f 100644
--- a/components/autofill/core/common/autofill_payments_features.h
+++ b/components/autofill/core/common/autofill_payments_features.h
@@ -27,6 +27,7 @@
 extern const base::Feature kAutofillEnableManualFallbackForVirtualCards;
 extern const base::Feature kAutofillEnableOfferNotificationForPromoCodes;
 extern const base::Feature kAutofillEnableOffersInClankKeyboardAccessory;
+extern const base::Feature kAutofillEnableRemadeDownstreamMetrics;
 extern const base::Feature kAutofillEnableSendingBcnInGetUploadDetails;
 extern const base::Feature kAutofillEnableStickyManualFallbackForCards;
 extern const base::Feature kAutofillEnableToolbarStatusChip;
@@ -48,7 +49,6 @@
 extern const base::FeatureParam<int>
     kAutofillSaveCardUiExperimentSelectorInNumber;
 extern const base::Feature kAutofillShowUnmaskedCachedCardInManualFillingView;
-extern const base::Feature kAutofillSuggestVirtualCardsOnIncompleteForm;
 extern const base::Feature kAutofillUpstream;
 extern const base::Feature kAutofillUpstreamAllowAdditionalEmailDomains;
 extern const base::Feature kAutofillUpstreamAllowAllEmailDomains;
diff --git a/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheet.java b/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheet.java
index d3830ee..90c9cc3 100644
--- a/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheet.java
+++ b/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheet.java
@@ -25,6 +25,7 @@
 import org.chromium.base.Log;
 import org.chromium.base.MathUtils;
 import org.chromium.base.ObserverList;
+import org.chromium.base.supplier.Supplier;
 import org.chromium.base.task.PostTask;
 import org.chromium.base.task.TaskTraits;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent.HeightMode;
@@ -164,6 +165,18 @@
     /** A means of checking whether accessibility is currently enabled. */
     private AccessibilityUtil mAccessibilityUtil;
 
+    private Window mWindow;
+
+    /**
+     * Provides the height of the base app area on which bottom sheet client is drawn. This is
+     * not necessary for most embedders of BottomSheet, unless they have non-zero vertical Window
+     * offset that would push down a part of app area out of the screen. BottomSheet then uses
+     * this height to resize the sheet content so all of it is visible.
+     *
+     * Note: The only embedder for which BottomSheet needs this is partial-height custom tabs.
+     */
+    private Supplier<Integer> mBaseHeightProvider;
+
     /**
      * A view used to render a shadow behind the sheet and extends outside the bounds of its parent
      * view.
@@ -303,8 +316,10 @@
      * calculations in this class.
      * @param window Android window for getting insets.
      * @param keyboardDelegate Delegate for hiding the keyboard.
+     * @param baseHeightProvider Provides the height of base app area the sheet content is drawn on.
      */
-    public void init(Window window, KeyboardVisibilityDelegate keyboardDelegate) {
+    public void init(Window window, KeyboardVisibilityDelegate keyboardDelegate,
+            Supplier<Integer> baseHeightProvider) {
         mSheetContainer = (ViewGroup) getParent();
 
         mToolbarHolder =
@@ -317,6 +332,8 @@
         mContainerWidth = mSheetContainer.getWidth();
         mContainerHeight = mSheetContainer.getHeight();
         mContentWidth = mContainerWidth;
+        mWindow = window;
+        mBaseHeightProvider = baseHeightProvider;
 
         sizeAndPositionSheetInParent();
 
@@ -1016,6 +1033,8 @@
             if (getFocusedChild() == null) requestFocus();
         }
 
+        sizeAndPositionSheetInParent();
+
         for (BottomSheetObserver o : mObservers) {
             o.onSheetStateChanged(mCurrentState, reason);
         }
@@ -1063,12 +1082,25 @@
         return mContainerWidth;
     }
 
-    /** Center and size the sheet in its container. */
     private void sizeAndPositionSheetInParent() {
+        // Center and size the sheet in its container.
         int maxSheetWidth = getMaxSheetWidth();
         getLayoutParams().width = maxSheetWidth;
         setTranslationX((LocalizationUtils.isLayoutRtl() ? -1 : 1)
                 * (mContainerWidth - maxSheetWidth) / 2f);
+
+        // Resizing is necessary if we have non-zero translation on Window Y, which can change
+        // throughout the lifecycle. Ensure sheet content's bottom is aligned with the base layout.
+        if (mWindow.getAttributes().y != 0
+                && (mCurrentState == SheetState.PEEK || mCurrentState == SheetState.HALF
+                        || mCurrentState == SheetState.FULL)) {
+            BottomSheetContent content = mSheetContent;
+            assert content != null
+                    && content.getContentView() != null : "Current content should exist";
+
+            int bottomY = mBaseHeightProvider.get();
+            content.getContentView().getLayoutParams().height = bottomY - (int) getTranslationY();
+        }
         requestLayout();
     }
 
diff --git a/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerFactory.java b/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerFactory.java
index 94d0dcc9..d697812 100644
--- a/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerFactory.java
+++ b/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerFactory.java
@@ -22,13 +22,15 @@
      * @param window The activity's window.
      * @param keyboardDelegate A means of hiding the keyboard.
      * @param root The view that should contain the sheet.
+     * @param baseHeightProvider Provides the height of base app area the sheet content is drawn on.
      * @return A new instance of the {@link BottomSheetController}.
      */
     public static ManagedBottomSheetController createBottomSheetController(
             final Supplier<ScrimCoordinator> scrim, Callback<View> initializedCallback,
-            Window window, KeyboardVisibilityDelegate keyboardDelegate, Supplier<ViewGroup> root) {
+            Window window, KeyboardVisibilityDelegate keyboardDelegate, Supplier<ViewGroup> root,
+            Supplier<Integer> baseHeightProvider) {
         return new BottomSheetControllerImpl(
-                scrim, initializedCallback, window, keyboardDelegate, root);
+                scrim, initializedCallback, window, keyboardDelegate, root, baseHeightProvider);
     }
 
     // Redirect methods to provider to make them only accessible to classes that have access to the
diff --git a/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImpl.java b/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImpl.java
index 318cfc1..4c52f4c 100644
--- a/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImpl.java
+++ b/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImpl.java
@@ -109,16 +109,19 @@
      * @param window A means of accessing the screen size.
      * @param keyboardDelegate A means of hiding the keyboard.
      * @param root The view that should contain the sheet.
+     * @param baseHeightProvider Provides the height of base app area the sheet content is drawn on.
      */
     public BottomSheetControllerImpl(final Supplier<ScrimCoordinator> scrim,
             Callback<View> initializedCallback, Window window,
-            KeyboardVisibilityDelegate keyboardDelegate, Supplier<ViewGroup> root) {
+            KeyboardVisibilityDelegate keyboardDelegate, Supplier<ViewGroup> root,
+            Supplier<Integer> baseHeightProvider) {
         mScrimCoordinatorSupplier = scrim;
         mPendingSheetObservers = new ArrayList<>();
         mSuppressionTokens = new TokenHolder(() -> onSuppressionTokensChanged());
 
         mSheetInitializer = () -> {
-            initializeSheet(initializedCallback, window, keyboardDelegate, root);
+            initializeSheet(
+                    initializedCallback, window, keyboardDelegate, root, baseHeightProvider);
         };
 
         mBackPressHandler = new BackPressHandler() {
@@ -154,9 +157,11 @@
      * @param window A means of accessing the screen size.
      * @param keyboardDelegate A means of hiding the keyboard.
      * @param root The view that should contain the sheet.
+     * @param baseHeightProvider Provides the height of base app area the sheet content is drawn on.
      */
     private void initializeSheet(Callback<View> initializedCallback, Window window,
-            KeyboardVisibilityDelegate keyboardDelegate, Supplier<ViewGroup> root) {
+            KeyboardVisibilityDelegate keyboardDelegate, Supplier<ViewGroup> root,
+            Supplier<Integer> baseHeightProvider) {
         mBottomSheetContainer = root.get();
         mBottomSheetContainer.setVisibility(View.VISIBLE);
 
@@ -165,7 +170,7 @@
         mBottomSheet = (BottomSheet) root.get().findViewById(R.id.bottom_sheet);
         initializedCallback.onResult(mBottomSheet);
 
-        mBottomSheet.init(window, keyboardDelegate);
+        mBottomSheet.init(window, keyboardDelegate, baseHeightProvider);
         mBottomSheet.setAccessibilityUtil(mAccessibilityUtil);
 
         // Initialize the queue with a comparator that checks content priority.
diff --git a/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetObserverTest.java b/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetObserverTest.java
index f0c695b..9a0918e 100644
--- a/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetObserverTest.java
+++ b/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetObserverTest.java
@@ -144,7 +144,7 @@
             Callback<View> initializedCallback = (v) -> {};
             return new BottomSheetControllerImpl(scrimSupplier, initializedCallback,
                     sTestRule.getActivity().getWindow(), KeyboardVisibilityDelegate.getInstance(),
-                    () -> rootView);
+                    () -> rootView, () -> 0);
         });
 
         mTestSupport = new BottomSheetTestSupport(mBottomSheetController);
diff --git a/components/components_strings.grd b/components/components_strings.grd
index d62bb020..21e030a 100644
--- a/components/components_strings.grd
+++ b/components/components_strings.grd
@@ -306,6 +306,7 @@
       <part file="history_strings.grdp" />
       <part file="javascript_dialogs_strings.grdp" />
       <part file="login_dialog_strings.grdp" />
+      <part file="management_strings.grdp" />
       <part file="media_message_center_strings.grdp" />
       <part file="new_or_sad_tab_strings.grdp" />
       <part file="ntp_snippets_strings.grdp" />
@@ -338,9 +339,6 @@
       <part file="webapps_strings.grdp" />
 
       <if expr="not is_ios">
-        <part file="management_strings.grdp" />
-      </if>
-      <if expr="not is_ios">
         <part file="history_clusters_strings.grdp" />
       </if>
       <if expr="is_android">
diff --git a/components/error_page/common/localized_error.cc b/components/error_page/common/localized_error.cc
index db6afc68..8baf9a5 100644
--- a/components/error_page/common/localized_error.cc
+++ b/components/error_page/common/localized_error.cc
@@ -514,11 +514,11 @@
 
 // Returns a dictionary containing the strings for the settings menu under the
 // app menu, and the advanced settings button.
-base::DictionaryValue GetStandardMenuItemsText() {
-  base::DictionaryValue standard_menu_items_text;
-  standard_menu_items_text.SetStringPath(
-      "settingsTitle", l10n_util::GetStringUTF16(IDS_SETTINGS_TITLE));
-  standard_menu_items_text.SetStringPath(
+base::Value::Dict GetStandardMenuItemsText() {
+  base::Value::Dict standard_menu_items_text;
+  standard_menu_items_text.Set("settingsTitle",
+                               l10n_util::GetStringUTF16(IDS_SETTINGS_TITLE));
+  standard_menu_items_text.Set(
       "advancedTitle",
       l10n_util::GetStringUTF16(IDS_SETTINGS_SHOW_ADVANCED_SETTINGS));
   return standard_menu_items_text;
@@ -532,19 +532,18 @@
              : "icon-generic";
 }
 
-base::DictionaryValue SingleEntryDictionary(base::StringPiece path,
-                                            int message_id) {
-  base::DictionaryValue result;
-  result.SetStringPath(path, l10n_util::GetStringUTF16(message_id));
+base::Value::Dict SingleEntryDictionary(base::StringPiece path,
+                                        int message_id) {
+  base::Value::Dict result;
+  result.Set(path, l10n_util::GetStringUTF16(message_id));
   return result;
 }
 
 // Adds a linked suggestion dictionary entry to the suggestions list.
-void AddLinkedSuggestionToList(
-    const int error_code,
-    const std::string& locale,
-    std::vector<base::Value>& suggestions_summary_list,
-    bool standalone_suggestion) {
+void AddLinkedSuggestionToList(const int error_code,
+                               const std::string& locale,
+                               base::Value::List& suggestions_summary_list,
+                               bool standalone_suggestion) {
   GURL learn_more_url;
   std::u16string suggestion_string =
       standalone_suggestion
@@ -571,11 +570,10 @@
   repl.SetQueryStr(query);
   GURL learn_more_url_with_locale = learn_more_url.ReplaceComponents(repl);
 
-  base::DictionaryValue suggestion_list_item;
-  suggestion_list_item.SetStringPath("summary", suggestion_string);
-  suggestion_list_item.SetStringPath("learnMoreUrl",
-                                     learn_more_url_with_locale.spec());
-  suggestions_summary_list.push_back(std::move(suggestion_list_item));
+  base::Value::Dict suggestion_list_item;
+  suggestion_list_item.Set("summary", suggestion_string);
+  suggestion_list_item.Set("learnMoreUrl", learn_more_url_with_locale.spec());
+  suggestions_summary_list.Append(std::move(suggestion_list_item));
 }
 
 // Check if a suggestion is in the bitmap of suggestions.
@@ -590,14 +588,13 @@
 
 // Creates a list of suggestions that a user may try to resolve a particular
 // network error. Appears above the fold underneath heading and intro paragraph.
-void GetSuggestionsSummaryList(
-    int error_code,
-    base::Value::Dict& error_strings,
-    int suggestions,
-    const std::string& locale,
-    std::vector<base::Value>& suggestions_summary_list,
-    bool can_show_network_diagnostics_dialog,
-    const GURL& failed_url) {
+void GetSuggestionsSummaryList(int error_code,
+                               base::Value::Dict& error_strings,
+                               int suggestions,
+                               const std::string& locale,
+                               base::Value::List& suggestions_summary_list,
+                               bool can_show_network_diagnostics_dialog,
+                               const GURL& failed_url) {
   // Remove the diagnostic tool suggestion if the platform doesn't support it
   // or the url isn't valid.
   if (!can_show_network_diagnostics_dialog || !failed_url.is_valid() ||
@@ -611,21 +608,21 @@
   if (IsOnlySuggestion(suggestions, SUGGEST_CONTACT_ADMINISTRATOR)) {
     DCHECK(suggestions_summary_list.empty());
     DCHECK(!(suggestions & ~SUGGEST_CONTACT_ADMINISTRATOR));
-    suggestions_summary_list.push_back(SingleEntryDictionary(
+    suggestions_summary_list.Append(SingleEntryDictionary(
         "summary", IDS_ERRORPAGES_SUGGESTION_CONTACT_ADMIN_SUMMARY_STANDALONE));
     return;
   }
   if (IsSuggested(suggestions, SUGGEST_CONTACT_ADMINISTRATOR)) {
-    suggestions_summary_list.push_back(SingleEntryDictionary(
+    suggestions_summary_list.Append(SingleEntryDictionary(
         "summary", IDS_ERRORPAGES_SUGGESTION_CONTACT_ADMIN_SUMMARY));
   }
 
   if (IsOnlySuggestion(suggestions, SUGGEST_COMPLETE_SETUP)) {
     DCHECK(suggestions_summary_list.empty());
     DCHECK(!(suggestions & ~SUGGEST_COMPLETE_SETUP));
-    suggestions_summary_list.push_back(SingleEntryDictionary(
+    suggestions_summary_list.Append(SingleEntryDictionary(
         "summary", IDS_ERRORPAGES_SUGGESTION_DIAGNOSE_CONNECTION_SUMMARY));
-    suggestions_summary_list.push_back(SingleEntryDictionary(
+    suggestions_summary_list.Append(SingleEntryDictionary(
         "summary", IDS_ERRORPAGES_SUGGESTION_COMPLETE_SETUP_SUMMARY));
     return;
   }
@@ -638,7 +635,7 @@
     // way, so just add a suggestion instead.
     // TODO(mmenke):  Make the reload button bring up the repost confirmation
     //                dialog for pages resulting from posts.
-    suggestions_summary_list.push_back(SingleEntryDictionary(
+    suggestions_summary_list.Append(SingleEntryDictionary(
         "summary", IDS_ERRORPAGES_SUGGESTION_RELOAD_REPOST_SUMMARY));
     return;
   }
@@ -651,12 +648,12 @@
     if (failed_origin.opaque())
       return;
 
-    base::DictionaryValue suggestion;
-    suggestion.SetStringPath("summary",
-                             l10n_util::GetStringUTF16(
-                                 IDS_ERRORPAGES_SUGGESTION_NAVIGATE_TO_ORIGIN));
-    suggestion.SetStringPath("originURL", failed_origin.Serialize());
-    suggestions_summary_list.push_back(std::move(suggestion));
+    base::Value::Dict suggestion;
+    suggestion.Set("summary",
+                   l10n_util::GetStringUTF16(
+                       IDS_ERRORPAGES_SUGGESTION_NAVIGATE_TO_ORIGIN));
+    suggestion.Set("originURL", failed_origin.Serialize());
+    suggestions_summary_list.Append(std::move(suggestion));
     return;
   }
   DCHECK(!IsSuggested(suggestions, SUGGEST_NAVIGATE_TO_ORIGIN));
@@ -674,14 +671,14 @@
 
   if (suggestions & SUGGEST_DISABLE_EXTENSION) {
     DCHECK(suggestions_summary_list.empty());
-    suggestions_summary_list.push_back(SingleEntryDictionary(
+    suggestions_summary_list.Append(SingleEntryDictionary(
         "summary", IDS_ERRORPAGES_SUGGESTION_DISABLE_EXTENSION_SUMMARY));
     return;
   }
   DCHECK(!IsSuggested(suggestions, SUGGEST_DISABLE_EXTENSION));
 
   if (suggestions & SUGGEST_CHECK_CONNECTION) {
-    suggestions_summary_list.push_back(SingleEntryDictionary(
+    suggestions_summary_list.Append(SingleEntryDictionary(
         "summary", IDS_ERRORPAGES_SUGGESTION_CHECK_CONNECTION_SUMMARY));
   }
 
@@ -689,24 +686,24 @@
   if (IsSuggested(suggestions, SUGGEST_DNS_CONFIG) &&
       IsSuggested(suggestions, SUGGEST_FIREWALL_CONFIG) &&
       IsSuggested(suggestions, SUGGEST_PROXY_CONFIG)) {
-    suggestions_summary_list.push_back(SingleEntryDictionary(
+    suggestions_summary_list.Append(SingleEntryDictionary(
         "summary", IDS_ERRORPAGES_SUGGESTION_CHECK_PROXY_FIREWALL_DNS_SUMMARY));
   } else if (IsSuggested(suggestions, SUGGEST_SECURE_DNS_CONFIG) &&
              IsSuggested(suggestions, SUGGEST_FIREWALL_CONFIG) &&
              IsSuggested(suggestions, SUGGEST_PROXY_CONFIG)) {
-    suggestions_summary_list.push_back(SingleEntryDictionary(
+    suggestions_summary_list.Append(SingleEntryDictionary(
         "summary",
         IDS_ERRORPAGES_SUGGESTION_CHECK_PROXY_FIREWALL_SECURE_DNS_SUMMARY));
   } else if (IsSuggested(suggestions, SUGGEST_FIREWALL_CONFIG) &&
              IsSuggested(suggestions, SUGGEST_ANTIVIRUS_CONFIG)) {
-    suggestions_summary_list.push_back(SingleEntryDictionary(
+    suggestions_summary_list.Append(SingleEntryDictionary(
         "summary", IDS_ERRORPAGES_SUGGESTION_CHECK_FIREWALL_ANTIVIRUS_SUMMARY));
   } else if (IsSuggested(suggestions, SUGGEST_PROXY_CONFIG) &&
              IsSuggested(suggestions, SUGGEST_FIREWALL_CONFIG)) {
-    suggestions_summary_list.push_back(SingleEntryDictionary(
+    suggestions_summary_list.Append(SingleEntryDictionary(
         "summary", IDS_ERRORPAGES_SUGGESTION_CHECK_PROXY_FIREWALL_SUMMARY));
   } else if (IsSuggested(suggestions, SUGGEST_PROXY_CONFIG)) {
-    suggestions_summary_list.push_back(SingleEntryDictionary(
+    suggestions_summary_list.Append(SingleEntryDictionary(
         "summary", IDS_ERRORPAGES_SUGGESTION_CHECK_PROXY_ADDRESS_SUMMARY));
   } else {
     DCHECK(!(suggestions & SUGGEST_PROXY_CONFIG));
@@ -716,23 +713,23 @@
   }
 #elif BUILDFLAG(IS_ANDROID)
   if (IsSuggested(suggestions, SUGGEST_SECURE_DNS_CONFIG)) {
-    suggestions_summary_list.push_back(SingleEntryDictionary(
+    suggestions_summary_list.Append(SingleEntryDictionary(
         "summary", IDS_ERRORPAGES_SUGGESTION_CHECK_SECURE_DNS_SUMMARY));
   }
 #endif
 
   if (IsSuggested(suggestions, SUGGEST_OFFLINE_CHECKS)) {
 #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
-    suggestions_summary_list.push_back(SingleEntryDictionary(
+    suggestions_summary_list.Append(SingleEntryDictionary(
         "summary", IDS_ERRORPAGES_SUGGESTION_TURN_OFF_AIRPLANE_SUMMARY));
-    suggestions_summary_list.push_back(SingleEntryDictionary(
+    suggestions_summary_list.Append(SingleEntryDictionary(
         "summary", IDS_ERRORPAGES_SUGGESTION_TURN_ON_DATA_SUMMARY));
-    suggestions_summary_list.push_back(SingleEntryDictionary(
+    suggestions_summary_list.Append(SingleEntryDictionary(
         "summary", IDS_ERRORPAGES_SUGGESTION_CHECKING_SIGNAL_SUMMARY));
 #else
-    suggestions_summary_list.push_back(SingleEntryDictionary(
+    suggestions_summary_list.Append(SingleEntryDictionary(
         "summary", IDS_ERRORPAGES_SUGGESTION_CHECK_HARDWARE_SUMMARY));
-    suggestions_summary_list.push_back(SingleEntryDictionary(
+    suggestions_summary_list.Append(SingleEntryDictionary(
         "summary", IDS_ERRORPAGES_SUGGESTION_CHECK_WIFI_SUMMARY));
 #endif
   }
@@ -746,12 +743,12 @@
             ? IDS_ERRORPAGES_SUGGESTION_DIAGNOSE_CHECK_TYPO_STANDALONE
             : IDS_ERRORPAGES_SUGGESTION_DIAGNOSE_STANDALONE;
 
-    suggestions_summary_list.push_back(
+    suggestions_summary_list.Append(
         SingleEntryDictionary("summary", diagose_message_id));
     return;
   }
   if (IsSuggested(suggestions, SUGGEST_DIAGNOSE_TOOL)) {
-    suggestions_summary_list.push_back(
+    suggestions_summary_list.Append(
         SingleEntryDictionary("summary", IDS_ERRORPAGES_SUGGESTION_DIAGNOSE));
   }
 #else
@@ -765,30 +762,30 @@
 }
 
 // Creates a dictionary with "header" and "body" entries and adds it to `list`.
-void AddSuggestionDetailDictionaryToList(std::vector<base::Value>& list,
+void AddSuggestionDetailDictionaryToList(base::Value::List& list,
                                          int header_message_id,
                                          int body_message_id,
                                          bool append_standard_menu_items) {
-  base::DictionaryValue suggestion_list_item;
+  base::Value::Dict suggestion_list_item;
   if (append_standard_menu_items)
     suggestion_list_item = GetStandardMenuItemsText();
 
   if (header_message_id) {
-    suggestion_list_item.SetStringPath(
-        "header", l10n_util::GetStringUTF16(header_message_id));
+    suggestion_list_item.Set("header",
+                             l10n_util::GetStringUTF16(header_message_id));
   }
   if (body_message_id) {
-    suggestion_list_item.SetStringPath(
-        "body", l10n_util::GetStringUTF16(body_message_id));
+    suggestion_list_item.Set("body",
+                             l10n_util::GetStringUTF16(body_message_id));
   }
-  list.push_back(std::move(suggestion_list_item));
+  list.Append(std::move(suggestion_list_item));
 }
 
 // Certain suggestions have supporting details which get displayed under
 // the "Details" button.
 void AddSuggestionsDetails(int error_code,
                            int suggestions,
-                           std::vector<base::Value>& suggestions_details) {
+                           base::Value::List& suggestions_details) {
   if (suggestions & SUGGEST_CHECK_CONNECTION) {
     AddSuggestionDetailDictionaryToList(suggestions_details,
           IDS_ERRORPAGES_SUGGESTION_CHECK_CONNECTION_HEADER,
@@ -1050,8 +1047,8 @@
   }
   result.strings.Set("errorCode", error_string);
 
-  std::vector<base::Value> suggestions_details;
-  std::vector<base::Value> suggestions_summary_list;
+  base::Value::List suggestions_details;
+  base::Value::List suggestions_summary_list;
 
   // Add the reload suggestion, if needed, for pages that didn't come
   // from a post.
diff --git a/components/management_strings.grdp b/components/management_strings.grdp
index 5dc2edb6..b374a6c1d 100644
--- a/components/management_strings.grdp
+++ b/components/management_strings.grdp
@@ -11,16 +11,18 @@
 -->
 
 <grit-part>
-  <!-- Title of the page -->
-  <message name="IDS_MANAGEMENT_TITLE" desc="Title of management page.">
-    Settings - Management
-  </message>
-  <message name="IDS_MANAGEMENT_TOOLBAR_TITLE" desc="Title of the toolbar in the management page.">
-    Settings
-  </message>
-
+  <if expr="not is_ios">
+    <!-- Title of the page -->
+    <message name="IDS_MANAGEMENT_TITLE" desc="Title of management page.">
+      Settings - Management
+    </message>
+    <message name="IDS_MANAGEMENT_TOOLBAR_TITLE" desc="Title of the toolbar in the management page.">
+      Settings
+    </message>
+  </if>
+  
   <!-- Subtitles of the ChromeOS version of the management page -->
- <if expr="chromeos_ash">
+  <if expr="chromeos_ash">
     <message name="IDS_MANAGEMENT_SUBTITLE_MANAGED" desc="Title of chrome://management page, shows when device is managed by an unknown manager">
       Your <ph name="DEVICE_NAME">$1<ex>Chromebook</ex></ph> is managed
     </message>
@@ -43,28 +45,39 @@
     <message name="IDS_MANAGEMENT_NOT_MANAGED_SUBTITLE" desc="Title of chrome://management page for a browser that is not managed">
       Your browser is not managed
     </message>
+    <message name="IDS_MANAGEMENT_SUBTITLE_BROWSER_AND_PROFILE_DIFFERENT_MANAGED_BY" desc="Text displayed to inform that the browser is managed by BROWSER_DOMAIN and the profile by PROFILE_DOMAIN.">
+      Your browser is managed by <ph name="BROWSER_DOMAIN">$1<ex>foo.com</ex></ph> and your profile is managed by <ph name="PROFILE_DOMAIN">$2<ex>bar.com</ex></ph>
+    </message>
+    <message name="IDS_MANAGEMENT_SUBTITLE_BROWSER_AND_PROFILE_SAME_MANAGED_BY" desc="Text displayed to inform that the browser and profile are managed by the same DOMAIN.">
+      Your profile and browser are managed by <ph name="DOMAIN">$1<ex>google.com</ex></ph>
+    </message>
+    <message name="IDS_MANAGEMENT_SUBTITLE_PROFILE_MANAGED_BY" desc="Text displayed to inform the user that the browser is managed by DOMAIN. [iOS only]">
+      Your profile is managed by <ph name="DOMAIN">$1<ex>google.com</ex></ph>
+    </message>
   </if>
 
-  <!-- Browser managed status section -->
-  <if expr="not chromeos_ash">
-    <if expr="_google_chrome">
-      <message name="IDS_MANAGEMENT_BROWSER_NOTICE" desc="Message shown when the (Google-branded) Chrome browser is managed, it indicates what the administrator can do on the browser.">
-      Your administrator can change your browser setup remotely. Activity on this device may also be managed outside of Chrome. <ph name="BEGIN_LINK">&lt;a target="_blank" href="$1"&gt;</ph>Learn more<ph name="END_LINK">&lt;/a&gt;</ph>
-      </message>
-      <message name="IDS_MANAGEMENT_NOT_MANAGED_NOTICE" desc="Message indicating that the (Google-branded) Chrome browser is not managed">
-       This browser is not managed by a company or other organization. Activity on this device may be managed outside of Chrome. <ph name="BEGIN_LINK">&lt;a target="_blank" href="$1"&gt;</ph>Learn more<ph name="END_LINK">&lt;/a&gt;</ph>
-      </message>
-    </if>
-    <if expr="not _google_chrome">
-      <message name="IDS_MANAGEMENT_BROWSER_NOTICE" desc="Message shown when the (non-Google-branded) Chromium browser is managed, it indicates what the administrator can do on the browser.">
-      Your administrator can change your browser setup remotely. Activity on this device may also be managed outside of Chromium. <ph name="BEGIN_LINK">&lt;a target="_blank" href="$1"&gt;</ph>Learn more<ph name="END_LINK">&lt;/a&gt;</ph>
-      </message>
-      <message name="IDS_MANAGEMENT_NOT_MANAGED_NOTICE" desc="Message indicating that the (non-Google-branded) Chromium browser is not managed">
-       This browser is not managed by a company or other organization. Activity on this device may be managed outside of Chromium. <ph name="BEGIN_LINK">&lt;a target="_blank" href="$1"&gt;</ph>Learn more<ph name="END_LINK">&lt;/a&gt;</ph>
-      </message>
+  <if expr="not is_ios">
+    <!-- Browser managed status section -->
+    <if expr="not chromeos_ash">
+      <if expr="_google_chrome">
+        <message name="IDS_MANAGEMENT_BROWSER_NOTICE" desc="Message shown when the (Google-branded) Chrome browser is managed, it indicates what the administrator can do on the browser.">
+        Your administrator can change your browser setup remotely. Activity on this device may also be managed outside of Chrome. <ph name="BEGIN_LINK">&lt;a target="_blank" href="$1"&gt;</ph>Learn more<ph name="END_LINK">&lt;/a&gt;</ph>
+        </message>
+        <message name="IDS_MANAGEMENT_NOT_MANAGED_NOTICE" desc="Message indicating that the (Google-branded) Chrome browser is not managed">
+        This browser is not managed by a company or other organization. Activity on this device may be managed outside of Chrome. <ph name="BEGIN_LINK">&lt;a target="_blank" href="$1"&gt;</ph>Learn more<ph name="END_LINK">&lt;/a&gt;</ph>
+        </message>
+      </if>
+      <if expr="not _google_chrome">
+        <message name="IDS_MANAGEMENT_BROWSER_NOTICE" desc="Message shown when the (non-Google-branded) Chromium browser is managed, it indicates what the administrator can do on the browser.">
+        Your administrator can change your browser setup remotely. Activity on this device may also be managed outside of Chromium. <ph name="BEGIN_LINK">&lt;a target="_blank" href="$1"&gt;</ph>Learn more<ph name="END_LINK">&lt;/a&gt;</ph>
+        </message>
+        <message name="IDS_MANAGEMENT_NOT_MANAGED_NOTICE" desc="Message indicating that the (non-Google-branded) Chromium browser is not managed">
+        This browser is not managed by a company or other organization. Activity on this device may be managed outside of Chromium. <ph name="BEGIN_LINK">&lt;a target="_blank" href="$1"&gt;</ph>Learn more<ph name="END_LINK">&lt;/a&gt;</ph>
+        </message>
+      </if>
     </if>
   </if>
-
+  
   <!-- ChromeOS managed status section -->
   <if expr="chromeos_ash">
     <message name="IDS_MANAGEMENT_DEVICE_NOT_MANAGED" desc="Message indicating that the device and account are not managed">
@@ -176,114 +189,116 @@
     </message>
   </if>
 
-  <!-- Strings related to extension reporting section of the management page -->
-  <message name="IDS_MANAGEMENT_EXTENSION_REPORTING" desc="Title of the force installed extensions section of the page">
-    Extensions
-  </message>
-  <message name="IDS_MANAGEMENT_EXTENSIONS_INSTALLED" desc="Message describing that the administrator has installed some powerful extensions on the managed user's browser">
-    The administrator of this device has installed extensions for additional functions. Extensions have access to some of your data.
-  </message>
-  <message name="IDS_MANAGEMENT_EXTENSIONS_INSTALLED_BY" desc="Message describing that the administrator has installed some powerful extensions on the managed user's browser from a specific domain">
-    <ph name="ENROLLMENT_DOMAIN">$1<ex>example.com</ex></ph> has installed extensions for additional functions. Extensions have access to some of your data.
-  </message>
-  <message name="IDS_MANAGEMENT_EXTENSIONS_NAME" desc="Title of a column of the extension table showing the name of the extension" meaning="Name of an inanimate object">
-    Name
-  </message>
-  <message name="IDS_MANAGEMENT_EXTENSIONS_PERMISSIONS" desc="Title of a column of the extension table showing the permissions of the extension">
-    Permissions
-  </message>
+  <if expr="not is_ios">
+    <!-- Strings related to extension reporting section of the management page -->
+    <message name="IDS_MANAGEMENT_EXTENSION_REPORTING" desc="Title of the force installed extensions section of the page">
+      Extensions
+    </message>
+    <message name="IDS_MANAGEMENT_EXTENSIONS_INSTALLED" desc="Message describing that the administrator has installed some powerful extensions on the managed user's browser">
+      The administrator of this device has installed extensions for additional functions. Extensions have access to some of your data.
+    </message>
+    <message name="IDS_MANAGEMENT_EXTENSIONS_INSTALLED_BY" desc="Message describing that the administrator has installed some powerful extensions on the managed user's browser from a specific domain">
+      <ph name="ENROLLMENT_DOMAIN">$1<ex>example.com</ex></ph> has installed extensions for additional functions. Extensions have access to some of your data.
+    </message>
+    <message name="IDS_MANAGEMENT_EXTENSIONS_NAME" desc="Title of a column of the extension table showing the name of the extension" meaning="Name of an inanimate object">
+      Name
+    </message>
+    <message name="IDS_MANAGEMENT_EXTENSIONS_PERMISSIONS" desc="Title of a column of the extension table showing the permissions of the extension">
+      Permissions
+    </message>
 
-  <!-- Strings related to website managed configuration of the management page -->
-  <message name="IDS_MANAGEMENT_MANAGED_WEBSITES" desc="Title of the configured websites section of the page">
-    Websites with info from your organization
-  </message>
-  <message name="IDS_MANAGEMENT_MANAGED_WEBSITES_EXPLANATION" desc="Message describing that administrator has set some preset organization data to be sent to the following websites">
-    The administrator of this device has sent some info to the following websites, like settings or policies.
-  </message>
-  <message name="IDS_MANAGEMENT_MANAGED_WEBSITES_BY_EXPLANATION" desc="Message describing that administrator has set some preset organization data to be sent to the following websites">
-    Your organization, <ph name="ENROLLMENT_DOMAIN">$1<ex>example.com</ex></ph>, has sent some info to the following websites, like settings or policies.
-  </message>
+    <!-- Strings related to website managed configuration of the management page -->
+    <message name="IDS_MANAGEMENT_MANAGED_WEBSITES" desc="Title of the configured websites section of the page">
+      Websites with info from your organization
+    </message>
+    <message name="IDS_MANAGEMENT_MANAGED_WEBSITES_EXPLANATION" desc="Message describing that administrator has set some preset organization data to be sent to the following websites">
+      The administrator of this device has sent some info to the following websites, like settings or policies.
+    </message>
+    <message name="IDS_MANAGEMENT_MANAGED_WEBSITES_BY_EXPLANATION" desc="Message describing that administrator has set some preset organization data to be sent to the following websites">
+      Your organization, <ph name="ENROLLMENT_DOMAIN">$1<ex>example.com</ex></ph>, has sent some info to the following websites, like settings or policies.
+    </message>
 
 
-  <!-- Strings related to browser reporting section of the management page -->
-  <message name="IDS_MANAGEMENT_BROWSER_REPORTING" desc="Title of the types of browser reporting section of the page" formatter_data="android_java">
-    Browser
-  </message>
-  <message name="IDS_MANAGEMENT_BROWSER_REPORTING_EXPLANATION" desc="Message explaining browser reporting" formatter_data="android_java">
-    Your administrator can see:
-  </message>
-  <message name="IDS_MANAGEMENT_EXTENSION_REPORT_MACHINE_NAME" desc="Message explaining that an extension currently reports the user's machine name">
-    Your device name
-  </message>
-  <message name="IDS_MANAGEMENT_EXTENSION_REPORT_MACHINE_NAME_ADDRESS" desc="Message explaining that an extension currently reports the user's machine name and address">
-    Your device name and network address
-  </message>
-  <message name="IDS_MANAGEMENT_EXTENSION_REPORT_USERNAME" desc="Message explaining that an extension currently reports the user's username" formatter_data="android_java">
-    Your device username and Chrome username
-  </message>
-  <message name="IDS_MANAGEMENT_EXTENSION_REPORT_VERSION" desc="Message explaining that an extension currently reports the user's browser and machine version" formatter_data="android_java">
-    Version information about your device and browser
-  </message>
-  <message name="IDS_MANAGEMENT_EXTENSION_REPORT_EXTENSIONS_PLUGINS" desc="Message explaining that an extension currently reports the user's exensions and plugins">
-    Which extensions and plugins you have installed
-  </message>
-  <message name="IDS_MANAGEMENT_EXTENSION_REPORT_USER_BROWSING_DATA" desc="Message explaining that an extension currently reports the user's browsing data">
-    Websites you visit and time spent on them
-  </message>
-  <message name="IDS_MANAGEMENT_EXTENSION_REPORT_PERF_CRASH" desc="Message explaining that an extension currently reports the user's  performance data and crash report">
-    Performance data and crash reports
-  </message>
+    <!-- Strings related to browser reporting section of the management page -->
+    <message name="IDS_MANAGEMENT_BROWSER_REPORTING" desc="Title of the types of browser reporting section of the page" formatter_data="android_java">
+      Browser
+    </message>
+    <message name="IDS_MANAGEMENT_BROWSER_REPORTING_EXPLANATION" desc="Message explaining browser reporting" formatter_data="android_java">
+      Your administrator can see:
+    </message>
+    <message name="IDS_MANAGEMENT_EXTENSION_REPORT_MACHINE_NAME" desc="Message explaining that an extension currently reports the user's machine name">
+      Your device name
+    </message>
+    <message name="IDS_MANAGEMENT_EXTENSION_REPORT_MACHINE_NAME_ADDRESS" desc="Message explaining that an extension currently reports the user's machine name and address">
+      Your device name and network address
+    </message>
+    <message name="IDS_MANAGEMENT_EXTENSION_REPORT_USERNAME" desc="Message explaining that an extension currently reports the user's username" formatter_data="android_java">
+      Your device username and Chrome username
+    </message>
+    <message name="IDS_MANAGEMENT_EXTENSION_REPORT_VERSION" desc="Message explaining that an extension currently reports the user's browser and machine version" formatter_data="android_java">
+      Version information about your device and browser
+    </message>
+    <message name="IDS_MANAGEMENT_EXTENSION_REPORT_EXTENSIONS_PLUGINS" desc="Message explaining that an extension currently reports the user's exensions and plugins">
+      Which extensions and plugins you have installed
+    </message>
+    <message name="IDS_MANAGEMENT_EXTENSION_REPORT_USER_BROWSING_DATA" desc="Message explaining that an extension currently reports the user's browsing data">
+      Websites you visit and time spent on them
+    </message>
+    <message name="IDS_MANAGEMENT_EXTENSION_REPORT_PERF_CRASH" desc="Message explaining that an extension currently reports the user's  performance data and crash report">
+      Performance data and crash reports
+    </message>
 
-  <!-- Strings related to Chrome Enterprise Connectors -->
-  <message name="IDS_MANAGEMENT_THREAT_PROTECTION" desc="Title of the Chrome Enterprise Connectors section of the page">
-    Chrome Enterprise Connectors
-  </message>
-  <message name="IDS_MANAGEMENT_THREAT_PROTECTION_DESCRIPTION" desc="Description of the Chrome Enterprise Connectors section of the page">
-    Your administrator has turned on Chrome Enterprise Connectors on your browser. These connectors have access to some of your data.
-  </message>
-  <message name="IDS_MANAGEMENT_THREAT_PROTECTION_DESCRIPTION_BY" desc="Description of the Chrome Enterprise Connectors section of the page">
-    <ph name="ENROLLMENT_DOMAIN">$1<ex>example.com</ex></ph> has turned on Chrome Enterprise Connectors on your browser. These connectors have access to some of your data.
-  </message>
-  <message name="IDS_MANAGEMENT_CONNECTORS_EVENT" desc="Title of a column of the Connectors table showing the event that triggers a Connector">
-    Event
-  </message>
-  <message name="IDS_MANAGEMENT_CONNECTORS_VISIBLE_DATA" desc="Title of a column of the Connectors table showing the data visible to a Connector">
-    Visible data
-  </message>
-  <message name="IDS_MANAGEMENT_FILE_ATTACHED_EVENT" desc="Event for the file attachment scanning feature.">
-    File is attached
-  </message>
-  <message name="IDS_MANAGEMENT_FILE_DOWNLOADED_EVENT" desc="Event for the file download scanning feature.">
-    File is downloaded
-  </message>
-  <message name="IDS_MANAGEMENT_TEXT_ENTERED_EVENT" desc="Event for the text entry scanning feature.">
-    Text is entered
-  </message>
-  <message name="IDS_MANAGEMENT_PAGE_PRINTED_EVENT" desc="Event for the page print scanning feature.">
-    Page is printed
-  </message>
-  <message name="IDS_MANAGEMENT_ENTERPRISE_REPORTING_EVENT" desc="Event for the enterprise reporting feature">
-    Security event occurs
-  </message>
-  <message name="IDS_MANAGEMENT_PAGE_VISITED_EVENT" desc="Event for the real time URL check feature.">
-    Page is visited
-  </message>
-  <message name="IDS_MANAGEMENT_FILE_ATTACHED_VISIBLE_DATA" desc="Description of the visible data for the file attachment scanning feature.">
-    Files you upload or attach are sent to Google Cloud or third parties for analysis. For example, they might be scanned for sensitive data or malware.
-  </message>
-  <message name="IDS_MANAGEMENT_FILE_DOWNLOADED_VISIBLE_DATA" desc="Description of the visible data for the file download scanning feature.">
-    Files you download are sent to Google Cloud or third parties for analysis. For example, they might be scanned for sensitive data or malware.
-  </message>
-  <message name="IDS_MANAGEMENT_TEXT_ENTERED_VISIBLE_DATA" desc="Description of the visible data for the text entry scanning feature.">
-    Text you paste or attach is sent to Google Cloud or third parties for analysis. For example, it might be scanned for sensitive data.
-  </message>
-  <message name="IDS_MANAGEMENT_PAGE_PRINTED_VISIBLE_DATA" desc="Description of the visible data for the page print scanning feature.">
-    The content of pages you print is sent to Google Cloud or third parties for analysis. For example, it might be scanned for sensitive data.
-  </message>
-  <message name="IDS_MANAGEMENT_ENTERPRISE_REPORTING_VISIBLE_DATA" desc="Description of the visible data for the Connectors reporting feature">
-    When security events are flagged by Chrome, relevant data about the events is sent to your administrator. This can include URLs of pages you visit in Chrome, file names or metadata, and the username that you use to sign in to web based applications, your device and Chrome.
-  </message>
-  <message name="IDS_MANAGEMENT_PAGE_VISITED_VISIBLE_DATA" desc="Description of the visible data for the real time URL check feature.">
-    URLs of pages you visit are sent to Google Cloud or third parties for analysis. For example, they might be scanned to detect unsafe websites.
-  </message>
+    <!-- Strings related to Chrome Enterprise Connectors -->
+    <message name="IDS_MANAGEMENT_THREAT_PROTECTION" desc="Title of the Chrome Enterprise Connectors section of the page">
+      Chrome Enterprise Connectors
+    </message>
+    <message name="IDS_MANAGEMENT_THREAT_PROTECTION_DESCRIPTION" desc="Description of the Chrome Enterprise Connectors section of the page">
+      Your administrator has turned on Chrome Enterprise Connectors on your browser. These connectors have access to some of your data.
+    </message>
+    <message name="IDS_MANAGEMENT_THREAT_PROTECTION_DESCRIPTION_BY" desc="Description of the Chrome Enterprise Connectors section of the page">
+      <ph name="ENROLLMENT_DOMAIN">$1<ex>example.com</ex></ph> has turned on Chrome Enterprise Connectors on your browser. These connectors have access to some of your data.
+    </message>
+    <message name="IDS_MANAGEMENT_CONNECTORS_EVENT" desc="Title of a column of the Connectors table showing the event that triggers a Connector">
+      Event
+    </message>
+    <message name="IDS_MANAGEMENT_CONNECTORS_VISIBLE_DATA" desc="Title of a column of the Connectors table showing the data visible to a Connector">
+      Visible data
+    </message>
+    <message name="IDS_MANAGEMENT_FILE_ATTACHED_EVENT" desc="Event for the file attachment scanning feature.">
+      File is attached
+    </message>
+    <message name="IDS_MANAGEMENT_FILE_DOWNLOADED_EVENT" desc="Event for the file download scanning feature.">
+      File is downloaded
+    </message>
+    <message name="IDS_MANAGEMENT_TEXT_ENTERED_EVENT" desc="Event for the text entry scanning feature.">
+      Text is entered
+    </message>
+    <message name="IDS_MANAGEMENT_PAGE_PRINTED_EVENT" desc="Event for the page print scanning feature.">
+      Page is printed
+    </message>
+    <message name="IDS_MANAGEMENT_ENTERPRISE_REPORTING_EVENT" desc="Event for the enterprise reporting feature">
+      Security event occurs
+    </message>
+    <message name="IDS_MANAGEMENT_PAGE_VISITED_EVENT" desc="Event for the real time URL check feature.">
+      Page is visited
+    </message>
+    <message name="IDS_MANAGEMENT_FILE_ATTACHED_VISIBLE_DATA" desc="Description of the visible data for the file attachment scanning feature.">
+      Files you upload or attach are sent to Google Cloud or third parties for analysis. For example, they might be scanned for sensitive data or malware.
+    </message>
+    <message name="IDS_MANAGEMENT_FILE_DOWNLOADED_VISIBLE_DATA" desc="Description of the visible data for the file download scanning feature.">
+      Files you download are sent to Google Cloud or third parties for analysis. For example, they might be scanned for sensitive data or malware.
+    </message>
+    <message name="IDS_MANAGEMENT_TEXT_ENTERED_VISIBLE_DATA" desc="Description of the visible data for the text entry scanning feature.">
+      Text you paste or attach is sent to Google Cloud or third parties for analysis. For example, it might be scanned for sensitive data.
+    </message>
+    <message name="IDS_MANAGEMENT_PAGE_PRINTED_VISIBLE_DATA" desc="Description of the visible data for the page print scanning feature.">
+      The content of pages you print is sent to Google Cloud or third parties for analysis. For example, it might be scanned for sensitive data.
+    </message>
+    <message name="IDS_MANAGEMENT_ENTERPRISE_REPORTING_VISIBLE_DATA" desc="Description of the visible data for the Connectors reporting feature">
+      When security events are flagged by Chrome, relevant data about the events is sent to your administrator. This can include URLs of pages you visit in Chrome, file names or metadata, and the username that you use to sign in to web based applications, your device and Chrome.
+    </message>
+    <message name="IDS_MANAGEMENT_PAGE_VISITED_VISIBLE_DATA" desc="Description of the visible data for the real time URL check feature.">
+      URLs of pages you visit are sent to Google Cloud or third parties for analysis. For example, they might be scanned to detect unsafe websites.
+    </message>
+  </if>
 </grit-part>
diff --git a/components/management_strings_grdp/IDS_MANAGEMENT_SUBTITLE_BROWSER_AND_PROFILE_DIFFERENT_MANAGED_BY.png.sha1 b/components/management_strings_grdp/IDS_MANAGEMENT_SUBTITLE_BROWSER_AND_PROFILE_DIFFERENT_MANAGED_BY.png.sha1
new file mode 100644
index 0000000..f3e7d95
--- /dev/null
+++ b/components/management_strings_grdp/IDS_MANAGEMENT_SUBTITLE_BROWSER_AND_PROFILE_DIFFERENT_MANAGED_BY.png.sha1
@@ -0,0 +1 @@
+6861b6773aa245d203e98bfab6301ef5c1731cf0
\ No newline at end of file
diff --git a/components/management_strings_grdp/IDS_MANAGEMENT_SUBTITLE_BROWSER_AND_PROFILE_MANAGED_BY.png.sha1 b/components/management_strings_grdp/IDS_MANAGEMENT_SUBTITLE_BROWSER_AND_PROFILE_MANAGED_BY.png.sha1
new file mode 100644
index 0000000..f3e7d95
--- /dev/null
+++ b/components/management_strings_grdp/IDS_MANAGEMENT_SUBTITLE_BROWSER_AND_PROFILE_MANAGED_BY.png.sha1
@@ -0,0 +1 @@
+6861b6773aa245d203e98bfab6301ef5c1731cf0
\ No newline at end of file
diff --git a/components/management_strings_grdp/IDS_MANAGEMENT_SUBTITLE_BROWSER_AND_PROFILE_SAME_MANAGED_BY.png.sha1 b/components/management_strings_grdp/IDS_MANAGEMENT_SUBTITLE_BROWSER_AND_PROFILE_SAME_MANAGED_BY.png.sha1
new file mode 100644
index 0000000..378d190
--- /dev/null
+++ b/components/management_strings_grdp/IDS_MANAGEMENT_SUBTITLE_BROWSER_AND_PROFILE_SAME_MANAGED_BY.png.sha1
@@ -0,0 +1 @@
+443a14477a959ab288c96fd1108e2d6574cbbedd
\ No newline at end of file
diff --git a/components/management_strings_grdp/IDS_MANAGEMENT_SUBTITLE_PROFILE_MANAGED_BY.png.sha1 b/components/management_strings_grdp/IDS_MANAGEMENT_SUBTITLE_PROFILE_MANAGED_BY.png.sha1
new file mode 100644
index 0000000..8c75cd0
--- /dev/null
+++ b/components/management_strings_grdp/IDS_MANAGEMENT_SUBTITLE_PROFILE_MANAGED_BY.png.sha1
@@ -0,0 +1 @@
+2810ad95cd0d2abb7a047e46478673d5adf5005c
\ No newline at end of file
diff --git a/components/media_router/browser/BUILD.gn b/components/media_router/browser/BUILD.gn
index 7890fff..5b8464e 100644
--- a/components/media_router/browser/BUILD.gn
+++ b/components/media_router/browser/BUILD.gn
@@ -2,14 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-# TODO(crbug.com/1299869): IssueManager and IssuesObserver are not used on
-# Android; exclude them from Android builds.
 source_set("browser") {
   sources = [
-    "issue_manager.cc",
-    "issue_manager.h",
-    "issues_observer.cc",
-    "issues_observer.h",
     "media_router.h",
     "media_router_base.cc",
     "media_router_base.h",
@@ -85,6 +79,10 @@
     deps += [ "android:jni_headers" ]
   } else {
     sources += [
+      "issue_manager.cc",
+      "issue_manager.h",
+      "issues_observer.cc",
+      "issues_observer.h",
       "logger_impl.cc",
       "logger_impl.h",
       "presentation/presentation_navigation_policy.cc",
@@ -115,7 +113,6 @@
   testonly = true
 
   sources = [
-    "issue_manager_unittest.cc",
     "media_router_base_unittest.cc",
     "media_router_dialog_controller_unittest.cc",
     "media_router_metrics_unittest.cc",
@@ -140,6 +137,9 @@
     sources += [ "android/media_router_android_unittest.cc" ]
     deps += [ "android:test_jni_headers" ]
   } else {
-    sources += [ "logger_impl_unittest.cc" ]
+    sources += [
+      "issue_manager_unittest.cc",
+      "logger_impl_unittest.cc",
+    ]
   }
 }
diff --git a/components/media_router/browser/test/test_helper.cc b/components/media_router/browser/test/test_helper.cc
index 12243d8..23f2b96 100644
--- a/components/media_router/browser/test/test_helper.cc
+++ b/components/media_router/browser/test/test_helper.cc
@@ -10,10 +10,11 @@
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace media_router {
-
+#if !BUILDFLAG(IS_ANDROID)
 MockIssuesObserver::MockIssuesObserver(IssueManager* issue_manager)
     : IssuesObserver(issue_manager) {}
 MockIssuesObserver::~MockIssuesObserver() = default;
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 MockMediaSinksObserver::MockMediaSinksObserver(MediaRouter* router,
                                                const MediaSource& source,
diff --git a/components/media_router/browser/test/test_helper.h b/components/media_router/browser/test/test_helper.h
index 519c0f34..29ae115 100644
--- a/components/media_router/browser/test/test_helper.h
+++ b/components/media_router/browser/test/test_helper.h
@@ -10,14 +10,17 @@
 
 #include "base/strings/string_piece.h"
 #include "base/test/values_test_util.h"
-#include "components/media_router/browser/issue_manager.h"
-#include "components/media_router/browser/issues_observer.h"
 #include "components/media_router/browser/media_routes_observer.h"
 #include "components/media_router/browser/media_sinks_observer.h"
 #include "components/media_router/common/mojom/logger.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/blink/public/mojom/presentation/presentation.mojom.h"
 
+#if !BUILDFLAG(IS_ANDROID)
+#include "components/media_router/browser/issue_manager.h"
+#include "components/media_router/browser/issues_observer.h"
+#endif  // !BUILDFALG(IS_ANDROID)
+
 namespace media_router {
 
 // Matcher for IssueInfo title.
@@ -30,6 +33,7 @@
          arg.message == other.message;
 }
 
+#if !BUILDFLAG(IS_ANDROID)
 class MockIssuesObserver : public IssuesObserver {
  public:
   explicit MockIssuesObserver(IssueManager* issue_manager);
@@ -38,6 +42,7 @@
   MOCK_METHOD1(OnIssue, void(const Issue& issue));
   MOCK_METHOD0(OnIssuesCleared, void());
 };
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 class MockMediaSinksObserver : public MediaSinksObserver {
  public:
diff --git a/components/omnibox/browser/omnibox_field_trial.cc b/components/omnibox/browser/omnibox_field_trial.cc
index 19dde84a..ecf4fb2 100644
--- a/components/omnibox/browser/omnibox_field_trial.cc
+++ b/components/omnibox/browser/omnibox_field_trial.cc
@@ -870,11 +870,6 @@
 }
 
 // Local history zero-prefix (aka zero-suggest) and prefix suggestions.
-const base::FeatureParam<int> kZeroSuggestCacheDurationSec(
-    &omnibox::kZeroSuggestPrefetching,
-    "ZeroSuggestCacheDurationSec",
-    0);
-
 const base::FeatureParam<bool> kZeroSuggestIgnoreDuplicateVisits(
     &omnibox::kLocalHistorySuggestRevamp,
     "ZeroSuggestIgnoreDuplicateVisits",
diff --git a/components/omnibox/browser/omnibox_field_trial.h b/components/omnibox/browser/omnibox_field_trial.h
index 0b1776e0..93e781a 100644
--- a/components/omnibox/browser/omnibox_field_trial.h
+++ b/components/omnibox/browser/omnibox_field_trial.h
@@ -518,14 +518,6 @@
 // Shortcut Expanding.
 bool IsShortcutExpandingEnabled();
 
-// Specifies the HTTP cache duration for the zero prefix suggest responses. If
-// the provided value is a positive number, the cache duration will be sent as a
-// query string parameter in the zero suggest requests and relayed back in the
-// response cache control headers.
-// This param is tied to omnibox::kZeroSuggestPrefetching which controls
-// prefetching and theoretically works with any caching mechanism. If no valid
-// HTTP cache duration is provided the existing caching mechanism is used.
-extern const base::FeatureParam<int> kZeroSuggestCacheDurationSec;
 // Whether duplicative visits should be ignored for local history zero-suggest.
 // A duplicative visit is a visit to the same search term in an interval smaller
 // than kAutocompleteDuplicateVisitIntervalThreshold.
diff --git a/components/omnibox/browser/zero_suggest_provider.cc b/components/omnibox/browser/zero_suggest_provider.cc
index 57d774a..caa27d6 100644
--- a/components/omnibox/browser/zero_suggest_provider.cc
+++ b/components/omnibox/browser/zero_suggest_provider.cc
@@ -12,40 +12,30 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/feature_list.h"
-#include "base/i18n/case_conversion.h"
-#include "base/json/json_string_value_serializer.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
 #include "base/strings/escape.h"
-#include "base/strings/string_util.h"
-#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 "components/history/core/browser/history_types.h"
-#include "components/history/core/browser/top_sites.h"
-#include "components/omnibox/browser/autocomplete_classifier.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/autocomplete_match_classification.h"
 #include "components/omnibox/browser/autocomplete_provider_client.h"
 #include "components/omnibox/browser/autocomplete_provider_listener.h"
+#include "components/omnibox/browser/base_search_provider.h"
 #include "components/omnibox/browser/omnibox_field_trial.h"
 #include "components/omnibox/browser/omnibox_prefs.h"
 #include "components/omnibox/browser/remote_suggestions_service.h"
-#include "components/omnibox/browser/search_provider.h"
 #include "components/omnibox/browser/search_suggestion_parser.h"
-#include "components/omnibox/browser/verbatim_match.h"
 #include "components/omnibox/common/omnibox_features.h"
-#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "components/search_engines/omnibox_focus_type.h"
 #include "components/search_engines/search_engine_type.h"
 #include "components/search_engines/template_url_service.h"
 #include "components/url_formatter/url_formatter.h"
-#include "components/variations/net/variations_http_headers.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"
 #include "third_party/metrics_proto/omnibox_event.pb.h"
@@ -78,7 +68,7 @@
   ZERO_SUGGEST_REQUEST_SENT = 1,
   ZERO_SUGGEST_REQUEST_INVALIDATED = 2,
   ZERO_SUGGEST_RESPONSE_RECEIVED = 3,
-  ZERO_SUGGEST_RESPONSE_LOADED_FROM_HTTP_CACHE = 4,
+  // ZERO_SUGGEST_RESPONSE_LOADED_FROM_HTTP_CACHE = 4, no longer used.
   // ZERO_SUGGEST_CACHED_RESPONSE_IS_OUT_OF_DATE = 5,  no longer used.
   ZERO_SUGGEST_RESPONSE_UPDATED_RESULTS = 6,
   ZERO_SUGGEST_MAX_REQUEST_HISTOGRAM_VALUE
@@ -176,11 +166,6 @@
   TemplateURLRef::SearchTermsArgs search_terms_args;
   search_terms_args.page_classification = input.current_page_classification();
   search_terms_args.focus_type = input.focus_type();
-  const int cache_duration_sec =
-      OmniboxFieldTrial::kZeroSuggestCacheDurationSec.Get();
-  if (cache_duration_sec > 0) {
-    search_terms_args.zero_suggest_cache_duration_sec = cache_duration_sec;
-  }
   GURL suggest_url = RemoteSuggestionsService::EndpointUrl(
       search_terms_args, client()->GetTemplateURLService());
   if (!suggest_url.is_valid())
@@ -190,26 +175,30 @@
   if (result_type_running_ == NONE)
     return;
 
-  if (is_prefetch)
+  if (is_prefetch) {
     prefetch_done_ = false;
-  else
+  } else {
     done_ = false;
 
-  MaybeUpdateResultsWithStoredResponse();
+    // Prefetching is only meant to update the stored response with a fresh
+    // response. It is unnecessary to update the results when prefetching;
+    // as they will get cleared on the next call to `Start()`.
+    auto response_data = ReadStoredResponse();
+    if (response_data) {
+      ConvertResponseToAutocompleteMatches(std::move(response_data));
+    }
+  }
 
   search_terms_args.current_page_url = result_type_running_ == REMOTE_SEND_URL
                                            ? input.current_url().spec()
                                            : std::string();
-  // Grab ownership of the loader until results come in to
-  // `OnURLLoadComplete()`.
-  loader_ = client()
-                ->GetRemoteSuggestionsService(/*create_if_necessary=*/true)
-                ->StartSuggestionsRequest(
-                    search_terms_args, client()->GetTemplateURLService(),
-                    base::BindOnce(&ZeroSuggestProvider::OnURLLoadComplete,
-                                   weak_ptr_factory_.GetWeakPtr(),
-                                   client()->GetWeakPtr(), search_terms_args,
-                                   is_prefetch, base::TimeTicks::Now()));
+  loader_ =
+      client()
+          ->GetRemoteSuggestionsService(/*create_if_necessary=*/true)
+          ->StartSuggestionsRequest(
+              search_terms_args, client()->GetTemplateURLService(),
+              base::BindOnce(&ZeroSuggestProvider::OnURLLoadComplete,
+                             weak_ptr_factory_.GetWeakPtr(), is_prefetch));
 
   LogOmniboxZeroSuggestRequest(ZERO_SUGGEST_REQUEST_SENT,
                                /*is_prefetch=*/!prefetch_done_);
@@ -226,19 +215,18 @@
   loader_.reset();
   prefetch_done_ = true;
   result_type_running_ = NONE;
+
+  if (clear_cached_results) {
+    experiment_stats_v2s_.clear();
+  }
 }
 
 void ZeroSuggestProvider::DeleteMatch(const AutocompleteMatch& match) {
   // Remove the deleted match from the cache, so it is not shown to the user
   // again. Since we cannot remove just one result, blow away the cache.
-  //
   // Although the cache is currently only used for REMOTE_NO_URL, we have no
   // easy way of checking the request type after-the-fact. It's safe though, to
   // always clear the cache even if we are on a different request type.
-  //
-  // TODO(tommycli): It seems quite odd that the cache is saved to a pref, as
-  // if we would want to persist it across restarts. That seems to be directly
-  // contradictory to the fact that ZeroSuggest results can change rapidly.
   client()->GetPrefs()->SetString(omnibox::kZeroSuggestCachedResults,
                                   std::string());
   BaseSearchProvider::DeleteMatch(match);
@@ -246,7 +234,7 @@
 
 void ZeroSuggestProvider::AddProviderInfo(ProvidersInfo* provider_info) const {
   BaseSearchProvider::AddProviderInfo(provider_info);
-  if (!results_.suggest_results.empty() || !results_.navigation_results.empty())
+  if (!matches().empty())
     provider_info->back().set_times_returned_results_in_session(1);
 }
 
@@ -293,32 +281,32 @@
 }
 
 void ZeroSuggestProvider::OnURLLoadComplete(
-    const base::WeakPtr<AutocompleteProviderClient> client,
-    TemplateURLRef::SearchTermsArgs search_terms_args,
     bool is_prefetch,
-    base::TimeTicks request_time,
     const network::SimpleURLLoader* source,
     std::unique_ptr<std::string> response_body) {
   DCHECK(!done_ || !prefetch_done_);
   DCHECK_EQ(loader_.get(), source);
 
-  LogOmniboxZeroSuggestRequest(ZERO_SUGGEST_RESPONSE_RECEIVED, is_prefetch);
-  if (source->LoadedFromCache()) {
-    LogOmniboxZeroSuggestRequest(ZERO_SUGGEST_RESPONSE_LOADED_FROM_HTTP_CACHE,
-                                 is_prefetch);
-  }
+  std::unique_ptr<base::Value> response_data = nullptr;
 
   const bool response_received =
       response_body && source->NetError() == net::OK &&
       (source->ResponseInfo() && source->ResponseInfo()->headers &&
        source->ResponseInfo()->headers->response_code() == 200);
-  const bool results_updated =
-      response_received &&
-      UpdateResultsWithResponse(SearchSuggestionParser::ExtractJsonData(
-          source, std::move(response_body)));
+  if (response_received) {
+    LogOmniboxZeroSuggestRequest(ZERO_SUGGEST_RESPONSE_RECEIVED, is_prefetch);
+    response_data = StoreRemoteResponse(SearchSuggestionParser::ExtractJsonData(
+        source, std::move(response_body)));
+  }
+
+  // Prefetching is only meant to update the stored response with a fresh
+  // response. It is unnecessary to update the results with prefetched response;
+  // as they will get cleared on the next call to `Start()`.
+  const bool results_updated = response_data && !is_prefetch;
   if (results_updated) {
     LogOmniboxZeroSuggestRequest(ZERO_SUGGEST_RESPONSE_UPDATED_RESULTS,
                                  is_prefetch);
+    ConvertResponseToAutocompleteMatches(std::move(response_data));
   }
 
   loader_.reset();
@@ -331,66 +319,55 @@
     NotifyListeners(results_updated);
 }
 
-bool ZeroSuggestProvider::UpdateResultsWithResponse(
-    const std::string& json_data) {
-  if (json_data.empty())
-    return false;
+std::unique_ptr<base::Value> ZeroSuggestProvider::StoreRemoteResponse(
+    const std::string& response_json) {
+  if (response_json.empty()) {
+    return nullptr;
+  }
 
-  std::unique_ptr<base::Value> data(
-      SearchSuggestionParser::DeserializeJsonData(json_data));
-  if (!data)
-    return false;
+  auto response_data =
+      SearchSuggestionParser::DeserializeJsonData(response_json);
+  if (!response_data) {
+    return nullptr;
+  }
 
-  // Store non-empty response if running the REMOTE_NO_URL variant.
+  // Store the valid response only if running the REMOTE_NO_URL variant.
   if (result_type_running_ == REMOTE_NO_URL) {
     client()->GetPrefs()->SetString(omnibox::kZeroSuggestCachedResults,
-                                    json_data);
-
-    // If we received an empty result list, we should update the display, as it
-    // may be showing cached results that should not be shown.
-    //
-    // `data->GetListDeprecated()[1]` is the results list.
-    const bool non_empty_parsed_list =
-        data->is_list() && data->GetListDeprecated().size() >= 2u &&
-        data->GetListDeprecated()[1].is_list() &&
-        !data->GetListDeprecated()[1].GetListDeprecated().empty();
-    const bool non_empty_cache = !results_.suggest_results.empty() ||
-                                 !results_.navigation_results.empty();
-    if (non_empty_parsed_list && non_empty_cache)
-      return false;
+                                    response_json);
   }
 
-  const bool results_updated = ParseSuggestResults(
-      *data, kDefaultZeroSuggestRelevance, false, &results_);
-  if (results_updated) {
-    ConvertResultsToAutocompleteMatches();
+  // For display stability reasons, use the remote response to update the
+  // displayed results, if they are empty or if an empty result set is received;
+  // in which case, the display may be showing results that should not be shown.
+  const bool non_empty_matches = !matches().empty();
+  if (non_empty_matches) {
+    SearchSuggestionParser::Results results;
+    ParseSuggestResults(*response_data, kDefaultZeroSuggestRelevance, false,
+                        &results);
+    const bool non_empty_response =
+        !results.suggest_results.empty() || !results.navigation_results.empty();
+    if (non_empty_response) {
+      return nullptr;
+    }
   }
-  return results_updated;
+
+  return response_data;
 }
 
-void ZeroSuggestProvider::MaybeUpdateResultsWithStoredResponse() {
+std::unique_ptr<base::Value> ZeroSuggestProvider::ReadStoredResponse() {
   // Use the stored response only if running the REMOTE_NO_URL variant.
   if (result_type_running_ != REMOTE_NO_URL) {
-    return;
+    return nullptr;
   }
 
-  std::string json_data =
+  const std::string response_json =
       client()->GetPrefs()->GetString(omnibox::kZeroSuggestCachedResults);
-  if (json_data.empty()) {
-    return;
+  if (response_json.empty()) {
+    return nullptr;
   }
 
-  std::unique_ptr<base::Value> data(
-      SearchSuggestionParser::DeserializeJsonData(json_data));
-  if (!data) {
-    return;
-  }
-
-  const bool results_updated = ParseSuggestResults(
-      *data, kDefaultZeroSuggestRelevance, false, &results_);
-  if (results_updated) {
-    ConvertResultsToAutocompleteMatches();
-  }
+  return SearchSuggestionParser::DeserializeJsonData(response_json);
 }
 
 AutocompleteMatch ZeroSuggestProvider::NavigationToMatch(
@@ -421,9 +398,16 @@
   return match;
 }
 
-void ZeroSuggestProvider::ConvertResultsToAutocompleteMatches() {
+void ZeroSuggestProvider::ConvertResponseToAutocompleteMatches(
+    std::unique_ptr<base::Value> response) {
+  DCHECK(response);
+
   matches_.clear();
   suggestion_groups_map_.clear();
+  experiment_stats_v2s_.clear();
+
+  SearchSuggestionParser::Results results;
+  ParseSuggestResults(*response, kDefaultZeroSuggestRelevance, false, &results);
 
   TemplateURLService* template_url_service = client()->GetTemplateURLService();
   DCHECK(template_url_service);
@@ -439,13 +423,13 @@
 
   // Add all the SuggestResults to the map. We display all ZeroSuggest search
   // suggestions as unbolded.
-  for (size_t i = 0; i < results_.suggest_results.size(); ++i) {
-    AddMatchToMap(results_.suggest_results[i], std::string(), i, false, false,
+  for (size_t i = 0; i < results.suggest_results.size(); ++i) {
+    AddMatchToMap(results.suggest_results[i], std::string(), i, false, false,
                   &map);
   }
 
   const int num_query_results = map.size();
-  const int num_nav_results = results_.navigation_results.size();
+  const int num_nav_results = results.navigation_results.size();
   const int num_results = num_query_results + num_nav_results;
   UMA_HISTOGRAM_COUNTS_1M("ZeroSuggest.QueryResults", num_query_results);
   UMA_HISTOGRAM_COUNTS_1M("ZeroSuggest.URLResults", num_nav_results);
@@ -458,15 +442,20 @@
     matches_.push_back(it->second);
 
   const SearchSuggestionParser::NavigationResults& nav_results(
-      results_.navigation_results);
+      results.navigation_results);
   for (const auto& nav_result : nav_results) {
     matches_.push_back(NavigationToMatch(nav_result));
   }
 
   // Update the suggestion groups information from the server response.
-  for (const auto& entry : results_.suggestion_groups_map) {
+  for (const auto& entry : results.suggestion_groups_map) {
     suggestion_groups_map_[entry.first].MergeFrom(entry.second);
   }
+
+  // Update the list of experiment stats from the server response.
+  for (const auto& experiment_stats_v2 : results.experiment_stats_v2s) {
+    experiment_stats_v2s_.push_back(experiment_stats_v2);
+  }
 }
 
 bool ZeroSuggestProvider::AllowZeroSuggestSuggestions(
diff --git a/components/omnibox/browser/zero_suggest_provider.h b/components/omnibox/browser/zero_suggest_provider.h
index 446ba5e..4798570 100644
--- a/components/omnibox/browser/zero_suggest_provider.h
+++ b/components/omnibox/browser/zero_suggest_provider.h
@@ -12,10 +12,7 @@
 #include <memory>
 #include <string>
 
-#include "base/compiler_specific.h"
 #include "base/gtest_prod_util.h"
-#include "base/memory/raw_ptr.h"
-#include "components/history/core/browser/history_types.h"
 #include "components/omnibox/browser/base_search_provider.h"
 #include "components/omnibox/browser/search_provider.h"
 #include "third_party/metrics_proto/omnibox_event.pb.h"
@@ -77,11 +74,11 @@
   // Sets |field_trial_triggered_| to false.
   void ResetSession() override;
 
-  // Returns the list of experiment stats corresponding to the latest |results_|
-  // to be logged to SearchboxStats as part of a GWS experiment, if any.
+  // Returns the list of experiment stats corresponding to |matches_|. Will be
+  // logged to SearchboxStats as part of a GWS experiment, if any.
   const SearchSuggestionParser::ExperimentStatsV2s& experiment_stats_v2s()
       const {
-    return results_.experiment_stats_v2s;
+    return experiment_stats_v2s_;
   }
 
   ResultType GetResultTypeRunningForTesting() const {
@@ -118,41 +115,34 @@
   void RecordDeletionResult(bool success) override;
 
   // Called when the network request for suggestions has completed.
-  // `is_prefetch` and `request_time` are bound to this callback and indicate if
-  // the request is a prefetch one and the time it was issued respectively.
-  void OnURLLoadComplete(const base::WeakPtr<AutocompleteProviderClient> client,
-                         TemplateURLRef::SearchTermsArgs search_terms_args,
-                         bool is_prefetch,
-                         base::TimeTicks request_time,
+  // `is_prefetch` is bound to this callback and indicates if the request is a
+  // prefetch one.
+  void OnURLLoadComplete(bool is_prefetch,
                          const network::SimpleURLLoader* source,
                          std::unique_ptr<std::string> response_body);
 
-  // Called when the remote response is received, populates |results_| with
-  // |json_data|; and converts them to |matches_|.
+  // Called when the remote response is received. Stores the remote response in
+  // the user prefs, if valid and applicable based on |result_type_running_|.
   //
-  // * The update is not performed if |json_data| is invalid.
-  // * Updates the zero suggest response stored in user prefs, if applicable.
-  //   In those cases, when |results_| is non-empty, it is updated only if
-  //   |json_data| corresponds to an empty list. This is done to ensure that
-  //   the display is cleared, as it may be showing cached results that should
-  //   not be shown.
-  // * Otherwise, the update is performed.
-  //
-  // Returns whether |results_| changed.
-  bool UpdateResultsWithResponse(const std::string& json_data);
+  // Returns the successfully parsed response if it is eligible to update the
+  // displayed results or nullptr otherwise.
+  std::unique_ptr<base::Value> StoreRemoteResponse(
+      const std::string& response_json);
 
-  // Called in Start(), populates |results_| with the zero suggest response
-  // stored in user prefs, if applicable; and converts them to |matches_|.
-  void MaybeUpdateResultsWithStoredResponse();
+  // Returns the response stored in the user prefs, if applicable based on
+  // |result_type_running_|.
+  // Returns the successfully parsed response or nullptr otherwise.
+  std::unique_ptr<base::Value> ReadStoredResponse();
 
   // Returns an AutocompleteMatch for a navigational suggestion |navigation|.
   AutocompleteMatch NavigationToMatch(
       const SearchSuggestionParser::NavigationResult& navigation);
 
-  // Converts the parsed results to a set of AutocompleteMatches and adds them
-  // to |matches_|.  Also update the histograms for how many results were
-  // received.
-  void ConvertResultsToAutocompleteMatches();
+  // Converts the parsed response to a set of AutocompleteMatches and populates
+  // |matches_| and its associated metadata. Also records histograms for how
+  // many results were received.
+  void ConvertResponseToAutocompleteMatches(
+      std::unique_ptr<base::Value> response);
 
   // Whether zero suggest suggestions are allowed in the given context.
   // Invoked early, confirms all the external conditions for ZeroSuggest are
@@ -189,9 +179,8 @@
   // never both be true, a `Start()` request stops ongoing requests.
   bool prefetch_done_;
 
-  // Contains suggest and navigation results as well as relevance parsed from
-  // the response for the most recent zero suggest input URL.
-  SearchSuggestionParser::Results results_;
+  // The list of experiment stats corresponding to |matches_|.
+  SearchSuggestionParser::ExperimentStatsV2s experiment_stats_v2s_;
 
   // For callbacks that may be run after destruction.
   base::WeakPtrFactory<ZeroSuggestProvider> weak_ptr_factory_{this};
diff --git a/components/omnibox/browser/zero_suggest_provider_unittest.cc b/components/omnibox/browser/zero_suggest_provider_unittest.cc
index 1d7de05..7861f2d6 100644
--- a/components/omnibox/browser/zero_suggest_provider_unittest.cc
+++ b/components/omnibox/browser/zero_suggest_provider_unittest.cc
@@ -112,8 +112,6 @@
     TemplateURLRef::SearchTermsArgs search_terms_args;
     search_terms_args.page_classification = page_classification;
     search_terms_args.focus_type = OmniboxFocusType::ON_FOCUS;
-    search_terms_args.zero_suggest_cache_duration_sec =
-        OmniboxFieldTrial::kZeroSuggestCacheDurationSec.Get();
     return RemoteSuggestionsService::EndpointUrl(
         search_terms_args, client_->GetTemplateURLService());
   }
@@ -129,6 +127,13 @@
     return input;
   }
 
+  AutocompleteInput CreateNTPOnFocusPrefetchInput() {
+    AutocompleteInput input(u"", metrics::OmniboxEventProto::NTP_ZPS_PREFETCH,
+                            TestSchemeClassifier());
+    input.set_focus_type(OmniboxFocusType::ON_FOCUS);
+    return input;
+  }
+
   base::test::SingleThreadTaskEnvironment task_environment_;
   std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_;
   variations::ScopedVariationsIdsProvider scoped_variations_ids_provider_{
@@ -157,9 +162,7 @@
   prefs->SetString(omnibox::kZeroSuggestCachedResults, std::string());
 
   scoped_feature_list_ = std::make_unique<base::test::ScopedFeatureList>();
-  scoped_feature_list_->InitAndEnableFeatureWithParameters(
-      omnibox::kZeroSuggestPrefetching,
-      {{OmniboxFieldTrial::kZeroSuggestCacheDurationSec.name, GetParam()}});
+  scoped_feature_list_->InitAndEnableFeature(omnibox::kZeroSuggestPrefetching);
 }
 
 void ZeroSuggestProviderTest::OnProviderUpdate(
@@ -168,12 +171,7 @@
   provider_did_notify_ = true;
 }
 
-INSTANTIATE_TEST_SUITE_P(All,
-                         ZeroSuggestProviderTest,
-                         ::testing::ValuesIn({std::string("0"),
-                                              std::string("60")}));
-
-TEST_P(ZeroSuggestProviderTest, AllowZeroSuggestSuggestions) {
+TEST_F(ZeroSuggestProviderTest, AllowZeroSuggestSuggestions) {
   std::string input_url = "https://example.com/";
 
   AutocompleteInput prefix_input(base::ASCIIToUTF16(input_url),
@@ -255,7 +253,7 @@
 }
 
 // TODO(tommycli): Break up this test into smaller ones.
-TEST_P(ZeroSuggestProviderTest, TypeOfResultToRun) {
+TEST_F(ZeroSuggestProviderTest, TypeOfResultToRun) {
   // Verifies the unconfigured state. Returns platorm-specific defaults.
   // TODO(tommycli): The remote_no_url_allowed idiom seems kind of confusing,
   // its true meaning seems closer to "expect_remote_no_url". Ideally we can
@@ -337,7 +335,7 @@
       /*remote_no_url_allowed=*/false);
 }
 
-TEST_P(ZeroSuggestProviderTest, TypeOfResultToRunForContextualWeb) {
+TEST_F(ZeroSuggestProviderTest, TypeOfResultToRunForContextualWeb) {
   std::string input_url = "https://example.com/";
   GURL suggest_url = GetSuggestURL(metrics::OmniboxEventProto::OTHER);
 
@@ -424,7 +422,7 @@
   }
 }
 
-TEST_P(ZeroSuggestProviderTest, TestDoesNotReturnMatchesForPrefix) {
+TEST_F(ZeroSuggestProviderTest, TestDoesNotReturnMatchesForPrefix) {
   // Use NTP because REMOTE_NO_URL is enabled by default for NTP.
   AutocompleteInput prefix_input(
       u"foobar input",
@@ -448,7 +446,7 @@
   EXPECT_EQ(0, test_loader_factory()->NumPending());
 }
 
-TEST_P(ZeroSuggestProviderTest, TestStartWillStopForSomeInput) {
+TEST_F(ZeroSuggestProviderTest, TestStartWillStopForSomeInput) {
   EXPECT_CALL(*client_, IsAuthenticated())
       .WillRepeatedly(testing::Return(true));
 
@@ -470,7 +468,9 @@
   EXPECT_TRUE(provider_->done_);
 }
 
-TEST_P(ZeroSuggestProviderTest, TestPsuggestZeroSuggestCachingFirstRun) {
+TEST_F(ZeroSuggestProviderTest, TestPsuggestZeroSuggestCachingFirstRun) {
+  base::HistogramTester histogram_tester;
+
   EXPECT_CALL(*client_, IsAuthenticated())
       .WillRepeatedly(testing::Return(true));
 
@@ -493,6 +493,18 @@
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(provider_->done());
 
+  // Expect correct histograms to have been logged.
+  histogram_tester.ExpectTotalCount("Omnibox.ZeroSuggestRequests.NonPrefetch",
+                                    3);
+  histogram_tester.ExpectBucketCount("Omnibox.ZeroSuggestRequests.NonPrefetch",
+                                     1 /*ZERO_SUGGEST_REQUEST_SENT*/, 1);
+  histogram_tester.ExpectBucketCount("Omnibox.ZeroSuggestRequests.NonPrefetch",
+                                     3 /*ZERO_SUGGEST_RESPONSE_RECEIVED*/, 1);
+  histogram_tester.ExpectBucketCount(
+      "Omnibox.ZeroSuggestRequests.NonPrefetch",
+      6 /*ZERO_SUGGEST_RESPONSE_UPDATED_RESULTS*/, 1);
+  histogram_tester.ExpectTotalCount("Omnibox.ZeroSuggestRequests.Prefetch", 0);
+
   // Expect the provider to have notified the provider listener.
   EXPECT_TRUE(provider_did_notify_);
 
@@ -502,7 +514,7 @@
             prefs->GetString(omnibox::kZeroSuggestCachedResults));
 }
 
-TEST_P(ZeroSuggestProviderTest, TestPsuggestZeroSuggestHasCachedResults) {
+TEST_F(ZeroSuggestProviderTest, TestPsuggestZeroSuggestHasCachedResults) {
   base::HistogramTester histogram_tester;
 
   EXPECT_CALL(*client_, IsAuthenticated())
@@ -520,110 +532,11 @@
   ASSERT_EQ(ZeroSuggestProvider::REMOTE_NO_URL,
             provider_->GetResultTypeRunningForTesting());
 
-    // Expect that matches get populated synchronously out of the cache.
-    ASSERT_EQ(3U, provider_->matches().size());  // 3 results, no verbatim match
-    EXPECT_EQ(u"search1", provider_->matches()[0].contents);
-    EXPECT_EQ(u"search2", provider_->matches()[1].contents);
-    EXPECT_EQ(u"search3", provider_->matches()[2].contents);
-
-    GURL suggest_url = GetSuggestURL(
-        metrics::OmniboxEventProto::INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS);
-    EXPECT_TRUE(test_loader_factory()->IsPending(suggest_url.spec()));
-    std::string json_response2(
-        "[\"\",[\"search4\", \"search5\", \"search6\"],"
-        "[],[],{\"google:suggestrelevance\":[602, 601, 600],"
-        "\"google:verbatimrelevance\":1300}]");
-    test_loader_factory()->AddResponse(suggest_url.spec(), json_response2);
-
-    base::RunLoop().RunUntilIdle();
-    EXPECT_TRUE(provider_->done());
-
-    // Expect the provider to have notified the provider listener.
-    EXPECT_TRUE(provider_did_notify_);
-
-    // Expect correct histograms to have been logged.
-    histogram_tester.ExpectTotalCount("Omnibox.ZeroSuggestRequests.NonPrefetch",
-                                      2);
-    histogram_tester.ExpectBucketCount(
-        "Omnibox.ZeroSuggestRequests.NonPrefetch",
-        1 /*ZERO_SUGGEST_REQUEST_SENT*/, 1);
-    histogram_tester.ExpectBucketCount(
-        "Omnibox.ZeroSuggestRequests.NonPrefetch",
-        3 /*ZERO_SUGGEST_RESPONSE_RECEIVED*/, 1);
-    histogram_tester.ExpectTotalCount("Omnibox.ZeroSuggestRequests.Prefetch",
-                                      0);
-
-    // Expect the same results after the response has been handled.
-    ASSERT_EQ(3U, provider_->matches().size());  // 3 results, no verbatim match
-    EXPECT_EQ(u"search1", provider_->matches()[0].contents);
-    EXPECT_EQ(u"search2", provider_->matches()[1].contents);
-    EXPECT_EQ(u"search3", provider_->matches()[2].contents);
-
-    // Expect the new results to have been stored.
-    EXPECT_EQ(json_response2,
-              prefs->GetString(omnibox::kZeroSuggestCachedResults));
-}
-
-TEST_P(ZeroSuggestProviderTest, TestPsuggestZeroSuggestReceivedEmptyResults) {
-  EXPECT_CALL(*client_, IsAuthenticated())
-      .WillRepeatedly(testing::Return(true));
-
-  // Set up the pref to cache the response from the previous run.
-  std::string json_response("[\"\",[\"search1\", \"search2\", \"search3\"],"
-      "[],[],{\"google:suggestrelevance\":[602, 601, 600],"
-      "\"google:verbatimrelevance\":1300}]");
-  PrefService* prefs = client_->GetPrefs();
-  prefs->SetString(omnibox::kZeroSuggestCachedResults, json_response);
-
-  AutocompleteInput input = CreateNTPOnFocusInputForRemoteNoUrl();
-  provider_->Start(input, false);
-  ASSERT_EQ(ZeroSuggestProvider::REMOTE_NO_URL,
-            provider_->GetResultTypeRunningForTesting());
-
-    // Expect that matches get populated synchronously out of the cache.
-    ASSERT_EQ(3U, provider_->matches().size());  // 3 results, no verbatim match
-    EXPECT_EQ(u"search1", provider_->matches()[0].contents);
-    EXPECT_EQ(u"search2", provider_->matches()[1].contents);
-    EXPECT_EQ(u"search3", provider_->matches()[2].contents);
-
-    GURL suggest_url = GetSuggestURL(
-        metrics::OmniboxEventProto::INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS);
-    EXPECT_TRUE(test_loader_factory()->IsPending(suggest_url.spec()));
-    std::string empty_response("[\"\",[],[],[],{}]");
-    test_loader_factory()->AddResponse(suggest_url.spec(), empty_response);
-
-    base::RunLoop().RunUntilIdle();
-    EXPECT_TRUE(provider_->done());
-
-    // Expect the provider to have notified the provider listener.
-    EXPECT_TRUE(provider_did_notify_);
-
-    // Expect that the matches have been cleared.
-    ASSERT_TRUE(provider_->matches().empty());
-
-    // Expect the new results to have been stored.
-    EXPECT_EQ(empty_response,
-              prefs->GetString(omnibox::kZeroSuggestCachedResults));
-}
-
-TEST_P(ZeroSuggestProviderTest, TestPsuggestZeroSuggestPrefetch) {
-  base::HistogramTester histogram_tester;
-
-  EXPECT_CALL(*client_, IsAuthenticated())
-      .WillRepeatedly(testing::Return(true));
-
-  // Set up the pref to cache the response from the previous run.
-  std::string json_response(
-      "[\"\",[\"search1\", \"search2\", \"search3\"],"
-      "[],[],{\"google:suggestrelevance\":[602, 601, 600],"
-      "\"google:verbatimrelevance\":1300}]");
-  PrefService* prefs = client_->GetPrefs();
-  prefs->SetString(omnibox::kZeroSuggestCachedResults, json_response);
-
-  AutocompleteInput input = CreateNTPOnFocusInputForRemoteNoUrl();
-  provider_->StartPrefetch(input);
-  ASSERT_EQ(ZeroSuggestProvider::REMOTE_NO_URL,
-            provider_->GetResultTypeRunningForTesting());
+  // Expect that matches get populated synchronously out of the cache.
+  ASSERT_EQ(3U, provider_->matches().size());  // 3 results, no verbatim match
+  EXPECT_EQ(u"search1", provider_->matches()[0].contents);
+  EXPECT_EQ(u"search2", provider_->matches()[1].contents);
+  EXPECT_EQ(u"search3", provider_->matches()[2].contents);
 
   GURL suggest_url = GetSuggestURL(
       metrics::OmniboxEventProto::INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS);
@@ -637,25 +550,202 @@
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(provider_->done());
 
-  // Expect correct histograms to have been logged.
-  histogram_tester.ExpectTotalCount("Omnibox.ZeroSuggestRequests.Prefetch", 2);
-  histogram_tester.ExpectBucketCount("Omnibox.ZeroSuggestRequests.Prefetch",
-                                     1 /*ZERO_SUGGEST_REQUEST_SENT*/, 1);
-  histogram_tester.ExpectBucketCount("Omnibox.ZeroSuggestRequests.Prefetch",
-                                     3 /*ZERO_SUGGEST_RESPONSE_RECEIVED*/, 1);
-  histogram_tester.ExpectTotalCount("Omnibox.ZeroSuggestRequests.NonPrefetch",
-                                    0);
+  // Expect the provider to have notified the provider listener.
+  EXPECT_TRUE(provider_did_notify_);
 
-  // Expect the provider not to have notified the provider listener.
-  EXPECT_FALSE(provider_did_notify_);
+  // Expect correct histograms to have been logged.
+  histogram_tester.ExpectTotalCount("Omnibox.ZeroSuggestRequests.NonPrefetch",
+                                    2);
+  histogram_tester.ExpectBucketCount("Omnibox.ZeroSuggestRequests.NonPrefetch",
+                                     1 /*ZERO_SUGGEST_REQUEST_SENT*/, 1);
+  histogram_tester.ExpectBucketCount("Omnibox.ZeroSuggestRequests.NonPrefetch",
+                                     3 /*ZERO_SUGGEST_RESPONSE_RECEIVED*/, 1);
+  histogram_tester.ExpectTotalCount("Omnibox.ZeroSuggestRequests.Prefetch", 0);
+  histogram_tester.ExpectBucketCount(
+      "Omnibox.ZeroSuggestRequests.NonPrefetch",
+      6 /*ZERO_SUGGEST_RESPONSE_UPDATED_RESULTS*/, 0);
+
+  // Expect the same results after the response has been handled.
+  ASSERT_EQ(3U, provider_->matches().size());  // 3 results, no verbatim match
+  EXPECT_EQ(u"search1", provider_->matches()[0].contents);
+  EXPECT_EQ(u"search2", provider_->matches()[1].contents);
+  EXPECT_EQ(u"search3", provider_->matches()[2].contents);
+
+  // Expect the new results to have been stored.
+  EXPECT_EQ(json_response2,
+            prefs->GetString(omnibox::kZeroSuggestCachedResults));
+}
+
+TEST_F(ZeroSuggestProviderTest, TestPsuggestZeroSuggestReceivedEmptyResults) {
+  base::HistogramTester histogram_tester;
+
+  EXPECT_CALL(*client_, IsAuthenticated())
+      .WillRepeatedly(testing::Return(true));
+
+  // Set up the pref to cache the response from the previous run.
+  std::string json_response("[\"\",[\"search1\", \"search2\", \"search3\"],"
+      "[],[],{\"google:suggestrelevance\":[602, 601, 600],"
+      "\"google:verbatimrelevance\":1300}]");
+  PrefService* prefs = client_->GetPrefs();
+  prefs->SetString(omnibox::kZeroSuggestCachedResults, json_response);
+
+  AutocompleteInput input = CreateNTPOnFocusInputForRemoteNoUrl();
+  provider_->Start(input, false);
+  ASSERT_EQ(ZeroSuggestProvider::REMOTE_NO_URL,
+            provider_->GetResultTypeRunningForTesting());
+
+  // Expect that matches get populated synchronously out of the cache.
+  ASSERT_EQ(3U, provider_->matches().size());  // 3 results, no verbatim match
+  EXPECT_EQ(u"search1", provider_->matches()[0].contents);
+  EXPECT_EQ(u"search2", provider_->matches()[1].contents);
+  EXPECT_EQ(u"search3", provider_->matches()[2].contents);
+
+  GURL suggest_url = GetSuggestURL(
+      metrics::OmniboxEventProto::INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS);
+  EXPECT_TRUE(test_loader_factory()->IsPending(suggest_url.spec()));
+  std::string empty_response("[\"\",[],[],[],{}]");
+  test_loader_factory()->AddResponse(suggest_url.spec(), empty_response);
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(provider_->done());
+
+  // Expect correct histograms to have been logged.
+  histogram_tester.ExpectTotalCount("Omnibox.ZeroSuggestRequests.NonPrefetch",
+                                    3);
+  histogram_tester.ExpectBucketCount("Omnibox.ZeroSuggestRequests.NonPrefetch",
+                                     1 /*ZERO_SUGGEST_REQUEST_SENT*/, 1);
+  histogram_tester.ExpectBucketCount("Omnibox.ZeroSuggestRequests.NonPrefetch",
+                                     3 /*ZERO_SUGGEST_RESPONSE_RECEIVED*/, 1);
+  histogram_tester.ExpectBucketCount(
+      "Omnibox.ZeroSuggestRequests.NonPrefetch",
+      6 /*ZERO_SUGGEST_RESPONSE_UPDATED_RESULTS*/, 1);
+  histogram_tester.ExpectTotalCount("Omnibox.ZeroSuggestRequests.Prefetch", 0);
+
+  // Expect the provider to have notified the provider listener.
+  EXPECT_TRUE(provider_did_notify_);
+
+  // Expect that the matches have been cleared.
+  ASSERT_TRUE(provider_->matches().empty());
+
+  // Expect the new results to have been stored.
+  EXPECT_EQ(empty_response,
+            prefs->GetString(omnibox::kZeroSuggestCachedResults));
+}
+
+TEST_F(ZeroSuggestProviderTest, TestPsuggestZeroSuggestPrefetchThenNTPOnFocus) {
+  EXPECT_CALL(*client_, IsAuthenticated())
+      .WillRepeatedly(testing::Return(true));
+
+  // Set up the pref to cache the response from the previous run.
+  std::string json_response(
+      "[\"\",[\"search1\", \"search2\", \"search3\"],"
+      "[],[],{\"google:suggestrelevance\":[602, 601, 600],"
+      "\"google:verbatimrelevance\":1300}]");
+  PrefService* prefs = client_->GetPrefs();
+  prefs->SetString(omnibox::kZeroSuggestCachedResults, json_response);
+
+  {
+    base::HistogramTester histogram_tester;
+
+    // Start a prefetch request.
+    AutocompleteInput input = CreateNTPOnFocusPrefetchInput();
+    provider_->StartPrefetch(input);
+    EXPECT_TRUE(provider_->done());
+    ASSERT_EQ(ZeroSuggestProvider::REMOTE_NO_URL,
+              provider_->GetResultTypeRunningForTesting());
+
+    // Expect the results to be empty.
+    ASSERT_EQ(0U, provider_->matches().size());
+
+    GURL suggest_url =
+        GetSuggestURL(metrics::OmniboxEventProto::NTP_ZPS_PREFETCH);
+    EXPECT_TRUE(test_loader_factory()->IsPending(suggest_url.spec()));
+    std::string json_response2(
+        "[\"\",[\"search4\", \"search5\", \"search6\"],"
+        "[],[],{\"google:suggestrelevance\":[602, 601, 600],"
+        "\"google:verbatimrelevance\":1300}]");
+    test_loader_factory()->AddResponse(suggest_url.spec(), json_response2);
+
+    base::RunLoop().RunUntilIdle();
+    EXPECT_TRUE(provider_->done());
+
+    // Expect correct histograms to have been logged.
+    histogram_tester.ExpectTotalCount("Omnibox.ZeroSuggestRequests.Prefetch",
+                                      2);
+    histogram_tester.ExpectBucketCount("Omnibox.ZeroSuggestRequests.Prefetch",
+                                       1 /*ZERO_SUGGEST_REQUEST_SENT*/, 1);
+    histogram_tester.ExpectBucketCount("Omnibox.ZeroSuggestRequests.Prefetch",
+                                       3 /*ZERO_SUGGEST_RESPONSE_RECEIVED*/, 1);
+    histogram_tester.ExpectBucketCount(
+        "Omnibox.ZeroSuggestRequests.NonPrefetch",
+        6 /*ZERO_SUGGEST_RESPONSE_UPDATED_RESULTS*/, 0);
+    histogram_tester.ExpectTotalCount("Omnibox.ZeroSuggestRequests.NonPrefetch",
+                                      0);
+
+    // Expect the provider not to have notified the provider listener.
+    EXPECT_FALSE(provider_did_notify_);
+
+    // Expect the same empty results after the response has been handled.
+    ASSERT_EQ(0U, provider_->matches().size());  // 3 results, no verbatim match
+
+    // Expect the new response to have been stored in the pref.
+    EXPECT_EQ(json_response2,
+              prefs->GetString(omnibox::kZeroSuggestCachedResults));
+  }
+  {
+    base::HistogramTester histogram_tester;
+
+    // Start a non-prefetch request.
+    AutocompleteInput input = CreateNTPOnFocusInputForRemoteNoUrl();
+    provider_->Start(input, false);
+    EXPECT_FALSE(provider_->done());
+    ASSERT_EQ(ZeroSuggestProvider::REMOTE_NO_URL,
+              provider_->GetResultTypeRunningForTesting());
+
+    // Expect the results from the cached response.
+    ASSERT_EQ(3U, provider_->matches().size());  // 3 results, no verbatim match
+    EXPECT_EQ(u"search4", provider_->matches()[0].contents);
+    EXPECT_EQ(u"search5", provider_->matches()[1].contents);
+    EXPECT_EQ(u"search6", provider_->matches()[2].contents);
+
+    GURL suggest_url = GetSuggestURL(
+        metrics::OmniboxEventProto::INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS);
+    EXPECT_TRUE(test_loader_factory()->IsPending(suggest_url.spec()));
+    std::string json_response3(
+        "[\"\",[\"search7\", \"search8\", \"search9\"],"
+        "[],[],{\"google:suggestrelevance\":[602, 601, 600],"
+        "\"google:verbatimrelevance\":1300}]");
+    test_loader_factory()->AddResponse(suggest_url.spec(), json_response3);
+
+    base::RunLoop().RunUntilIdle();
+    EXPECT_TRUE(provider_->done());
+
+    // Expect correct histograms to have been logged.
+    histogram_tester.ExpectTotalCount("Omnibox.ZeroSuggestRequests.NonPrefetch",
+                                      2);
+    histogram_tester.ExpectBucketCount(
+        "Omnibox.ZeroSuggestRequests.NonPrefetch",
+        1 /*ZERO_SUGGEST_REQUEST_SENT*/, 1);
+    histogram_tester.ExpectBucketCount(
+        "Omnibox.ZeroSuggestRequests.NonPrefetch",
+        3 /*ZERO_SUGGEST_RESPONSE_RECEIVED*/, 1);
+    histogram_tester.ExpectBucketCount(
+        "Omnibox.ZeroSuggestRequests.NonPrefetch",
+        6 /*ZERO_SUGGEST_RESPONSE_UPDATED_RESULTS*/, 0);
+    histogram_tester.ExpectTotalCount("Omnibox.ZeroSuggestRequests.Prefetch",
+                                      0);
+
+    // Expect the provider to have notified the provider listener.
+    EXPECT_TRUE(provider_did_notify_);
 
     // Expect the same results after the response has been handled.
     ASSERT_EQ(3U, provider_->matches().size());  // 3 results, no verbatim match
-    EXPECT_EQ(u"search1", provider_->matches()[0].contents);
-    EXPECT_EQ(u"search2", provider_->matches()[1].contents);
-    EXPECT_EQ(u"search3", provider_->matches()[2].contents);
+    EXPECT_EQ(u"search4", provider_->matches()[0].contents);
+    EXPECT_EQ(u"search5", provider_->matches()[1].contents);
+    EXPECT_EQ(u"search6", provider_->matches()[2].contents);
 
-    // Expect the new results to have been stored.
-    EXPECT_EQ(json_response2,
+    // Expect the new response to have been stored in the pref.
+    EXPECT_EQ(json_response3,
               prefs->GetString(omnibox::kZeroSuggestCachedResults));
+  }
 }
diff --git a/components/omnibox/common/omnibox_features.cc b/components/omnibox/common/omnibox_features.cc
index d87cdbea..eb8130428 100644
--- a/components/omnibox/common/omnibox_features.cc
+++ b/components/omnibox/common/omnibox_features.cc
@@ -246,6 +246,12 @@
 extern const base::Feature kMostVisitedTiles{"OmniboxMostVisitedTiles",
                                              enabled_by_default_android_only};
 
+// If enabled, permits the title on the MostVisitedTiles to wrap around to
+// second line.
+extern const base::Feature kMostVisitedTilesTitleWrapAround{
+    "OmniboxMostVisitedTilesTitleWrapAround",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
 // If enabled, expands autocompletion to possibly (depending on params) include
 // suggestion titles and non-prefixes as opposed to be restricted to URL
 // prefixes. Will also adjust the location bar UI and omnibox text selection to
diff --git a/components/omnibox/common/omnibox_features.h b/components/omnibox/common/omnibox_features.h
index 5f89b0f..923d10c8 100644
--- a/components/omnibox/common/omnibox_features.h
+++ b/components/omnibox/common/omnibox_features.h
@@ -73,6 +73,7 @@
 extern const base::Feature kDocumentProviderDedupingOptimization;
 extern const base::Feature kSuggestionAnswersColorReverse;
 extern const base::Feature kMostVisitedTiles;
+extern const base::Feature kMostVisitedTilesTitleWrapAround;
 extern const base::Feature kRichAutocompletion;
 extern const base::Feature kNtpRealboxPedals;
 extern const base::Feature kNtpRealboxSuggestionAnswers;
diff --git a/components/password_manager/core/browser/password_manager_util_unittest.cc b/components/password_manager/core/browser/password_manager_util_unittest.cc
index a5b94c5..436036bc 100644
--- a/components/password_manager/core/browser/password_manager_util_unittest.cc
+++ b/components/password_manager/core/browser/password_manager_util_unittest.cc
@@ -262,7 +262,7 @@
               (base::OnceCallback<void(const std::string&)>),
               (override));
   MOCK_METHOD(void,
-              OnPromoCodeSuggestionsFooterSelected,
+              OpenPromoCodeOfferDetailsURL,
               (const GURL& url),
               (override));
 };
diff --git a/components/policy/core/browser/BUILD.gn b/components/policy/core/browser/BUILD.gn
index 516c562..73e1c4d 100644
--- a/components/policy/core/browser/BUILD.gn
+++ b/components/policy/core/browser/BUILD.gn
@@ -48,6 +48,8 @@
     "webui/json_generation.h",
     "webui/machine_level_user_cloud_policy_status_provider.cc",
     "webui/machine_level_user_cloud_policy_status_provider.h",
+    "webui/policy_data_utils.cc",
+    "webui/policy_data_utils.h",
     "webui/policy_status_provider.cc",
     "webui/policy_status_provider.h",
   ]
diff --git a/components/policy/core/browser/webui/policy_data_utils.cc b/components/policy/core/browser/webui/policy_data_utils.cc
new file mode 100644
index 0000000..4317acc
--- /dev/null
+++ b/components/policy/core/browser/webui/policy_data_utils.cc
@@ -0,0 +1,33 @@
+// 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/browser/webui/policy_data_utils.h"
+
+#include "components/policy/core/common/cloud/cloud_policy_core.h"
+#include "components/policy/core/common/cloud/cloud_policy_manager.h"
+#include "components/policy/core/common/cloud/cloud_policy_store.h"
+#include "components/policy/proto/device_management_backend.pb.h"
+
+namespace policy {
+
+absl::optional<std::string> GetManagedBy(
+    const policy::CloudPolicyManager* manager) {
+  if (!manager) {
+    return absl::nullopt;
+  }
+
+  const policy::CloudPolicyStore* store = manager->core()->store();
+  if (!store) {
+    return absl::nullopt;
+  }
+
+  const enterprise_management::PolicyData* policy = store->policy();
+  if (!policy || !policy->has_managed_by()) {
+    return absl::nullopt;
+  }
+
+  return policy->managed_by();
+}
+
+}  // namespace policy
diff --git a/components/policy/core/browser/webui/policy_data_utils.h b/components/policy/core/browser/webui/policy_data_utils.h
new file mode 100644
index 0000000..82411ef
--- /dev/null
+++ b/components/policy/core/browser/webui/policy_data_utils.h
@@ -0,0 +1,25 @@
+// 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_BROWSER_WEBUI_POLICY_DATA_UTILS_H_
+#define COMPONENTS_POLICY_CORE_BROWSER_WEBUI_POLICY_DATA_UTILS_H_
+
+#include <string>
+
+#include "components/policy/policy_export.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace policy {
+class CloudPolicyManager;
+}
+
+namespace policy {
+
+// Gets the domain that manages the policy.
+POLICY_EXPORT absl::optional<std::string> GetManagedBy(
+    const policy::CloudPolicyManager* manager);
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_CORE_BROWSER_WEBUI_POLICY_DATA_UTILS_H_
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 054432ec..106bacb 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -9924,6 +9924,7 @@
       'type': 'int',
       'schema': { 'type': 'integer' },
       'supported_on': ['chrome.*:14-'],
+      'future_on': [ 'chrome_os' ],
       'features': {
         'dynamic_refresh': False,
         'per_profile': False,
@@ -29205,6 +29206,7 @@
         'items': { 'type': 'string' },
       },
       'supported_on': ['chrome.*:91-'],
+      'future_on': ['chrome_os'],
       'features': {
         'dynamic_refresh': True,
         'per_profile': True,
diff --git a/components/policy/test_support/BUILD.gn b/components/policy/test_support/BUILD.gn
index e0d88bd..0251b52 100644
--- a/components/policy/test_support/BUILD.gn
+++ b/components/policy/test_support/BUILD.gn
@@ -66,41 +66,43 @@
   ]
 }
 
-source_set("unittests") {
-  testonly = true
+if (!is_ios) {
+  source_set("unittests") {
+    testonly = true
 
-  sources = [
-    "client_storage_unittest.cc",
-    "embedded_policy_test_server_test_base.cc",
-    "embedded_policy_test_server_test_base.h",
-    "embedded_policy_test_server_unittest.cc",
-    "failing_request_handler_unittest.cc",
-    "policy_storage_unittest.cc",
-    "request_handler_for_api_authorization_unittest.cc",
-    "request_handler_for_auto_enrollment_unittest.cc",
-    "request_handler_for_check_android_management_unittest.cc",
-    "request_handler_for_chrome_desktop_report_unittest.cc",
-    "request_handler_for_device_attribute_update_permission_unittest.cc",
-    "request_handler_for_device_attribute_update_unittest.cc",
-    "request_handler_for_device_initial_enrollment_state_unittest.cc",
-    "request_handler_for_device_state_retrieval_unittest.cc",
-    "request_handler_for_policy_unittest.cc",
-    "request_handler_for_psm_auto_enrollment_unittest.cc",
-    "request_handler_for_register_browser_unittest.cc",
-    "request_handler_for_register_cert_based_unittest.cc",
-    "request_handler_for_register_device_and_user_unittest.cc",
-    "request_handler_for_remote_commands_unittest.cc",
-    "request_handler_for_status_upload_unittest.cc",
-    "request_handler_for_unregister_unittest.cc",
-    "signature_provider_unittest.cc",
-  ]
+    sources = [
+      "client_storage_unittest.cc",
+      "embedded_policy_test_server_test_base.cc",
+      "embedded_policy_test_server_test_base.h",
+      "embedded_policy_test_server_unittest.cc",
+      "failing_request_handler_unittest.cc",
+      "policy_storage_unittest.cc",
+      "request_handler_for_api_authorization_unittest.cc",
+      "request_handler_for_auto_enrollment_unittest.cc",
+      "request_handler_for_check_android_management_unittest.cc",
+      "request_handler_for_chrome_desktop_report_unittest.cc",
+      "request_handler_for_device_attribute_update_permission_unittest.cc",
+      "request_handler_for_device_attribute_update_unittest.cc",
+      "request_handler_for_device_initial_enrollment_state_unittest.cc",
+      "request_handler_for_device_state_retrieval_unittest.cc",
+      "request_handler_for_policy_unittest.cc",
+      "request_handler_for_psm_auto_enrollment_unittest.cc",
+      "request_handler_for_register_browser_unittest.cc",
+      "request_handler_for_register_cert_based_unittest.cc",
+      "request_handler_for_register_device_and_user_unittest.cc",
+      "request_handler_for_remote_commands_unittest.cc",
+      "request_handler_for_status_upload_unittest.cc",
+      "request_handler_for_unregister_unittest.cc",
+      "signature_provider_unittest.cc",
+    ]
 
-  deps = [
-    ":test_support",
-    "//chrome/test:test_support",
-    "//services/network:test_support",
-    "//services/network/public/cpp",
-    "//services/network/public/mojom",
-    "//third_party/abseil-cpp:absl",
-  ]
+    deps = [
+      ":test_support",
+      "//chrome/test:test_support",
+      "//services/network:test_support",
+      "//services/network/public/cpp",
+      "//services/network/public/mojom",
+      "//third_party/abseil-cpp:absl",
+    ]
+  }
 }
diff --git a/components/policy/test_support/embedded_policy_test_server.cc b/components/policy/test_support/embedded_policy_test_server.cc
index 8a811b0..46b27ec 100644
--- a/components/policy/test_support/embedded_policy_test_server.cc
+++ b/components/policy/test_support/embedded_policy_test_server.cc
@@ -10,9 +10,9 @@
 #include "base/bind.h"
 #include "base/logging.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
-#if !BUILDFLAG(IS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
 #include "components/policy/proto/chrome_extension_policy.pb.h"
-#endif  // !BUILDFLAG(IS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
 #include "components/policy/test_support/client_storage.h"
 #include "components/policy/test_support/failing_request_handler.h"
 #include "components/policy/test_support/policy_storage.h"
@@ -133,7 +133,7 @@
       std::make_unique<FailingRequestHandler>(this, request_type, error_code));
 }
 
-#if !BUILDFLAG(IS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
 void EmbeddedPolicyTestServer::UpdateExternalPolicy(
     const std::string& type,
     const std::string& entity_id,
@@ -154,7 +154,7 @@
   policy_storage()->SetPolicyPayload(type, entity_id,
                                      external_policy_data.SerializeAsString());
 }
-#endif  // !BUILDFLAG(IS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
 
 std::unique_ptr<HttpResponse> EmbeddedPolicyTestServer::HandleRequest(
     const HttpRequest& request) {
diff --git a/components/policy/test_support/embedded_policy_test_server.h b/components/policy/test_support/embedded_policy_test_server.h
index 9c788bd..46c721a7 100644
--- a/components/policy/test_support/embedded_policy_test_server.h
+++ b/components/policy/test_support/embedded_policy_test_server.h
@@ -90,7 +90,7 @@
   void ResetPolicyStorage();
   void ResetClientStorage();
 
-#if !BUILDFLAG(IS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
   // Updates policy selected by |type| and optional |entity_id|. The
   // |raw_policy| is served via an external endpoint. This does not trigger
   // policy invalidation, hence test authors must manually trigger a policy
@@ -98,7 +98,7 @@
   void UpdateExternalPolicy(const std::string& type,
                             const std::string& entity_id,
                             const std::string& raw_policy);
-#endif  // !BUILDFLAG(IS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
 
  private:
   // Default request handler.
diff --git a/components/policy/tools/generate_policy_source.py b/components/policy/tools/generate_policy_source.py
index 2e1f6e7..7fb456b 100755
--- a/components/policy/tools/generate_policy_source.py
+++ b/components/policy/tools/generate_policy_source.py
@@ -504,7 +504,7 @@
 # Ensure only windows supported policies are returned when building for windows.
 # Eventually only supported policies on every platforms will be returned.
 def _GetSupportedPolicies(policies, target_platform):
-  if target_platform in ['win', 'linux', 'mac', 'ios']:
+  if target_platform in ['win', 'linux', 'mac', 'ios', 'android']:
     return [policy for policy in policies if policy.is_supported]
 
   return [
diff --git a/components/user_notes/browser/frame_user_note_changes.cc b/components/user_notes/browser/frame_user_note_changes.cc
index 72b52b3..d8c5abd 100644
--- a/components/user_notes/browser/frame_user_note_changes.cc
+++ b/components/user_notes/browser/frame_user_note_changes.cc
@@ -4,7 +4,7 @@
 
 #include "components/user_notes/browser/frame_user_note_changes.h"
 
-#include "base/barrier_closure.h"
+#include "base/barrier_callback.h"
 #include "components/user_notes/browser/user_note_manager.h"
 #include "components/user_notes/model/user_note.h"
 #include "components/user_notes/model/user_note_target.h"
@@ -81,7 +81,7 @@
 std::unique_ptr<UserNoteInstance> FrameUserNoteChanges::MakeNoteInstance(
     const UserNote* note_model,
     UserNoteManager* manager) const {
-  return std::make_unique<UserNoteInstance>(note_model->GetSafeRef(), manager);
+  return UserNoteInstance::Create(note_model->GetSafeRef(), manager);
 }
 
 }  // namespace user_notes
diff --git a/components/user_notes/browser/frame_user_note_changes_unittest.cc b/components/user_notes/browser/frame_user_note_changes_unittest.cc
index 5fabf4e8..301c17a 100644
--- a/components/user_notes/browser/frame_user_note_changes_unittest.cc
+++ b/components/user_notes/browser/frame_user_note_changes_unittest.cc
@@ -9,6 +9,7 @@
 
 #include "base/memory/safe_ref.h"
 #include "base/test/bind.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/unguessable_token.h"
 #include "components/user_notes/browser/user_note_base_test.h"
 #include "components/user_notes/browser/user_note_instance.h"
@@ -34,7 +35,7 @@
 
   MOCK_METHOD(void,
               InitializeHighlightIfNeeded,
-              (base::OnceClosure callback),
+              (UserNoteInstance::AttachmentFinishedCallback callback),
               (override));
 };
 
diff --git a/components/user_notes/browser/user_note_base_test.cc b/components/user_notes/browser/user_note_base_test.cc
index f06d17ea..6b48ffff 100644
--- a/components/user_notes/browser/user_note_base_test.cc
+++ b/components/user_notes/browser/user_note_base_test.cc
@@ -13,6 +13,7 @@
 #include "content/public/browser/page.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/test/navigation_simulator.h"
+#include "services/service_manager/public/cpp/interface_provider.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace user_notes {
@@ -23,6 +24,14 @@
 
 }  // namespace
 
+MockAnnotationAgentContainer::MockAnnotationAgentContainer() = default;
+
+MockAnnotationAgentContainer::~MockAnnotationAgentContainer() = default;
+
+MockAnnotationAgent::MockAnnotationAgent() = default;
+
+MockAnnotationAgent::~MockAnnotationAgent() = default;
+
 UserNoteBaseTest::UserNoteBaseTest() {
   scoped_feature_list_.InitAndEnableFeature(user_notes::kUserNotes);
 }
@@ -67,7 +76,8 @@
   }
 }
 
-UserNoteManager* UserNoteBaseTest::ConfigureNewManager() {
+UserNoteManager* UserNoteBaseTest::ConfigureNewManager(
+    MockAnnotationAgentContainer* mock_container) {
   // Create a test frame and navigate it to a unique URL.
   std::unique_ptr<content::WebContents> wc = CreateTestWebContents();
   content::RenderFrameHostTester::For(wc->GetPrimaryMainFrame())
@@ -76,6 +86,17 @@
       wc.get(),
       GURL(kBaseUrl + base::NumberToString(web_contents_list_.size())));
 
+  std::unique_ptr<service_manager::InterfaceProvider::TestApi> test_api;
+  if (mock_container) {
+    CHECK(!mock_container->is_bound());
+    test_api = std::make_unique<service_manager::InterfaceProvider::TestApi>(
+        wc->GetPrimaryMainFrame()->GetRemoteInterfaces());
+    test_api->SetBinderForName(
+        blink::mojom::AnnotationAgentContainer::Name_,
+        base::BindRepeating(&MockAnnotationAgentContainer::Bind,
+                            base::Unretained(mock_container)));
+  }
+
   // Create and attach a `UserNoteManager` to the primary page.
   content::Page& page = wc->GetPrimaryPage();
   UserNoteManager::CreateForPage(page, note_service_->GetSafeRef());
@@ -83,6 +104,8 @@
   DCHECK(note_manager);
   web_contents_list_.emplace_back(std::move(wc));
 
+  CHECK(!mock_container || mock_container->is_bound());
+
   return note_manager;
 }
 
@@ -91,8 +114,8 @@
   DCHECK(manager);
   const auto& entry_it = note_service_->model_map_.find(note_id);
   ASSERT_FALSE(entry_it == note_service_->model_map_.end());
-  manager->AddNoteInstance(std::make_unique<UserNoteInstance>(
-      entry_it->second.model->GetSafeRef(), manager));
+  manager->AddNoteInstance(
+      UserNoteInstance::Create(entry_it->second.model->GetSafeRef(), manager));
 }
 
 size_t UserNoteBaseTest::ManagerCountForId(
diff --git a/components/user_notes/browser/user_note_base_test.h b/components/user_notes/browser/user_note_base_test.h
index 0b92f95..67cb5cf6 100644
--- a/components/user_notes/browser/user_note_base_test.h
+++ b/components/user_notes/browser/user_note_base_test.h
@@ -14,9 +14,62 @@
 #include "components/user_notes/browser/user_note_service.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/test_renderer_host.h"
+#include "third_party/blink/public/mojom/annotation/annotation.mojom.h"
 
 namespace user_notes {
 
+// Mock implementation of the renderer's AnnotationAgentContainer interface.
+// Tests can use this to simulate and make checks on the renderer end that a
+// UserNoteManager interacts with.
+class MockAnnotationAgentContainer
+    : public blink::mojom::AnnotationAgentContainer {
+ public:
+  MockAnnotationAgentContainer();
+  ~MockAnnotationAgentContainer() override;
+
+  // blink::mojom::AnnotationAgentContainer
+  MOCK_METHOD4(CreateAgent,
+               void(mojo::PendingRemote<blink::mojom::AnnotationAgentHost>,
+                    mojo::PendingReceiver<blink::mojom::AnnotationAgent>,
+                    blink::mojom::AnnotationType,
+                    const std::string& /*serialized_selector*/));
+
+  MOCK_METHOD2(CreateAgentFromSelection,
+               void(blink::mojom::AnnotationType,
+                    CreateAgentFromSelectionCallback));
+
+  void Bind(mojo::ScopedMessagePipeHandle handle) {
+    is_bound_ = true;
+    receiver_.Bind(
+        mojo::PendingReceiver<blink::mojom::AnnotationAgentContainer>(
+            std::move(handle)));
+  }
+
+  bool is_bound() const { return is_bound_; }
+
+ private:
+  mojo::Receiver<blink::mojom::AnnotationAgentContainer> receiver_{this};
+  bool is_bound_ = false;
+};
+
+// Similar to above but for the agent interface.
+class MockAnnotationAgent : public blink::mojom::AnnotationAgent {
+ public:
+  MockAnnotationAgent();
+  ~MockAnnotationAgent() override;
+
+  // blink::mojom::AnnotationAgent
+  MOCK_METHOD0(ScrollIntoView, void());
+
+  mojo::PendingRemote<blink::mojom::AnnotationAgent>
+  BindNewPipeAndPassRemote() {
+    return receiver_.BindNewPipeAndPassRemote();
+  }
+
+ private:
+  mojo::Receiver<blink::mojom::AnnotationAgent> receiver_{this};
+};
+
 // A base test harness for User Notes unit tests. The harness sets up a note
 // service and exposes methods to create new note models, as well as methods to
 // create and manipulate note managers attached to mock pages.
@@ -38,7 +91,11 @@
 
   void AddPartialNotesToService(size_t count);
 
-  UserNoteManager* ConfigureNewManager();
+  // Creates and returns a new UserNoteManager for a new WebContents. Callers
+  // can optionally pass a MockAnnotationAgentContainer which the new manager's
+  // annotation_agent_container_ will bind to.
+  UserNoteManager* ConfigureNewManager(
+      MockAnnotationAgentContainer* mock_container = nullptr);
 
   void AddNewInstanceToManager(UserNoteManager* manager,
                                base::UnguessableToken note_id);
diff --git a/components/user_notes/browser/user_note_instance.cc b/components/user_notes/browser/user_note_instance.cc
index 65ec9f7..5ff6002 100644
--- a/components/user_notes/browser/user_note_instance.cc
+++ b/components/user_notes/browser/user_note_instance.cc
@@ -10,36 +10,82 @@
 namespace user_notes {
 
 UserNoteInstance::UserNoteInstance(base::SafeRef<UserNote> model,
-                                   UserNoteManager* parent_manager)
-    : UserNoteInstance(model, parent_manager, gfx::Rect()) {}
+                                   UserNoteManager* parent_manager,
+                                   PassKey pass_key)
+    : model_(std::move(model)),
+      parent_manager_(parent_manager),
+      receiver_(this) {}
 
 UserNoteInstance::UserNoteInstance(base::SafeRef<UserNote> model,
-                                   UserNoteManager* parent_manager,
-                                   gfx::Rect rect)
-    : model_(model),
-      parent_manager_(parent_manager),
-      rect_(rect),
-      receiver_(this) {}
+                                   UserNoteManager* parent_manager)
+    : UserNoteInstance(std::move(model), parent_manager, PassKey()) {}
+
+// static
+std::unique_ptr<UserNoteInstance> UserNoteInstance::Create(
+    base::SafeRef<UserNote> model,
+    UserNoteManager* parent_manager) {
+  return std::make_unique<UserNoteInstance>(std::move(model), parent_manager,
+                                            PassKey());
+}
 
 UserNoteInstance::~UserNoteInstance() = default;
 
 bool UserNoteInstance::IsDetached() const {
-  return is_initialized_ && rect_.IsEmpty() &&
+  return finished_attachment_ && rect_.IsEmpty() &&
          model_->target().type() == UserNoteTarget::TargetType::kPageText;
 }
 
-void UserNoteInstance::InitializeHighlightIfNeeded(base::OnceClosure callback) {
-  DCHECK(!is_initialized_);
+void UserNoteInstance::BindToHighlight(
+    mojo::PendingReceiver<blink::mojom::AnnotationAgentHost> host_receiver,
+    mojo::PendingRemote<blink::mojom::AnnotationAgent> agent_remote,
+    AttachmentFinishedCallback callback) {
+  DCHECK_EQ(model_->target().type(), UserNoteTarget::TargetType::kPageText);
+  DCHECK(!agent_.is_bound());
+  DCHECK(!receiver_.is_bound());
+  DCHECK(agent_remote.is_valid());
+  DCHECK(host_receiver.is_valid());
 
-  if (model_->target().type() == UserNoteTarget::TargetType::kPage) {
-    // Page-level notes are not associated with text in the page, so there is no
-    // highlight to create on the renderer side.
-    is_initialized_ = true;
-    DCHECK(callback);
-    std::move(callback).Run();
-  } else {
-    did_finish_attachment_callback_ = std::move(callback);
-    InitializeHighlightInternal();
+  did_finish_attachment_callback_ = std::move(callback);
+  agent_.Bind(std::move(agent_remote));
+  // base::Unretained since note instances are always destroyed before the
+  // manager (the manager's destructor explicitly destroys them).
+  agent_.set_disconnect_handler(
+      base::BindOnce(&UserNoteManager::RemoveNote,
+                     base::Unretained(parent_manager_), model_->id()));
+
+  receiver_.Bind(std::move(host_receiver));
+}
+
+void UserNoteInstance::InitializeHighlightIfNeeded(
+    AttachmentFinishedCallback callback) {
+  // If the UserNoteInstance was instantiated to create a new note, the
+  // highlight will already be initialized in the renderer. In this case,
+  // BindToHighlight must already have been called and so a `callback` must not
+  // be passed to this method since a callback was already passed in
+  // BindToHighlight.
+  if (agent_.is_bound()) {
+    DCHECK(receiver_.is_bound());
+    DCHECK(!callback);
+    DCHECK_EQ(model_->target().type(), UserNoteTarget::TargetType::kPageText);
+    DCHECK_EQ(finished_attachment_, did_finish_attachment_callback_.is_null());
+    return;
+  }
+
+  DCHECK(callback);
+
+  switch (model_->target().type()) {
+    case UserNoteTarget::TargetType::kPage: {
+      // Page-level notes are not associated with text in the page, so there is
+      // no highlight to create on the renderer side. Also, this instance may
+      // already have been initialized as part of generating a new note. In
+      // these cases, do nothing.
+      std::move(callback).Run();
+    } break;
+    case UserNoteTarget::TargetType::kPageText: {
+      DCHECK(!did_finish_attachment_callback_);
+      did_finish_attachment_callback_ = std::move(callback);
+      InitializeHighlightInternal();
+    } break;
   }
 }
 
@@ -50,11 +96,11 @@
 }
 
 void UserNoteInstance::DidFinishAttachment(const gfx::Rect& rect) {
-  is_initialized_ = true;
+  finished_attachment_ = true;
   rect_ = rect;
 
-  DCHECK(did_finish_attachment_callback_);
-  std::move(did_finish_attachment_callback_).Run();
+  if (did_finish_attachment_callback_)
+    std::move(did_finish_attachment_callback_).Run();
 }
 
 void UserNoteInstance::OnWebHighlightFocused() {
@@ -77,7 +123,7 @@
 
   // Set a disconnect handler because the renderer can close the pipe at any
   // moment to signal that the highlight has been removed from the page. It's ok
-  // to use base::unretained here because the note instances are always
+  // to use base::Unretained here because the note instances are always
   // destroyed before the manager (the manager's destructor explicitly destroys
   // them).
   agent_.set_disconnect_handler(
diff --git a/components/user_notes/browser/user_note_instance.h b/components/user_notes/browser/user_note_instance.h
index 99845b8..7428144 100644
--- a/components/user_notes/browser/user_note_instance.h
+++ b/components/user_notes/browser/user_note_instance.h
@@ -5,8 +5,11 @@
 #ifndef COMPONENTS_USER_NOTES_BROWSER_USER_NOTE_INSTANCE_H_
 #define COMPONENTS_USER_NOTES_BROWSER_USER_NOTE_INSTANCE_H_
 
+#include <memory>
+
 #include "base/barrier_closure.h"
 #include "base/memory/safe_ref.h"
+#include "base/types/pass_key.h"
 #include "components/user_notes/model/user_note.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -21,15 +24,22 @@
 // page.
 class UserNoteInstance : public blink::mojom::AnnotationAgentHost {
  public:
-  // The main constructor.
-  UserNoteInstance(base::SafeRef<UserNote> model,
-                   UserNoteManager* parent_manager);
+  using PassKey = base::PassKey<UserNoteInstance>;
 
-  // A constructor for when the bounding rect of the highlight is known in
-  // advance, for example during the note creation process.
+  // The callback type invoked when attachment in the renderer is completed.
+  using AttachmentFinishedCallback = base::OnceClosure;
+
+  // Creates a UserNoteInstance. This instance will attach to a highlight in
+  // the renderer (if the note is a non-page note) when
+  // InitializeHighlightIfNeeded is called.
+  static std::unique_ptr<UserNoteInstance> Create(
+      base::SafeRef<UserNote> model,
+      UserNoteManager* parent_manager);
+
+  // Use the Create static methods above. Public for use with std::make_unique.
   UserNoteInstance(base::SafeRef<UserNote> model,
                    UserNoteManager* parent_manager,
-                   gfx::Rect rect);
+                   PassKey pass_key);
 
   ~UserNoteInstance() override;
   UserNoteInstance(const UserNoteInstance&) = delete;
@@ -45,11 +55,19 @@
   // 3) Its bounding rect is empty.
   bool IsDetached() const;
 
-  // If this note is a text-level note, this method kicks off the asynchronous
-  // process to set up the Mojo connection with the corresponding agent in the
-  // renderer process. Otherwise, it invokes the provided callback.
-  // Marked virtual for tests to override.
-  virtual void InitializeHighlightIfNeeded(base::OnceClosure callback);
+  // Binds this instance to an existing highlight in the renderer. Must be
+  // called before the instance is added to the manager.
+  void BindToHighlight(
+      mojo::PendingReceiver<blink::mojom::AnnotationAgentHost> host_receiver,
+      mojo::PendingRemote<blink::mojom::AnnotationAgent> agent_remote,
+      AttachmentFinishedCallback callback);
+
+  // If this note is a text-level note and hasn't yet been bound to a highlight
+  // (via BindToHighlight), this method kicks off the asynchronous process to
+  // set up the Mojo connection with the corresponding agent in the renderer
+  // process. Otherwise, it invokes the provided callback. Marked virtual for
+  // tests to override.
+  virtual void InitializeHighlightIfNeeded(AttachmentFinishedCallback callback);
 
   void OnNoteSelected();
 
@@ -65,6 +83,11 @@
   // Mark this one as override.
   void OnNoteDetached();
 
+ protected:
+  // For mock descendants in tests since they can't instantiate the PassKey.
+  UserNoteInstance(base::SafeRef<UserNote> model,
+                   UserNoteManager* parent_manager);
+
  private:
   friend class UserNoteInstanceTest;
 
@@ -85,11 +108,12 @@
   gfx::Rect rect_;
 
   // Callback to invoke after the renderer agent has initialized.
-  base::OnceClosure did_finish_attachment_callback_;
+  AttachmentFinishedCallback did_finish_attachment_callback_;
 
-  // Stores whether this note instance has had its highlight initialized in the
-  // renderer process.
-  bool is_initialized_ = false;
+  // Stores whether this note instance has had its highlight initialized
+  // ("attached") in the renderer process. This will be true, even if
+  // attachment failed.
+  bool finished_attachment_ = false;
 
   // Receiver and agent for communication with the renderer process.
   mojo::Receiver<blink::mojom::AnnotationAgentHost> receiver_;
diff --git a/components/user_notes/browser/user_note_instance_unittest.cc b/components/user_notes/browser/user_note_instance_unittest.cc
index 70fe087..86cdc0d 100644
--- a/components/user_notes/browser/user_note_instance_unittest.cc
+++ b/components/user_notes/browser/user_note_instance_unittest.cc
@@ -75,8 +75,8 @@
         entry_it->second.model->GetSafeRef(), manager, simulate_attach_rect);
   }
 
-  bool IsNoteInstanceInitialized(UserNoteInstance* instance) {
-    return instance->is_initialized_;
+  bool IsAttachmentFinished(UserNoteInstance* instance) {
+    return instance->finished_attachment_;
   }
 };
 
@@ -99,7 +99,7 @@
   // The mocks ensure the callback is invoked synchronously, so verifications
   // can happen immediately.
   EXPECT_TRUE(callback_called);
-  EXPECT_TRUE(IsNoteInstanceInitialized(instance.get()));
+  EXPECT_FALSE(IsAttachmentFinished(instance.get()));
   EXPECT_EQ(instance->rect(), empty_rect);
 }
 
@@ -124,7 +124,7 @@
   // The mocks ensure the callback is invoked synchronously, so verifications
   // can happen immediately.
   EXPECT_TRUE(callback_called);
-  EXPECT_TRUE(IsNoteInstanceInitialized(instance.get()));
+  EXPECT_TRUE(IsAttachmentFinished(instance.get()));
   EXPECT_EQ(instance->rect(), rect);
 }
 
diff --git a/components/user_notes/browser/user_note_manager.cc b/components/user_notes/browser/user_note_manager.cc
index 5bc63df..3f8e1e0 100644
--- a/components/user_notes/browser/user_note_manager.cc
+++ b/components/user_notes/browser/user_note_manager.cc
@@ -14,7 +14,7 @@
 
 UserNoteManager::UserNoteManager(content::Page& page,
                                  base::SafeRef<UserNoteService> service)
-    : PageUserData<UserNoteManager>(page), service_(service) {
+    : PageUserData<UserNoteManager>(page), service_(std::move(service)) {
   // TODO(crbug.com/1313967): If / when user notes are supported in subframes,
   // caching the agent container of the primary main frame will not work. In
   // that case, the frame's container will probably need to be fetched on each
@@ -72,7 +72,7 @@
 
 void UserNoteManager::AddNoteInstance(
     std::unique_ptr<UserNoteInstance> note_instance,
-    base::OnceClosure initialize_callback) {
+    UserNoteInstance::AttachmentFinishedCallback initialize_callback) {
   // TODO(crbug.com/1313967): This DCHECK is only applicable if notes are only
   // supported in the top-level frame. If notes are ever supported in subframes,
   // it is possible for the same note ID to be added to the same page more than
@@ -90,6 +90,11 @@
       std::move(initialize_callback));
 }
 
+void UserNoteManager::OnAddNoteRequested(content::RenderFrameHost* frame,
+                                         bool has_selected_text) {
+  service_->OnAddNoteRequested(frame, has_selected_text);
+}
+
 PAGE_USER_DATA_KEY_IMPL(UserNoteManager);
 
 }  // namespace user_notes
diff --git a/components/user_notes/browser/user_note_manager.h b/components/user_notes/browser/user_note_manager.h
index 461d935..46e55ed2a 100644
--- a/components/user_notes/browser/user_note_manager.h
+++ b/components/user_notes/browser/user_note_manager.h
@@ -21,6 +21,7 @@
 
 namespace content {
 class Page;
+class RenderFrameHost;
 }  // namespace content
 
 namespace user_notes {
@@ -35,8 +36,7 @@
   UserNoteManager(const UserNoteManager&) = delete;
   UserNoteManager& operator=(const UserNoteManager&) = delete;
 
-  const mojo::Remote<blink::mojom::AnnotationAgentContainer>&
-  note_agent_container() {
+  mojo::Remote<blink::mojom::AnnotationAgentContainer>& note_agent_container() {
     return note_agent_container_;
   };
 
@@ -60,8 +60,12 @@
   // TODO(gujen): Remove the overload without the callback after tests are
   //              fixed.
   void AddNoteInstance(std::unique_ptr<UserNoteInstance> note);
-  void AddNoteInstance(std::unique_ptr<UserNoteInstance> note,
-                       base::OnceClosure initialize_callback);
+  void AddNoteInstance(
+      std::unique_ptr<UserNoteInstance> note,
+      UserNoteInstance::AttachmentFinishedCallback initialize_callback);
+
+  void OnAddNoteRequested(content::RenderFrameHost* frame,
+                          bool has_selected_text);
 
  private:
   friend class content::PageUserData<UserNoteManager>;
diff --git a/components/user_notes/browser/user_note_service.cc b/components/user_notes/browser/user_note_service.cc
index 0463285..62ae783 100644
--- a/components/user_notes/browser/user_note_service.cc
+++ b/components/user_notes/browser/user_note_service.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "base/notreached.h"
+#include "base/strings/utf_string_conversions.h"
 #include "components/user_notes/browser/frame_user_note_changes.h"
 #include "components/user_notes/browser/user_note_manager.h"
 #include "components/user_notes/browser/user_note_utils.h"
@@ -125,53 +126,63 @@
 }
 
 void UserNoteService::OnAddNoteRequested(content::RenderFrameHost* frame,
-                                         std::string original_text,
-                                         std::string selector,
-                                         gfx::Rect rect) {
+                                         bool has_selected_text) {
   DCHECK(IsUserNotesEnabled());
   DCHECK(frame);
   UserNoteManager* manager = UserNoteManager::GetForPage(frame->GetPage());
   DCHECK(manager);
 
-  // TODO(gujen): This partial note creation logic will be moved to an API
-  // exposed by the storage layer in order to keep the creation of UserNote
-  // models centralized. However, until the storage layer is finished, manually
-  // create a partial note here.
-  base::Time now = base::Time::Now();
-  int note_version = 1;
-  auto metadata = std::make_unique<UserNoteMetadata>(now, now, note_version);
-  auto body = std::make_unique<UserNoteBody>(/*plain_text_value=*/"");
-  auto target = std::make_unique<UserNoteTarget>(
-      UserNoteTarget::TargetType::kPageText, original_text,
-      GURL(frame->GetLastCommittedURL()), selector);
-  auto partial_note = std::make_unique<UserNote>(
-      base::UnguessableToken::Create(), std::move(metadata), std::move(body),
-      std::move(target));
-  UserNote* partial_note_raw = partial_note.get();
+  // TODO(crbug.com/1313967): `has_selected_text` is used to determine whether
+  // or not to create a page-level note. This will need to be reassessed when
+  // page-level UX is finalized.
+  if (has_selected_text) {
+    auto create_agent_callback = base::BindOnce(
+        [](base::SafeRef<UserNoteService> service,
+           content::WeakDocumentPtr document,
+           mojo::PendingReceiver<blink::mojom::AnnotationAgentHost>
+               host_receiver,
+           mojo::PendingRemote<blink::mojom::AnnotationAgent> agent_remote,
+           const std::string& serialized_selector,
+           const std::u16string& selected_text) {
+          if (agent_remote.is_valid() != host_receiver.is_valid()) {
+            mojo::ReportBadMessage(
+                "User note creation received only one invalid remote/receiver");
+            return;
+          }
 
-  // Store the partial note model into the creation map (not the model map)
-  // until it is finalized.
-  UserNoteService::ModelMapEntry entry(std::move(partial_note));
-  entry.managers.emplace(manager);
-  DCHECK(creation_map_.find(entry.model->id()) == creation_map_.end())
-      << "Attempted to create a partial note that already exists";
-  creation_map_.emplace(entry.model->id(), std::move(entry));
+          if (agent_remote.is_valid() == serialized_selector.empty()) {
+            mojo::ReportBadMessage(
+                "User note creation received unexpected selector for mojo "
+                "binding result");
+            return;
+          }
 
-  // Create an instance for this note so the highlight can be shown in the page,
-  // and add it to the page's note manager. The instance's initialization does
-  // not need to be awaited, since the highlight's rect is already known.
-  auto instance = std::make_unique<UserNoteInstance>(
-      partial_note_raw->GetSafeRef(), manager, rect);
-  UserNoteInstance* instance_raw = instance.get();
-  manager->AddNoteInstance(std::move(instance), base::DoNothing());
+          if (agent_remote.is_valid() == selected_text.empty()) {
+            mojo::ReportBadMessage(
+                "User note creation received unexpected text for mojo binding "
+                "result");
+            return;
+          }
 
-  // Finally, notify the UI layer that it should start the note creation UX for
-  // this note. The UI layer will eventually call either `OnNoteCreationDone` or
-  // `OnNoteCreationCancelled`, in which the partial note will be finalized or
-  // deleted, respectively.
-  UserNotesUI* ui = delegate_->GetUICoordinatorForFrame(frame);
-  DCHECK(ui);
-  ui->StartNoteCreation(instance_raw);
+          service->InitializeNewNoteForCreation(
+              document, /*is_page_level=*/false, std::move(host_receiver),
+              std::move(agent_remote), serialized_selector, selected_text);
+        },
+        // SafeRef is safe since the service owns the UserNoteManager which
+        // owns the mojo binding so if we receive this callback both manager
+        // and service must still be live.
+        weak_ptr_factory_.GetSafeRef(), frame->GetWeakDocumentPtr());
+
+    manager->note_agent_container()->CreateAgentFromSelection(
+        blink::mojom::AnnotationType::kUserNote,
+        std::move(create_agent_callback));
+  } else {
+    InitializeNewNoteForCreation(frame->GetWeakDocumentPtr(),
+                                 /*is_page_level=*/true, mojo::NullReceiver(),
+                                 mojo::NullRemote(),
+                                 /*serialized_selector=*/"",
+                                 /*selected_text=*/std::u16string());
+  }
 }
 
 void UserNoteService::OnWebHighlightFocused(const base::UnguessableToken& id,
@@ -257,6 +268,118 @@
                            weak_ptr_factory_.GetWeakPtr(), all_frames));
 }
 
+void UserNoteService::InitializeNewNoteForCreation(
+    content::WeakDocumentPtr document,
+    bool is_page_level,
+    mojo::PendingReceiver<blink::mojom::AnnotationAgentHost> host_receiver,
+    mojo::PendingRemote<blink::mojom::AnnotationAgent> agent_remote,
+    const std::string& serialized_selector,
+    const std::u16string& selected_text) {
+  content::RenderFrameHost* frame = document.AsRenderFrameHostIfValid();
+  if (!frame)
+    return;
+
+  UserNoteManager* manager = UserNoteManager::GetForPage(frame->GetPage());
+  DCHECK(manager);
+
+  // If attachment succeeded, the returned mojo endpoints must all be valid and
+  // the selector/text must be non empty. If attachment failed (or wasn't
+  // attempted since the note is a kPage type) these will all be invalid/empty.
+  bool has_renderer_agent = agent_remote.is_valid();
+
+  DCHECK_EQ(has_renderer_agent, host_receiver.is_valid());
+  DCHECK_NE(has_renderer_agent, serialized_selector.empty());
+  DCHECK_NE(has_renderer_agent, selected_text.empty());
+
+  // If this is a page-level note, we must not have a renderer agent. If we
+  // received a renderer agent, it must be a text-level note.
+  DCHECK(!is_page_level || !has_renderer_agent);
+  DCHECK(!has_renderer_agent || !is_page_level);
+
+  // If this is a text-targeted note and we didn't receive back an agent,
+  // selector generation must have failed. For now, simply abort.
+  // TODO(crbug.com/1313967): Decide how to handle the case where a selector
+  // for the selected text couldn't be generated. (
+  if (!is_page_level && !has_renderer_agent)
+    return;
+
+  // TODO(bokan): The original_text is used in UI where it's converted to
+  // UTF-16, however, the model currently uses std::string which assumes it's
+  // UTF-8. Convert the model to UTF-16 and remove this conversion.
+  std::string original_text = base::UTF16ToUTF8(selected_text);
+
+  auto target = std::make_unique<UserNoteTarget>(
+      is_page_level ? UserNoteTarget::TargetType::kPage
+                    : UserNoteTarget::TargetType::kPageText,
+      original_text, GURL(frame->GetLastCommittedURL()), serialized_selector);
+
+  // TODO(gujen): This partial note creation logic will be moved to an API
+  // exposed by the storage layer in order to keep the creation of UserNote
+  // models centralized. However, until the storage layer is finished, manually
+  // create a partial note here.
+  base::Time now = base::Time::Now();
+  int note_version = 1;
+  auto metadata = std::make_unique<UserNoteMetadata>(now, now, note_version);
+  auto body = std::make_unique<UserNoteBody>(/*plain_text_value=*/"");
+
+  auto partial_note = std::make_unique<UserNote>(
+      base::UnguessableToken::Create(), std::move(metadata), std::move(body),
+      std::move(target));
+
+  std::unique_ptr<UserNoteInstance> instance =
+      UserNoteInstance::Create(partial_note->GetSafeRef(), manager);
+
+  // When attachment completes the instance will have received its rect on the
+  // page so the UI note creation flow can begin at that point. Note, this
+  // callback is guaranteed to be invoked after the current method returns (so
+  // after the creation map is updated and instance added to its manager).
+  auto attachment_finished_callback = base::BindOnce(
+      [](base::SafeRef<UserNoteService> service,
+         content::WeakDocumentPtr document, UserNoteInstance& instance) {
+        content::RenderFrameHost* frame = document.AsRenderFrameHostIfValid();
+
+        // TODO(bokan): delegate_ can be nullptr in unit tests - should we mock
+        // it?
+        if (!service->delegate_ || !frame)
+          return;
+
+        // Finally, notify the UI layer that it should start the note creation
+        // UX for this note. The UI layer will eventually call either
+        // `OnNoteCreationDone` or `OnNoteCreationCancelled`, in which the
+        // partial note will be finalized or deleted, respectively.
+        if (UserNotesUI* ui =
+                service->delegate_->GetUICoordinatorForFrame(frame)) {
+          ui->StartNoteCreation(&instance);
+        }
+      },
+      // SafeRef is safe for the service since it owns the manager which owns
+      // the instance.
+      weak_ptr_factory_.GetSafeRef(), frame->GetWeakDocumentPtr(),
+      // std::ref is safe since it owns the mojo endpoint which will cause this
+      // invocation (so if it's deleted this callback won't be invoked).
+      std::ref(*instance));
+
+  if (!is_page_level) {
+    DCHECK(has_renderer_agent);
+    instance->BindToHighlight(std::move(host_receiver), std::move(agent_remote),
+                              std::move(attachment_finished_callback));
+    // Silence use-after-move warning; if this path is taken, we want to pass a
+    // null callback in AddNoteInstance.
+    attachment_finished_callback = base::NullCallback();
+  }
+
+  // Store the partial note model into the creation map (not the model map)
+  // until it is finalized.
+  UserNoteService::ModelMapEntry entry(std::move(partial_note));
+  entry.managers.emplace(manager);
+  DCHECK(creation_map_.find(entry.model->id()) == creation_map_.end())
+      << "Attempted to create a partial note that already exists";
+  creation_map_.emplace(entry.model->id(), std::move(entry));
+
+  manager->AddNoteInstance(std::move(instance),
+                           std::move(attachment_finished_callback));
+}
+
 void UserNoteService::OnNoteMetadataFetchedForNavigation(
     const std::vector<content::RenderFrameHost*>& all_frames,
     const content::RenderFrameHost* navigated_frame,
@@ -320,9 +443,10 @@
     const auto& creation_entry_it = creation_map_.find(id);
     const auto& model_entry_it = model_map_.find(id);
 
-    if (new_note_it == new_notes.end()) {
-      // This note was modified; simply update the existing model in the model
-      // map.
+    if (new_note_it == new_notes.end() || model_entry_it != model_map_.end()) {
+      // Either this note was updated or the URL it is attached to was already
+      // loaded in another tab. Either way, its model already exists in the
+      // model map, so simply update it with the latest model.
       DCHECK(creation_entry_it == creation_map_.end());
       DCHECK(model_entry_it != model_map_.end());
       model_entry_it->second.model->Update(std::move(note));
@@ -351,9 +475,11 @@
   // Now that the creation and model maps have been updated, apply all the diffs
   // to propagate the changes to the webpages and UI.
   for (std::unique_ptr<FrameUserNoteChanges>& diff : note_changes) {
-    diff->Apply(base::BindOnce(&UserNoteService::OnFrameChangesApplied,
-                               weak_ptr_factory_.GetWeakPtr(), diff->id()));
+    FrameUserNoteChanges* diff_raw = diff.get();
     note_changes_in_progress_.emplace(diff->id(), std::move(diff));
+    diff_raw->Apply(base::BindOnce(&UserNoteService::OnFrameChangesApplied,
+                                   weak_ptr_factory_.GetWeakPtr(),
+                                   diff_raw->id()));
   }
 }
 
diff --git a/components/user_notes/browser/user_note_service.h b/components/user_notes/browser/user_note_service.h
index b2d87f06..e43cc0c3 100644
--- a/components/user_notes/browser/user_note_service.h
+++ b/components/user_notes/browser/user_note_service.h
@@ -21,6 +21,10 @@
 #include "components/user_notes/interfaces/user_note_storage.h"
 #include "components/user_notes/interfaces/user_notes_ui_delegate.h"
 #include "components/user_notes/model/user_note.h"
+#include "content/public/browser/weak_document_ptr.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "third_party/blink/public/mojom/annotation/annotation.mojom.h"
 
 class UserNoteUICoordinatorTest;
 
@@ -28,10 +32,6 @@
 class RenderFrameHost;
 }  // namespace content
 
-namespace gfx {
-class Rect;
-}  // namespace gfx
-
 namespace user_notes {
 
 class FrameUserNoteChanges;
@@ -83,14 +83,8 @@
 
   // Called by a note manager when the user selects "Add a note" from the
   // associated page's context menu. Kicks off the note creation process.
-  // TODO(gujen) and TODO(bokan): Make the renderer notify the manager and the
-  // manager call this method. Also consider pre-creating the agent on the
-  // renderer side so it is immediately ready when the browser side receives the
-  // request.
   void OnAddNoteRequested(content::RenderFrameHost* frame,
-                          std::string original_text,
-                          std::string selector,
-                          gfx::Rect rect);
+                          bool has_selected_text);
 
   // Called by a note manager when a user selects a web highlight in the page.
   // This causes the associated note to become focused in the UserNotesUI.
@@ -138,6 +132,14 @@
   FRIEND_TEST_ALL_PREFIXES(UserNoteServiceTest, OnNoteModelsFetched);
   FRIEND_TEST_ALL_PREFIXES(UserNoteServiceTest, OnFrameChangesApplied);
 
+  void InitializeNewNoteForCreation(
+      content::WeakDocumentPtr document,
+      bool is_page_level,
+      mojo::PendingReceiver<blink::mojom::AnnotationAgentHost> host_receiver,
+      mojo::PendingRemote<blink::mojom::AnnotationAgent> agent_remote,
+      const std::string& serialized_selector,
+      const std::u16string& selected_text);
+
   // Private helpers used when processing note storage changes. Marked virtual
   // for tests to override.
   virtual void OnNoteMetadataFetchedForNavigation(
diff --git a/components/user_notes/browser/user_note_service_unittest.cc b/components/user_notes/browser/user_note_service_unittest.cc
index 11eb2cb8..5aa130b 100644
--- a/components/user_notes/browser/user_note_service_unittest.cc
+++ b/components/user_notes/browser/user_note_service_unittest.cc
@@ -1088,4 +1088,77 @@
   EXPECT_EQ(note_service_->note_changes_in_progress_.size(), 0u);
 }
 
+// Verify the creation flow is started and a partial note inserted into the
+// creation map when "Add Note" is requested (as it would be from the context
+// menu). The creation should begin synchronously when there's no selection
+// since the renderer doesn't need to create a highlight and selector.
+TEST_F(UserNoteServiceTest, OnAddNoteRequestedWithoutSelection) {
+  // Initial setup.
+  ConfigureNewManager();
+
+  // Verify initial setup.
+  ASSERT_EQ(web_contents_list_.size(), 1ul);
+  ASSERT_EQ(ModelMapSize(), 2u);
+  ASSERT_EQ(CreationMapSize(), 0u);
+
+  content::RenderFrameHost* rfh = web_contents_list_[0]->GetPrimaryMainFrame();
+
+  // Simulate the "Add Note" context menu item being invoked while there's no
+  // selection in the renderer.
+  note_service_->OnAddNoteRequested(rfh, /*has_selected_text=*/false);
+
+  // Since there's no selection, a new partial note should be added to the
+  // creation map synchronously. The model map should be unchanged.
+  EXPECT_EQ(CreationMapSize(), 1u);
+  EXPECT_EQ(ModelMapSize(), 2u);
+}
+
+// Verify the creation flow is started when "Add Note" is requested with a
+// selection in the renderer.
+TEST_F(UserNoteServiceTest, OnAddNoteRequestedWithSelection) {
+  MockAnnotationAgentContainer container;
+
+  // Initial setup.
+  UserNoteManager* manager = ConfigureNewManager(&container);
+
+  // Verify initial setup.
+  ASSERT_TRUE(container.is_bound());
+  ASSERT_EQ(web_contents_list_.size(), 1ul);
+  ASSERT_EQ(ModelMapSize(), 2u);
+  ASSERT_EQ(CreationMapSize(), 0u);
+
+  content::RenderFrameHost* rfh = web_contents_list_[0]->GetPrimaryMainFrame();
+
+  // Simulate the "Add Note" context menu item being invoked while there's no
+  // selection in the renderer.
+  note_service_->OnAddNoteRequested(rfh, /*has_selected_text=*/true);
+
+  // Since there's a selection, a partial note won't be created until the
+  // renderer replies with a selector.
+  EXPECT_EQ(CreationMapSize(), 0u);
+  EXPECT_EQ(ModelMapSize(), 2u);
+
+  mojo::Remote<blink::mojom::AnnotationAgentHost> host;
+  MockAnnotationAgent agent;
+  EXPECT_CALL(container,
+              CreateAgentFromSelection(blink::mojom::AnnotationType::kUserNote,
+                                       testing::_))
+      .WillOnce(
+          [&](blink::mojom::AnnotationType type,
+              MockAnnotationAgentContainer::CreateAgentFromSelectionCallback
+                  cb) {
+            std::move(cb).Run(host.BindNewPipeAndPassReceiver(),
+                              agent.BindNewPipeAndPassRemote(),
+                              /*serialized_selector=*/"FOO",
+                              /*selected_text=*/std::u16string(u"FOO"));
+          });
+  manager->note_agent_container().FlushForTesting();
+  testing::Mock::VerifyAndClearExpectations(&container);
+
+  // Now that the renderer replied, a new partial note should be added to the
+  // creation map.
+  EXPECT_EQ(CreationMapSize(), 1u);
+  EXPECT_EQ(ModelMapSize(), 2u);
+}
+
 }  // namespace user_notes
diff --git a/components/user_notes/browser/user_note_utils_unittest.cc b/components/user_notes/browser/user_note_utils_unittest.cc
index 1f5f761..c0ca8b335 100644
--- a/components/user_notes/browser/user_note_utils_unittest.cc
+++ b/components/user_notes/browser/user_note_utils_unittest.cc
@@ -309,8 +309,8 @@
             note_service_->model_map_.find(token_it->second);
         UserNote* model = note_entry_it->second.model.get();
         note_manager->instance_map_.emplace(
-            model->id(), std::make_unique<UserNoteInstance>(model->GetSafeRef(),
-                                                            note_manager));
+            model->id(),
+            UserNoteInstance::Create(model->GetSafeRef(), note_manager));
       }
     }
 
diff --git a/components/user_notes/storage/user_note_database.cc b/components/user_notes/storage/user_note_database.cc
index ab34c11..73eb0516 100644
--- a/components/user_notes/storage/user_note_database.cc
+++ b/components/user_notes/storage/user_note_database.cc
@@ -238,21 +238,19 @@
   if (!create_note.Run())
     return false;
 
-  if (model->target().type() == UserNoteTarget::TargetType::kPageText) {
-    sql::Statement notes_text_target(db_.GetCachedStatement(
-        SQL_FROM_HERE,
-        "INSERT INTO notes_text_target(note_id, original_text, selector) "
-        "VALUES(?,?,?)"));
-    if (!notes_text_target.is_valid())
-      return false;
+  sql::Statement notes_text_target(db_.GetCachedStatement(
+      SQL_FROM_HERE,
+      "INSERT INTO notes_text_target(note_id, original_text, selector) "
+      "VALUES(?,?,?)"));
+  if (!notes_text_target.is_valid())
+    return false;
 
-    notes_text_target.BindString(0, model->id().ToString());
-    notes_text_target.BindString(1, model->target().original_text());
-    notes_text_target.BindString(2, model->target().selector());
+  notes_text_target.BindString(0, model->id().ToString());
+  notes_text_target.BindString(1, model->target().original_text());
+  notes_text_target.BindString(2, model->target().selector());
 
-    if (!notes_text_target.Run())
-      return false;
-  }
+  if (!notes_text_target.Run())
+    return false;
 
   sql::Statement notes_body(db_.GetCachedStatement(
       SQL_FROM_HERE,
diff --git a/components/variations/variations_request_scheduler.cc b/components/variations/variations_request_scheduler.cc
index 23aa828..d3c89ff 100644
--- a/components/variations/variations_request_scheduler.cc
+++ b/components/variations/variations_request_scheduler.cc
@@ -9,15 +9,48 @@
 #include "base/strings/string_number_conversions.h"
 #include "build/build_config.h"
 #include "components/variations/variations_associated_data.h"
+#include "components/variations/variations_switches.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace variations {
+namespace {
+
+// The minimum time between consecutive variations seed fetches.
+constexpr base::TimeDelta kVariationsSeedFetchIntervalMinimum =
+    base::Minutes(1);
+
+// Returns the variations seed fetch interval specified through the
+// |kVariationsSeedFetchInterval| switch. The value returned is subject to a
+// minimum value, |kVariationsSeedFetchIntervalMinimum|. If no overridden value
+// is specified, or if it is malformed, an empty optional is returned.
+absl::optional<base::TimeDelta> GetOverriddenFetchPeriod() {
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+
+  const std::string switch_value =
+      command_line->GetSwitchValueASCII(switches::kVariationsSeedFetchInterval);
+
+  if (switch_value.empty())
+    return absl::nullopt;
+
+  int overridden_period;
+  if (!base::StringToInt(switch_value, &overridden_period)) {
+    LOG(DFATAL) << "Malformed value for --"
+                << switches::kVariationsSeedFetchInterval << ". "
+                << "Expected int, got: " << switch_value;
+    return absl::nullopt;
+  }
+
+  return std::max(base::Minutes(overridden_period),
+                  kVariationsSeedFetchIntervalMinimum);
+}
+
+}  // namespace
 
 VariationsRequestScheduler::VariationsRequestScheduler(
     const base::RepeatingClosure& task)
     : task_(task) {}
 
-VariationsRequestScheduler::~VariationsRequestScheduler() {
-}
+VariationsRequestScheduler::~VariationsRequestScheduler() = default;
 
 void VariationsRequestScheduler::Start() {
   task_.Run();
@@ -33,10 +66,19 @@
 void VariationsRequestScheduler::ScheduleFetchShortly() {
   // Reset the regular timer to avoid it triggering soon after.
   Reset();
-  // The delay before attempting a fetch shortly, in minutes.
-  const int kFetchShortlyDelayMinutes = 5;
-  one_shot_timer_.Start(FROM_HERE, base::Minutes(kFetchShortlyDelayMinutes),
-                        task_);
+  // The delay before attempting a fetch shortly.
+  base::TimeDelta fetch_shortly_delay = base::Minutes(5);
+
+  // If there is a fetch interval specified in the command line, and it is
+  // shorter than |fetch_shortly_delay|, use it instead.
+  absl::optional<base::TimeDelta> overridden_period =
+      GetOverriddenFetchPeriod();
+  if (overridden_period.has_value()) {
+    fetch_shortly_delay =
+        std::min(fetch_shortly_delay, overridden_period.value());
+  }
+
+  one_shot_timer_.Start(FROM_HERE, fetch_shortly_delay, task_);
 }
 
 void VariationsRequestScheduler::OnAppEnterForeground() {
@@ -44,6 +86,12 @@
 }
 
 base::TimeDelta VariationsRequestScheduler::GetFetchPeriod() const {
+  // The fetch interval can be overridden through the command line.
+  absl::optional<base::TimeDelta> overridden_period =
+      GetOverriddenFetchPeriod();
+  if (overridden_period.has_value())
+    return overridden_period.value();
+
   // The fetch interval can be overridden by a variation param.
   std::string period_min_str =
       GetVariationParamValue("VariationsServiceControl", "fetch_period_min");
diff --git a/components/variations/variations_request_scheduler_mobile.cc b/components/variations/variations_request_scheduler_mobile.cc
index b33d3cd..445941c 100644
--- a/components/variations/variations_request_scheduler_mobile.cc
+++ b/components/variations/variations_request_scheduler_mobile.cc
@@ -4,8 +4,10 @@
 
 #include "components/variations/variations_request_scheduler_mobile.h"
 
+#include "base/command_line.h"
 #include "components/prefs/pref_service.h"
 #include "components/variations/pref_names.h"
+#include "components/variations/variations_switches.h"
 
 namespace variations {
 
@@ -21,8 +23,7 @@
     PrefService* local_state)
     : VariationsRequestScheduler(task), local_state_(local_state) {}
 
-VariationsRequestSchedulerMobile::~VariationsRequestSchedulerMobile() {
-}
+VariationsRequestSchedulerMobile::~VariationsRequestSchedulerMobile() = default;
 
 void VariationsRequestSchedulerMobile::Start() {
   // Check the time of the last request. If it has been longer than the fetch
@@ -31,7 +32,7 @@
   // enough for the timer to fire.
   const base::Time last_fetch_time =
       local_state_->GetTime(prefs::kVariationsLastFetchTime);
-  if (base::Time::Now() > last_fetch_time + GetFetchPeriod()) {
+  if (ShouldFetchSeed(last_fetch_time)) {
     last_request_time_ = base::Time::Now();
     task().Run();
   }
@@ -44,7 +45,7 @@
   // Verify we haven't just attempted a fetch (which has not completed). This
   // is mainly used to verify we don't trigger a second fetch for the
   // OnAppEnterForeground right after startup.
-  if (base::Time::Now() < last_request_time_ + GetFetchPeriod())
+  if (!ShouldFetchSeed(last_request_time_))
     return;
 
   // Since Start() launches a one-off execution, we can reuse it here. Also
@@ -55,6 +56,15 @@
                               &VariationsRequestSchedulerMobile::Start);
 }
 
+bool VariationsRequestSchedulerMobile::ShouldFetchSeed(
+    base::Time last_fetch_time) {
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kDisableVariationsSeedFetchThrottling)) {
+    return true;
+  }
+  return base::Time::Now() > (last_fetch_time + GetFetchPeriod());
+}
+
 // static
 VariationsRequestScheduler* VariationsRequestScheduler::Create(
     const base::RepeatingClosure& task,
diff --git a/components/variations/variations_request_scheduler_mobile.h b/components/variations/variations_request_scheduler_mobile.h
index c82b9ee..51d8bf0 100644
--- a/components/variations/variations_request_scheduler_mobile.h
+++ b/components/variations/variations_request_scheduler_mobile.h
@@ -47,6 +47,12 @@
   FRIEND_TEST_ALL_PREFIXES(VariationsRequestSchedulerMobileTest,
                            OnAppEnterForegroundOnStartup);
 
+  // Determines whether we should fetch a seed depending on how much time has
+  // passed since the last seed fetch. If the
+  // |kDisableVariationsSeedFetchThrottling| command line switch is present,
+  // this always returns true.
+  bool ShouldFetchSeed(base::Time last_fetch_time);
+
   // The local state instance that provides the last fetch time.
   raw_ptr<PrefService> local_state_;
 
diff --git a/components/variations/variations_switches.cc b/components/variations/variations_switches.cc
index e01559a..e265dfad 100644
--- a/components/variations/variations_switches.cc
+++ b/components/variations/variations_switches.cc
@@ -13,6 +13,13 @@
 // Disable variations safe mode.
 const char kDisableVariationsSafeMode[] = "disable-variations-safe-mode";
 
+// Disables throttling for fetching the variations seed on mobile platforms. The
+// seed will be fetched on startup and every time the app enters the foreground,
+// regardless of the time passed in between the fetches. On Desktop, this switch
+// has no effect (the seed is fetched periodically instead).
+const char kDisableVariationsSeedFetchThrottling[] =
+    "disable-variations-seed-fetch-throttling";
+
 // TODO(asvitkine): Consider removing or renaming this functionality.
 // Enables the benchmarking extensions.
 const char kEnableBenchmarking[] = "enable-benchmarking";
@@ -75,6 +82,10 @@
 // encrypted.
 const char kVariationsInsecureServerURL[] = "variations-insecure-server-url";
 
+// Override the time interval between each variation seed fetches. Unit is in
+// minutes. The minimum is 1 minute. The default is 30 minutes.
+const char kVariationsSeedFetchInterval[] = "variations-seed-fetch-interval";
+
 // Enables delta-compression when fetching a new seed via the "first run" code
 // path on Android.
 const char kEnableFinchSeedDeltaCompression[] =
diff --git a/components/variations/variations_switches.h b/components/variations/variations_switches.h
index b063023..2d63e28 100644
--- a/components/variations/variations_switches.h
+++ b/components/variations/variations_switches.h
@@ -18,6 +18,8 @@
 COMPONENT_EXPORT(VARIATIONS)
 extern const char kDisableVariationsSafeMode[];
 COMPONENT_EXPORT(VARIATIONS)
+extern const char kDisableVariationsSeedFetchThrottling[];
+COMPONENT_EXPORT(VARIATIONS)
 extern const char kEnableBenchmarking[];
 COMPONENT_EXPORT(VARIATIONS)
 extern const char kEnableFieldTrialTestingConfig[];
@@ -38,6 +40,8 @@
 COMPONENT_EXPORT(VARIATIONS)
 extern const char kVariationsInsecureServerURL[];
 COMPONENT_EXPORT(VARIATIONS)
+extern const char kVariationsSeedFetchInterval[];
+COMPONENT_EXPORT(VARIATIONS)
 extern const char kEnableFinchSeedDeltaCompression[];
 
 }  // namespace switches
diff --git a/components/viz/common/quads/compositor_frame_metadata.h b/components/viz/common/quads/compositor_frame_metadata.h
index acd850f3f..3a35eaff 100644
--- a/components/viz/common/quads/compositor_frame_metadata.h
+++ b/components/viz/common/quads/compositor_frame_metadata.h
@@ -93,7 +93,7 @@
   // This color is usually obtained from the background color of the <body>
   // element. It can be used for filling in gutter areas around the frame when
   // it's too small to fill the box the parent reserved for it.
-  SkColor root_background_color = SK_ColorWHITE;
+  SkColor4f root_background_color = SkColors::kWhite;
 
   std::vector<ui::LatencyInfo> latency_info;
 
diff --git a/components/viz/common/quads/compositor_frame_metadata_unittest.cc b/components/viz/common/quads/compositor_frame_metadata_unittest.cc
index 7535e55e..c2f21f4 100644
--- a/components/viz/common/quads/compositor_frame_metadata_unittest.cc
+++ b/components/viz/common/quads/compositor_frame_metadata_unittest.cc
@@ -65,7 +65,7 @@
   metadata.content_color_usage = gfx::ContentColorUsage::kHDR;
   metadata.may_contain_video = true;
   metadata.is_resourceless_software_draw_with_scroll_or_animation = true;
-  metadata.root_background_color = SK_ColorBLUE;
+  metadata.root_background_color = SkColors::kBlue;
   metadata.latency_info.emplace_back(ui::SourceEventType::KEY_PRESS);
   metadata.referenced_surfaces.emplace_back(
       SurfaceId(frame_sink_id, local_id1), SurfaceId(frame_sink_id, local_id2));
diff --git a/components/viz/service/compositor_frame_fuzzer/compositor_frame_fuzzer.proto b/components/viz/service/compositor_frame_fuzzer/compositor_frame_fuzzer.proto
index 388181e..74baa9e 100644
--- a/components/viz/service/compositor_frame_fuzzer/compositor_frame_fuzzer.proto
+++ b/components/viz/service/compositor_frame_fuzzer/compositor_frame_fuzzer.proto
@@ -50,8 +50,16 @@
   required sint32 sorting_context_id = 9;
 }
 
+// SolidColorDrawQuad defaults to white
+message Color {
+  required float r = 1 [default = 1];
+  required float g = 2 [default = 1];
+  required float b = 3 [default = 1];
+  required float a = 4 [default = 1];
+}
+
 message SolidColorDrawQuad {
-  required fixed32 color = 1 [default = 0xffffffff];
+  required Color color = 1;
   required bool force_anti_aliasing_off = 2;
 }
 
@@ -60,7 +68,7 @@
 
   // Allocate an SkBitmap from these values and pass the ResourceId
   // to the TileDrawQuad.
-  required fixed32 texture_color = 2;
+  required Color texture_color = 2;
   required Size texture_size = 3;
 
   // TODO(kylechar): enable fuzzing gfx::RectF
diff --git a/components/viz/service/compositor_frame_fuzzer/compositor_frame_fuzzer_util.cc b/components/viz/service/compositor_frame_fuzzer/compositor_frame_fuzzer_util.cc
index 21002e7..f0da106d 100644
--- a/components/viz/service/compositor_frame_fuzzer/compositor_frame_fuzzer_util.cc
+++ b/components/viz/service/compositor_frame_fuzzer/compositor_frame_fuzzer_util.cc
@@ -66,6 +66,10 @@
   return transform;
 }
 
+SkColor4f GetColorFromProtobuf(const proto::Color& color) {
+  return SkColor4f{color.r(), color.g(), color.b(), color.a()};
+}
+
 // Mutates a gfx::Rect to ensure width and height are both at least min_size.
 // Use in case e.g. a 0-width/height Rect would cause a validation error on
 // deserialization.
@@ -145,7 +149,7 @@
   // Allocate and map memory for a bitmap filled with |color| and appends it to
   // |allocated_bitmaps|. Performs no checks to ensure that the bitmap will fit
   // in memory (see TryReserveBitmapBytes).
-  FuzzedBitmap* AllocateFuzzedBitmap(const gfx::Size& size, SkColor color);
+  FuzzedBitmap* AllocateFuzzedBitmap(const gfx::Size& size, SkColor4f color);
 
   // Number of bytes that have already been reserved for the allocation of
   // specific bitmaps/textures.
@@ -243,7 +247,7 @@
   ConfigureSharedQuadState(shared_quad_state, quad_spec);
   auto* quad = pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
   quad->SetNew(shared_quad_state, rect, visible_rect,
-               SkColor4f::FromColor(quad_spec.solid_color_quad().color()),
+               GetColorFromProtobuf(quad_spec.solid_color_quad().color()),
                quad_spec.solid_color_quad().force_anti_aliasing_off());
 }
 
@@ -264,8 +268,8 @@
     return;
   }
 
-  FuzzedBitmap* fuzzed_bitmap =
-      AllocateFuzzedBitmap(tile_size, quad_spec.tile_quad().texture_color());
+  FuzzedBitmap* fuzzed_bitmap = AllocateFuzzedBitmap(
+      tile_size, GetColorFromProtobuf(quad_spec.tile_quad().texture_color()));
   TransferableResource transferable_resource =
       TransferableResource::MakeSoftware(fuzzed_bitmap->id, fuzzed_bitmap->size,
                                          RGBA_8888);
@@ -392,7 +396,7 @@
 
 FuzzedBitmap* FuzzedCompositorFrameBuilder::AllocateFuzzedBitmap(
     const gfx::Size& size,
-    SkColor color) {
+    SkColor4f color) {
   SharedBitmapId shared_bitmap_id = SharedBitmap::GenerateId();
   base::MappedReadOnlyRegion shm =
       bitmap_allocation::AllocateSharedBitmap(size, RGBA_8888);
diff --git a/components/viz/service/compositor_frame_fuzzer/text_format_seed_corpus/1_quad_renderpass.asciipb b/components/viz/service/compositor_frame_fuzzer/text_format_seed_corpus/1_quad_renderpass.asciipb
index 4bf77d0..a5e9825 100644
--- a/components/viz/service/compositor_frame_fuzzer/text_format_seed_corpus/1_quad_renderpass.asciipb
+++ b/components/viz/service/compositor_frame_fuzzer/text_format_seed_corpus/1_quad_renderpass.asciipb
@@ -25,7 +25,12 @@
       height: 400
     }
     solid_color_quad: {
-      color: 0xffffffff
+      color: {
+        r: 1
+        g: 1
+        b: 1
+        a: 1
+      }
       force_anti_aliasing_off: false
     }
     sqs: {
diff --git a/components/viz/service/compositor_frame_fuzzer/text_format_seed_corpus/2_quad_renderpass.asciipb b/components/viz/service/compositor_frame_fuzzer/text_format_seed_corpus/2_quad_renderpass.asciipb
index 8f0e52b..aa7a65fa 100644
--- a/components/viz/service/compositor_frame_fuzzer/text_format_seed_corpus/2_quad_renderpass.asciipb
+++ b/components/viz/service/compositor_frame_fuzzer/text_format_seed_corpus/2_quad_renderpass.asciipb
@@ -25,7 +25,12 @@
       height: 400
     }
     solid_color_quad: {
-      color: 0xffffffff
+      color: {
+        r: 1
+        g: 1
+        b: 1
+        a: 1
+      }
       force_anti_aliasing_off: false
     }
     sqs: {
@@ -75,7 +80,12 @@
     }
     tile_quad: {
       needs_blending: true
-      texture_color: 0xff000000
+      texture_color: {
+        r: 0
+        g: 0
+        b: 0
+        a: 1
+      }
       texture_size: {
         width: 620
         height: 400
diff --git a/components/viz/service/compositor_frame_fuzzer/text_format_seed_corpus/nested_render_pass_draw_quads.asciipb b/components/viz/service/compositor_frame_fuzzer/text_format_seed_corpus/nested_render_pass_draw_quads.asciipb
index e028dda..5e5b46c 100644
--- a/components/viz/service/compositor_frame_fuzzer/text_format_seed_corpus/nested_render_pass_draw_quads.asciipb
+++ b/components/viz/service/compositor_frame_fuzzer/text_format_seed_corpus/nested_render_pass_draw_quads.asciipb
@@ -81,7 +81,12 @@
                       height: 300
                     }
                     solid_color_quad: {
-                      color: 0xFF0070FF
+                      color: {
+                        r: 0
+                        g: 0.439
+                        b: 1
+                        a: 1
+                      }
                       force_anti_aliasing_off: false
                     }
                   }
@@ -138,7 +143,12 @@
                       height: 300
                     }
                     solid_color_quad: {
-                      color: 0xFF9970FF
+                      color: {
+                        r: 0.6
+                        g: 0.439
+                        b: 1
+                        a: 1
+                      }
                       force_anti_aliasing_off: false
                     }
                   }
@@ -160,7 +170,12 @@
               height: 300
             }
             solid_color_quad: {
-              color: 0xFFFF0070
+              color: {
+                r: 1
+                g: 0
+                b: 0.439
+                a: 1
+              }
               force_anti_aliasing_off: false
             }
           }
diff --git a/components/viz/service/compositor_frame_fuzzer/text_format_seed_corpus/overlapping_quads_in_render_pass_draw_quad.asciipb b/components/viz/service/compositor_frame_fuzzer/text_format_seed_corpus/overlapping_quads_in_render_pass_draw_quad.asciipb
index 96dc650..a8d2bcbb 100644
--- a/components/viz/service/compositor_frame_fuzzer/text_format_seed_corpus/overlapping_quads_in_render_pass_draw_quad.asciipb
+++ b/components/viz/service/compositor_frame_fuzzer/text_format_seed_corpus/overlapping_quads_in_render_pass_draw_quad.asciipb
@@ -25,7 +25,12 @@
       height: 100
     }
     solid_color_quad: {
-      color: 0xFFFF0070
+      color: {
+        r: 1
+        g: 0
+        b: 0.439
+        a: 1
+      }
       force_anti_aliasing_off: false
     }
     sqs: {
@@ -74,7 +79,12 @@
       height: 100
     }
     solid_color_quad: {
-      color: 0xFF70FF00
+      color: {
+        r: 0.439
+        g: 1
+        b: 0
+        a: 1
+      }
       force_anti_aliasing_off: false
     }
     sqs: {
@@ -158,7 +168,12 @@
               height: 100
             }
             solid_color_quad: {
-              color: 0xFF0070FF
+              color: {
+                r: 0
+                g: 0.439
+                b: 1
+                a: 1
+              }
               force_anti_aliasing_off: false
             }
             sqs: {
@@ -207,7 +222,12 @@
               height: 100
             }
             solid_color_quad: {
-              color: 0xFF9970FF
+              color: {
+                r: 0.6
+                g: 0.439
+                b: 1
+                a: 1
+              }
               force_anti_aliasing_off: false
             }
             sqs: {
diff --git a/components/viz/service/compositor_frame_fuzzer/text_format_seed_corpus/solid_color_tiled_background_with_2_quads_on_top.asciipb b/components/viz/service/compositor_frame_fuzzer/text_format_seed_corpus/solid_color_tiled_background_with_2_quads_on_top.asciipb
index 33f304b..bfde18b 100644
--- a/components/viz/service/compositor_frame_fuzzer/text_format_seed_corpus/solid_color_tiled_background_with_2_quads_on_top.asciipb
+++ b/components/viz/service/compositor_frame_fuzzer/text_format_seed_corpus/solid_color_tiled_background_with_2_quads_on_top.asciipb
@@ -26,7 +26,12 @@
     }
     tile_quad: {
       needs_blending: false
-      texture_color: 0xffff0000
+      texture_color: {
+        r: 1
+        g: 0
+        b: 0
+        a: 1
+      }
       texture_size: {
         width: 100
         height: 100
@@ -88,7 +93,12 @@
       height: 100
     }
     solid_color_quad: {
-      color: 0xffffffff
+      color: {
+        r: 1
+        g: 1
+        b: 1
+        a: 1
+      }
       force_anti_aliasing_off: false
     }
     sqs: {
@@ -138,7 +148,12 @@
     }
     tile_quad: {
       needs_blending: false
-      texture_color: 0xff000000
+      texture_color: {
+        r: 0
+        g: 0
+        b: 0
+        a: 1
+      }
       texture_size: {
         width: 256
         height: 256
@@ -200,7 +215,12 @@
       height: 256
     }
     solid_color_quad: {
-      color: 0xff00ff00
+      color: {
+        r: 0
+        g: 1
+        b: 0
+        a: 1
+      }
       force_anti_aliasing_off: false
     }
     sqs: {
@@ -249,7 +269,12 @@
       height: 256
     }
     solid_color_quad: {
-      color: 0xff0000ff
+      color: {
+        r: 0
+        g: 0
+        b: 1
+        a: 1
+      }
       force_anti_aliasing_off: false
     }
     sqs: {
@@ -298,7 +323,12 @@
       height: 144
     }
     solid_color_quad: {
-      color: 0xff0000ff
+      color: {
+        r: 0
+        g: 0
+        b: 1
+        a: 1
+      }
       force_anti_aliasing_off: false
     }
     sqs: {
@@ -347,7 +377,12 @@
       height: 144
     }
     solid_color_quad: {
-      color: 0xff00ff00
+      color: {
+        r: 0
+        g: 1
+        b: 0
+        a: 1
+      }
       force_anti_aliasing_off: false
     }
     sqs: {
@@ -396,7 +431,12 @@
       height: 144
     }
     solid_color_quad: {
-      color: 0xff000000
+      color: {
+        r: 0
+        g: 0
+        b: 0
+        a: 1
+      }
       force_anti_aliasing_off: false
     }
     sqs: {
diff --git a/components/viz/service/display/surface_aggregator.cc b/components/viz/service/display/surface_aggregator.cc
index 5e032721..ad880441 100644
--- a/components/viz/service/display/surface_aggregator.cc
+++ b/components/viz/service/display/surface_aggregator.cc
@@ -649,11 +649,11 @@
         gfx::IntersectRects(fallback_rect, surface_quad->visible_rect);
 
     // TODO(crbug.com/1308932): CompositorFrameMetadata to SkColor4f
-    EmitGutterQuadsIfNecessary(
-        surface_quad->visible_rect, fallback_rect,
-        surface_quad->shared_quad_state, target_transform, surface_clip_rect,
-        SkColor4f::FromColor(fallback_frame.metadata.root_background_color),
-        dest_pass, mask_filter_info);
+    EmitGutterQuadsIfNecessary(surface_quad->visible_rect, fallback_rect,
+                               surface_quad->shared_quad_state,
+                               target_transform, surface_clip_rect,
+                               fallback_frame.metadata.root_background_color,
+                               dest_pass, mask_filter_info);
   }
 
   EmitSurfaceContent(*resolved_frame, parent_device_scale_factor, surface_quad,
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_unittest.cc b/components/viz/service/display_embedder/skia_output_surface_impl_unittest.cc
index 8d69440..055416a3 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_unittest.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_unittest.cc
@@ -31,7 +31,7 @@
 namespace {
 
 constexpr gfx::Rect kSurfaceRect(0, 0, 100, 100);
-constexpr SkColor kOutputColor = SK_ColorRED;
+constexpr SkColor4f kOutputColor = SkColors::kRed;
 
 }  // namespace
 
@@ -131,7 +131,7 @@
   SkBitmap expected;
   expected.allocPixels(SkImageInfo::MakeN32Premul(
       output_rect.width(), output_rect.height(), color_space.ToSkColorSpace()));
-  expected.eraseColor(kOutputColor);
+  expected.eraseColor(kOutputColor, /*colorSpace=*/nullptr);
 
   EXPECT_TRUE(cc::MatchesBitmap(result_bitmap, expected,
                                 cc::ExactPixelComparator(false)));
diff --git a/components/viz/service/frame_sinks/video_capture/video_capture_overlay_unittest.cc b/components/viz/service/frame_sinks/video_capture/video_capture_overlay_unittest.cc
index 5a0788b..5d8d130 100644
--- a/components/viz/service/frame_sinks/video_capture/video_capture_overlay_unittest.cc
+++ b/components/viz/service/frame_sinks/video_capture/video_capture_overlay_unittest.cc
@@ -83,13 +83,13 @@
     // Test colors have been chosen to exercise different opacities,
     // intensities, and color channels; to confirm all aspects of the "SrcOver"
     // image blending algorithms are working properly.
-    constexpr SkColor kTestImageBackground =
-        SkColorSetARGB(0xff, 0xff, 0xff, 0xff);
-    constexpr SkColor kTestImageColors[4] = {
-        SkColorSetARGB(0xaa, 0xff, 0x00, 0x00),
-        SkColorSetARGB(0xbb, 0x00, 0xee, 0x00),
-        SkColorSetARGB(0xcc, 0x00, 0x00, 0x77),
-        SkColorSetARGB(0xdd, 0x66, 0x66, 0x00),
+    constexpr SkColor4f kTestImageBackground =
+        SkColor4f{1.0f, 1.0f, 1.0f, 1.0f};
+    constexpr SkColor4f kTestImageColors[4] = {
+        SkColor4f{1.0f, 0.0f, 0.0f, 0.667f},
+        SkColor4f{0.0f, 0.933f, 0.0f, 0.733f},
+        SkColor4f{0.0f, 0.0f, 0.467f, 0.8f},
+        SkColor4f{0.4f, 0.4f, 0.0f, 0.867f},
     };
     constexpr SkIRect kTestImageColorRects[4] = {
         SkIRect::MakeXYWH(4, 2, 4, 4), SkIRect::MakeXYWH(16, 2, 4, 4),
@@ -107,8 +107,7 @@
       const size_t idx = (i + cycle) % std::size(kTestImageColors);
       SkPaint paint;
       paint.setBlendMode(SkBlendMode::kSrc);
-      paint.setColor(SkColor4f::FromColor(kTestImageColors[idx]),
-                     info.colorSpace());
+      paint.setColor(kTestImageColors[idx], info.colorSpace());
       canvas.drawIRect(kTestImageColorRects[i], paint);
     }
 
diff --git a/components/viz/test/gl_scaler_test_util.cc b/components/viz/test/gl_scaler_test_util.cc
index 3c9b503..2882d6b 100644
--- a/components/viz/test/gl_scaler_test_util.cc
+++ b/components/viz/test/gl_scaler_test_util.cc
@@ -56,7 +56,7 @@
 
   // Set all pixels to a color that should not exist in the result. Later, a
   // sanity-check will ensure all pixels have been overwritten.
-  constexpr SkColor kDeadColor = SkColorSetARGB(0xde, 0xad, 0xbe, 0xef);
+  constexpr SkColor4f kDeadColor = SkColor4f{0.678f, 0.745f, 0.937f, 0.871f};
   result.eraseColor(kDeadColor);
 
   // Set the pixels corresponding to each color bar.
@@ -68,7 +68,7 @@
   // the color bars.
   for (int y = 0; y < result.height(); ++y) {
     for (int x = 0; x < result.width(); ++x) {
-      if (result.getColor(x, y) == kDeadColor) {
+      if (result.getColor4f(x, y) == kDeadColor) {
         NOTREACHED() << "TEST BUG: Error creating SMPTE test image. Bad size ("
                      << size.ToString() << ")?";
         return result;
@@ -84,7 +84,7 @@
                                                const gfx::Size& src_size,
                                                const gfx::Rect& src_rect,
                                                int fuzzy_pixels,
-                                               int* max_color_diff) {
+                                               float* max_color_diff) {
   if (image.width() <= 0 || image.height() <= 0) {
     return false;
   }
@@ -95,7 +95,7 @@
       static_cast<SkScalar>(image.width()) / src_rect.width();
   const SkScalar scale_y =
       static_cast<SkScalar>(image.height()) / src_rect.height();
-  int measured_max_diff = 0;
+  float measured_max_diff = 0.0f;
   for (const auto& cr : GetScaledSMPTEColorBars(src_size)) {
     const SkIRect offset_rect = cr.rect.makeOffset(-offset_x, -offset_y);
     const SkIRect rect =
@@ -109,17 +109,12 @@
       for (int x = std::max(0, rect.fLeft),
                x_end = std::min(image.width(), rect.fRight);
            x < x_end; ++x) {
-        const SkColor actual = image.getColor(x, y);
+        const SkColor4f actual = image.getColor4f(x, y);
         measured_max_diff =
-            std::max({measured_max_diff,
-                      std::abs(static_cast<int>(SkColorGetR(cr.color)) -
-                               static_cast<int>(SkColorGetR(actual))),
-                      std::abs(static_cast<int>(SkColorGetG(cr.color)) -
-                               static_cast<int>(SkColorGetG(actual))),
-                      std::abs(static_cast<int>(SkColorGetB(cr.color)) -
-                               static_cast<int>(SkColorGetB(actual))),
-                      std::abs(static_cast<int>(SkColorGetA(cr.color)) -
-                               static_cast<int>(SkColorGetA(actual)))});
+            std::max({measured_max_diff, std::abs((cr.color.fR) - (actual.fR)),
+                      std::abs((cr.color.fG) - (actual.fG)),
+                      std::abs((cr.color.fB) - (actual.fB)),
+                      std::abs((cr.color.fA) - (actual.fA))});
       }
     }
   }
@@ -129,14 +124,14 @@
     *max_color_diff = measured_max_diff;
     return measured_max_diff <= threshold;
   }
-  return measured_max_diff == 0;
+  return measured_max_diff == 0.0f;
 }
 
 // static
 SkBitmap GLScalerTestUtil::CreateCyclicalTestImage(
     const gfx::Size& size,
     CyclicalPattern pattern,
-    const std::vector<SkColor>& cycle,
+    const std::vector<SkColor4f>& cycle,
     size_t rotation) {
   CHECK(!cycle.empty());
 
@@ -144,11 +139,11 @@
   // the rest of the code below.
   std::vector<uint32_t> cycle_as_rgba(cycle.size());
   for (size_t i = 0; i < cycle.size(); ++i) {
-    const SkColor color = cycle[(i + rotation) % cycle.size()];
-    cycle_as_rgba[i] = ((SkColorGetR(color) << kRedShift) |
-                        (SkColorGetG(color) << kGreenShift) |
-                        (SkColorGetB(color) << kBlueShift) |
-                        (SkColorGetA(color) << kAlphaShift));
+    const SkColor4f color = cycle[(i + rotation) % cycle.size()];
+    cycle_as_rgba[i] = ((static_cast<int>(color.fR * 255) << kRedShift) |
+                        (static_cast<int>(color.fG * 255) << kGreenShift) |
+                        (static_cast<int>(color.fB * 255) << kBlueShift) |
+                        (static_cast<int>(color.fA * 255) << kAlphaShift));
   }
 
   SkBitmap result = AllocateRGBABitmap(size);
@@ -399,35 +394,35 @@
 // linear gradient bar is defined as half solid 0-level black and half solid
 // full-intensity white).
 const ColorBar GLScalerTestUtil::kSMPTEColorBars[30] = {
-    {{0, 0, 240, 630}, SkColorSetRGB(0x66, 0x66, 0x66)},
-    {{240, 0, 445, 630}, SkColorSetRGB(0xbf, 0xbf, 0xbf)},
-    {{445, 0, 651, 630}, SkColorSetRGB(0xbf, 0xbf, 0x00)},
-    {{651, 0, 857, 630}, SkColorSetRGB(0x00, 0xbf, 0xbf)},
-    {{857, 0, 1063, 630}, SkColorSetRGB(0x00, 0xbf, 0x00)},
-    {{1063, 0, 1269, 630}, SkColorSetRGB(0xbf, 0x00, 0xbf)},
-    {{1269, 0, 1475, 630}, SkColorSetRGB(0xbf, 0x00, 0x00)},
-    {{1475, 0, 1680, 630}, SkColorSetRGB(0x00, 0x00, 0xbf)},
-    {{1680, 0, 1920, 630}, SkColorSetRGB(0x66, 0x66, 0x66)},
-    {{0, 630, 240, 720}, SkColorSetRGB(0x00, 0xff, 0xff)},
-    {{240, 630, 445, 720}, SkColorSetRGB(0x00, 0x21, 0x4c)},
-    {{445, 630, 1680, 720}, SkColorSetRGB(0xbf, 0xbf, 0xbf)},
-    {{1680, 630, 1920, 720}, SkColorSetRGB(0x00, 0x00, 0xff)},
-    {{0, 720, 240, 810}, SkColorSetRGB(0xff, 0xff, 0x00)},
-    {{240, 720, 445, 810}, SkColorSetRGB(0x32, 0x00, 0x6a)},
-    {{445, 720, 1063, 810}, SkColorSetRGB(0x00, 0x00, 0x00)},
-    {{1063, 720, 1680, 810}, SkColorSetRGB(0xff, 0xff, 0xff)},
-    {{1680, 720, 1920, 810}, SkColorSetRGB(0xff, 0x00, 0x00)},
-    {{0, 810, 240, 1080}, SkColorSetRGB(0x26, 0x26, 0x26)},
-    {{240, 810, 549, 1080}, SkColorSetRGB(0x00, 0x00, 0x00)},
-    {{549, 810, 960, 1080}, SkColorSetRGB(0xff, 0xff, 0xff)},
-    {{960, 810, 1131, 1080}, SkColorSetRGB(0x00, 0x00, 0x00)},
-    {{1131, 810, 1200, 1080}, SkColorSetRGB(0x00, 0x00, 0x00)},
-    {{1200, 810, 1268, 1080}, SkColorSetRGB(0x00, 0x00, 0x00)},
-    {{1268, 810, 1337, 1080}, SkColorSetRGB(0x05, 0x05, 0x05)},
-    {{1337, 810, 1405, 1080}, SkColorSetRGB(0x00, 0x00, 0x00)},
-    {{1405, 810, 1474, 1080}, SkColorSetRGB(0x0a, 0x0a, 0x0a)},
-    {{1474, 810, 1680, 1080}, SkColorSetRGB(0x00, 0x00, 0x00)},
-    {{1680, 810, 1920, 1080}, SkColorSetRGB(0x26, 0x26, 0x26)},
+    {{0, 240, 630}, SkColor4f{0.4f, 0.4f, 0.4f, 1.0f}},
+    {{240, 0, 445, 630}, SkColor4f{0.749f, 0.749f, 0.749f, 1.0f}},
+    {{0, 651, 445, 630}, SkColor4f{0.749f, 0.749f, 0.0f, 1.0f}},
+    {{0, 857, 651, 630}, SkColor4f{0.0f, 0.749f, 0.749f, 1.0f}},
+    {{0, 857, 630, 1063}, SkColor4f{0.0f, 0.749f, 0.0f, 1.0f}},
+    {{0, 1269, 630, 1063}, SkColor4f{0.749f, 0.0f, 0.749f, 1.0f}},
+    {{0, 1475, 1269, 630}, SkColor4f{0.749f, 0.0f, 0.0f, 1.0f}},
+    {{0, 1475, 1680, 630}, SkColor4f{0.0f, 0.0f, 0.749f, 1.0f}},
+    {{1680, 0, 1920, 630}, SkColor4f{0.4f, 0.4f, 0.4f, 1.0f}},
+    {{0, 240, 630, 720}, SkColor4f{0.0f, 1.0f, 1.0f, 1.0f}},
+    {{240, 445, 630, 720}, SkColor4f{0.0f, 0.129f, 0.298f, 1.0f}},
+    {{1680, 445, 630, 720}, SkColor4f{0.749f, 0.749f, 0.749f, 1.0f}},
+    {{1680, 1920, 630, 720}, SkColor4f{0.0f, 0.0f, 1.0f, 1.0f}},
+    {{0, 240, 810, 720}, SkColor4f{1.0f, 1.0f, 0.0f, 1.0f}},
+    {{240, 810, 445, 720}, SkColor4f{0.196f, 0.0f, 0.416f, 1.0f}},
+    {{720, 810, 445, 1063}, SkColor4f{0.0f, 0.0f, 0.0f, 1.0f}},
+    {{720, 810, 1680, 1063}, SkColor4f{1.0f, 1.0f, 1.0f, 1.0f}},
+    {{1680, 810, 1920, 720}, SkColor4f{1.0f, 0.0f, 0.0f, 1.0f}},
+    {{0, 240, 810, 1080}, SkColor4f{0.149f, 0.149f, 0.149f, 1.0f}},
+    {{240, 810, 1080, 549}, SkColor4f{0.0f, 0.0f, 0.0f, 1.0f}},
+    {{960, 810, 1080, 549}, SkColor4f{1.0f, 1.0f, 1.0f, 1.0f}},
+    {{960, 810, 1131, 1080}, SkColor4f{0.0f, 0.0f, 0.0f, 1.0f}},
+    {{1200, 810, 1131, 1080}, SkColor4f{0.0f, 0.0f, 0.0f, 1.0f}},
+    {{1200, 810, 1268, 1080}, SkColor4f{0.0f, 0.0f, 0.0f, 1.0f}},
+    {{1080, 1337, 810, 1268}, SkColor4f{0.02f, 0.02f, 0.02f, 1.0f}},
+    {{1080, 1337, 810, 1405}, SkColor4f{0.0f, 0.0f, 0.0f, 1.0f}},
+    {{1080, 1474, 810, 1405}, SkColor4f{0.039f, 0.039f, 0.039f, 1.0f}},
+    {{1680, 1474, 810, 1080}, SkColor4f{0.0f, 0.0f, 0.0f, 1.0f}},
+    {{1680, 810, 1080, 1920}, SkColor4f{0.149f, 0.149f, 0.149f, 1.0f}},
 };
 
 constexpr gfx::Size GLScalerTestUtil::kSMPTEFullSize;
diff --git a/components/viz/test/gl_scaler_test_util.h b/components/viz/test/gl_scaler_test_util.h
index 8e971acf..27e9420 100644
--- a/components/viz/test/gl_scaler_test_util.h
+++ b/components/viz/test/gl_scaler_test_util.h
@@ -30,7 +30,7 @@
  public:
   struct ColorBar {
     SkIRect rect;
-    SkColor color;
+    SkColor4f color;
   };
 
   // The patterns that can be created by CreateCyclicalTestImage().
@@ -67,14 +67,14 @@
                                       const gfx::Size& src_size,
                                       const gfx::Rect& src_rect,
                                       int fuzzy_pixels,
-                                      int* max_color_diff);
+                                      float* max_color_diff);
 
   // Returns an image of the given |size| with the colors in |cycle| used to
   // generate a striped or staggered |pattern|. |rotation| specifies which index
   // in the |cycle| to start with.
   static SkBitmap CreateCyclicalTestImage(const gfx::Size& size,
                                           CyclicalPattern pattern,
-                                          const std::vector<SkColor>& cycle,
+                                          const std::vector<SkColor4f>& cycle,
                                           size_t rotation);
 
   // Returns the RGB/YUV color spaces used by default when color space
diff --git a/content/browser/accessibility/dump_accessibility_node_browsertest.cc b/content/browser/accessibility/dump_accessibility_node_browsertest.cc
index 8bfc8e9..0afb4d2 100644
--- a/content/browser/accessibility/dump_accessibility_node_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_node_browsertest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/command_line.h"
+#include "base/containers/cxx20_erase_list.h"
 #include "base/files/file_util.h"
 #include "base/strings/escape.h"
 #include "content/browser/accessibility/dump_accessibility_browsertest_base.h"
@@ -93,16 +94,6 @@
     base::FilePath html_file = test_path.Append(base::FilePath(file_path));
     RunTest(html_file, "accessibility/html", FILE_PATH_LITERAL("node"));
   }
-
-  void RunMathMLTest(const base::FilePath::CharType* file_path) {
-    base::FilePath test_path = GetTestFilePath("accessibility", "mathml");
-    {
-      base::ScopedAllowBlockingForTesting allow_blocking;
-      ASSERT_TRUE(base::PathExists(test_path)) << test_path.LossyDisplayName();
-    }
-    base::FilePath mathml_file = test_path.Append(base::FilePath(file_path));
-    RunTest(mathml_file, "accessibility/mathml", FILE_PATH_LITERAL("node"));
-  }
 };
 
 class DumpAccessibilityAccNameTest : public DumpAccessibilityNodeTest {
@@ -139,8 +130,23 @@
   }
 };
 
+// Revert CL 3721405 once crbug.com/1260585 (implement MathML UIA) is fixed,
+// also see related crbug.com/1272996 (MathML tests fail on UIA).
+class DumpAccessibilityMathMLNodeTest : public DumpAccessibilityNodeTest {
+ public:
+  void RunMathMLTest(const base::FilePath::CharType* file_path) {
+    base::FilePath test_path = GetTestFilePath("accessibility", "mathml");
+    {
+      base::ScopedAllowBlockingForTesting allow_blocking;
+      ASSERT_TRUE(base::PathExists(test_path)) << test_path.LossyDisplayName();
+    }
+    base::FilePath mathml_file = test_path.Append(base::FilePath(file_path));
+    RunTest(mathml_file, "accessibility/mathml", FILE_PATH_LITERAL("node"));
+  }
+};
+
 class DumpAccessibilityNodeWithoutMathMLTest
-    : public DumpAccessibilityNodeTest {
+    : public DumpAccessibilityMathMLNodeTest {
  public:
   void SetUpCommandLine(base::CommandLine* command_line) override {
     DumpAccessibilityNodeTest::SetUpCommandLine(command_line);
@@ -157,6 +163,13 @@
   }
 };
 
+std::vector<ui::AXApiType::Type> TreeTestPassesExceptUIA() {
+  std::vector<ui::AXApiType::Type> passes =
+      ui::AXInspectTestHelper::TreeTestPasses();
+  base::Erase(passes, ui::AXApiType::kWinUIA);
+  return passes;
+}
+
 INSTANTIATE_TEST_SUITE_P(
     All,
     DumpAccessibilityNodeTest,
@@ -169,11 +182,15 @@
     ::testing::ValuesIn(ui::AXInspectTestHelper::TreeTestPasses()),
     TestPassToString());
 
-INSTANTIATE_TEST_SUITE_P(
-    All,
-    DumpAccessibilityNodeWithoutMathMLTest,
-    ::testing::ValuesIn(ui::AXInspectTestHelper::TreeTestPasses()),
-    TestPassToString());
+INSTANTIATE_TEST_SUITE_P(All,
+                         DumpAccessibilityNodeWithoutMathMLTest,
+                         ::testing::ValuesIn(TreeTestPassesExceptUIA()),
+                         TestPassToString());
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         DumpAccessibilityMathMLNodeTest,
+                         ::testing::ValuesIn(TreeTestPassesExceptUIA()),
+                         TestPassToString());
 
 // ARIA tests.
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityNodeTest, AccessibilityAriaScrollbar) {
@@ -190,103 +207,103 @@
 // MathML tests.
 //
 
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityNodeTest, MathMLAction) {
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityMathMLNodeTest, MathMLAction) {
   RunMathMLTest(FILE_PATH_LITERAL("maction.html"));
 }
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityNodeTest, MathMLAnnotation) {
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityMathMLNodeTest, MathMLAnnotation) {
   RunMathMLTest(FILE_PATH_LITERAL("annotation.html"));
 }
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityNodeTest, MathMLAnnotationXML) {
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityMathMLNodeTest, MathMLAnnotationXML) {
   RunMathMLTest(FILE_PATH_LITERAL("annotation-xml.html"));
 }
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityNodeTest, MathMLError) {
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityMathMLNodeTest, MathMLError) {
   RunMathMLTest(FILE_PATH_LITERAL("merror.html"));
 }
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityNodeTest, MathMLFraction) {
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityMathMLNodeTest, MathMLFraction) {
   RunMathMLTest(FILE_PATH_LITERAL("mfrac.html"));
 }
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityNodeTest, MathMLIdentifier) {
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityMathMLNodeTest, MathMLIdentifier) {
   RunMathMLTest(FILE_PATH_LITERAL("mi.html"));
 }
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityNodeTest, MathMLMath) {
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityMathMLNodeTest, MathMLMath) {
   RunMathMLTest(FILE_PATH_LITERAL("math.html"));
 }
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityNodeWithoutMathMLTest, MathMLMath) {
   RunMathMLTest(FILE_PATH_LITERAL("math-disabled.html"));
 }
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityNodeTest, MathMLMultiscripts) {
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityMathMLNodeTest, MathMLMultiscripts) {
   RunMathMLTest(FILE_PATH_LITERAL("mmultiscripts.html"));
 }
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityNodeTest, MathMLNone) {
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityMathMLNodeTest, MathMLNone) {
   RunMathMLTest(FILE_PATH_LITERAL("none.html"));
 }
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityNodeTest, MathMLNumber) {
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityMathMLNodeTest, MathMLNumber) {
   RunMathMLTest(FILE_PATH_LITERAL("mn.html"));
 }
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityNodeTest, MathMLOperator) {
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityMathMLNodeTest, MathMLOperator) {
   RunMathMLTest(FILE_PATH_LITERAL("mo.html"));
 }
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityNodeTest, MathMLOver) {
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityMathMLNodeTest, MathMLOver) {
   RunMathMLTest(FILE_PATH_LITERAL("mover.html"));
 }
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityNodeTest, MathMLPadded) {
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityMathMLNodeTest, MathMLPadded) {
   RunMathMLTest(FILE_PATH_LITERAL("mpadded.html"));
 }
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityNodeTest, MathMLPhantom) {
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityMathMLNodeTest, MathMLPhantom) {
   RunMathMLTest(FILE_PATH_LITERAL("mphantom.html"));
 }
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityNodeTest, MathMLPrescripts) {
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityMathMLNodeTest, MathMLPrescripts) {
   RunMathMLTest(FILE_PATH_LITERAL("mprescripts.html"));
 }
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityNodeTest, MathMLRoot) {
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityMathMLNodeTest, MathMLRoot) {
   RunMathMLTest(FILE_PATH_LITERAL("mroot.html"));
 }
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityNodeTest, MathMLRow) {
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityMathMLNodeTest, MathMLRow) {
   RunMathMLTest(FILE_PATH_LITERAL("mrow.html"));
 }
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityNodeTest, MathMLSemantics) {
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityMathMLNodeTest, MathMLSemantics) {
   RunMathMLTest(FILE_PATH_LITERAL("semantics.html"));
 }
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityNodeTest, MathMLSpace) {
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityMathMLNodeTest, MathMLSpace) {
   RunMathMLTest(FILE_PATH_LITERAL("mspace.html"));
 }
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityNodeTest, MathMLSquareRoot) {
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityMathMLNodeTest, MathMLSquareRoot) {
   RunMathMLTest(FILE_PATH_LITERAL("msqrt.html"));
 }
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityNodeTest, MathMLStringLiteral) {
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityMathMLNodeTest, MathMLStringLiteral) {
   RunMathMLTest(FILE_PATH_LITERAL("ms.html"));
 }
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityNodeTest, MathMLStyle) {
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityMathMLNodeTest, MathMLStyle) {
   RunMathMLTest(FILE_PATH_LITERAL("mstyle.html"));
 }
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityNodeTest, MathMLSub) {
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityMathMLNodeTest, MathMLSub) {
   RunMathMLTest(FILE_PATH_LITERAL("msub.html"));
 }
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityNodeTest, MathMLSubSup) {
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityMathMLNodeTest, MathMLSubSup) {
   RunMathMLTest(FILE_PATH_LITERAL("msubsup.html"));
 }
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityNodeTest, MathMLSup) {
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityMathMLNodeTest, MathMLSup) {
   RunMathMLTest(FILE_PATH_LITERAL("msup.html"));
 }
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityNodeTest, MathMLTable) {
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityMathMLNodeTest, MathMLTable) {
   RunMathMLTest(FILE_PATH_LITERAL("mtable.html"));
 }
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityNodeTest, MathMLTableCell) {
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityMathMLNodeTest, MathMLTableCell) {
   RunMathMLTest(FILE_PATH_LITERAL("mtd.html"));
 }
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityNodeTest, MathMLTableRow) {
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityMathMLNodeTest, MathMLTableRow) {
   RunMathMLTest(FILE_PATH_LITERAL("mtr.html"));
 }
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityNodeTest, MathMLText) {
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityMathMLNodeTest, MathMLText) {
   RunMathMLTest(FILE_PATH_LITERAL("mtext.html"));
 }
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityNodeTest, MathMLUnder) {
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityMathMLNodeTest, MathMLUnder) {
   RunMathMLTest(FILE_PATH_LITERAL("munder.html"));
 }
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityNodeTest, MathMLUnderOver) {
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityMathMLNodeTest, MathMLUnderOver) {
   RunMathMLTest(FILE_PATH_LITERAL("munderover.html"));
 }
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityNodeTest, MathMLUnknown) {
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityMathMLNodeTest, MathMLUnknown) {
   RunMathMLTest(FILE_PATH_LITERAL("unknown.html"));
 }
 
diff --git a/content/browser/attribution_reporting/attributions_browsertest.cc b/content/browser/attribution_reporting/attributions_browsertest.cc
index 2dc0b3e4..79509fc 100644
--- a/content/browser/attribution_reporting/attributions_browsertest.cc
+++ b/content/browser/attribution_reporting/attributions_browsertest.cc
@@ -1050,8 +1050,16 @@
   content::test::PrerenderTestHelper prerender_helper_;
 };
 
-IN_PROC_BROWSER_TEST_F(AttributionsPrerenderBrowserTest,
-                       NoConversionsOnPrerender) {
+// TODO(crbug.com/1344264): these tests are flaky on most release bots.
+#if defined(NDEBUG)
+#define ATTRIBUTION_PRERENDER_BROWSER_TEST(TEST_NAME) \
+  IN_PROC_BROWSER_TEST_F(AttributionsPrerenderBrowserTest, DISABLED_##TEST_NAME)
+#else
+#define ATTRIBUTION_PRERENDER_BROWSER_TEST(TEST_NAME) \
+  IN_PROC_BROWSER_TEST_F(AttributionsPrerenderBrowserTest, TEST_NAME)
+#endif
+
+ATTRIBUTION_PRERENDER_BROWSER_TEST(NoConversionsOnPrerender) {
   ExpectedReportWaiter expected_report(
       GURL("https://a.test/.well-known/attribution-reporting/"
            "report-event-attribution"),
@@ -1108,8 +1116,7 @@
   EXPECT_FALSE(expected_report.HasRequest());
 }
 
-IN_PROC_BROWSER_TEST_F(AttributionsPrerenderBrowserTest,
-                       ConversionsRegisteredOnActivatedPrerender) {
+ATTRIBUTION_PRERENDER_BROWSER_TEST(ConversionsRegisteredOnActivatedPrerender) {
   ExpectedReportWaiter expected_report(
       GURL("https://a.test/.well-known/attribution-reporting/"
            "report-event-attribution"),
diff --git a/content/browser/interest_group/auction_url_loader_factory_proxy.cc b/content/browser/interest_group/auction_url_loader_factory_proxy.cc
index 321fc4e..868a18de9 100644
--- a/content/browser/interest_group/auction_url_loader_factory_proxy.cc
+++ b/content/browser/interest_group/auction_url_loader_factory_proxy.cc
@@ -106,6 +106,7 @@
   new_request.redirect_mode = network::mojom::RedirectMode::kError;
   new_request.credentials_mode = network::mojom::CredentialsMode::kOmit;
   new_request.request_initiator = frame_origin_;
+  new_request.enable_load_timing = url_request.enable_load_timing;
 
   // CORS is not needed.
   //
diff --git a/content/browser/renderer_host/frame_tree_browsertest.cc b/content/browser/renderer_host/frame_tree_browsertest.cc
index 4113b40..fe78150 100644
--- a/content/browser/renderer_host/frame_tree_browsertest.cc
+++ b/content/browser/renderer_host/frame_tree_browsertest.cc
@@ -1023,7 +1023,9 @@
   ~FencedFrameTreeBrowserTest() override {
     // Shutdown the server explicitly so that there is no race with the
     // destruction of cookie_headers_map_ and invocation of RequestMonitor.
-    EXPECT_TRUE(https_server_.ShutdownAndWaitUntilComplete());
+    if (https_server_.Started()) {
+      EXPECT_TRUE(https_server_.ShutdownAndWaitUntilComplete());
+    }
   }
 
   WebContentsImpl* web_contents() {
@@ -3361,85 +3363,362 @@
     https_server()->SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
   }
 
+  // An object representing a single step of a reportEvent test.
+  // First, we navigate the fenced frame to a new URL.
+  // Second, we call reportEvent and validate the results.
+  struct Step {
+    // Whether the navigation should be embedder-initiated or fenced-frame
+    // initiated.
+    bool is_embedder_initiated = false;
+    // Whether the navigation should be via a urn:uuid or a normal URL.
+    // (This should always be false when `!is_embedder_initiated`.
+    bool is_opaque = false;
+
+    struct Target {
+      // The origin for the navigation.
+      std::string origin;
+      // The path for the resource to load.
+      std::string path;
+    };
+
+    // The initial navigation target (may be redirected).
+    Target target;
+    // A list of redirects that the navigation should take. The last redirect
+    // target will be the ultimate destination of the navigation.
+    std::vector<Target> redirects;
+
+    // Whether the reportEvent should succeed.
+    bool should_have_metadata = false;
+  };
+
+  // A helper function for specifying reportEvent tests. Each step consists of a
+  // series of `Step`s specified above.
+  void RunTest(std::vector<Step>& steps) {
+    // In order to check events reported over the network, we register an HTTP
+    // response interceptor for each successful reportEvent request we expect.
+    // We register an additional one so that we can check for spurious requests
+    // at the end of the test.
+    EXPECT_TRUE(steps.size() > 0);
+    std::vector<std::unique_ptr<net::test_server::ControllableHttpResponse>>
+        responses;
+    std::vector<std::unique_ptr<net::test_server::ControllableHttpResponse>>
+        redirects;
+    for (size_t i = 0; i < steps.size() + 1; ++i) {
+      responses.emplace_back(
+          std::make_unique<net::test_server::ControllableHttpResponse>(
+              https_server(), "/_report_event_server.html"));
+    }
+    // We also register interceptors for redirections that we want to perform.
+    // Each redirect must be from a unique path so that messages aren't
+    // unintentionally intercepted and blocked.
+    {
+      std::set<std::string> paths;
+      for (auto& step : steps) {
+        ASSERT_FALSE(step.target.origin.empty());
+        ASSERT_FALSE(step.target.path.empty());
+        int redirect_index = 0;
+        for (auto& redirect_target : step.redirects) {
+          ASSERT_TRUE(paths.find(redirect_target.path) == paths.end());
+          ASSERT_FALSE(redirect_target.origin.empty());
+          ASSERT_FALSE(redirect_target.path.empty());
+          paths.insert(redirect_target.path);
+
+          // Intercept the previous navigation target in the chain.
+          std::string previous_path =
+              redirect_index ? step.redirects[redirect_index - 1].path
+                             : step.target.path;
+          redirects.emplace_back(
+              std::make_unique<net::test_server::ControllableHttpResponse>(
+                  https_server(), previous_path));
+          redirect_index++;
+        }
+      }
+    }
+    ASSERT_TRUE(https_server()->Start());
+
+    // Set up the embedder and a fenced frame.
+    GURL main_url = https_server()->GetURL("a.test", "/hello.html");
+    EXPECT_TRUE(NavigateToURL(shell(), main_url));
+    FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
+                              ->GetPrimaryFrameTree()
+                              .root();
+    EXPECT_TRUE(ExecJs(root,
+                       "var f = document.createElement('fencedframe');"
+                       "f.mode = 'opaque-ads';"
+                       "document.body.appendChild(f);"));
+    EXPECT_EQ(1U, root->child_count());
+    FrameTreeNode* fenced_frame_root_node =
+        GetFencedFrameRootNode(root->child_at(0));
+    EXPECT_TRUE(fenced_frame_root_node->IsFencedFrameRoot());
+    EXPECT_TRUE(fenced_frame_root_node->IsInFencedFrameTree());
+
+    // Create reporting metadata.
+    ReportingMetadata fenced_frame_reporting;
+    GURL reporting_url(
+        https_server()->GetURL("c.test", "/_report_event_server.html"));
+    fenced_frame_reporting
+        .metadata[blink::mojom::ReportingDestination::kBuyer]["click"] =
+        reporting_url;
+    // Get the urn mapping object.
+    FencedFrameURLMapping& url_mapping =
+        root->current_frame_host()->GetPage().fenced_frame_urls_map();
+
+    int navigation_index = 0;
+    int response_index = 0;
+    int redirect_index = 0;
+    for (auto& step : steps) {
+      // Configure the navigation.
+      GURL navigate_url =
+          https_server()->GetURL(step.target.origin, step.target.path);
+      GURL expect_url = navigate_url;
+      if (step.is_opaque) {
+        GURL urn_uuid =
+            url_mapping.AddFencedFrameURL(navigate_url, fenced_frame_reporting);
+        EXPECT_TRUE(urn_uuid.is_valid());
+        navigate_url = urn_uuid;
+      }
+
+      // Initiate the navigation.
+      TestFrameNavigationObserver observer(fenced_frame_root_node);
+      if (step.is_embedder_initiated) {
+        EXPECT_TRUE(ExecJs(root, JsReplace("f.src = $1", navigate_url)));
+      } else {
+        EXPECT_TRUE(ExecJs(fenced_frame_root_node,
+                           JsReplace("location.href = $1", navigate_url)));
+      }
+
+      // Redirect the navigation if relevant.
+      for (auto& redirect_target : step.redirects) {
+        GURL redirect_url = https_server()->GetURL(redirect_target.origin,
+                                                   redirect_target.path);
+        expect_url = redirect_url;
+        auto& redirect = *redirects[redirect_index];
+        redirect.WaitForRequest();
+        std::string redirect_response =
+            std::string("HTTP/1.1 302 Moved Temporarily\r\nLocation: ") +
+            redirect_url.spec() + std::string("\r\n\r\n");
+        redirect.Send(redirect_response);
+        redirect.Done();
+        redirect_index++;
+      }
+
+      // Check that the navigation worked as intended.
+      observer.WaitForCommit();
+      EXPECT_EQ(
+          expect_url,
+          fenced_frame_root_node->current_frame_host()->GetLastCommittedURL());
+      EXPECT_EQ(url::Origin::Create(expect_url),
+                fenced_frame_root_node->current_frame_host()
+                    ->GetLastCommittedOrigin());
+      navigation_index++;
+
+      // Perform the reportEvent call, with a unique body.
+      const char report_event_script[] = R"(
+        window.fence.reportEvent({
+          eventType: 'click',
+          eventData: 'click $1',
+          destination: ['buyer'],
+        });
+      )";
+      EXPECT_TRUE(ExecJs(fenced_frame_root_node,
+                         JsReplace(report_event_script, navigation_index)));
+
+      // If relevant, check that the event report succeeded.
+      if (step.should_have_metadata) {
+        auto& response = *responses[response_index];
+        response.WaitForRequest();
+        EXPECT_EQ(response.http_request()->content,
+                  JsReplace("click $1", navigation_index));
+        response.Done();
+        response_index++;
+      }
+    }
+
+    // Check for any spurious waiting reported events.
+    EXPECT_TRUE(ExecJs(root, JsReplace("f.src = $1", reporting_url)));
+    auto& response = *responses[response_index];
+    response.WaitForRequest();
+    EXPECT_EQ(response.http_request()->content, "");
+    response.Done();
+  }
+
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
-// Tests that the fenced frame with a urn:uuid commits the navigation with the
-// associated reporting metadata and `fence.reportEvent` sends the beacon to
-// the registered reporting url.
+// The simplest test case: URN navigation into reportEvent.
 IN_PROC_BROWSER_TEST_P(FencedFrameReportEventBrowserTest,
-                       FencedFrameReportingMetadata) {
-  net::test_server::ControllableHttpResponse response(https_server(),
-                                                      "/title2.html");
-  ASSERT_TRUE(https_server()->Start());
+                       FencedFrameReportEventEmbedderURNNavigation) {
+  std::vector<Step> config = {
+      {
+          .is_embedder_initiated = true,
+          .is_opaque = true,
+          .target = {"a.test", "/fenced_frames/title1.html"},
+          .should_have_metadata = true,
+      },
+  };
+  RunTest(config);
+}
 
-  GURL main_url = https_server()->GetURL("b.test", "/hello.html");
-  EXPECT_TRUE(NavigateToURL(shell(), main_url));
-  // It is safe to obtain the root frame tree node here, as it doesn't change.
-  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
-                            ->GetPrimaryFrameTree()
-                            .root();
+// Reporting metadata should persist across FF-initiated same-origin
+// navigations.
+IN_PROC_BROWSER_TEST_P(FencedFrameReportEventBrowserTest,
+                       FencedFrameReportEventFFSameOriginNavigation) {
+  std::vector<Step> config = {
+      {
+          .is_embedder_initiated = true,
+          .is_opaque = true,
+          .target = {"a.test", "/fenced_frames/title1.html"},
+          .should_have_metadata = true,
+      },
+      {
+          .target = {"a.test", "/fenced_frames/title1.html?foo"},
+          .should_have_metadata = true,
+      },
+  };
+  RunTest(config);
+}
 
-  EXPECT_TRUE(ExecJs(root,
-                     "var f = document.createElement('fencedframe');"
-                     "f.mode = 'opaque-ads';"
-                     "document.body.appendChild(f);"));
-  EXPECT_EQ(1U, root->child_count());
-  FrameTreeNode* fenced_frame_root_node =
-      GetFencedFrameRootNode(root->child_at(0));
+// Reporting metadata should be dropped upon cross-origin navigations,
+// but come back upon new URN navigations.
+IN_PROC_BROWSER_TEST_P(FencedFrameReportEventBrowserTest,
+                       FencedFrameReportEventFFCrossOriginNavigation) {
+  std::vector<Step> config = {
+      {
+          .is_embedder_initiated = true,
+          .is_opaque = true,
+          .target = {"a.test", "/fenced_frames/title1.html"},
+          .should_have_metadata = true,
+      },
+      {
+          .target = {"b.test", "/fenced_frames/title1.html"},
+          .should_have_metadata = false,
+      },
+      {
+          .is_embedder_initiated = true,
+          .is_opaque = true,
+          .target = {"a.test", "/fenced_frames/title1.html"},
+          .should_have_metadata = true,
+      },
+  };
+  RunTest(config);
+}
 
-  EXPECT_TRUE(fenced_frame_root_node->IsFencedFrameRoot());
-  EXPECT_TRUE(fenced_frame_root_node->IsInFencedFrameTree());
+// Embedder-initiated URL navigations should always be considered cross-origin.
+IN_PROC_BROWSER_TEST_P(FencedFrameReportEventBrowserTest,
+                       FencedFrameReportEventEmbedderURLNavigation) {
+  // In ShadowDOM, embedder-initiated navigations aren't given a unique
+  // initiator origin, so we have to disable this test.
+  // (Same-origin embedder-initiated navigations will be considered
+  // same-origin, and therefore the reporting metadata will remain.)
+  if (GetParam() ==
+      blink::features::FencedFramesImplementationType::kShadowDOM) {
+    return;
+  }
 
-  // Add reporting metadata.
-  ReportingMetadata fenced_frame_reporting;
-  GURL reporting_url(https_server()->GetURL("c.test", "/title2.html"));
-  fenced_frame_reporting.metadata[blink::mojom::ReportingDestination::kBuyer]
-                                 ["mouse interaction"] = reporting_url;
-  fenced_frame_reporting
-      .metadata[blink::mojom::ReportingDestination::kBuyer]["click"] =
-      https_server()->GetURL("c.test", "/title1.html");
+  std::vector<Step> config = {
+      {
+          .is_embedder_initiated = true,
+          .is_opaque = true,
+          .target = {"a.test", "/fenced_frames/title1.html"},
+          .should_have_metadata = true,
+      },
+      {
+          .is_embedder_initiated = true,
+          .is_opaque = false,
+          .target = {"a.test", "/fenced_frames/title1.html"},
+          .should_have_metadata = false,
+      },
+  };
+  RunTest(config);
+}
 
-  GURL https_url(
-      https_server()->GetURL("a.test", "/fenced_frames/title1.html"));
-  FencedFrameURLMapping& url_mapping =
-      root->current_frame_host()->GetPage().fenced_frame_urls_map();
-  GURL urn_uuid =
-      url_mapping.AddFencedFrameURL(https_url, fenced_frame_reporting);
-  EXPECT_TRUE(urn_uuid.is_valid());
+// Same-origin redirects in the initial URN navigation shouldn't affect
+// reporting metadata.
+IN_PROC_BROWSER_TEST_P(FencedFrameReportEventBrowserTest,
+                       FencedFrameReportEventEmbedderSameOriginRedirect) {
+  std::vector<Step> config = {
+      {
+          .is_embedder_initiated = true,
+          .is_opaque = true,
+          .target = {"a.test", "/fenced_frames/redirect1.html"},
+          .redirects =
+              {
+                  {"a.test", "/fenced_frames/redirect2.html"},
+                  {"a.test", "/fenced_frames/title1.html"},
+              },
+          .should_have_metadata = true,
+      },
+  };
+  RunTest(config);
+}
 
-  TestFencedFrameURLMappingResultObserver mapping_observer;
-  url_mapping.ConvertFencedFrameURNToURL(urn_uuid, &mapping_observer);
-  TestFrameNavigationObserver observer(fenced_frame_root_node);
+// Cross-origin redirects in the initial URN navigation shouldn't affect
+// reporting metadata either.
+IN_PROC_BROWSER_TEST_P(FencedFrameReportEventBrowserTest,
+                       FencedFrameReportEventEmbedderCrossOriginRedirect) {
+  std::vector<Step> config = {
+      {
+          .is_embedder_initiated = true,
+          .is_opaque = true,
+          .target = {"a.test", "/fenced_frames/redirect1.html"},
+          .redirects =
+              {
+                  {"b.test", "/fenced_frames/redirect2.html"},
+                  {"c.test", "/fenced_frames/title1.html"},
+              },
+          .should_have_metadata = true,
+      },
+  };
+  RunTest(config);
+}
 
-  std::string navigate_urn_script = JsReplace("f.src = $1;", urn_uuid);
-  EXPECT_EQ(urn_uuid.spec(), EvalJs(root, navigate_urn_script));
+// Metadata should be preserved if all URLs in an FF-initiated redirect chain
+// are same-origin.
+IN_PROC_BROWSER_TEST_P(FencedFrameReportEventBrowserTest,
+                       FencedFrameReportEventFFSameOriginRedirect) {
+  std::vector<Step> config = {
+      {
+          .is_embedder_initiated = true,
+          .is_opaque = true,
+          .target = {"a.test", "/fenced_frames/title1.html"},
+          .should_have_metadata = true,
+      },
+      {
+          .target = {"a.test", "/fenced_frames/redirect1.html"},
+          .redirects =
+              {
+                  {"a.test", "/fenced_frames/redirect2.html"},
+                  {"a.test", "/fenced_frames/title1.html?foo"},
+              },
+          .should_have_metadata = true,
+      },
+  };
+  RunTest(config);
+}
 
-  observer.WaitForCommit();
-  EXPECT_TRUE(mapping_observer.mapping_complete_observed());
-  EXPECT_EQ(reporting_url,
-            mapping_observer.reporting_metadata()
-                .metadata[blink::mojom::ReportingDestination::kBuyer]
-                         ["mouse interaction"]);
-
-  EXPECT_EQ(
-      https_url,
-      fenced_frame_root_node->current_frame_host()->GetLastCommittedURL());
-  EXPECT_EQ(
-      url::Origin::Create(https_url),
-      fenced_frame_root_node->current_frame_host()->GetLastCommittedOrigin());
-
-  std::string event_data = "this is a click";
-  EXPECT_TRUE(ExecJs(fenced_frame_root_node,
-                     JsReplace("window.fence.reportEvent({"
-                               "  eventType: 'mouse interaction',"
-                               "  eventData: $1,"
-                               "  destination: ['buyer']});",
-                               event_data)));
-
-  response.WaitForRequest();
-  EXPECT_EQ(response.http_request()->content, event_data);
+// Metadata should be dropped if any URLs in an FF-initiated redirect chain
+// are cross-origin.
+IN_PROC_BROWSER_TEST_P(FencedFrameReportEventBrowserTest,
+                       FencedFrameReportEventFFCrossOriginRedirect) {
+  std::vector<Step> config = {
+      {
+          .is_embedder_initiated = true,
+          .is_opaque = true,
+          .target = {"a.test", "/fenced_frames/title1.html"},
+          .should_have_metadata = true,
+      },
+      {
+          .target = {"a.test", "/fenced_frames/redirect1.html"},
+          .redirects =
+              {
+                  {"b.test", "/fenced_frames/redirect2.html"},
+                  {"a.test", "/fenced_frames/title1.html"},
+              },
+          .should_have_metadata = false,
+      },
+  };
+  RunTest(config);
 }
 
 // (Temporary test for FLEDGE iframe OT.)
diff --git a/content/browser/renderer_host/input/scroll_behavior_browsertest.cc b/content/browser/renderer_host/input/scroll_behavior_browsertest.cc
index 9854416..05d59aa 100644
--- a/content/browser/renderer_host/input/scroll_behavior_browsertest.cc
+++ b/content/browser/renderer_host/input/scroll_behavior_browsertest.cc
@@ -142,10 +142,10 @@
     if (enable_percent_based_scrolling.has_value() &&
         *enable_percent_based_scrolling) {
       scoped_feature_list.InitAndEnableFeature(
-          features::kPercentBasedScrolling);
+          features::kWindowsScrollingPersonality);
     } else {
       scoped_feature_list.InitAndDisableFeature(
-          features::kPercentBasedScrolling);
+          features::kWindowsScrollingPersonality);
     }
   }
 
diff --git a/content/browser/renderer_host/input/synthetic_input_browsertest.cc b/content/browser/renderer_host/input/synthetic_input_browsertest.cc
index 5d1b4ff..c3f5adb 100644
--- a/content/browser/renderer_host/input/synthetic_input_browsertest.cc
+++ b/content/browser/renderer_host/input/synthetic_input_browsertest.cc
@@ -41,10 +41,10 @@
   SyntheticInputTest() {
     if (GetParam()) {
       scoped_feature_list.InitAndEnableFeature(
-          features::kPercentBasedScrolling);
+          features::kWindowsScrollingPersonality);
     } else {
       scoped_feature_list.InitAndDisableFeature(
-          features::kPercentBasedScrolling);
+          features::kWindowsScrollingPersonality);
     }
   }
 
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
index 2c5262e1..c7ceca5 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -1737,14 +1737,16 @@
 
 TEST_F(RenderWidgetHostViewAuraTest, TimerBasedWheelEventPhaseInfo) {
   base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndDisableFeature(features::kPercentBasedScrolling);
+  scoped_feature_list.InitAndDisableFeature(
+      features::kWindowsScrollingPersonality);
   RunTimerBasedWheelEventPhaseInfoTest(false);
 }
 
 TEST_F(RenderWidgetHostViewAuraTest,
        TimerBasedWheelEventPhaseInfoWithPercentBasedScrolling) {
   base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(features::kPercentBasedScrolling);
+  scoped_feature_list.InitAndEnableFeature(
+      features::kWindowsScrollingPersonality);
   RunTimerBasedWheelEventPhaseInfoTest(true);
 }
 
diff --git a/content/browser/shared_storage/shared_storage_browsertest.cc b/content/browser/shared_storage/shared_storage_browsertest.cc
index 94975bf..46f455c56 100644
--- a/content/browser/shared_storage/shared_storage_browsertest.cc
+++ b/content/browser/shared_storage/shared_storage_browsertest.cc
@@ -1746,8 +1746,11 @@
       fenced_frame_root_node->current_frame_host()->GetLastCommittedURL());
 }
 
+// Currently, Shared Storage is not allowed in Fenced Frames as Fenced Frames
+// disallow all permissions policies. This may change in the future.
+// https://github.com/WICG/fenced-frame/issues/44
 IN_PROC_BROWSER_TEST_P(SharedStorageFencedFrameInteractionBrowserTest,
-                       SelectURLNotAllowedInFencedFrame) {
+                       SharedStorageNotAllowedInFencedFrame) {
   GURL main_frame_url = https_server()->GetURL("a.test", kSimplePagePath);
 
   EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));
@@ -1757,21 +1760,17 @@
 
   FrameTreeNode* fenced_frame_node = CreateFencedFrame(fenced_frame_url);
 
-  EXPECT_TRUE(ExecJs(fenced_frame_node, R"(
-      sharedStorage.worklet.addModule('/shared_storage/simple_module.js');
-    )"));
-
-  EXPECT_EQ(1u, test_worklet_host_manager().GetAttachedWorkletHostsCount());
-  EXPECT_EQ(0u, test_worklet_host_manager().GetKeepAliveWorkletHostsCount());
-
   EvalJsResult result = EvalJs(fenced_frame_node, R"(
-      sharedStorage.selectURL(
-          'test-url-selection-operation',
-          [{url: "title0.html"}], {data: {'mockResult': 0}});
+      sharedStorage.worklet.addModule('/shared_storage/simple_module.js');
     )");
 
-  EXPECT_TRUE(result.error.find("sharedStorage.selectURL() is not allowed in "
-                                "fenced frame") != std::string::npos);
+  EXPECT_THAT(
+      result.error,
+      testing::HasSubstr("The \"shared-storage\" Permissions Policy denied the "
+                         "method on window.sharedStorage."));
+
+  EXPECT_EQ(0u, test_worklet_host_manager().GetAttachedWorkletHostsCount());
+  EXPECT_EQ(0u, test_worklet_host_manager().GetKeepAliveWorkletHostsCount());
 }
 
 IN_PROC_BROWSER_TEST_P(SharedStorageFencedFrameInteractionBrowserTest,
diff --git a/content/browser/webauth/authenticator_impl_unittest.cc b/content/browser/webauth/authenticator_impl_unittest.cc
index cf554e3e..8953fcc 100644
--- a/content/browser/webauth/authenticator_impl_unittest.cc
+++ b/content/browser/webauth/authenticator_impl_unittest.cc
@@ -940,28 +940,23 @@
 // also in the second, and with the same value.
 static void CheckJSONIsSubsetOfJSON(base::StringPiece subset_str,
                                     base::StringPiece test_str) {
-  std::unique_ptr<base::Value> subset(
-      base::JSONReader::ReadDeprecated(subset_str));
+  absl::optional<base::Value> subset = base::JSONReader::Read(subset_str);
   ASSERT_TRUE(subset);
   ASSERT_TRUE(subset->is_dict());
-  std::unique_ptr<base::Value> test(base::JSONReader::ReadDeprecated(test_str));
+  const base::Value::Dict& subset_dict = subset->GetDict();
+  absl::optional<base::Value> test = base::JSONReader::Read(test_str);
   ASSERT_TRUE(test);
   ASSERT_TRUE(test->is_dict());
+  const base::Value::Dict& test_dict = test->GetDict();
 
-  for (auto item : subset->DictItems()) {
-    base::Value* test_value = test->FindKey(item.first);
+  for (auto item : subset_dict) {
+    const base::Value* test_value = test_dict.Find(item.first);
     if (test_value == nullptr) {
       ADD_FAILURE() << item.first << " does not exist in the test dictionary";
       continue;
     }
 
-    if (!item.second.Equals(test_value)) {
-      std::string want, got;
-      ASSERT_TRUE(base::JSONWriter::Write(item.second, &want));
-      ASSERT_TRUE(base::JSONWriter::Write(*test_value, &got));
-      ADD_FAILURE() << "Value of " << item.first << " is unequal: want " << want
-                    << " got " << got;
-    }
+    EXPECT_EQ(item.second, *test_value);
   }
 }
 
diff --git a/content/browser/webrtc/webrtc_internals_message_handler.cc b/content/browser/webrtc/webrtc_internals_message_handler.cc
index acfc83a0e..5498e3d4 100644
--- a/content/browser/webrtc/webrtc_internals_message_handler.cc
+++ b/content/browser/webrtc/webrtc_internals_message_handler.cc
@@ -32,40 +32,40 @@
 }
 
 void WebRTCInternalsMessageHandler::RegisterMessages() {
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getStandardStats",
       base::BindRepeating(&WebRTCInternalsMessageHandler::OnGetStandardStats,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getLegacyStats",
       base::BindRepeating(&WebRTCInternalsMessageHandler::OnGetLegacyStats,
                           base::Unretained(this)));
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "enableAudioDebugRecordings",
       base::BindRepeating(
           &WebRTCInternalsMessageHandler::OnSetAudioDebugRecordingsEnabled,
           base::Unretained(this), true));
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "disableAudioDebugRecordings",
       base::BindRepeating(
           &WebRTCInternalsMessageHandler::OnSetAudioDebugRecordingsEnabled,
           base::Unretained(this), false));
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "enableEventLogRecordings",
       base::BindRepeating(
           &WebRTCInternalsMessageHandler::OnSetEventLogRecordingsEnabled,
           base::Unretained(this), true));
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "disableEventLogRecordings",
       base::BindRepeating(
           &WebRTCInternalsMessageHandler::OnSetEventLogRecordingsEnabled,
           base::Unretained(this), false));
 
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "finishedDOMLoad",
       base::BindRepeating(&WebRTCInternalsMessageHandler::OnDOMLoadDone,
                           base::Unretained(this)));
@@ -89,21 +89,22 @@
 }
 
 void WebRTCInternalsMessageHandler::OnGetStandardStats(
-    const base::ListValue* /* unused_list */) {
+    const base::Value::List& /* unused_list */) {
   for (auto* host : PeerConnectionTrackerHost::GetAllHosts()) {
     host->GetStandardStats();
   }
 }
 
 void WebRTCInternalsMessageHandler::OnGetLegacyStats(
-    const base::ListValue* /* unused_list */) {
+    const base::Value::List& /* unused_list */) {
   for (auto* host : PeerConnectionTrackerHost::GetAllHosts()) {
     host->GetLegacyStats();
   }
 }
 
 void WebRTCInternalsMessageHandler::OnSetAudioDebugRecordingsEnabled(
-    bool enable, const base::ListValue* /* unused_list */) {
+    bool enable,
+    const base::Value::List& /* unused_list */) {
   if (enable) {
     webrtc_internals_->EnableAudioDebugRecordings(web_ui()->GetWebContents());
   } else {
@@ -113,7 +114,7 @@
 
 void WebRTCInternalsMessageHandler::OnSetEventLogRecordingsEnabled(
     bool enable,
-    const base::ListValue* /* unused_list */) {
+    const base::Value::List& /* unused_list */) {
   if (!webrtc_internals_->CanToggleEventLogRecordings()) {
     LOG(WARNING) << "Cannot toggle WebRTC event logging.";
     return;
@@ -127,8 +128,8 @@
   }
 }
 
-void WebRTCInternalsMessageHandler::OnDOMLoadDone(const base::ListValue* args) {
-  base::Value::ConstListView args_list = args->GetListDeprecated();
+void WebRTCInternalsMessageHandler::OnDOMLoadDone(
+    const base::Value::List& args_list) {
   CHECK_GE(args_list.size(), 1u);
 
   const std::string callback_id = args_list[0].GetString();
@@ -136,15 +137,16 @@
 
   webrtc_internals_->UpdateObserver(this);
 
-  base::Value params(base::Value::Type::DICTIONARY);
-  params.SetBoolKey("audioDebugRecordingsEnabled",
-                    webrtc_internals_->IsAudioDebugRecordingsEnabled());
-  params.SetBoolKey("eventLogRecordingsEnabled",
-                    webrtc_internals_->IsEventLogRecordingsEnabled());
-  params.SetBoolKey("eventLogRecordingsToggleable",
-                    webrtc_internals_->CanToggleEventLogRecordings());
+  base::Value::Dict params;
+  params.Set("audioDebugRecordingsEnabled",
+             webrtc_internals_->IsAudioDebugRecordingsEnabled());
+  params.Set("eventLogRecordingsEnabled",
+             webrtc_internals_->IsEventLogRecordingsEnabled());
+  params.Set("eventLogRecordingsToggleable",
+             webrtc_internals_->CanToggleEventLogRecordings());
 
-  ResolveJavascriptCallback(base::Value(callback_id), std::move(params));
+  ResolveJavascriptCallback(base::Value(callback_id),
+                            base::Value(std::move(params)));
 }
 
 void WebRTCInternalsMessageHandler::OnUpdate(const std::string& event_name,
diff --git a/content/browser/webrtc/webrtc_internals_message_handler.h b/content/browser/webrtc/webrtc_internals_message_handler.h
index 60008177..7c6bdd2 100644
--- a/content/browser/webrtc/webrtc_internals_message_handler.h
+++ b/content/browser/webrtc/webrtc_internals_message_handler.h
@@ -7,14 +7,11 @@
 
 #include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
+#include "base/values.h"
 #include "content/browser/webrtc/webrtc_internals_ui_observer.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/web_ui_message_handler.h"
 
-namespace base {
-class ListValue;
-}  // namespace base
-
 namespace content {
 
 class RenderFrameHost;
@@ -49,19 +46,17 @@
   RenderFrameHost* GetWebRTCInternalsHost();
 
   // Javascript message handler.
-  void OnGetStandardStats(const base::ListValue* list);
-  void OnGetLegacyStats(const base::ListValue* list);
+  void OnGetStandardStats(const base::Value::List& list);
+  void OnGetLegacyStats(const base::Value::List& list);
   void OnSetAudioDebugRecordingsEnabled(bool enable,
-                                        const base::ListValue* list);
-  void OnSetEventLogRecordingsEnabled(bool enable, const base::ListValue* list);
-  void OnDOMLoadDone(const base::ListValue* list);
+                                        const base::Value::List& list);
+  void OnSetEventLogRecordingsEnabled(bool enable,
+                                      const base::Value::List& list);
+  void OnDOMLoadDone(const base::Value::List& list);
 
   // WebRTCInternalsUIObserver override.
   void OnUpdate(const std::string& event_name,
                 const base::Value* event_data) override;
-
-  // Executes Javascript command.
-  void ExecuteJavascriptCommand(const char* command, const base::Value* args);
 };
 
 }  // namespace content
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index e63251b..7d20dbe 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -251,7 +251,7 @@
     {wf::EnablePaymentApp, features::kServiceWorkerPaymentApps},
     {wf::EnablePaymentRequest, features::kWebPayments},
     {wf::EnablePaymentRequestBasicCard, features::kPaymentRequestBasicCard},
-    {wf::EnablePercentBasedScrolling, features::kPercentBasedScrolling},
+    {wf::EnablePercentBasedScrolling, features::kWindowsScrollingPersonality},
     {wf::EnablePeriodicBackgroundSync, features::kPeriodicBackgroundSync},
     {wf::EnablePictureInPicture, media::kPictureInPicture},
     {wf::EnablePictureInPictureV2, features::kPictureInPictureV2,
diff --git a/content/public/android/java/src/org/chromium/content/browser/GestureListenerManagerImpl.java b/content/public/android/java/src/org/chromium/content/browser/GestureListenerManagerImpl.java
index c860ec8..2e6dfd6 100644
--- a/content/public/android/java/src/org/chromium/content/browser/GestureListenerManagerImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/GestureListenerManagerImpl.java
@@ -37,7 +37,8 @@
  */
 @JNINamespace("content")
 public class GestureListenerManagerImpl
-        implements GestureListenerManager, WindowEventObserver, UserData {
+        implements GestureListenerManager, WindowEventObserver, UserData,
+                   ViewAndroidDelegate.VerticalScrollDirectionChangeListener {
     private static final class UserDataFactoryLazyHolder {
         private static final UserDataFactory<GestureListenerManagerImpl> INSTANCE =
                 GestureListenerManagerImpl::new;
@@ -81,6 +82,7 @@
         mListeners = new ObserverList<GestureStateListener>();
         mIterator = mListeners.rewindableIterator();
         mViewDelegate = mWebContents.getViewAndroidDelegate();
+        mViewDelegate.addVerticalScrollDirectionChangeListener(this);
         WindowEventObserverManager.from(mWebContents).addObserver(this);
         mNativeGestureListenerManager = GestureListenerManagerImplJni.get().init(
                 GestureListenerManagerImpl.this, mWebContents);
@@ -118,6 +120,11 @@
         }
     }
 
+    @Override
+    public boolean hasListener(GestureStateListener listener) {
+        return mListeners.hasObserver(listener);
+    }
+
     private boolean hasGestureStateListenerWithScroll() {
         for (GestureStateListener listener : mListeners) {
             if (listener instanceof GestureStateListenerWithScroll) return true;
@@ -200,6 +207,13 @@
         }
     }
 
+    @Override
+    public void onVerticalScrollDirectionChanged(boolean directionUp, float currentScrollRatio) {
+        for (mIterator.rewind(); mIterator.hasNext();) {
+            mIterator.next().onVerticalScrollDirectionChanged(directionUp, currentScrollRatio);
+        }
+    }
+
     /* Called when ongoing fling gesture needs to be reset. */
     private void resetFlingGesture() {
         if (mHasActiveFlingScroll) {
@@ -306,6 +320,7 @@
     private void onNativeDestroyed() {
         for (mIterator.rewind(); mIterator.hasNext();) mIterator.next().onDestroyed();
         mListeners.clear();
+        mViewDelegate.removeVerticalScrollDirectionChangeListener(this);
         mNativeGestureListenerManager = 0;
     }
 
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/GestureListenerManager.java b/content/public/android/java/src/org/chromium/content_public/browser/GestureListenerManager.java
index 487428c0..01e7624 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/GestureListenerManager.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/GestureListenerManager.java
@@ -36,6 +36,9 @@
      */
     void removeListener(GestureStateListener listener);
 
+    /** Returns whether the provided listener has been added. */
+    boolean hasListener(GestureStateListener listener);
+
     /**
      * @return Whether a scroll targeting web content is in progress.
      */
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/GestureStateListener.java b/content/public/android/java/src/org/chromium/content_public/browser/GestureStateListener.java
index 175190d..f27e3aa7 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/GestureStateListener.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/GestureStateListener.java
@@ -43,6 +43,13 @@
     public void onScrollStarted(int scrollOffsetY, int scrollExtentY, boolean isDirectionUp) {}
 
     /**
+     * Called when the scroll direction changes.
+     * @param directionUp Whether the scroll direction is up, i.e. swiping down.
+     * @param currentScrollRatio The current scroll ratio of the page.
+     */
+    public void onVerticalScrollDirectionChanged(boolean directionUp, float currentScrollRatio) {}
+
+    /**
      * Called when a scroll gesture has stopped.
      */
     public void onScrollEnded(int scrollOffsetY, int scrollExtentY) {}
diff --git a/content/services/auction_worklet/auction_downloader.cc b/content/services/auction_worklet/auction_downloader.cc
index ad51152..731bab7 100644
--- a/content/services/auction_worklet/auction_downloader.cc
+++ b/content/services/auction_worklet/auction_downloader.cc
@@ -12,6 +12,7 @@
 #include "base/callback.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
+#include "base/trace_event/trace_event.h"
 #include "net/base/net_errors.h"
 #include "net/http/http_request_headers.h"
 #include "net/http/http_status_code.h"
@@ -101,6 +102,43 @@
   return false;
 }
 
+double CalculateMillisecondDelta(const net::LoadTimingInfo& timing,
+                                 base::TimeTicks time) {
+  return time.is_null() ? -1 : (time - timing.request_start).InMillisecondsF();
+}
+
+void WriteTraceTiming(const net::LoadTimingInfo& timing,
+                      perfetto::TracedValue dest) {
+  perfetto::TracedDictionary dict = std::move(dest).WriteDictionary();
+  dict.Add("requestTime", timing.request_start.since_origin().InSecondsF());
+  dict.Add("proxyStart",
+           CalculateMillisecondDelta(timing, timing.proxy_resolve_start));
+  dict.Add("proxyEnd",
+           CalculateMillisecondDelta(timing, timing.proxy_resolve_end));
+  dict.Add("dnsStart",
+           CalculateMillisecondDelta(timing, timing.connect_timing.dns_start));
+  dict.Add("dnsEnd",
+           CalculateMillisecondDelta(timing, timing.connect_timing.dns_end));
+  dict.Add("connectStart", CalculateMillisecondDelta(
+                               timing, timing.connect_timing.connect_start));
+  dict.Add("connectEnd", CalculateMillisecondDelta(
+                             timing, timing.connect_timing.connect_end));
+  dict.Add("sslStart",
+           CalculateMillisecondDelta(timing, timing.connect_timing.ssl_start));
+  dict.Add("sslEnd",
+           CalculateMillisecondDelta(timing, timing.connect_timing.ssl_end));
+  dict.Add("workerStart",
+           CalculateMillisecondDelta(timing, timing.service_worker_start_time));
+  dict.Add("workerReady",
+           CalculateMillisecondDelta(timing, timing.service_worker_ready_time));
+  dict.Add("sendStart", CalculateMillisecondDelta(timing, timing.send_start));
+  dict.Add("sendEnd", CalculateMillisecondDelta(timing, timing.send_end));
+  dict.Add("receiveHeadersEnd",
+           CalculateMillisecondDelta(timing, timing.receive_headers_end));
+  dict.Add("pushStart", timing.push_start.since_origin().InSecondsF());
+  dict.Add("pushEnd", timing.push_end.since_origin().InSecondsF());
+}
+
 }  // namespace
 
 AuctionDownloader::AuctionDownloader(
@@ -116,17 +154,34 @@
   resource_request->url = source_url;
   resource_request->redirect_mode = network::mojom::RedirectMode::kError;
   resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
+  // TODO(morlovich): We may need to set devtools_request_id here, and pass it
+  // along in AuctionUrlLoaderFactoryProxy when supporting the devtools network
+  // tab. At that point GetRequestId() should probably get a cheaper
+  // implementation.
+  resource_request->enable_load_timing =
+      *TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED("devtools.timeline");
   resource_request->headers.SetHeader(net::HttpRequestHeaders::kAccept,
                                       MimeTypeToString(mime_type_));
 
   simple_url_loader_ = network::SimpleURLLoader::Create(
       std::move(resource_request), kTrafficAnnotation);
 
+  TRACE_EVENT_INSTANT_WITH_TIMESTAMP1(
+      "devtools.timeline", "ResourceSendRequest", TRACE_EVENT_SCOPE_THREAD,
+      base::TimeTicks::Now(), "data", [&](perfetto::TracedValue dest) {
+        auto dict = std::move(dest).WriteDictionary();
+        dict.Add("requestId", GetRequestId());
+        dict.Add("url", source_url.spec());
+      });
+
   // Abort on redirects.
   // TODO(mmenke): May want a browser-side proxy to block redirects instead.
   simple_url_loader_->SetOnRedirectCallback(base::BindRepeating(
       &AuctionDownloader::OnRedirect, base::Unretained(this)));
 
+  simple_url_loader_->SetOnResponseStartedCallback(base::BindRepeating(
+      &AuctionDownloader::OnResponseStarted, base::Unretained(this)));
+
   simple_url_loader_->SetTimeoutDuration(base::Seconds(30));
 
   // TODO(mmenke): Consider limiting the size of response bodies.
@@ -159,12 +214,24 @@
           "Failed to load %s error = %s.", source_url_.spec().c_str(),
           net::ErrorToString(simple_url_loader->NetError()).c_str());
     }
+    TraceResult(/*failure=*/true, /*completion_time=*/base::TimeTicks(),
+                /*encoded_data_length=*/0,
+                /*decoded_body_length=*/0);
     std::move(auction_downloader_callback_)
         .Run(/*body=*/nullptr, /*headers=*/nullptr, error_msg);
-  } else if (!simple_url_loader->ResponseInfo()->headers ||
-             !simple_url_loader->ResponseInfo()->headers->GetNormalizedHeader(
-                 "X-Allow-FLEDGE", &allow_fledge) ||
-             !base::EqualsCaseInsensitiveASCII(allow_fledge, "true")) {
+    return;
+  }
+
+  // Everything below is a network success even if it's a semantic failure.
+  TraceResult(/*failure=*/false,
+              simple_url_loader->CompletionStatus()->completion_time,
+              simple_url_loader->ResponseInfo()->encoded_data_length,
+              /*decoded_body_length=*/body->size());
+
+  if (!simple_url_loader->ResponseInfo()->headers ||
+      !simple_url_loader->ResponseInfo()->headers->GetNormalizedHeader(
+          "X-Allow-FLEDGE", &allow_fledge) ||
+      !base::EqualsCaseInsensitiveASCII(allow_fledge, "true")) {
     std::move(auction_downloader_callback_)
         .Run(/*body=*/nullptr, /*headers=*/nullptr,
              base::StringPrintf(
@@ -203,10 +270,85 @@
   // Need to cancel the load, to prevent the request from continuing.
   simple_url_loader_.reset();
 
+  TraceResult(/*failure=*/true, /*completion_time=*/base::TimeTicks(),
+              /*encoded_data_length=*/0,
+              /*decoded_body_length=*/0);
+
   std::move(auction_downloader_callback_)
       .Run(/*body=*/nullptr, /*headers=*/nullptr,
            base::StringPrintf("Unexpected redirect on %s.",
                               source_url_.spec().c_str()));
 }
 
+void AuctionDownloader::OnResponseStarted(
+    const GURL& final_url,
+    const network::mojom::URLResponseHead& response_head) {
+  TRACE_EVENT_INSTANT1(
+      "devtools.timeline", "ResourceReceiveResponse", TRACE_EVENT_SCOPE_THREAD,
+      "data", [&](perfetto::TracedValue dest) {
+        perfetto::TracedDictionary dict = std::move(dest).WriteDictionary();
+        dict.Add("requestId", GetRequestId());
+        if (response_head.headers)
+          dict.Add("statusCode", response_head.headers->response_code());
+        dict.Add("mimeType", response_head.mime_type);
+        dict.Add("encodedDataLength", response_head.encoded_data_length);
+
+        // ref.  WebURLLoader::PopulateURLResponse
+        dict.Add("fromCache",
+                 (!response_head.load_timing.request_start_time.is_null() &&
+                  response_head.response_time <
+                      response_head.load_timing.request_start_time));
+        dict.Add("fromServiceWorker",
+                 response_head.was_fetched_via_service_worker);
+
+        if (response_head.was_fetched_via_service_worker) {
+          switch (response_head.service_worker_response_source) {
+            case network::mojom::FetchResponseSource::kCacheStorage:
+              dict.Add("serviceWorkerResponseSource", "cacheStorage");
+              break;
+            case network::mojom::FetchResponseSource::kHttpCache:
+              dict.Add("serviceWorkerResponseSource", "httpCache");
+              break;
+            case network::mojom::FetchResponseSource::kNetwork:
+              dict.Add("serviceWorkerResponseSource", "network");
+              break;
+            case network::mojom::FetchResponseSource::kUnspecified:
+              dict.Add("serviceWorkerResponseSource", "fallbackCode");
+          }
+        }
+
+        if (!response_head.response_time.is_null()) {
+          dict.Add("responseTime", response_head.response_time.ToJsTime());
+        }
+
+        // Only send load timing if it exists, e.g. not a cache hit.
+        if (!response_head.load_timing.receive_headers_end.is_null()) {
+          WriteTraceTiming(response_head.load_timing, dict.AddItem("timing"));
+        }
+      });
+}
+
+std::string AuctionDownloader::GetRequestId() {
+  if (!request_id_.has_value())
+    request_id_ = base::UnguessableToken::Create();
+  return request_id_->ToString();
+}
+
+void AuctionDownloader::TraceResult(bool failure,
+                                    base::TimeTicks completion_time,
+                                    int64_t encoded_data_length,
+                                    int64_t decoded_body_length) {
+  TRACE_EVENT_INSTANT1(
+      "devtools.timeline", "ResourceFinish", TRACE_EVENT_SCOPE_THREAD, "data",
+      [&](perfetto::TracedValue dest) {
+        perfetto::TracedDictionary dict = std::move(dest).WriteDictionary();
+        dict.Add("requestId", GetRequestId());
+        dict.Add("didFail", failure);
+        dict.Add("encodedDataLength", encoded_data_length);
+        dict.Add("decodedBodyLength", decoded_body_length);
+        if (!completion_time.is_null())
+          dict.Add("finishTime", completion_time.since_origin().InSecondsF());
+      });
+}
+
 }  // namespace auction_worklet
diff --git a/content/services/auction_worklet/auction_downloader.h b/content/services/auction_worklet/auction_downloader.h
index 73994d53..790e121 100644
--- a/content/services/auction_worklet/auction_downloader.h
+++ b/content/services/auction_worklet/auction_downloader.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "base/callback.h"
+#include "base/unguessable_token.h"
 #include "net/http/http_response_headers.h"
 #include "net/url_request/redirect_info.h"
 #include "services/network/public/mojom/url_loader_factory.mojom-forward.h"
@@ -57,9 +58,19 @@
   void OnRedirect(const net::RedirectInfo& redirect_info,
                   const network::mojom::URLResponseHead& response_head,
                   std::vector<std::string>* removed_headers);
+  void OnResponseStarted(const GURL& final_url,
+                         const network::mojom::URLResponseHead& response_head);
+  std::string GetRequestId();
+  void TraceResult(bool failure,
+                   base::TimeTicks completion_time,
+                   int64_t encoded_data_length,
+                   int64_t decoded_body_length);
 
   const GURL source_url_;
   const MimeType mime_type_;
+  // Filled in lazily if tracing is actually used.
+  absl::optional<base::UnguessableToken> request_id_;
+
   std::unique_ptr<network::SimpleURLLoader> simple_url_loader_;
   AuctionDownloaderCallback auction_downloader_callback_;
 };
diff --git a/content/test/data/accessibility/aria/aria-tabpanel-expected-android-external.txt b/content/test/data/accessibility/aria/aria-tabpanel-expected-android-external.txt
index c0adf9ad..67249e2 100644
--- a/content/test/data/accessibility/aria/aria-tabpanel-expected-android-external.txt
+++ b/content/test/data/accessibility/aria/aria-tabpanel-expected-android-external.txt
@@ -1,6 +1,7 @@
 WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
-++View actions:[AX_FOCUS] bundle:[chromeRole="tabPanel", roleDescription="tab panel"]
-++++View text:"Item" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="tab", roleDescription="tab"]
+++TabWidget actions:[AX_FOCUS] bundle:[chromeRole="tabList", roleDescription="tab list"]
+++++View text:"Item" viewIdResName:"item" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="tab", roleDescription="tab"]
 ++++++TextView text:"Item" CollectionItemInfo:[heading, rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="heading", roleDescription="heading 3"]
-++++View text:"Prices" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="tab", roleDescription="tab"]
-++++++TextView text:"Prices" CollectionItemInfo:[heading, rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="heading", roleDescription="heading 3"]
\ No newline at end of file
+++++View text:"Prices" viewIdResName:"prices" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="tab", roleDescription="tab"]
+++++++TextView text:"Prices" CollectionItemInfo:[heading, rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="heading", roleDescription="heading 3"]
+++View text:"Item" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="tabPanel", roleDescription="tab panel"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-tabpanel-expected-android.txt b/content/test/data/accessibility/aria/aria-tabpanel-expected-android.txt
index 03a9117b..c95bf55 100644
--- a/content/test/data/accessibility/aria/aria-tabpanel-expected-android.txt
+++ b/content/test/data/accessibility/aria/aria-tabpanel-expected-android.txt
@@ -1,6 +1,7 @@
 android.webkit.WebView focusable focused scrollable
-++android.view.View role_description='tab panel'
+++android.widget.TabWidget role_description='tab list'
 ++++android.view.View role_description='tab' clickable name='Item'
 ++++++android.widget.TextView role_description='heading 3' heading name='Item'
-++++android.view.View role_description='tab' clickable name='Prices'
-++++++android.widget.TextView role_description='heading 3' heading name='Prices'
\ No newline at end of file
+++++android.view.View role_description='tab' clickable name='Prices' item_index=1 row_index=1
+++++++android.widget.TextView role_description='heading 3' heading name='Prices'
+++android.view.View role_description='tab panel' name='Item'
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-tabpanel-expected-auralinux.txt b/content/test/data/accessibility/aria/aria-tabpanel-expected-auralinux.txt
index 130ed426..46694bf 100644
--- a/content/test/data/accessibility/aria/aria-tabpanel-expected-auralinux.txt
+++ b/content/test/data/accessibility/aria/aria-tabpanel-expected-auralinux.txt
@@ -1,8 +1,10 @@
 [document web]
-++[scroll pane]
-++++[page tab] name='Item' selectable
+++[page tab list] horizontal
+++++[page tab] name='Item' selectable label-for
 ++++++[heading] name='Item'
 ++++++++[static] name='Item'
 ++++[page tab] name='Prices' selectable
 ++++++[heading] name='Prices'
 ++++++++[static] name='Prices'
+++[scroll pane] name='Item' labelled-by
+++++[static] name='Item tab content'
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-tabpanel-expected-blink.txt b/content/test/data/accessibility/aria/aria-tabpanel-expected-blink.txt
index e7c4e7d1..d495e84 100644
--- a/content/test/data/accessibility/aria/aria-tabpanel-expected-blink.txt
+++ b/content/test/data/accessibility/aria/aria-tabpanel-expected-blink.txt
@@ -1,7 +1,7 @@
 rootWebArea
 ++genericContainer ignored
 ++++genericContainer ignored
-++++++tabPanel
+++++++tabList horizontal
 ++++++++tab name='Item' selected=false
 ++++++++++heading name='Item' hierarchicalLevel=3
 ++++++++++++staticText name='Item'
@@ -10,3 +10,6 @@
 ++++++++++heading name='Prices' hierarchicalLevel=3
 ++++++++++++staticText name='Prices'
 ++++++++++++++inlineTextBox name='Prices'
+++++++tabPanel name='Item'
+++++++++staticText name='Item tab content'
+++++++++++inlineTextBox name='Item tab content'
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-tabpanel-expected-mac.txt b/content/test/data/accessibility/aria/aria-tabpanel-expected-mac.txt
index b1526d7..7603db7 100644
--- a/content/test/data/accessibility/aria/aria-tabpanel-expected-mac.txt
+++ b/content/test/data/accessibility/aria/aria-tabpanel-expected-mac.txt
@@ -1,8 +1,10 @@
 AXWebArea AXRoleDescription='HTML content'
-++AXGroup AXSubrole=AXTabPanel AXRoleDescription='tab panel'
+++AXTabGroup AXRoleDescription='tab group'
 ++++AXRadioButton AXSubrole=AXTabButton AXRoleDescription='tab' AXTitle='Item' AXValue=0
 ++++++AXHeading AXRoleDescription='heading' AXTitle='Item' AXValue=3
 ++++++++AXStaticText AXRoleDescription='text' AXValue='Item'
 ++++AXRadioButton AXSubrole=AXTabButton AXRoleDescription='tab' AXTitle='Prices' AXValue=0
 ++++++AXHeading AXRoleDescription='heading' AXTitle='Prices' AXValue=3
 ++++++++AXStaticText AXRoleDescription='text' AXValue='Prices'
+++AXGroup AXSubrole=AXTabPanel AXDescription='Item' AXRoleDescription='tab panel'
+++++AXStaticText AXRoleDescription='text' AXValue='Item tab content'
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-tabpanel-expected-win.txt b/content/test/data/accessibility/aria/aria-tabpanel-expected-win.txt
index 63360ba..e70f5a0 100644
--- a/content/test/data/accessibility/aria/aria-tabpanel-expected-win.txt
+++ b/content/test/data/accessibility/aria/aria-tabpanel-expected-win.txt
@@ -1,8 +1,10 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
-++ROLE_SYSTEM_PROPERTYPAGE xml-roles:tabpanel
+++ROLE_SYSTEM_PAGETABLIST IA2_STATE_HORIZONTAL xml-roles:tablist
 ++++ROLE_SYSTEM_PAGETAB name='Item' xml-roles:tab
 ++++++IA2_ROLE_HEADING name='Item'
 ++++++++ROLE_SYSTEM_STATICTEXT name='Item'
 ++++ROLE_SYSTEM_PAGETAB name='Prices' xml-roles:tab
 ++++++IA2_ROLE_HEADING name='Prices'
-++++++++ROLE_SYSTEM_STATICTEXT name='Prices'
\ No newline at end of file
+++++++++ROLE_SYSTEM_STATICTEXT name='Prices'
+++ROLE_SYSTEM_PROPERTYPAGE name='Item' xml-roles:tabpanel
+++++ROLE_SYSTEM_STATICTEXT name='Item tab content'
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-tabpanel.html b/content/test/data/accessibility/aria/aria-tabpanel.html
index 17f2b5d..680c5ac 100644
--- a/content/test/data/accessibility/aria/aria-tabpanel.html
+++ b/content/test/data/accessibility/aria/aria-tabpanel.html
@@ -8,13 +8,16 @@
 <!DOCTYPE html>
 <html>
 <body>
-<div role="tabpanel">
-  <div role="tab">
+<div role="tablist">
+  <div role="tab" id="item">
     <h3>Item</h3>
   </div>
-  <div role="tab">
+  <div role="tab" id="prices">
     <h3>Prices</h3>
   </div>
 </div>
+<div role="tabpanel" aria-labelledby="item">
+  Item tab content
+</div>
 </body>
 </html>
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 796fb909..778773ae 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
@@ -509,7 +509,7 @@
 
 crbug.com/642822 [ mac amd ] conformance2/rendering/clipping-wide-points.html [ Failure ]
 
-crbug.com/1343793 [ mac amd-0x6821 angle-opengl ] deqp/functional/gles3/texturefiltering/3d_combinations_08.html [ RetryOnFailure ]
+crbug.com/1343793 [ mac amd-0x6821 angle-opengl ] deqp/functional/gles3/texturefiltering/3d_combinations_24.html [ RetryOnFailure ]
 
 crbug.com/1227762 [ bigsur amd-0x6821 angle-disabled no-passthrough skia-renderer-gl ] conformance2/textures/canvas/tex-2d-rgb5_a1-rgba-unsigned_byte.html [ Failure ]
 crbug.com/1227762 [ bigsur amd-0x6821 angle-disabled no-passthrough skia-renderer-gl ] conformance2/textures/canvas_sub_rectangle/tex-2d-rgb5_a1-rgba-unsigned_byte.html [ Failure ]
diff --git a/extensions/browser/api/cec_private/cec_private_api.cc b/extensions/browser/api/cec_private/cec_private_api.cc
index f3bfa4a..cb39ebc 100644
--- a/extensions/browser/api/cec_private/cec_private_api.cc
+++ b/extensions/browser/api/cec_private/cec_private_api.cc
@@ -8,7 +8,7 @@
 
 #include "base/bind.h"
 #include "base/notreached.h"
-#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/cec_service/cec_service_client.h"
 #include "extensions/common/api/cec_private.h"
 #include "extensions/common/manifest_handlers/kiosk_mode_info.h"
 
@@ -72,7 +72,7 @@
 CecPrivateSendStandByFunction::~CecPrivateSendStandByFunction() = default;
 
 ExtensionFunction::ResponseAction CecPrivateSendStandByFunction::Run() {
-  chromeos::DBusThreadManager::Get()->GetCecServiceClient()->SendStandBy();
+  chromeos::CecServiceClient::Get()->SendStandBy();
   return RespondNow(NoArguments());
 }
 
@@ -81,7 +81,7 @@
 CecPrivateSendWakeUpFunction::~CecPrivateSendWakeUpFunction() = default;
 
 ExtensionFunction::ResponseAction CecPrivateSendWakeUpFunction::Run() {
-  chromeos::DBusThreadManager::Get()->GetCecServiceClient()->SendWakeUp();
+  chromeos::CecServiceClient::Get()->SendWakeUp();
   return RespondNow(NoArguments());
 }
 
@@ -93,11 +93,8 @@
 
 ExtensionFunction::ResponseAction
 CecPrivateQueryDisplayCecPowerStateFunction::Run() {
-  chromeos::DBusThreadManager::Get()
-      ->GetCecServiceClient()
-      ->QueryDisplayCecPowerState(base::BindOnce(
-          &CecPrivateQueryDisplayCecPowerStateFunction::HandlePowerStates,
-          this));
+  chromeos::CecServiceClient::Get()->QueryDisplayCecPowerState(base::BindOnce(
+      &CecPrivateQueryDisplayCecPowerStateFunction::HandlePowerStates, this));
   return RespondLater();
 }
 
diff --git a/extensions/browser/api/cec_private/cec_private_apitest.cc b/extensions/browser/api/cec_private/cec_private_apitest.cc
index fff4f44..effa5a8 100644
--- a/extensions/browser/api/cec_private/cec_private_apitest.cc
+++ b/extensions/browser/api/cec_private/cec_private_apitest.cc
@@ -4,7 +4,6 @@
 
 #include "chromeos/dbus/cec_service/cec_service_client.h"
 #include "chromeos/dbus/cec_service/fake_cec_service_client.h"
-#include "chromeos/dbus/dbus_thread_manager.h"
 #include "extensions/common/features/feature_session_type.h"
 #include "extensions/common/mojom/feature_session_type.mojom.h"
 #include "extensions/common/switches.h"
@@ -30,8 +29,16 @@
   ~CecPrivateKioskApiTest() override = default;
 
   void SetUpOnMainThread() override {
+    // Unlike chrome's browser_tests, extensions_browsertests does not
+    // automatically create D-Bus fakes for us.
+    chromeos::CecServiceClient::InitializeFake();
     cec_ = static_cast<chromeos::FakeCecServiceClient*>(
-        chromeos::DBusThreadManager::Get()->GetCecServiceClient());
+        chromeos::CecServiceClient::Get());
+  }
+
+  void TearDownOnMainThread() override {
+    cec_ = nullptr;
+    chromeos::CecServiceClient::Shutdown();
   }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h
index 8dc6e11b..33d25b8 100644
--- a/extensions/browser/extension_function_histogram_value.h
+++ b/extensions/browser/extension_function_histogram_value.h
@@ -1746,6 +1746,9 @@
   OFFSCREEN_CLOSEDOCUMENT = 1683,
   WMDESKSPRIVATE_SETWINDOWPROPERTIES = 1684,
   AUTOTESTPRIVATE_LAUNCHFILESAPPTOPATH = 1685,
+  SIDEPANEL_GETOPTIONS = 1686,
+  SIDEPANEL_SETOPTIONS = 1687,
+  ENTERPRISEREPORTINGPRIVATE_GETFILESYSTEMINFO = 1688,
   // Last entry: Add new entries above, then run:
   // tools/metrics/histograms/update_extension_histograms.py
   ENUM_BOUNDARY
diff --git a/extensions/common/extension_features.cc b/extensions/common/extension_features.cc
index 15e502a..01455f3 100644
--- a/extensions/common/extension_features.cc
+++ b/extensions/common/extension_features.cc
@@ -114,4 +114,8 @@
 const base::Feature kNewWebstoreDomain{"NewWebstoreDomain",
                                        base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Side panel API availability.
+const base::Feature kExtensionSidePanelIntegration{
+    "ExtensionSidePanelIntegration", base::FEATURE_DISABLED_BY_DEFAULT};
+
 }  // namespace extensions_features
diff --git a/extensions/common/extension_features.h b/extensions/common/extension_features.h
index 8d035bf..100295c4 100644
--- a/extensions/common/extension_features.h
+++ b/extensions/common/extension_features.h
@@ -43,6 +43,8 @@
 
 extern const base::Feature kNewWebstoreDomain;
 
+extern const base::Feature kExtensionSidePanelIntegration;
+
 }  // namespace extensions_features
 
 #endif  // EXTENSIONS_COMMON_EXTENSION_FEATURES_H_
diff --git a/extensions/common/manifest.cc b/extensions/common/manifest.cc
index ecd2c6e..c08b960e 100644
--- a/extensions/common/manifest.cc
+++ b/extensions/common/manifest.cc
@@ -284,7 +284,7 @@
     std::vector<InstallWarning>* warnings) const {
   *error = "";
 
-  // Check every feature to see if its in the manifest. Note that this means
+  // Check every feature to see if it's in the manifest. Note that this means
   // we will ignore keys that are not features; we do this for forward
   // compatibility.
 
diff --git a/extensions/common/manifest_constants.cc b/extensions/common/manifest_constants.cc
index d65ddb3..94396eca 100644
--- a/extensions/common/manifest_constants.cc
+++ b/extensions/common/manifest_constants.cc
@@ -599,6 +599,7 @@
     "Invalid value for 'web_accessible_resources'.";
 const char kInvalidWebAccessibleResource[] =
     "Invalid value for 'web_accessible_resources[*]'. *";
+const char kInvalidSidePanel[] = "Invalid value for 'side_panel'. *";
 const char16_t kInvalidWebview[] = u"Invalid value for 'webview'.";
 const char16_t kInvalidWebviewAccessibleResourcesList[] =
     u"Invalid value for'webview.accessible_resources'.";
@@ -688,6 +689,8 @@
 const char kEnabledRulesetCountExceeded[] =
     "Invalid value for key '*.*': The number of enabled rulesets must be less "
     "than or equal to *.";
+const char kSidePanelManifestDefaultPathError[] =
+    "Side panel file path must exist.";
 const char16_t kTransientBackgroundConflictsWithPersistentBackground[] =
     u"The 'transientBackground' permission cannot be used with a persistent "
     "background page.";
diff --git a/extensions/common/manifest_constants.h b/extensions/common/manifest_constants.h
index bf9a953..62ab9105 100644
--- a/extensions/common/manifest_constants.h
+++ b/extensions/common/manifest_constants.h
@@ -414,6 +414,7 @@
 extern const char16_t kInvalidVersionName[];
 extern const char kInvalidWebAccessibleResourcesList[];
 extern const char kInvalidWebAccessibleResource[];
+extern const char kInvalidSidePanel[];
 extern const char16_t kInvalidWebview[];
 extern const char16_t kInvalidWebviewAccessibleResourcesList[];
 extern const char kInvalidWebviewAccessibleResource[];
@@ -456,6 +457,7 @@
 extern const char kRulesFileIsInvalid[];
 extern const char kRulesetCountExceeded[];
 extern const char kEnabledRulesetCountExceeded[];
+extern const char kSidePanelManifestDefaultPathError[];
 extern const char16_t kTransientBackgroundConflictsWithPersistentBackground[];
 extern const char kTtsGenderIsDeprecated[];
 extern const char kUnrecognizedManifestKey[];
diff --git a/extensions/common/mojom/api_permission_id.mojom b/extensions/common/mojom/api_permission_id.mojom
index 06bf5f6..7adee8f 100644
--- a/extensions/common/mojom/api_permission_id.mojom
+++ b/extensions/common/mojom/api_permission_id.mojom
@@ -264,6 +264,7 @@
   kSharedStoragePrivate = 238,
   kEnterpriseRemoteApps = 239,
   kOffscreen = 240,
+  kSidePanel = 241,
 
   // Add new entries at the end of the enum and be sure to update the
   // "ExtensionPermission3" enum in tools/metrics/histograms/enums.xml
diff --git a/extensions/renderer/bindings/api_binding_unittest.cc b/extensions/renderer/bindings/api_binding_unittest.cc
index f04ca5b..5f549cf6 100644
--- a/extensions/renderer/bindings/api_binding_unittest.cc
+++ b/extensions/renderer/bindings/api_binding_unittest.cc
@@ -1990,10 +1990,13 @@
   // Register a hook for the test.supportsPromises method with a result modifier
   // that changes the result when the async response type is callback based.
   auto hooks = std::make_unique<APIBindingHooksTestDelegate>();
+  int total_modifier_call_count = 0;
   auto result_modifier =
-      [](const std::vector<v8::Local<v8::Value>>& result_args,
-         v8::Local<v8::Context> context,
-         binding::AsyncResponseType async_type) {
+      [&total_modifier_call_count](
+          const std::vector<v8::Local<v8::Value>>& result_args,
+          v8::Local<v8::Context> context,
+          binding::AsyncResponseType async_type) {
+        total_modifier_call_count++;
         if (async_type == binding::AsyncResponseType::kCallback) {
           // For callback based calls change the result to a vector with
           // multiple arguments by appending "bar" to the end.
@@ -2011,7 +2014,8 @@
                          const APITypeReferenceMap& ref_map) {
         APIBindingHooks::RequestResult result(
             APIBindingHooks::RequestResult::NOT_HANDLED,
-            v8::Local<v8::Function>(), base::BindOnce(result_modifier));
+            v8::Local<v8::Function>(),
+            base::BindLambdaForTesting(result_modifier));
         return result;
       };
   hooks->AddHandler("test.supportsPromises",
@@ -2043,6 +2047,7 @@
 
     EXPECT_EQ(v8::Promise::kFulfilled, promise->State());
     EXPECT_EQ(R"("foo")", V8ToString(promise->Result(), context));
+    EXPECT_EQ(1, total_modifier_call_count);
   }
 
   // A callback-based call will be modified by the hook and return with multiple
@@ -2069,6 +2074,33 @@
                                                       context, "argument1"));
     EXPECT_EQ(R"("bar")", GetStringPropertyFromObject(context->Global(),
                                                       context, "argument2"));
+    EXPECT_EQ(2, total_modifier_call_count);
+  }
+
+  // A call which results in an error should reject as expected and the result
+  // modifier should never be called.
+  {
+    v8::Local<v8::Function> promise_api_call = FunctionFromString(
+        context, "(function(api) { return api.supportsPromises(3) });");
+    v8::Local<v8::Value> args[] = {binding_object};
+    v8::Local<v8::Value> api_result =
+        RunFunctionOnGlobal(promise_api_call, context, std::size(args), args);
+
+    v8::Local<v8::Promise> promise = api_result.As<v8::Promise>();
+    ASSERT_FALSE(api_result.IsEmpty());
+    EXPECT_EQ(v8::Promise::kPending, promise->State());
+
+    ASSERT_TRUE(last_request());
+    request_handler()->CompleteRequest(last_request()->request_id,
+                                       base::Value::List(), "Error message");
+    EXPECT_EQ(v8::Promise::kRejected, promise->State());
+    ASSERT_TRUE(promise->Result()->IsObject());
+    EXPECT_EQ(R"("Error message")",
+              GetStringPropertyFromObject(promise->Result().As<v8::Object>(),
+                                          context, "message"));
+    // Since the result modifier should have never been called, the total call
+    // count should still be the same as in the previous test case.
+    EXPECT_EQ(2, total_modifier_call_count);
   }
 }
 
diff --git a/extensions/renderer/bindings/api_request_handler.cc b/extensions/renderer/bindings/api_request_handler.cc
index b76a12b..dd89d03 100644
--- a/extensions/renderer/bindings/api_request_handler.cc
+++ b/extensions/renderer/bindings/api_request_handler.cc
@@ -208,8 +208,13 @@
     last_error->SetError(context, error);
   }
 
+  // If there is a result modifier for this async request and the response args
+  // are not empty, run the result modifier and allow it to massage the return
+  // arguments before we send them back.
+  // Note: a request can end up with a result modifier and be returning an empty
+  // set of args if we are responding that an error occurred.
   const std::vector<v8::Local<v8::Value>> args =
-      result_modifier_.is_null()
+      result_modifier_.is_null() || response_args.empty()
           ? response_args
           : std::move(result_modifier_)
                 .Run(response_args, context, async_type_);
diff --git a/extensions/renderer/bindings/api_request_handler_unittest.cc b/extensions/renderer/bindings/api_request_handler_unittest.cc
index a291751d..c469ce3 100644
--- a/extensions/renderer/bindings/api_request_handler_unittest.cc
+++ b/extensions/renderer/bindings/api_request_handler_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/callback_helpers.h"
 #include "base/strings/strcat.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/bind.h"
 #include "base/values.h"
 #include "extensions/renderer/bindings/api_binding_test.h"
 #include "extensions/renderer/bindings/api_binding_test_util.h"
@@ -634,6 +635,35 @@
     EXPECT_EQ("Unchecked runtime.lastError: some error", *logged_error);
     logged_error.reset();
   }
+
+  {
+    // Test a function call resulting in an error for a request handler that has
+    // an associated result modifier. The result modifier should never be called
+    // and since the callback checks last error no error should be logged to the
+    // console.
+    bool result_modifier_called = false;
+    auto result_modifier =
+        [&result_modifier_called](
+            const std::vector<v8::Local<v8::Value>>& result_args,
+            v8::Local<v8::Context> context,
+            binding::AsyncResponseType async_type) {
+          result_modifier_called = true;
+          return result_args;
+        };
+    v8::Local<v8::Function> callback =
+        FunctionFromString(context, kReportExposedLastError);
+    request_handler.StartRequest(
+        context, kMethod, std::make_unique<base::ListValue>(),
+        binding::AsyncResponseType::kCallback, callback,
+        v8::Local<v8::Function>(), base::BindLambdaForTesting(result_modifier));
+    int request_id = request_handler.last_sent_request_id();
+    request_handler.CompleteRequest(request_id, base::Value::List(),
+                                    "some error");
+    EXPECT_FALSE(logged_error);
+    EXPECT_EQ("\"some error\"", get_exposed_error());
+    EXPECT_FALSE(result_modifier_called);
+    logged_error.reset();
+  }
 }
 
 TEST_F(APIRequestHandlerTest, AddPendingRequestCallback) {
diff --git a/fuchsia_web/webengine/BUILD.gn b/fuchsia_web/webengine/BUILD.gn
index 55dcf85..d40151f 100644
--- a/fuchsia_web/webengine/BUILD.gn
+++ b/fuchsia_web/webengine/BUILD.gn
@@ -164,13 +164,30 @@
   ]
 
   assert_no_deps = [
+    # Although `enable_extensions` is true for Fuchsia, extensions code should
+    # not be linked into WebEngine because it cannot be used and this would
+    # unnecessarily increase the package size.
+    # Exclude everything except "//extensions/buildflags/*", which is allowed,
+    # and //extensions/common:common_constants and
+    # //extensions/common:constants_impl", which are a dependencies.
+    # //extensions:extensions is addressed below.
+    "//extensions:extensions_resources",
+    "//extensions/browser/*",
+    "//extensions/common:common",
+    "//extensions/common:core_api_provider",
+    "//extensions/common:export",
+    "//extensions/common:mojom",
+    "//extensions/renderer/*",
+    "//extensions/strings/*",
+
     # Although `enable_pdf` is true for Fuchsia, PDF code should not be linked
     # into WebEngine because it does not have the necessary infrastructure and
     # this would unnecessarily increase the package size.
     "//components/pdf/*",
+    "//components/plugins/*",
     "//pdf/*",
 
-    # Pepper code should not be included as plugins are disabled.
+    # Pepper code should not be included as enable_ppapi is false.
     # Exclude everything except "//ppapi/buildflags/*", which is allowed.
     "//ppapi:*",
     "//ppapi/c/*",
@@ -191,6 +208,11 @@
     "//printing/backend/*",
   ]
 
+  # //extensions:extensions is used by common_constants in component builds.
+  if (!is_component_build) {
+    assert_no_deps += [ "//extensions:*" ]
+  }
+
   # Technically the PAK files are only data dependencies, but specifying them
   # as |data_deps| causes metadata (.info) files to also be pulled-in to the
   # package.
diff --git a/gpu/command_buffer/service/shared_image_backing_d3d.cc b/gpu/command_buffer/service/shared_image_backing_d3d.cc
index 022f089..9a20298a 100644
--- a/gpu/command_buffer/service/shared_image_backing_d3d.cc
+++ b/gpu/command_buffer/service/shared_image_backing_d3d.cc
@@ -648,6 +648,10 @@
   texture_descriptor.nextInChain =
       reinterpret_cast<WGPUChainedStruct*>(&internalDesc);
 
+  // Evict invalid external images e.g. due to their device being destroyed.
+  base::EraseIf(dawn_external_images_,
+                [](const auto& kv) { return !kv.second->IsValid(); });
+
   // Persistently open the shared handle by caching it on this backing.
   auto it = dawn_external_images_.find(device);
   dawn::native::d3d12::ExternalImageDXGI* external_image_ptr = nullptr;
@@ -674,6 +678,7 @@
     external_image_ptr = it->second.get();
   }
   DCHECK(external_image_ptr);
+  DCHECK(external_image_ptr->IsValid());
   return std::make_unique<SharedImageRepresentationDawnD3D>(
       manager, this, tracker, device, external_image_ptr);
 #else
diff --git a/infra/archive_config/win-archive-rel.json b/infra/archive_config/win-archive-rel.json
index f73dc8e..37182af 100644
--- a/infra/archive_config/win-archive-rel.json
+++ b/infra/archive_config/win-archive-rel.json
@@ -9,6 +9,7 @@
                 "chrome_elf.dll",
                 "chrome_proxy.exe",
                 "chrome_pwa_launcher.exe",
+                "chrome_wer.dll",
                 "D3DCompiler_47.dll",
                 "elevation_service.exe",
                 "eventlog_provider.dll",
@@ -53,6 +54,7 @@
                 "chrome_elf.dll.pdb",
                 "chrome_proxy.exe.pdb",
                 "chrome_pwa_launcher.exe.pdb",
+                "chrome_wer.dll.pdb",
                 "elevation_service.exe.pdb",
                 "eventlog_provider.dll.pdb",
                 "gaia1_0.dll.pdb",
diff --git a/infra/archive_config/win32-archive-rel.json b/infra/archive_config/win32-archive-rel.json
index 6d284e197..1fb512a 100644
--- a/infra/archive_config/win32-archive-rel.json
+++ b/infra/archive_config/win32-archive-rel.json
@@ -9,6 +9,7 @@
                 "chrome_elf.dll",
                 "chrome_proxy.exe",
                 "chrome_pwa_launcher.exe",
+                "chrome_wer.dll",
                 "D3DCompiler_47.dll",
                 "elevation_service.exe",
                 "eventlog_provider.dll",
@@ -55,6 +56,7 @@
                 "chrome_elf.dll.pdb",
                 "chrome_proxy.exe.pdb",
                 "chrome_pwa_launcher.exe.pdb",
+                "chrome_wer.dll.pdb",
                 "elevation_service.exe.pdb",
                 "eventlog_provider.dll.pdb",
                 "gaia1_0.dll.pdb",
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_ENTERPRISE_BROWSER_AND_PROFILE_DIFFERENT_MANAGED_BY.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_ENTERPRISE_BROWSER_AND_PROFILE_DIFFERENT_MANAGED_BY.png.sha1
new file mode 100644
index 0000000..f3e7d95
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_ENTERPRISE_BROWSER_AND_PROFILE_DIFFERENT_MANAGED_BY.png.sha1
@@ -0,0 +1 @@
+6861b6773aa245d203e98bfab6301ef5c1731cf0
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_ENTERPRISE_BROWSER_MANAGED_BY.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_ENTERPRISE_BROWSER_MANAGED_BY.png.sha1
new file mode 100644
index 0000000..fbceb04
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_ENTERPRISE_BROWSER_MANAGED_BY.png.sha1
@@ -0,0 +1 @@
+4e632084022c33cf0701aa25de49ea3f5af9061a
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_ENTERPRISE_PROFILE_MANAGED_BY.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_ENTERPRISE_PROFILE_MANAGED_BY.png.sha1
new file mode 100644
index 0000000..8c75cd0
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_ENTERPRISE_PROFILE_MANAGED_BY.png.sha1
@@ -0,0 +1 @@
+2810ad95cd0d2abb7a047e46478673d5adf5005c
\ No newline at end of file
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index e460f99..d279dc59 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -1035,6 +1035,12 @@
      flag_descriptions::kMetrickitNonCrashReportName,
      flag_descriptions::kMetrickitNonCrashReportDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(kMetrickitNonCrashReport)},
+    {"autofill-enable-remade-downstream-metrics",
+     flag_descriptions::kAutofillEnableRemadeDownstreamMetricsName,
+     flag_descriptions::kAutofillEnableRemadeDownstreamMetricsDescription,
+     flags_ui::kOsIos,
+     FEATURE_VALUE_TYPE(
+         autofill::features::kAutofillEnableRemadeDownstreamMetrics)},
 };
 
 bool SkipConditionalFeatureEntry(const flags_ui::FeatureEntry& entry) {
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
index 9945176..4cdb8aa 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -34,6 +34,12 @@
     "When enabled, Autofill will use a new ranking formula to rank Autofill "
     "data model suggestions such as credit cards or profiles";
 
+const char kAutofillEnableRemadeDownstreamMetricsName[] =
+    "Enable remade Autofill Downstream metrics logging";
+const char kAutofillEnableRemadeDownstreamMetricsDescription[] =
+    "When enabled, some extra metrics logging for Autofill Downstream will "
+    "start.";
+
 const char kAutofillEnableSendingBcnInGetUploadDetailsName[] =
     "Enable sending billing customer number in GetUploadDetails";
 const char kAutofillEnableSendingBcnInGetUploadDetailsDescription[] =
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
index 5e27bfd..bd81603 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -30,6 +30,11 @@
 extern const char kAutofillEnableRankingFormulaName[];
 extern const char kAutofillEnableRankingFormulaDescription[];
 
+// Title and description for the flag that controls whether the remade Autofill
+// Downstream metrics are enabled.
+extern const char kAutofillEnableRemadeDownstreamMetricsName[];
+extern const char kAutofillEnableRemadeDownstreamMetricsDescription[];
+
 // Title and description for the flag enable sending billing customer number in
 // GetUploadDetails preflight call.
 extern const char kAutofillEnableSendingBcnInGetUploadDetailsName[];
diff --git a/ios/chrome/browser/policy/BUILD.gn b/ios/chrome/browser/policy/BUILD.gn
index 41718683..2e575658 100644
--- a/ios/chrome/browser/policy/BUILD.gn
+++ b/ios/chrome/browser/policy/BUILD.gn
@@ -244,6 +244,8 @@
     "//components/password_manager/core/common",
     "//components/policy:generated",
     "//components/policy/core/common:common_constants",
+    "//components/policy/core/common:test_support",
+    "//components/policy/test_support",
     "//components/strings",
     "//ios/chrome/app/strings:ios_strings_grit",
     "//ios/chrome/browser:chrome_url_constants",
diff --git a/ios/chrome/browser/policy/DEPS b/ios/chrome/browser/policy/DEPS
index 8b9b615..ef87d90 100644
--- a/ios/chrome/browser/policy/DEPS
+++ b/ios/chrome/browser/policy/DEPS
@@ -5,5 +5,8 @@
     "+ios/chrome/browser/ui/main/scene_state.h",
     "+ios/chrome/browser/ui/main/scene_state_browser_agent.h",
   ],
+  "^policy_egtest.mm": [
+    "+components/policy/test_support",
+  ]
 }
 
diff --git a/ios/chrome/browser/policy/policy_app_interface.h b/ios/chrome/browser/policy/policy_app_interface.h
index 28a3d46a..8b0c22ae 100644
--- a/ios/chrome/browser/policy/policy_app_interface.h
+++ b/ios/chrome/browser/policy/policy_app_interface.h
@@ -25,6 +25,12 @@
 // URLAllowlist policies.
 + (BOOL)isURLBlocked:(NSString*)URL;
 
+// Sets the browser cloud policy data with a domain.
++ (void)setBrowserCloudPolicyDataWithDomain:(NSString*)domain;
+
+// Sets the user cloud policy data with a domain.
++ (void)setUserCloudPolicyDataWithDomain:(NSString*)domain;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_POLICY_POLICY_APP_INTERFACE_H_
diff --git a/ios/chrome/browser/policy/policy_app_interface.mm b/ios/chrome/browser/policy/policy_app_interface.mm
index 160cacb..3466b6ee 100644
--- a/ios/chrome/browser/policy/policy_app_interface.mm
+++ b/ios/chrome/browser/policy/policy_app_interface.mm
@@ -4,26 +4,32 @@
 
 #import "ios/chrome/browser/policy/policy_app_interface.h"
 
-#include <memory>
+#import <memory>
 
-#include "base/json/json_string_value_serializer.h"
-#include "base/strings/sys_string_conversions.h"
-#include "base/values.h"
-#include "components/policy/core/browser/browser_policy_connector.h"
-#include "components/policy/core/browser/url_blocklist_manager.h"
-#include "components/policy/core/common/configuration_policy_provider.h"
-#include "components/policy/core/common/policy_bundle.h"
-#include "components/policy/core/common/policy_map.h"
-#include "components/policy/core/common/policy_namespace.h"
-#include "components/policy/core/common/policy_types.h"
-#include "components/policy/policy_constants.h"
-#include "ios/chrome/browser/application_context.h"
-#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#include "ios/chrome/browser/policy/browser_policy_connector_ios.h"
-#include "ios/chrome/browser/policy/test_platform_policy_provider.h"
+#import "base/json/json_string_value_serializer.h"
+#import "base/strings/sys_string_conversions.h"
+#import "base/values.h"
+#import "components/policy/core/browser/browser_policy_connector.h"
+#import "components/policy/core/browser/url_blocklist_manager.h"
+#import "components/policy/core/common/cloud/cloud_policy_core.h"
+#import "components/policy/core/common/cloud/cloud_policy_store.h"
+#import "components/policy/core/common/cloud/device_management_service.h"
+#import "components/policy/core/common/cloud/machine_level_user_cloud_policy_manager.h"
+#import "components/policy/core/common/cloud/user_cloud_policy_manager.h"
+#import "components/policy/core/common/configuration_policy_provider.h"
+#import "components/policy/core/common/policy_bundle.h"
+#import "components/policy/core/common/policy_map.h"
+#import "components/policy/core/common/policy_namespace.h"
+#import "components/policy/core/common/policy_types.h"
+#import "components/policy/policy_constants.h"
+#import "ios/chrome/browser/application_context.h"
+#import "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/browser_state/chrome_browser_state_manager.h"
+#import "ios/chrome/browser/policy/browser_policy_connector_ios.h"
+#import "ios/chrome/browser/policy/test_platform_policy_provider.h"
 #import "ios/chrome/browser/policy_url_blocking/policy_url_blocking_service.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
+#import "third_party/abseil-cpp/absl/types/optional.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -113,4 +119,32 @@
          policy::URLBlocklist::URLBlocklistState::URL_IN_BLOCKLIST;
 }
 
++ (void)setBrowserCloudPolicyDataWithDomain:(NSString*)domain {
+  policy::MachineLevelUserCloudPolicyManager* manager =
+      GetApplicationContext()
+          ->GetBrowserPolicyConnector()
+          ->machine_level_user_cloud_policy_manager();
+  DCHECK(manager);
+
+  policy::CloudPolicyStore* store = manager->core()->store();
+  DCHECK(store);
+
+  auto policy_data = std::make_unique<enterprise_management::PolicyData>();
+  policy_data->set_managed_by(base::SysNSStringToUTF8(domain));
+  store->set_policy_data_for_testing(std::move(policy_data));
+}
+
++ (void)setUserCloudPolicyDataWithDomain:(NSString*)domain {
+  policy::UserCloudPolicyManager* manager =
+      chrome_test_util::GetOriginalBrowserState()->GetUserCloudPolicyManager();
+  DCHECK(manager);
+
+  policy::CloudPolicyStore* store = manager->core()->store();
+  DCHECK(store);
+
+  auto policy_data = std::make_unique<enterprise_management::PolicyData>();
+  policy_data->set_managed_by(base::SysNSStringToUTF8(domain));
+  store->set_policy_data_for_testing(std::move(policy_data));
+}
+
 @end
diff --git a/ios/chrome/browser/policy/policy_egtest.mm b/ios/chrome/browser/policy/policy_egtest.mm
index a3035c4..c4da832c 100644
--- a/ios/chrome/browser/policy/policy_egtest.mm
+++ b/ios/chrome/browser/policy/policy_egtest.mm
@@ -2,27 +2,33 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ios/testing/earl_grey/earl_grey_test.h"
+#import "ios/testing/earl_grey/earl_grey_test.h"
 
-#include "base/strings/sys_string_conversions.h"
+#import "base/strings/strcat.h"
+#import "base/strings/sys_string_conversions.h"
 #import "base/test/ios/wait_util.h"
-#include "components/autofill/core/common/autofill_prefs.h"
-#include "components/history/core/common/pref_names.h"
-#include "components/password_manager/core/common/password_manager_pref_names.h"
+#import "components/autofill/core/common/autofill_prefs.h"
+#import "components/enterprise/browser/enterprise_switches.h"
+#import "components/history/core/common/pref_names.h"
+#import "components/password_manager/core/common/password_manager_pref_names.h"
+#import "components/policy/core/common/cloud/cloud_policy_constants.h"
 #import "components/policy/core/common/policy_loader_ios_constants.h"
-#include "components/policy/policy_constants.h"
+#import "components/policy/core/common/policy_switches.h"
+#import "components/policy/policy_constants.h"
+#import "components/policy/test_support/embedded_policy_test_server.h"
+#import "components/strings/grit/components_strings.h"
 #include "components/strings/grit/components_strings.h"
-#include "ios/chrome/browser/chrome_url_constants.h"
+#import "ios/chrome/browser/chrome_url_constants.h"
 #import "ios/chrome/browser/policy/policy_app_interface.h"
 #import "ios/chrome/browser/policy/policy_earl_grey_utils.h"
-#include "ios/chrome/browser/pref_names.h"
+#import "ios/chrome/browser/pref_names.h"
 #import "ios/chrome/browser/translate/translate_app_interface.h"
 #import "ios/chrome/browser/ui/authentication/signin_earl_grey.h"
 #import "ios/chrome/browser/ui/authentication/signin_earl_grey_ui_test_util.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_constants.h"
-#include "ios/chrome/browser/ui/content_suggestions/content_suggestions_feature.h"
+#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_feature.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_constants.h"
-#include "ios/chrome/browser/ui/ntp/new_tab_page_feature.h"
+#import "ios/chrome/browser/ui/ntp/new_tab_page_feature.h"
 #import "ios/chrome/browser/ui/popup_menu/popup_menu_constants.h"
 #import "ios/chrome/browser/ui/settings/autofill/autofill_constants.h"
 #import "ios/chrome/browser/ui/settings/elements/elements_constants.h"
@@ -30,18 +36,18 @@
 #import "ios/chrome/browser/ui/settings/password/passwords_table_view_constants.h"
 #import "ios/chrome/browser/ui/settings/settings_table_view_controller_constants.h"
 #import "ios/chrome/common/ui/table_view/table_view_cells_constants.h"
-#include "ios/chrome/grit/ios_strings.h"
-#include "ios/chrome/test/earl_grey/chrome_earl_grey.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_earl_grey_app_interface.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
-#include "ios/chrome/test/earl_grey/chrome_test_case.h"
-#include "ios/chrome/test/earl_grey/test_switches.h"
+#import "ios/chrome/test/earl_grey/chrome_test_case.h"
+#import "ios/chrome/test/earl_grey/test_switches.h"
 #import "ios/public/provider/chrome/browser/signin/fake_chrome_identity.h"
-#include "ios/testing/earl_grey/app_launch_configuration.h"
+#import "ios/testing/earl_grey/app_launch_configuration.h"
 #import "ios/testing/earl_grey/app_launch_manager.h"
-#include "net/test/embedded_test_server/embedded_test_server.h"
-#include "ui/base/l10n/l10n_util.h"
+#import "net/test/embedded_test_server/embedded_test_server.h"
+#import "ui/base/l10n/l10n_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -114,6 +120,9 @@
       assertWithMatcher:grey_notVisible()];
 }
 
+NSString* const kDomain1 = @"domain1.com";
+NSString* const kDomain2 = @"domain2.com";
+
 }  // namespace
 
 // Test case to verify that enterprise policies are set and respected.
@@ -122,6 +131,7 @@
 
 @implementation PolicyTestCase {
   BOOL _settingsOpened;
+  std::unique_ptr<policy::EmbeddedPolicyTestServer> test_server_;
 }
 
 - (void)tearDown {
@@ -411,7 +421,7 @@
                            kLanguageSettingsTableViewAccessibilityIdentifier);
 }
 
-// Test whether the managed item will be shown if a policy is set.
+// Tests whether the managed item will be shown if a policy is set.
 - (void)testPopupMenuItem {
   // Setup a machine level policy.
   SetPolicy(false, policy::key::kTranslateEnabled);
@@ -428,7 +438,7 @@
       assertWithMatcher:grey_notNil()];
 }
 
-// Test the chrome://management page when no machine level policy is set.
+// Tests the chrome://management page when no machine level policy is set.
 - (void)testManagementPageUnmanaged {
   // Open the management page and check if the content is expected.
   [ChromeEarlGrey loadURL:GURL(kChromeUIManagementURL)];
@@ -437,8 +447,8 @@
                                         IDS_IOS_MANAGEMENT_UI_UNMANAGED_DESC)];
 }
 
-// Test the chrome://management page when one or more machine level policies are
-// set.
+// Tests the chrome://management page when one or more machine level policies
+// are set.
 - (void)testManagementPageManaged {
   // Setup a machine level policy.
   SetPolicy(false, policy::key::kTranslateEnabled);
@@ -453,6 +463,118 @@
   [ChromeEarlGrey tapWebStateElementWithID:@"learn-more-link"];
 }
 
+// Tests the chrome://management page when there are machine level policies.
+- (void)testManagementPageManagedWithCBCM {
+  test_server_ = std::make_unique<policy::EmbeddedPolicyTestServer>();
+  test_server_->Start();
+
+  // Enable machine level (browser) cloud policies.
+  AppLaunchConfiguration config;
+  config.additional_args.push_back(
+      base::StrCat({"--", switches::kEnableChromeBrowserCloudManagement}));
+  config.additional_args.push_back("-com.apple.configuration.managed");
+  // Use an enrollment token that will start chrome browser cloud management
+  // without making network calls.
+  config.additional_args.push_back(
+      base::StrCat({"<dict><key>CloudManagementEnrollmentToken</key><string>",
+                    policy::kInvalidEnrollmentToken, "</string></dict>"}));
+  // Use the embedded test server as the policy server.
+  config.additional_args.push_back(
+      base::StrCat({"--", policy::switches::kDeviceManagementUrl, "=",
+                    test_server_->GetServiceURL().spec()}));
+  [[AppLaunchManager sharedManager] ensureAppLaunchedWithConfiguration:config];
+
+  [PolicyAppInterface setBrowserCloudPolicyDataWithDomain:kDomain1];
+
+  // Open the management page and check if the content is expected.
+  [ChromeEarlGrey loadURL:GURL(kChromeUIManagementURL)];
+  [ChromeEarlGrey
+      waitForWebStateContainingText:l10n_util::GetStringFUTF8(
+                                        IDS_MANAGEMENT_SUBTITLE_MANAGED_BY,
+                                        base::SysNSStringToUTF16(kDomain1))];
+}
+
+// Tests the chrome://management page when there are user level policies.
+- (void)testManagementPageManagedWithUserPolicy {
+  [PolicyAppInterface setUserCloudPolicyDataWithDomain:kDomain1];
+
+  // Open the management page and check if the content is expected.
+  [ChromeEarlGrey loadURL:GURL(kChromeUIManagementURL)];
+  [ChromeEarlGrey
+      waitForWebStateContainingText:
+          l10n_util::GetStringFUTF8(IDS_MANAGEMENT_SUBTITLE_PROFILE_MANAGED_BY,
+                                    base::SysNSStringToUTF16(kDomain1))];
+}
+
+// Tests the chrome://management page when there are machine level policies and
+// user level policies from the same domain.
+- (void)testManagementPageManagedWithCBCMAndUserPolicyDifferentDomains {
+  test_server_ = std::make_unique<policy::EmbeddedPolicyTestServer>();
+  test_server_->Start();
+
+  // Enable browser cloud policies.
+  AppLaunchConfiguration config;
+  config.additional_args.push_back(
+      base::StrCat({"--", switches::kEnableChromeBrowserCloudManagement}));
+  config.additional_args.push_back("-com.apple.configuration.managed");
+  // Use a CBCM enrollment token that will start chrome browser cloud management
+  // without making network calls.
+  config.additional_args.push_back(
+      base::StrCat({"<dict><key>CloudManagementEnrollmentToken</key><string>",
+                    policy::kInvalidEnrollmentToken, "</string></dict>"}));
+  // Use the embedded test server as the policy server.
+  config.additional_args.push_back(
+      base::StrCat({"--", policy::switches::kDeviceManagementUrl, "=",
+                    test_server_->GetServiceURL().spec()}));
+  [[AppLaunchManager sharedManager] ensureAppLaunchedWithConfiguration:config];
+
+  [PolicyAppInterface setBrowserCloudPolicyDataWithDomain:kDomain1];
+  [PolicyAppInterface setUserCloudPolicyDataWithDomain:kDomain2];
+
+  // Open the management page and check if the content is expected.
+  [ChromeEarlGrey loadURL:GURL(kChromeUIManagementURL)];
+  [ChromeEarlGrey
+      waitForWebStateContainingText:
+          l10n_util::GetStringFUTF8(
+              IDS_MANAGEMENT_SUBTITLE_BROWSER_AND_PROFILE_DIFFERENT_MANAGED_BY,
+              base::SysNSStringToUTF16(kDomain1),
+              base::SysNSStringToUTF16(kDomain2))];
+}
+
+// Tests the chrome://management page when there are machine level policies and
+// user level policies from different domains.
+- (void)testManagementPageManagedWithCBCMAndUserPolicySameDomains {
+  test_server_ = std::make_unique<policy::EmbeddedPolicyTestServer>();
+  test_server_->Start();
+
+  // Enable browser cloud policies.
+  AppLaunchConfiguration config;
+  config.additional_args.push_back(
+      base::StrCat({"--", switches::kEnableChromeBrowserCloudManagement}));
+  config.additional_args.push_back("-com.apple.configuration.managed");
+  // Use a CBCM enrollment token that will start chrome browser cloud management
+  // without making network calls.
+  config.additional_args.push_back(
+      base::StrCat({"<dict><key>CloudManagementEnrollmentToken</key><string>",
+                    policy::kInvalidEnrollmentToken, "</string></dict>"}));
+  // Use the embedded test server as the policy server.
+  config.additional_args.push_back(
+      base::StrCat({"--", policy::switches::kDeviceManagementUrl, "=",
+                    test_server_->GetServiceURL().spec()}));
+  [[AppLaunchManager sharedManager] ensureAppLaunchedWithConfiguration:config];
+
+  [PolicyAppInterface setBrowserCloudPolicyDataWithDomain:kDomain1];
+  [PolicyAppInterface setUserCloudPolicyDataWithDomain:kDomain1];
+
+  // Open the management page and check if the content is expected.
+  [ChromeEarlGrey loadURL:GURL(kChromeUIManagementURL)];
+  [ChromeEarlGrey
+      waitForWebStateContainingText:
+          l10n_util::GetStringFUTF8(
+              IDS_MANAGEMENT_SUBTITLE_BROWSER_AND_PROFILE_SAME_MANAGED_BY,
+              base::SysNSStringToUTF16(kDomain1))];
+}
+
 // Tests that when the BrowserSignin policy is updated while the app is not
 // launched, a policy screen is displayed at startup.
 - (void)testBrowserSignInDisabledAtStartup {
diff --git a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.h b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.h
index 44806e0..fb7e61b 100644
--- a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.h
+++ b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.h
@@ -129,7 +129,7 @@
   bool ShouldShowSigninPromo() override;
   bool AreServerCardsSupported() const override;
   void ExecuteCommand(int id) override;
-  void OnPromoCodeSuggestionsFooterSelected(const GURL& url) override;
+  void OpenPromoCodeOfferDetailsURL(const GURL& url) override;
 
   // RiskDataLoader:
   void LoadRiskData(
diff --git a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm
index 9e656d8..065bcbc 100644
--- a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm
+++ b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm
@@ -432,8 +432,7 @@
   NOTIMPLEMENTED();
 }
 
-void ChromeAutofillClientIOS::OnPromoCodeSuggestionsFooterSelected(
-    const GURL& url) {
+void ChromeAutofillClientIOS::OpenPromoCodeOfferDetailsURL(const GURL& url) {
   web_state_->OpenURL(web::WebState::OpenURLParams(
       url, web::Referrer(), WindowOpenDisposition::NEW_FOREGROUND_TAB,
       ui::PageTransition::PAGE_TRANSITION_AUTO_TOPLEVEL,
diff --git a/ios/chrome/browser/ui/first_run/BUILD.gn b/ios/chrome/browser/ui/first_run/BUILD.gn
index 1dfb408..8d2e8558 100644
--- a/ios/chrome/browser/ui/first_run/BUILD.gn
+++ b/ios/chrome/browser/ui/first_run/BUILD.gn
@@ -190,9 +190,11 @@
   testonly = true
   sources = [
     "first_run_util_unittest.mm",
+    "fre_field_trial_unittest.mm",
     "welcome_to_chrome_view_controller_unittest.mm",
   ]
   deps = [
+    ":field_trial",
     ":first_run",
     ":utils",
     "//base",
@@ -201,8 +203,10 @@
     "//components/policy/core/common:common_constants",
     "//components/prefs",
     "//components/prefs:test_support",
+    "//components/variations",
     "//ios/chrome/browser/browser_state:test_support",
     "//ios/chrome/browser/main:test_support",
+    "//ios/chrome/browser/ui:feature_flags",
     "//ios/chrome/browser/ui/authentication",
     "//ios/chrome/browser/ui/fancy_ui",
     "//ios/chrome/browser/ui/util",
diff --git a/ios/chrome/browser/ui/first_run/fre_field_trial.cc b/ios/chrome/browser/ui/first_run/fre_field_trial.cc
index a0e3dc16..b4816ac 100644
--- a/ios/chrome/browser/ui/first_run/fre_field_trial.cc
+++ b/ios/chrome/browser/ui/first_run/fre_field_trial.cc
@@ -4,13 +4,14 @@
 
 #include "ios/chrome/browser/ui/first_run/fre_field_trial.h"
 
+#include <map>
+
 #include "base/feature_list.h"
 #include "base/metrics/field_trial.h"
 #include "base/metrics/field_trial_params.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "components/signin/ios/browser/features.h"
-#include "components/variations/variations_associated_data.h"
 #include "components/version_info/version_info.h"
 #include "ios/chrome/browser/first_run/first_run.h"
 #include "ios/chrome/browser/ui/first_run/ios_first_run_field_trials.h"
@@ -72,7 +73,7 @@
 // Experiment IDs defined for the above field trial groups.
 const variations::VariationID kControlTrialID = 3348210;
 const variations::VariationID kHoldbackTrialID = 3348217;
-const variations::VariationID kDefaultBrowserPromoAtFirstRunOnlyID = 3348842;
+const variations::VariationID kFREDefaultBrowserPromoAtFirstRunOnlyID = 3348842;
 const variations::VariationID
     kFREDefaultBrowserAndDefaultDelayBeforeOtherPromosID = 3348843;
 const variations::VariationID
@@ -116,6 +117,19 @@
         NewMobileIdentityConsistencyFRE::kUMADialog,
         &kNewMobileIdentityConsistencyFREOptions};
 
+// Adds a trial group to a FRE field trial config with the given group name,
+// variation ID, and weight.
+void AddGroupToConfig(
+    const std::string& group_name,
+    const variations::VariationID group_id,
+    const std::map<variations::VariationID, int>& weight_by_id,
+    FirstRunFieldTrialConfig& config) {
+  auto it = weight_by_id.find(group_id);
+  DCHECK(it != weight_by_id.end())
+      << "Required variation ID missing: " << group_id;
+  config.AddGroup(group_name, group_id, it->second);
+}
+
 // Sets the parameter value of the new default browser parameter.
 void AssociateFieldTrialParamsForDefaultBrowserGroup(
     const std::string& group_name,
@@ -156,10 +170,38 @@
   return NewMobileIdentityConsistencyFRE::kOld;
 }
 
+// Returns the weight for each trial group according to the FRE variations.
+std::map<variations::VariationID, int> GetGroupWeightsForFREVariations() {
+  std::map<variations::VariationID, int> weight_by_id = {
+      {kControlTrialID, 0},
+      {kHoldbackTrialID, 0},
+      {kNewMICEFREWithUMADialogSetID, 0},
+      {kNewMICEFREWithThreeStepsSetID, 0},
+      {kNewMICEFREWithTwoStepsSetID, 0},
+      {kFREDefaultBrowserAndDefaultDelayBeforeOtherPromosID, 0},
+      {kFREDefaultBrowserAndSmallDelayBeforeOtherPromosID, 0},
+      {kFREDefaultBrowserPromoAtFirstRunOnlyID, 0}};
+  switch (GetChannel()) {
+    case version_info::Channel::UNKNOWN:
+    case version_info::Channel::CANARY:
+    case version_info::Channel::DEV:
+    case version_info::Channel::BETA:
+      std::for_each(
+          weight_by_id.begin(), weight_by_id.end(),
+          [&](auto& weight_by_id_pair) { weight_by_id_pair.second = 10; });
+      break;
+    case version_info::Channel::STABLE:
+      std::for_each(
+          weight_by_id.begin(), weight_by_id.end(),
+          [&](auto& weight_by_id_pair) { weight_by_id_pair.second = 8; });
+      break;
+  }
+  return weight_by_id;
+}
+
 // Creates the trial config, initializes the trial that puts clients into
 // different groups, and returns the version number of the current trial. There
-// are 9 groups:
-// - Default
+// are 8 groups other than the default group:
 // - Control
 // - Holdback
 // - New MICE FRE with UMA dialog
@@ -169,70 +211,32 @@
 // - FRE default browser promo: show 3 days after first run
 // - FRE default browser promo: only on first run
 int CreateNewMICeAndDefaultBrowserFRETrial(
+    const std::map<variations::VariationID, int>& weight_by_id,
     const base::FieldTrial::EntropyProvider& low_entropy_provider,
     base::FeatureList* feature_list) {
-  // Experiment groups
-  int new_fre_control_percent = 0;
-  int new_fre_holdback_percent = 0;
-  // MICe FRE experiment.
-  int new_fre_with_uma_dialog_set_percent = 0;
-  int new_fre_with_three_steps_set_percent = 0;
-  int new_fre_with_two_steps_set_percent = 0;
-  // FRE's default browser screen experiment
-  int new_fre_with_default_screen_and_default_cooldown_percent = 0;
-  int new_fre_with_default_screen_and_short_cooldown_percent = 0;
-  int new_fre_with_default_screen_only_percent = 0;
-
-  switch (GetChannel()) {
-    case version_info::Channel::UNKNOWN:
-    case version_info::Channel::CANARY:
-    case version_info::Channel::DEV:
-    case version_info::Channel::BETA:
-      new_fre_control_percent = 10;
-      new_fre_holdback_percent = 10;
-      new_fre_with_uma_dialog_set_percent = 10;
-      new_fre_with_three_steps_set_percent = 10;
-      new_fre_with_two_steps_set_percent = 10;
-      new_fre_with_default_screen_and_default_cooldown_percent = 10;
-      new_fre_with_default_screen_and_short_cooldown_percent = 10;
-      new_fre_with_default_screen_only_percent = 10;
-      break;
-    case version_info::Channel::STABLE:
-      new_fre_control_percent = 8;
-      new_fre_holdback_percent = 8;
-      new_fre_with_uma_dialog_set_percent = 8;
-      new_fre_with_three_steps_set_percent = 8;
-      new_fre_with_two_steps_set_percent = 8;
-      new_fre_with_default_screen_and_default_cooldown_percent = 8;
-      new_fre_with_default_screen_and_short_cooldown_percent = 8;
-      new_fre_with_default_screen_only_percent = 8;
-      break;
-  }
-
   // Set up the trial and groups.
   FirstRunFieldTrialConfig config(kIOSMICeAndDefaultBrowserTrialName);
+
   // Disabled and control groups.
-  config.AddGroup(kControlGroup, kControlTrialID, new_fre_control_percent);
-  config.AddGroup(kHoldbackGroup, kHoldbackTrialID, new_fre_holdback_percent);
-  // MICe experiment groups. (No default browser promo.)
-  config.AddGroup(kNewMICEFREWithUMADialogSetGroup,
-                  kNewMICEFREWithUMADialogSetID,
-                  new_fre_with_uma_dialog_set_percent);
-  config.AddGroup(kNewMICEFREWithThreeStepsSetGroup,
-                  kNewMICEFREWithThreeStepsSetID,
-                  new_fre_with_three_steps_set_percent);
-  config.AddGroup(kNewMICEFREWithTwoStepsSetGroup, kNewMICEFREWithTwoStepsSetID,
-                  new_fre_with_two_steps_set_percent);
+  AddGroupToConfig(kControlGroup, kControlTrialID, weight_by_id, config);
+  AddGroupToConfig(kHoldbackGroup, kHoldbackTrialID, weight_by_id, config);
+  // MICe experiment. (No default browser promo.)
+  AddGroupToConfig(kNewMICEFREWithUMADialogSetGroup,
+                   kNewMICEFREWithUMADialogSetID, weight_by_id, config);
+  AddGroupToConfig(kNewMICEFREWithThreeStepsSetGroup,
+                   kNewMICEFREWithThreeStepsSetID, weight_by_id, config);
+  AddGroupToConfig(kNewMICEFREWithTwoStepsSetGroup,
+                   kNewMICEFREWithTwoStepsSetID, weight_by_id, config);
   // Default browser promo experiment groups. (New FRE with MICe disabled.)
-  config.AddGroup(kFREDefaultBrowserAndDefaultDelayBeforeOtherPromosGroup,
-                  kFREDefaultBrowserAndDefaultDelayBeforeOtherPromosID,
-                  new_fre_with_default_screen_and_default_cooldown_percent);
-  config.AddGroup(kFREDefaultBrowserAndSmallDelayBeforeOtherPromosGroup,
-                  kFREDefaultBrowserAndSmallDelayBeforeOtherPromosID,
-                  new_fre_with_default_screen_and_short_cooldown_percent);
-  config.AddGroup(kDefaultBrowserPromoAtFirstRunOnlyGroup,
-                  kDefaultBrowserPromoAtFirstRunOnlyID,
-                  new_fre_with_default_screen_only_percent);
+  AddGroupToConfig(kFREDefaultBrowserAndDefaultDelayBeforeOtherPromosGroup,
+                   kFREDefaultBrowserAndDefaultDelayBeforeOtherPromosID,
+                   weight_by_id, config);
+  AddGroupToConfig(kFREDefaultBrowserAndSmallDelayBeforeOtherPromosGroup,
+                   kFREDefaultBrowserAndSmallDelayBeforeOtherPromosID,
+                   weight_by_id, config);
+  AddGroupToConfig(kDefaultBrowserPromoAtFirstRunOnlyGroup,
+                   kFREDefaultBrowserPromoAtFirstRunOnlyID, weight_by_id,
+                   config);
 
   // Associate field trial params to each group.
   AssociateFieldTrialParamsForNewMICEFREGroup(
@@ -340,11 +344,13 @@
           signin::kNewMobileIdentityConsistencyFRE.name)) {
     return;
   }
+  const std::map<variations::VariationID, int> weight_by_id =
+      GetGroupWeightsForFREVariations();
   if (FirstRun::IsChromeFirstRun()) {
     // Create trial and group for the first time, and store the experiment
     // version in prefs for subsequent runs.
     int trial_version = CreateNewMICeAndDefaultBrowserFRETrial(
-        low_entropy_provider, feature_list);
+        weight_by_id, low_entropy_provider, feature_list);
     local_state->SetInteger(kTrialGroupMICeAndDefaultBrowserVersionPrefName,
                             trial_version);
   } else if (local_state->GetInteger(
@@ -353,8 +359,17 @@
     // The client was enrolled in this version of the experiment and was
     // assigned to a group in a previous run, and should be kept in the same
     // group.
-    CreateNewMICeAndDefaultBrowserFRETrial(low_entropy_provider, feature_list);
+    CreateNewMICeAndDefaultBrowserFRETrial(weight_by_id, low_entropy_provider,
+                                           feature_list);
   }
 }
 
+int testing::CreateNewMICeAndDefaultBrowserFRETrialForTesting(
+    const std::map<variations::VariationID, int>& weight_by_id,
+    const base::FieldTrial::EntropyProvider& low_entropy_provider,
+    base::FeatureList* feature_list) {
+  return CreateNewMICeAndDefaultBrowserFRETrial(
+      weight_by_id, low_entropy_provider, feature_list);
+}
+
 }  // namespace fre_field_trial
diff --git a/ios/chrome/browser/ui/first_run/fre_field_trial.h b/ios/chrome/browser/ui/first_run/fre_field_trial.h
index ed9dabc..a5730da 100644
--- a/ios/chrome/browser/ui/first_run/fre_field_trial.h
+++ b/ios/chrome/browser/ui/first_run/fre_field_trial.h
@@ -6,6 +6,7 @@
 #define IOS_CHROME_BROWSER_UI_FIRST_RUN_FRE_FIELD_TRIAL_H_
 
 #include "base/metrics/field_trial.h"
+#include "components/variations/variations_associated_data.h"
 
 class PrefRegistrySimple;
 class PrefService;
@@ -83,6 +84,17 @@
             base::FeatureList* feature_list,
             PrefService* local_state);
 
+namespace testing {
+
+// Exposes CreateNewMICeAndDefaultBrowserFRETrial() for testing FieldTrial
+// set-up.
+int CreateNewMICeAndDefaultBrowserFRETrialForTesting(
+    const std::map<variations::VariationID, int>& weight_by_id,
+    const base::FieldTrial::EntropyProvider& low_entropy_provider,
+    base::FeatureList* feature_list);
+
+}  // namespace testing
+
 }  // namespace fre_field_trial
 
 #endif  // IOS_CHROME_BROWSER_UI_FIRST_RUN_FRE_FIELD_TRIAL_H_
diff --git a/ios/chrome/browser/ui/first_run/fre_field_trial_unittest.mm b/ios/chrome/browser/ui/first_run/fre_field_trial_unittest.mm
new file mode 100644
index 0000000..c31e61cc
--- /dev/null
+++ b/ios/chrome/browser/ui/first_run/fre_field_trial_unittest.mm
@@ -0,0 +1,245 @@
+// 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 "ios/chrome/browser/ui/first_run/fre_field_trial.h"
+
+#import "base/feature_list.h"
+#import "base/metrics/field_trial.h"
+#import "base/metrics/field_trial_param_associator.h"
+#import "base/test/mock_entropy_provider.h"
+#import "base/test/scoped_feature_list.h"
+#import "components/variations/variations_associated_data.h"
+#import "ios/chrome/browser/ui/ui_feature_flags.h"
+#import "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace fre_field_trial {
+namespace {
+
+using ::base::FeatureList;
+using ::base::FieldTrialList;
+using ::fre_field_trial::testing::
+    CreateNewMICeAndDefaultBrowserFRETrialForTesting;
+using ::variations::VariationID;
+
+// Experiment IDs defined for the above field trial groups.
+const VariationID kControlTrialID = 3348210;
+const VariationID kHoldbackTrialID = 3348217;
+const VariationID kFREDefaultBrowserPromoAtFirstRunOnlyID = 3348842;
+const VariationID kFREDefaultBrowserAndDefaultDelayBeforeOtherPromosID =
+    3348843;
+const VariationID kFREDefaultBrowserAndSmallDelayBeforeOtherPromosID = 3348844;
+const VariationID kNewMICEFREWithUMADialogSetID = 3348845;
+const VariationID kNewMICEFREWithThreeStepsSetID = 3348846;
+const VariationID kNewMICEFREWithTwoStepsSetID = 3348847;
+
+}  // namespace
+
+// Tests for field trial creation on FRE for the current experiment. Each test
+// case tests one experiment arm.
+class FREFieldTrialTest : public PlatformTest {
+ protected:
+  void SetUp() override {
+    weight_by_id_ = {{kControlTrialID, 0},
+                     {kHoldbackTrialID, 0},
+                     {kNewMICEFREWithUMADialogSetID, 0},
+                     {kNewMICEFREWithThreeStepsSetID, 0},
+                     {kNewMICEFREWithTwoStepsSetID, 0},
+                     {kFREDefaultBrowserAndDefaultDelayBeforeOtherPromosID, 0},
+                     {kFREDefaultBrowserAndSmallDelayBeforeOtherPromosID, 0},
+                     {kFREDefaultBrowserPromoAtFirstRunOnlyID, 0}};
+  }
+
+  void TearDown() override {
+    scoped_feature_list_.Reset();
+    base::FieldTrialParamAssociator::GetInstance()->ClearAllParamsForTesting();
+  }
+
+  base::test::ScopedFeatureList scoped_feature_list_;
+  std::map<variations::VariationID, int> weight_by_id_;
+  base::MockEntropyProvider low_entropy_provider_;
+};
+
+// Tests default FRE field trial.
+TEST_F(FREFieldTrialTest, TestFREDefault) {
+  // Create the FRE trial with an empty feature list.
+  auto feature_list = std::make_unique<FeatureList>();
+  // To force default behavior, all groups have a probability of 0.
+  CreateNewMICeAndDefaultBrowserFRETrialForTesting(
+      weight_by_id_, low_entropy_provider_, feature_list.get());
+
+  // Substitute the existing feature list with the one with field trial
+  // configurations we are testing, and check assertions.
+  scoped_feature_list_.InitWithFeatureList(std::move(feature_list));
+  ASSERT_TRUE(
+      FieldTrialList::IsTrialActive(kIOSMICeAndDefaultBrowserTrialName));
+  EXPECT_TRUE(FeatureList::IsEnabled(kEnableFREUIModuleIOS));
+  EXPECT_EQ(NewDefaultBrowserPromoFRE::kDisabled,
+            GetFREDefaultBrowserScreenPromoFRE());
+  EXPECT_EQ(NewMobileIdentityConsistencyFRE::kOld,
+            GetNewMobileIdentityConsistencyFRE());
+}
+
+// Tests control FRE field trial.
+TEST_F(FREFieldTrialTest, TestFREControl) {
+  // Create the FRE trial with an empty feature list.
+  auto feature_list = std::make_unique<FeatureList>();
+  weight_by_id_[kControlTrialID] = 100;
+  CreateNewMICeAndDefaultBrowserFRETrialForTesting(
+      weight_by_id_, low_entropy_provider_, feature_list.get());
+
+  // Substitute the existing feature list with the one with field trial
+  // configurations we are testing, and check assertions.
+  scoped_feature_list_.InitWithFeatureList(std::move(feature_list));
+  ASSERT_TRUE(
+      FieldTrialList::IsTrialActive(kIOSMICeAndDefaultBrowserTrialName));
+  EXPECT_TRUE(FeatureList::IsEnabled(kEnableFREUIModuleIOS));
+  EXPECT_EQ(NewDefaultBrowserPromoFRE::kDisabled,
+            GetFREDefaultBrowserScreenPromoFRE());
+  EXPECT_EQ(NewMobileIdentityConsistencyFRE::kOld,
+            GetNewMobileIdentityConsistencyFRE());
+}
+
+// Tests holdback FRE field trial.
+TEST_F(FREFieldTrialTest, TestFREHoldback) {
+  // Create the FRE trial with an empty feature list.
+  auto feature_list = std::make_unique<FeatureList>();
+  weight_by_id_[kHoldbackTrialID] = 100;
+  CreateNewMICeAndDefaultBrowserFRETrialForTesting(
+      weight_by_id_, low_entropy_provider_, feature_list.get());
+
+  // Substitute the existing feature list with the one with field trial
+  // configurations we are testing, and check assertions.
+  scoped_feature_list_.InitWithFeatureList(std::move(feature_list));
+  ASSERT_TRUE(
+      FieldTrialList::IsTrialActive(kIOSMICeAndDefaultBrowserTrialName));
+  EXPECT_FALSE(FeatureList::IsEnabled(kEnableFREUIModuleIOS));
+  EXPECT_EQ(NewDefaultBrowserPromoFRE::kDisabled,
+            GetFREDefaultBrowserScreenPromoFRE());
+}
+
+// Tests default browser promo (first run) FRE field trial.
+TEST_F(FREFieldTrialTest, TestFREDefaultBrowserPromoFirstRun) {
+  // Create the FRE trial with an empty feature list.
+  auto feature_list = std::make_unique<FeatureList>();
+  weight_by_id_[kFREDefaultBrowserPromoAtFirstRunOnlyID] = 100;
+  CreateNewMICeAndDefaultBrowserFRETrialForTesting(
+      weight_by_id_, low_entropy_provider_, feature_list.get());
+
+  // Substitute the existing feature list with the one with field trial
+  // configurations we are testing, and check assertions.
+  scoped_feature_list_.InitWithFeatureList(std::move(feature_list));
+  ASSERT_TRUE(
+      FieldTrialList::IsTrialActive(kIOSMICeAndDefaultBrowserTrialName));
+  EXPECT_TRUE(FeatureList::IsEnabled(kEnableFREUIModuleIOS));
+  EXPECT_EQ(NewDefaultBrowserPromoFRE::kFirstRunOnly,
+            GetFREDefaultBrowserScreenPromoFRE());
+  EXPECT_EQ(NewMobileIdentityConsistencyFRE::kOld,
+            GetNewMobileIdentityConsistencyFRE());
+}
+
+// Tests default browser promo (default delay) FRE field trial.
+TEST_F(FREFieldTrialTest, TestFREDefaultBrowserPromoDefaultDelay) {
+  // Create the FRE trial with an empty feature list.
+  auto feature_list = std::make_unique<FeatureList>();
+  weight_by_id_[kFREDefaultBrowserAndDefaultDelayBeforeOtherPromosID] = 100;
+  CreateNewMICeAndDefaultBrowserFRETrialForTesting(
+      weight_by_id_, low_entropy_provider_, feature_list.get());
+
+  // Substitute the existing feature list with the one with field trial
+  // configurations we are testing, and check assertions.
+  scoped_feature_list_.InitWithFeatureList(std::move(feature_list));
+  ASSERT_TRUE(
+      FieldTrialList::IsTrialActive(kIOSMICeAndDefaultBrowserTrialName));
+  EXPECT_TRUE(FeatureList::IsEnabled(kEnableFREUIModuleIOS));
+  EXPECT_EQ(NewDefaultBrowserPromoFRE::kDefaultDelay,
+            GetFREDefaultBrowserScreenPromoFRE());
+  EXPECT_EQ(NewMobileIdentityConsistencyFRE::kOld,
+            GetNewMobileIdentityConsistencyFRE());
+}
+
+// Tests default browser promo (short delay) FRE field trial.
+TEST_F(FREFieldTrialTest, TestFREDefaultBrowserPromoSmallDelay) {
+  // Create the FRE trial with an empty feature list.
+  auto feature_list = std::make_unique<FeatureList>();
+  weight_by_id_[kFREDefaultBrowserAndSmallDelayBeforeOtherPromosID] = 100;
+  CreateNewMICeAndDefaultBrowserFRETrialForTesting(
+      weight_by_id_, low_entropy_provider_, feature_list.get());
+
+  // Substitute the existing feature list with the one with field trial
+  // configurations we are testing, and check assertions.
+  scoped_feature_list_.InitWithFeatureList(std::move(feature_list));
+  ASSERT_TRUE(
+      FieldTrialList::IsTrialActive(kIOSMICeAndDefaultBrowserTrialName));
+  EXPECT_TRUE(FeatureList::IsEnabled(kEnableFREUIModuleIOS));
+  EXPECT_EQ(NewDefaultBrowserPromoFRE::kShortDelay,
+            GetFREDefaultBrowserScreenPromoFRE());
+  EXPECT_EQ(NewMobileIdentityConsistencyFRE::kOld,
+            GetNewMobileIdentityConsistencyFRE());
+}
+
+// Tests MICe with UMA dialog FRE field trial.
+TEST_F(FREFieldTrialTest, TestFREUMADialogMICe) {
+  // Create the FRE trial with an empty feature list.
+  auto feature_list = std::make_unique<FeatureList>();
+  weight_by_id_[kNewMICEFREWithUMADialogSetID] = 100;
+  CreateNewMICeAndDefaultBrowserFRETrialForTesting(
+      weight_by_id_, low_entropy_provider_, feature_list.get());
+
+  // Substitute the existing feature list with the one with field trial
+  // configurations we are testing, and check assertions.
+  scoped_feature_list_.InitWithFeatureList(std::move(feature_list));
+  ASSERT_TRUE(
+      FieldTrialList::IsTrialActive(kIOSMICeAndDefaultBrowserTrialName));
+  EXPECT_TRUE(FeatureList::IsEnabled(kEnableFREUIModuleIOS));
+  EXPECT_EQ(NewDefaultBrowserPromoFRE::kDisabled,
+            GetFREDefaultBrowserScreenPromoFRE());
+  EXPECT_EQ(NewMobileIdentityConsistencyFRE::kUMADialog,
+            GetNewMobileIdentityConsistencyFRE());
+}
+
+// Tests MICe with three steps FRE field trial.
+TEST_F(FREFieldTrialTest, TestFREThreeStepMICe) {
+  // Create the FRE trial with an empty feature list.
+  auto feature_list = std::make_unique<FeatureList>();
+  weight_by_id_[kNewMICEFREWithThreeStepsSetID] = 100;
+  CreateNewMICeAndDefaultBrowserFRETrialForTesting(
+      weight_by_id_, low_entropy_provider_, feature_list.get());
+
+  // Substitute the existing feature list with the one with field trial
+  // configurations we are testing, and check assertions.
+  scoped_feature_list_.InitWithFeatureList(std::move(feature_list));
+  ASSERT_TRUE(
+      FieldTrialList::IsTrialActive(kIOSMICeAndDefaultBrowserTrialName));
+  EXPECT_TRUE(FeatureList::IsEnabled(kEnableFREUIModuleIOS));
+  EXPECT_EQ(NewDefaultBrowserPromoFRE::kDisabled,
+            GetFREDefaultBrowserScreenPromoFRE());
+  EXPECT_EQ(NewMobileIdentityConsistencyFRE::kThreeSteps,
+            GetNewMobileIdentityConsistencyFRE());
+}
+
+// Tests MICe with two steps FRE field trial.
+TEST_F(FREFieldTrialTest, TestFRETwoStepMICe) {
+  // Create the FRE trial with an empty feature list.
+  auto feature_list = std::make_unique<FeatureList>();
+  weight_by_id_[kNewMICEFREWithTwoStepsSetID] = 100;
+  CreateNewMICeAndDefaultBrowserFRETrialForTesting(
+      weight_by_id_, low_entropy_provider_, feature_list.get());
+
+  // Substitute the existing feature list with the one with field trial
+  // configurations we are testing, and check assertions.
+  scoped_feature_list_.InitWithFeatureList(std::move(feature_list));
+  ASSERT_TRUE(
+      FieldTrialList::IsTrialActive(kIOSMICeAndDefaultBrowserTrialName));
+  EXPECT_TRUE(FeatureList::IsEnabled(kEnableFREUIModuleIOS));
+  EXPECT_EQ(NewDefaultBrowserPromoFRE::kDisabled,
+            GetFREDefaultBrowserScreenPromoFRE());
+  EXPECT_EQ(NewMobileIdentityConsistencyFRE::kTwoSteps,
+            GetNewMobileIdentityConsistencyFRE());
+}
+
+}  // namespace fre_field_trial
diff --git a/ios/chrome/browser/ui/omnibox/BUILD.gn b/ios/chrome/browser/ui/omnibox/BUILD.gn
index f0098bcd..1c47beb 100644
--- a/ios/chrome/browser/ui/omnibox/BUILD.gn
+++ b/ios/chrome/browser/ui/omnibox/BUILD.gn
@@ -136,6 +136,8 @@
     "omnibox_view_ios.mm",
     "web_omnibox_edit_controller_impl.h",
     "web_omnibox_edit_controller_impl.mm",
+    "zero_suggest_prefetch_helper.h",
+    "zero_suggest_prefetch_helper.mm",
   ]
   deps = [
     ":features",
@@ -277,6 +279,7 @@
     "omnibox_text_field_ios_experimental_unittest.mm",
     "omnibox_text_field_ios_unittest.mm",
     "omnibox_view_ios_unittest.mm",
+    "zero_suggest_prefetch_helper_unittest.mm",
   ]
   deps = [
     ":omnibox",
@@ -284,11 +287,18 @@
     ":resources_unit_tests",
     "//base",
     "//base/test:test_support",
+    "//components/omnibox/browser:test_support",
+    "//components/search_engines",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser",
     "//ios/chrome/browser/browser_state:test_support",
+    "//ios/chrome/browser/main",
+    "//ios/chrome/browser/main:test_support",
     "//ios/chrome/browser/ui/util",
+    "//ios/chrome/browser/web_state_list",
+    "//ios/chrome/browser/web_state_list:test_support",
     "//ios/web/common:uikit",
+    "//ios/web/public/test/fakes",
     "//testing/gtest",
     "//third_party/ocmock",
     "//ui/base",
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_coordinator.mm b/ios/chrome/browser/ui/omnibox/omnibox_coordinator.mm
index d85ed606..9dab297a 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_coordinator.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_coordinator.mm
@@ -10,6 +10,7 @@
 #include "base/metrics/user_metrics_action.h"
 #include "base/strings/sys_string_conversions.h"
 #include "components/omnibox/browser/omnibox_edit_model.h"
+#import "components/omnibox/common/omnibox_features.h"
 #include "components/omnibox/common/omnibox_focus_state.h"
 #include "components/open_from_clipboard/clipboard_recent_content.h"
 #include "components/strings/grit/components_strings.h"
@@ -41,6 +42,7 @@
 #import "ios/chrome/browser/ui/omnibox/popup/omnibox_popup_coordinator.h"
 #include "ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_ios.h"
 #import "ios/chrome/browser/ui/omnibox/popup/pedal_section_extractor.h"
+#import "ios/chrome/browser/ui/omnibox/zero_suggest_prefetch_helper.h"
 #include "ios/chrome/browser/ui/ui_feature_flags.h"
 #import "ios/chrome/browser/url_loading/image_search_param_generator.h"
 #import "ios/chrome/browser/url_loading/url_loading_browser_agent.h"
@@ -68,6 +70,10 @@
 // The return delegate.
 @property(nonatomic, strong) ForwardingReturnDelegate* returnDelegate;
 
+// Helper that starts ZPS prefetch when the user opens a NTP.
+@property(nonatomic, strong)
+    ZeroSuggestPrefetchHelper* zeroSuggestPrefetchHelper;
+
 @end
 
 @implementation OmniboxCoordinator {
@@ -134,6 +140,12 @@
   self.keyboardDelegate.omniboxTextField = self.textField;
   ConfigureAssistiveKeyboardViews(self.textField, kDotComTLD,
                                   self.keyboardDelegate);
+
+  if (base::FeatureList::IsEnabled(omnibox::kZeroSuggestPrefetching)) {
+    self.zeroSuggestPrefetchHelper = [[ZeroSuggestPrefetchHelper alloc]
+          initWithWebStateList:self.browser->GetWebStateList()
+        autocompleteController:_editView->model()->autocomplete_controller()];
+  }
 }
 
 - (void)stop {
@@ -144,6 +156,7 @@
   self.viewController = nil;
   self.mediator = nil;
   self.returnDelegate = nil;
+  self.zeroSuggestPrefetchHelper = nil;
 
   [NSNotificationCenter.defaultCenter removeObserver:self];
 }
diff --git a/ios/chrome/browser/ui/omnibox/zero_suggest_prefetch_helper.h b/ios/chrome/browser/ui/omnibox/zero_suggest_prefetch_helper.h
new file mode 100644
index 0000000..66f71e61
--- /dev/null
+++ b/ios/chrome/browser/ui/omnibox/zero_suggest_prefetch_helper.h
@@ -0,0 +1,33 @@
+// 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_CHROME_BROWSER_UI_OMNIBOX_ZERO_SUGGEST_PREFETCH_HELPER_H_
+#define IOS_CHROME_BROWSER_UI_OMNIBOX_ZERO_SUGGEST_PREFETCH_HELPER_H_
+
+#import <UIKit/UIKit.h>
+
+class WebStateList;
+class AutocompleteController;
+
+/// This object starts ZPS prefetch in the `autocompleteController` whenever an
+/// NTP is displayed in `webStateList`, specifically: upon creation of this
+/// object if the active web state is showing NTP; whenever a webstate that
+/// displays NTP is activated, or whenever the active web state navigates to the
+/// NTP.
+@interface ZeroSuggestPrefetchHelper : NSObject
+
+// Observed web state list.
+@property(nonatomic, readonly) WebStateList* webStateList;
+// The autocomplete controller for prefetching.
+@property(nonatomic, readonly) AutocompleteController* autocompleteController;
+
+// Designated initializer.
+- (instancetype)initWithWebStateList:(WebStateList*)webStateList
+              autocompleteController:
+                  (AutocompleteController*)autocompleteController;
+- (instancetype)init NS_UNAVAILABLE;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_OMNIBOX_ZERO_SUGGEST_PREFETCH_HELPER_H_
diff --git a/ios/chrome/browser/ui/omnibox/zero_suggest_prefetch_helper.mm b/ios/chrome/browser/ui/omnibox/zero_suggest_prefetch_helper.mm
new file mode 100644
index 0000000..2010ed6
--- /dev/null
+++ b/ios/chrome/browser/ui/omnibox/zero_suggest_prefetch_helper.mm
@@ -0,0 +1,95 @@
+// 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 "ios/chrome/browser/ui/omnibox/zero_suggest_prefetch_helper.h"
+
+#import "base/feature_list.h"
+#import "components/omnibox/browser/autocomplete_controller.h"
+#import "components/omnibox/browser/autocomplete_input.h"
+#import "components/omnibox/common/omnibox_features.h"
+#import "ios/chrome/browser/autocomplete/autocomplete_scheme_classifier_impl.h"
+#import "ios/chrome/browser/chrome_url_constants.h"
+#import "ios/chrome/browser/web_state_list/active_web_state_observation_forwarder.h"
+#import "ios/chrome/browser/web_state_list/web_state_list.h"
+#import "ios/web/public/web_state.h"
+#import "ios/web/public/web_state_observer_bridge.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+using web::WebState;
+using web::WebStateObserverBridge;
+
+@interface ZeroSuggestPrefetchHelper () <CRWWebStateObserver>
+@end
+
+@implementation ZeroSuggestPrefetchHelper {
+  /// Bridge to receive active web state events
+  std::unique_ptr<ActiveWebStateObservationForwarder>
+      _activeWebStateObservationForwarder;
+  /// Bridge to receive WS events for the active web state.
+  std::unique_ptr<WebStateObserverBridge> _webStateObserverBridge;
+}
+
+- (void)dealloc {
+  /// Reset the web state observation forwarder, which will remove
+  /// `_webStateObserverBridge` from the relevant observer list.
+  _activeWebStateObservationForwarder.reset();
+}
+
+- (instancetype)initWithWebStateList:(WebStateList*)webStateList
+              autocompleteController:
+                  (AutocompleteController*)autocompleteController {
+  self = [super init];
+  if (self) {
+    DCHECK(webStateList);
+    DCHECK(autocompleteController);
+    DCHECK(base::FeatureList::IsEnabled(omnibox::kZeroSuggestPrefetching));
+
+    _webStateList = webStateList;
+    _autocompleteController = autocompleteController;
+    _webStateObserverBridge = std::make_unique<WebStateObserverBridge>(self);
+    _activeWebStateObservationForwarder =
+        std::make_unique<ActiveWebStateObservationForwarder>(
+            webStateList, _webStateObserverBridge.get());
+    [self startPrefetchIfNecessary];
+  }
+  return self;
+}
+
+#pragma mark - CRWWebStateObserver
+
+- (void)webStateWasShown:(web::WebState*)webState {
+  [self startPrefetchIfNecessary];
+}
+
+- (void)webState:(web::WebState*)webState
+    didFinishNavigation:(web::NavigationContext*)navigationContext {
+  [self startPrefetchIfNecessary];
+}
+
+#pragma mark - private
+
++ (BOOL)isNTPURL:(GURL)url {
+  return url == kChromeUINewTabURL || url == kChromeUIAboutNewTabURL;
+}
+
+/// Start prefetching if the active web state is displaying an NTP.
+- (void)startPrefetchIfNecessary {
+  WebState* activeWebState = _webStateList->GetActiveWebState();
+  if (activeWebState == nullptr ||
+      ![ZeroSuggestPrefetchHelper
+          isNTPURL:activeWebState->GetLastCommittedURL()]) {
+    return;
+  }
+
+  AutocompleteInput autocomplete_input(
+      u"", metrics::OmniboxEventProto::NTP_ZPS_PREFETCH,
+      AutocompleteSchemeClassifierImpl());
+  autocomplete_input.set_focus_type(OmniboxFocusType::ON_FOCUS);
+  self.autocompleteController->StartPrefetch(autocomplete_input);
+}
+
+@end
diff --git a/ios/chrome/browser/ui/omnibox/zero_suggest_prefetch_helper_unittest.mm b/ios/chrome/browser/ui/omnibox/zero_suggest_prefetch_helper_unittest.mm
new file mode 100644
index 0000000..8aed1fa4
--- /dev/null
+++ b/ios/chrome/browser/ui/omnibox/zero_suggest_prefetch_helper_unittest.mm
@@ -0,0 +1,236 @@
+// 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 "ios/chrome/browser/ui/omnibox/zero_suggest_prefetch_helper.h"
+
+#import "base/test/scoped_feature_list.h"
+#import "base/test/task_environment.h"
+#import "components/omnibox/browser/autocomplete_controller.h"
+#import "components/omnibox/browser/mock_autocomplete_provider_client.h"
+#import "components/omnibox/common/omnibox_features.h"
+#import "components/search_engines/template_url_service.h"
+#import "components/search_engines/template_url_service_client.h"
+#import "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
+#import "ios/chrome/browser/chrome_url_constants.h"
+#import "ios/chrome/browser/main/browser_web_state_list_delegate.h"
+#import "ios/chrome/browser/main/test_browser.h"
+#import "ios/chrome/browser/ui/omnibox/omnibox_text_field_legacy.h"
+#import "ios/chrome/browser/web_state_list/fake_web_state_list_delegate.h"
+#import "ios/chrome/browser/web_state_list/web_state_list.h"
+#import "ios/chrome/browser/web_state_list/web_state_opener.h"
+#import "ios/web/public/test/fakes/fake_navigation_context.h"
+#import "ios/web/public/test/fakes/fake_navigation_manager.h"
+#import "ios/web/public/test/fakes/fake_web_state.h"
+#import "testing/gmock/include/gmock/gmock.h"
+#import "testing/gtest_mac.h"
+#import "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+using web::FakeWebState;
+
+namespace {
+
+const char kTestURL[] = "http://chromium.org";
+
+class MockAutocompleteController : public AutocompleteController {
+ public:
+  MockAutocompleteController(
+      std::unique_ptr<AutocompleteProviderClient> provider_client,
+      int provider_types)
+      : AutocompleteController(std::move(provider_client), provider_types) {}
+  ~MockAutocompleteController() override = default;
+  MockAutocompleteController(const MockAutocompleteController&) = delete;
+  MockAutocompleteController& operator=(const MockAutocompleteController&) =
+      delete;
+
+  // AutocompleteController:
+  MOCK_METHOD1(Start, void(const AutocompleteInput&));
+  MOCK_METHOD1(StartPrefetch, void(const AutocompleteInput&));
+};
+
+}  // namespace
+
+namespace {
+
+class ZeroSuggestPrefetchHelperTest : public PlatformTest {
+ protected:
+  void SetUp() override {
+    PlatformTest::SetUp();
+
+    feature_list_.InitWithFeatures({omnibox::kZeroSuggestPrefetching}, {});
+
+    TestChromeBrowserState::Builder test_cbs_builder;
+    chrome_browser_state_ = test_cbs_builder.Build();
+    web_state_list_ = std::make_unique<WebStateList>(&web_state_list_delegate_);
+
+    auto template_url_service = std::make_unique<TemplateURLService>(
+        /*prefs=*/nullptr, std::make_unique<SearchTermsData>(),
+        /*web_data_service=*/nullptr,
+        std::unique_ptr<TemplateURLServiceClient>(), base::RepeatingClosure());
+    auto client_ = std::make_unique<MockAutocompleteProviderClient>();
+    client_->set_template_url_service(std::move(template_url_service));
+    controller_ =
+        std::make_unique<testing::NiceMock<MockAutocompleteController>>(
+            std::move(client_), 0);
+  }
+
+  void CreateHelper() {
+    helper_ = [[ZeroSuggestPrefetchHelper alloc]
+          initWithWebStateList:web_state_list_.get()
+        autocompleteController:controller_.get()];
+  }
+
+  std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
+  FakeWebStateListDelegate web_state_list_delegate_;
+  std::unique_ptr<WebStateList> web_state_list_;
+
+  std::unique_ptr<testing::NiceMock<MockAutocompleteController>> controller_;
+
+  ZeroSuggestPrefetchHelper* helper_;
+
+  // Message loop for the main test thread.
+  base::test::TaskEnvironment environment_;
+
+  base::test::ScopedFeatureList feature_list_;
+};
+
+// Test that upon navigation, the prefetch happens when the new URL of the
+// current tab is a NTP URL.
+TEST_F(ZeroSuggestPrefetchHelperTest, TestReactToNavigation) {
+  CreateHelper();
+
+  EXPECT_CALL(*controller_, StartPrefetch).Times(0);
+  EXPECT_CALL(*controller_, Start).Times(0);
+
+  GURL not_ntp_url(kTestURL);
+  auto web_state = std::make_unique<web::FakeWebState>();
+  FakeWebState* web_state_ptr = web_state.get();
+  web_state_ptr->SetCurrentURL(not_ntp_url);
+  web_state_list_->InsertWebState(
+      0, std::move(web_state), WebStateList::INSERT_NO_FLAGS, WebStateOpener());
+  web_state_list_->ActivateWebStateAt(0);
+  testing::Mock::VerifyAndClearExpectations(controller_.get());
+
+  // Now navigate to NTP.
+  EXPECT_CALL(*controller_, StartPrefetch).Times(1);
+  EXPECT_CALL(*controller_, Start).Times(0);
+
+  GURL url(kChromeUINewTabURL);
+  web_state_ptr->SetCurrentURL(url);
+  web::FakeNavigationContext context;
+  web_state_ptr->OnNavigationFinished(&context);
+
+  testing::Mock::VerifyAndClearExpectations(controller_.get());
+
+  // Now navigate to non-NTP.
+  EXPECT_CALL(*controller_, StartPrefetch).Times(0);
+  EXPECT_CALL(*controller_, Start).Times(0);
+
+  web_state_ptr->SetCurrentURL(not_ntp_url);
+  web_state_ptr->OnNavigationFinished(&context);
+
+  testing::Mock::VerifyAndClearExpectations(controller_.get());
+}
+
+// Test switching between a NTP and a non-NTP tabs starts prefetch, but only
+// when switching to NTP.
+TEST_F(ZeroSuggestPrefetchHelperTest, TestSwitchNTPNonNTP) {
+  CreateHelper();
+
+  EXPECT_CALL(*controller_, StartPrefetch).Times(0);
+  EXPECT_CALL(*controller_, Start).Times(0);
+
+  auto web_state = std::make_unique<web::FakeWebState>();
+  FakeWebState* web_state_ptr = web_state.get();
+
+  GURL url(kChromeUINewTabURL);
+  web_state_ptr->SetCurrentURL(url);
+
+  web_state_list_->InsertWebState(
+      0, std::move(web_state), WebStateList::INSERT_NO_FLAGS, WebStateOpener());
+
+  GURL not_ntp_url(kTestURL);
+  auto second_tab = std::make_unique<web::FakeWebState>();
+  web::FakeWebState* second_tab_ptr = second_tab.get();
+  web_state_list_->InsertWebState(1, std::move(second_tab),
+                                  WebStateList::INSERT_NO_FLAGS,
+                                  WebStateOpener());
+
+  web::FakeNavigationContext context;
+  second_tab_ptr->SetCurrentURL(not_ntp_url);
+  second_tab_ptr->OnNavigationFinished(&context);
+
+  testing::Mock::VerifyAndClearExpectations(controller_.get());
+
+  // Now switch to first tab - which is NTP.
+  EXPECT_CALL(*controller_, StartPrefetch).Times(1);
+  EXPECT_CALL(*controller_, Start).Times(0);
+  web_state_list_->ActivateWebStateAt(0);
+  web_state_ptr->WasShown();
+  testing::Mock::VerifyAndClearExpectations(controller_.get());
+
+  // Now switch to second tab - which isn't NTP.
+  EXPECT_CALL(*controller_, StartPrefetch).Times(0);
+  EXPECT_CALL(*controller_, Start).Times(0);
+  web_state_list_->ActivateWebStateAt(1);
+  second_tab_ptr->WasShown();
+  testing::Mock::VerifyAndClearExpectations(controller_.get());
+
+  // Back to first.
+  EXPECT_CALL(*controller_, StartPrefetch).Times(1);
+  EXPECT_CALL(*controller_, Start).Times(0);
+  web_state_list_->ActivateWebStateAt(0);
+  web_state_ptr->WasShown();
+  testing::Mock::VerifyAndClearExpectations(controller_.get());
+
+  // And back to second!
+  EXPECT_CALL(*controller_, StartPrefetch).Times(0);
+  EXPECT_CALL(*controller_, Start).Times(0);
+  web_state_list_->ActivateWebStateAt(1);
+  second_tab_ptr->WasShown();
+  testing::Mock::VerifyAndClearExpectations(controller_.get());
+}
+
+// Test that adding a second NTP in the tab switcher and switching to it starts
+// prefetch again.
+TEST_F(ZeroSuggestPrefetchHelperTest, TestAddSecondTab) {
+  auto web_state = std::make_unique<web::FakeWebState>();
+  FakeWebState* web_state_ptr = web_state.get();
+
+  GURL url(kChromeUINewTabURL);
+  web_state_ptr->SetCurrentURL(url);
+
+  web_state_list_->InsertWebState(
+      0, std::move(web_state), WebStateList::INSERT_NO_FLAGS, WebStateOpener());
+  web_state_list_->ActivateWebStateAt(0);
+
+  EXPECT_CALL(*controller_, StartPrefetch).Times(1);
+  EXPECT_CALL(*controller_, Start).Times(0);
+
+  CreateHelper();
+
+  testing::Mock::VerifyAndClearExpectations(controller_.get());
+
+  // Now add a new NTP.
+  EXPECT_CALL(*controller_, StartPrefetch).Times(1);
+  EXPECT_CALL(*controller_, Start).Times(0);
+
+  auto second_tab = std::make_unique<web::FakeWebState>();
+  web::FakeWebState* second_tab_ptr = second_tab.get();
+  web_state_list_->InsertWebState(1, std::move(second_tab),
+                                  WebStateList::INSERT_NO_FLAGS,
+                                  WebStateOpener());
+  web_state_list_->ActivateWebStateAt(1);
+
+  web::FakeNavigationContext context;
+  second_tab_ptr->SetCurrentURL(url);
+  second_tab_ptr->OnNavigationFinished(&context);
+
+  testing::Mock::VerifyAndClearExpectations(controller_.get());
+}
+
+}  // namespace
diff --git a/ios/chrome/browser/ui/webui/BUILD.gn b/ios/chrome/browser/ui/webui/BUILD.gn
index 4418ba2a..85556aaa 100644
--- a/ios/chrome/browser/ui/webui/BUILD.gn
+++ b/ios/chrome/browser/ui/webui/BUILD.gn
@@ -57,6 +57,9 @@
     "//components/metrics_services_manager",
     "//components/ntp_tiles",
     "//components/password_manager/core/browser",
+    "//components/policy/core/browser",
+    "//components/policy/core/common",
+    "//components/policy/proto",
     "//components/prefs",
     "//components/resources",
     "//components/strings",
diff --git a/ios/chrome/browser/ui/webui/management/management_ui.h b/ios/chrome/browser/ui/webui/management/management_ui.h
index b5237d8..8f567305 100644
--- a/ios/chrome/browser/ui/webui/management/management_ui.h
+++ b/ios/chrome/browser/ui/webui/management/management_ui.h
@@ -7,7 +7,7 @@
 
 #include <string>
 
-#include "ios/web/public/webui/web_ui_ios_controller.h"
+#import "ios/web/public/webui/web_ui_ios_controller.h"
 
 // The WebUI handler for chrome://management which displays the details about
 // the current enterprise management state.
diff --git a/ios/chrome/browser/ui/webui/management/management_ui.mm b/ios/chrome/browser/ui/webui/management/management_ui.mm
index cc40114..9d24b25 100644
--- a/ios/chrome/browser/ui/webui/management/management_ui.mm
+++ b/ios/chrome/browser/ui/webui/management/management_ui.mm
@@ -4,33 +4,116 @@
 
 #import "ios/chrome/browser/ui/webui/management/management_ui.h"
 
-#include "components/grit/components_resources.h"
+#import "base/strings/utf_string_conversions.h"
+#import "components/grit/components_resources.h"
+#import "components/policy/core/browser/webui/policy_data_utils.h"
+#import "components/policy/core/common/cloud/cloud_policy_core.h"
+#import "components/policy/core/common/cloud/cloud_policy_store.h"
+#import "components/policy/core/common/cloud/machine_level_user_cloud_policy_manager.h"
+#import "components/policy/core/common/cloud/user_cloud_policy_manager.h"
+#import "components/policy/proto/device_management_backend.pb.h"
+#import "components/strings/grit/components_strings.h"
 #include "components/strings/grit/components_strings.h"
-#include "ios/chrome/browser/application_context.h"
-#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#include "ios/chrome/browser/chrome_url_constants.h"
-#include "ios/chrome/browser/policy/browser_policy_connector_ios.h"
-#include "ios/web/public/webui/web_ui_ios.h"
-#include "ios/web/public/webui/web_ui_ios_data_source.h"
+#import "ios/chrome/browser/application_context.h"
+#import "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/chrome_url_constants.h"
+#import "ios/chrome/browser/policy/browser_policy_connector_ios.h"
+#import "ios/chrome/grit/ios_strings.h"
+#import "ios/web/public/webui/web_ui_ios.h"
+#import "ios/web/public/webui/web_ui_ios_data_source.h"
+#import "third_party/abseil-cpp/absl/types/optional.h"
+#import "ui/base/l10n/l10n_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
 namespace {
-web::WebUIIOSDataSource* CreateManagementUIHTMLSource() {
-  web::WebUIIOSDataSource* source =
-      web::WebUIIOSDataSource::Create(kChromeUIManagementHost);
+
+// Returns the domain of the machine level cloud policy. Returns absl::nullopt
+// if the domain cannot be retrieved (eg. because there are no machine level
+// policies).
+absl::optional<std::string> GetMachineLevelPolicyDomain() {
+  policy::MachineLevelUserCloudPolicyManager* manager =
+      GetApplicationContext()
+          ->GetBrowserPolicyConnector()
+          ->machine_level_user_cloud_policy_manager();
+  return policy::GetManagedBy(manager);
+}
+
+// Returns the domain of the user cloud policy. Returns absl::nullopt if the
+// domain cannot be retrieved (eg. because there is no user policy).
+absl::optional<std::string> GetUserPolicyDomain(web::WebUIIOS* web_ui) {
+  // Get the user policy manager for the BrowserState that has the
+  // chrome://management page that hosts this UI. Each BrowserState has its own
+  // manager.
+  policy::UserCloudPolicyManager* manager =
+      ChromeBrowserState::FromWebUIIOS(web_ui)->GetUserCloudPolicyManager();
+  return policy::GetManagedBy(manager);
+}
+
+// Returns the management message depending on the levels of the policies that
+// are applied. Returns absl::nullopt if there are no policies.
+absl::optional<std::u16string> GetManagementMessage(web::WebUIIOS* web_ui) {
+  absl::optional<std::string> machine_level_policy_domain =
+      GetMachineLevelPolicyDomain();
+  absl::optional<std::string> user_policy_domain = GetUserPolicyDomain(web_ui);
+
+  if (machine_level_policy_domain && user_policy_domain) {
+    if (machine_level_policy_domain == user_policy_domain) {
+      // Return a message with a single domain for both the profile and the
+      // browser when the domains are the same. Any domain can be used in that
+      // case.
+      return l10n_util::GetStringFUTF16(
+          IDS_MANAGEMENT_SUBTITLE_BROWSER_AND_PROFILE_SAME_MANAGED_BY,
+          base::UTF8ToUTF16(*machine_level_policy_domain));
+    } else {
+      // Return a message with both domains when they are different.
+      return l10n_util::GetStringFUTF16(
+          IDS_MANAGEMENT_SUBTITLE_BROWSER_AND_PROFILE_DIFFERENT_MANAGED_BY,
+          base::UTF8ToUTF16(*machine_level_policy_domain),
+          base::UTF8ToUTF16(*user_policy_domain));
+    }
+  }
+
+  if (machine_level_policy_domain) {
+    return l10n_util::GetStringFUTF16(
+        IDS_MANAGEMENT_SUBTITLE_MANAGED_BY,
+        base::UTF8ToUTF16(*machine_level_policy_domain));
+  }
+
+  if (user_policy_domain) {
+    return l10n_util::GetStringFUTF16(
+        IDS_MANAGEMENT_SUBTITLE_PROFILE_MANAGED_BY,
+        base::UTF8ToUTF16(*user_policy_domain));
+  }
 
   BrowserPolicyConnectorIOS* policy_connector =
       GetApplicationContext()->GetBrowserPolicyConnector();
-  bool is_managed =
+  bool has_machine_level_policy =
       policy_connector && policy_connector->HasMachineLevelPolicies();
-  source->AddString("isManaged", is_managed ? "true" : "false");
+  if (has_machine_level_policy) {
+    // Return a message without the domain if there are policies on the machine
+    // but couldn't obtain the domain. This can happen when using MDM.
+    return l10n_util::GetStringUTF16(IDS_IOS_MANAGEMENT_UI_MESSAGE);
+  }
+
+  return absl::nullopt;
+}
+
+// Creates the HTML source for the chrome://management page.
+web::WebUIIOSDataSource* CreateManagementUIHTMLSource(web::WebUIIOS* web_ui) {
+  web::WebUIIOSDataSource* source =
+      web::WebUIIOSDataSource::Create(kChromeUIManagementHost);
+
+  absl::optional<std::u16string> management_message =
+      GetManagementMessage(web_ui);
+
+  source->AddString("isManaged", management_message ? "true" : "false");
   source->AddString("learnMoreURL", kManagementLearnMoreURL);
 
-  source->AddLocalizedString("managementMessage",
-                             IDS_IOS_MANAGEMENT_UI_MESSAGE);
+  source->AddString("managementMessage",
+                    management_message.value_or(std::u16string()));
   source->AddLocalizedString("managedInfo", IDS_IOS_MANAGEMENT_UI_DESC);
   source->AddLocalizedString("unmanagedInfo",
                              IDS_IOS_MANAGEMENT_UI_UNMANAGED_DESC);
@@ -49,7 +132,7 @@
 ManagementUI::ManagementUI(web::WebUIIOS* web_ui, const std::string& host)
     : web::WebUIIOSController(web_ui, host) {
   web::WebUIIOSDataSource::Add(ChromeBrowserState::FromWebUIIOS(web_ui),
-                               CreateManagementUIHTMLSource());
+                               CreateManagementUIHTMLSource(web_ui));
 }
 
 ManagementUI::~ManagementUI() {}
diff --git a/ios/web_view/internal/autofill/web_view_autofill_client_ios.h b/ios/web_view/internal/autofill/web_view_autofill_client_ios.h
index 13d152f..3a7de2c 100644
--- a/ios/web_view/internal/autofill/web_view_autofill_client_ios.h
+++ b/ios/web_view/internal/autofill/web_view_autofill_client_ios.h
@@ -122,7 +122,7 @@
   bool ShouldShowSigninPromo() override;
   bool AreServerCardsSupported() const override;
   void ExecuteCommand(int id) override;
-  void OnPromoCodeSuggestionsFooterSelected(const GURL& url) override;
+  void OpenPromoCodeOfferDetailsURL(const GURL& url) override;
 
   // RiskDataLoader:
   void LoadRiskData(
diff --git a/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm b/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm
index 64b4130e..8f0230c 100644
--- a/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm
+++ b/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm
@@ -327,8 +327,7 @@
   NOTIMPLEMENTED();
 }
 
-void WebViewAutofillClientIOS::OnPromoCodeSuggestionsFooterSelected(
-    const GURL& url) {
+void WebViewAutofillClientIOS::OpenPromoCodeOfferDetailsURL(const GURL& url) {
   web_state_->OpenURL(web::WebState::OpenURLParams(
       url, web::Referrer(), WindowOpenDisposition::NEW_FOREGROUND_TAB,
       ui::PageTransition::PAGE_TRANSITION_AUTO_TOPLEVEL,
diff --git a/media/mojo/services/gpu_mojo_media_client.cc b/media/mojo/services/gpu_mojo_media_client.cc
index 3102883..cf04773 100644
--- a/media/mojo/services/gpu_mojo_media_client.cc
+++ b/media/mojo/services/gpu_mojo_media_client.cc
@@ -9,6 +9,7 @@
 #include "base/bind.h"
 #include "base/feature_list.h"
 #include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_functions.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "gpu/ipc/service/gpu_channel.h"
@@ -145,6 +146,41 @@
   if (!supported_config_cache_) {
     supported_config_cache_ = GetSupportedVideoDecoderConfigsStatic(
         gpu_preferences_, gpu_workarounds_, gpu_info_);
+
+    // Once per GPU process record accelerator information. Profile support is
+    // often just manufactured and not tested, so just record the base codec.
+    bool has_accelerated_h264 = false;
+    bool has_accelerated_h265 = false;
+    bool has_accelerated_vp9 = false;
+    bool has_accelerated_av1 = false;
+    if (supported_config_cache_) {
+      for (const auto& config : *supported_config_cache_) {
+        if (config.profile_min >= H264PROFILE_MIN &&
+            config.profile_max <= H264PROFILE_MAX) {
+          has_accelerated_h264 = true;
+        } else if (config.profile_min >= VP9PROFILE_MIN &&
+                   config.profile_max <= VP9PROFILE_MAX) {
+          has_accelerated_vp9 = true;
+        } else if (config.profile_min >= AV1PROFILE_MIN &&
+                   config.profile_max <= AV1PROFILE_MAX) {
+          has_accelerated_av1 = true;
+        } else if ((config.profile_min >= HEVCPROFILE_MIN &&
+                    config.profile_max <= HEVCPROFILE_MAX) ||
+                   (config.profile_min >= HEVCPROFILE_EXT_MIN &&
+                    config.profile_max <= HEVCPROFILE_EXT_MAX)) {
+          has_accelerated_h265 = true;
+        }
+      }
+    }
+
+    base::UmaHistogramBoolean("Media.HasAcceleratedVideoDecode.H264",
+                              has_accelerated_h264);
+    base::UmaHistogramBoolean("Media.HasAcceleratedVideoDecode.H265",
+                              has_accelerated_h265);
+    base::UmaHistogramBoolean("Media.HasAcceleratedVideoDecode.VP9",
+                              has_accelerated_vp9);
+    base::UmaHistogramBoolean("Media.HasAcceleratedVideoDecode.AV1",
+                              has_accelerated_av1);
   }
   return supported_config_cache_.value_or(SupportedVideoDecoderConfigs{});
 }
diff --git a/net/base/backoff_entry_serializer_unittest.cc b/net/base/backoff_entry_serializer_unittest.cc
index 02e515a4..5bcb3eb 100644
--- a/net/base/backoff_entry_serializer_unittest.cc
+++ b/net/base/backoff_entry_serializer_unittest.cc
@@ -205,13 +205,11 @@
 // Test that deserialization fails instead of producing an entry with an
 // infinite release time. (Regression test for https://crbug.com/1293904)
 TEST(BackoffEntrySerializerTest, DeserializeNeverInfiniteReleaseTime) {
-  const base::Value kSerialized[] = {
-      base::Value(2),
-      base::Value(2),
-      base::Value("-9223372036854775807"),
-      base::Value("2"),
-  };
-  base::Value serialized(base::make_span(kSerialized));
+  base::Value::List serialized;
+  serialized.Append(2);
+  serialized.Append(2);
+  serialized.Append("-9223372036854775807");
+  serialized.Append("2");
 
   TestTickClock original_ticks;
   original_ticks.set_now(base::TimeTicks() + base::Microseconds(-1));
@@ -220,8 +218,9 @@
       base::Time::FromDeltaSinceWindowsEpoch(base::Microseconds(-1));
 
   std::unique_ptr<BackoffEntry> entry =
-      BackoffEntrySerializer::DeserializeFromValue(serialized, &base_policy,
-                                                   &original_ticks, time_now);
+      BackoffEntrySerializer::DeserializeFromValue(
+          base::Value(std::move(serialized)), &base_policy, &original_ticks,
+          time_now);
   ASSERT_FALSE(entry);
 }
 
diff --git a/net/data/ssl/chrome_root_store/root_store.md b/net/data/ssl/chrome_root_store/root_store.md
new file mode 100644
index 0000000..58cdc1a
--- /dev/null
+++ b/net/data/ssl/chrome_root_store/root_store.md
@@ -0,0 +1,138 @@
+<!-- mdformat off(generated) -->
+<!-- mdlint off(generated) -->
+Version: 6
+
+SHA 256 Hash | Subject | NotBefore | NotAfter
+---|---|---|---
+55926084ec963a64b96e2abe01ce0ba86a64fbfebcc7aab5afc155b37fd76066 | CN=Actalis Authentication Root CA,O=Actalis S.p.A./03358520967,L=Milan,C=IT | 2011-09-22 | 2030-09-22
+18ce6cfe7bf14e60b2e347b8dfe868cb31d02ebb3ada271569f50343b46db3a4 | CN=Amazon Root CA 3,O=Amazon,C=US | 2015-05-26 | 2040-05-26
+1ba5b2aa8c65401a82960118f80bec4f62304d83cec4713a19c39c011ea46db4 | CN=Amazon Root CA 2,O=Amazon,C=US | 2015-05-26 | 2040-05-26
+568d6905a2c88708a4b3025190edcfedb1974a606a13c6e5290fcb2ae63edab5 | CN=Starfield Services Root Certificate Authority - G2,O=Starfield Technologies\, Inc.,L=Scottsdale,ST=Arizona,C=US | 2009-09-01 | 2037-12-31
+8ecde6884f3d87b1125ba31ac3fcb13d7016de7f57cc904fe1cb97c6ae98196e | CN=Amazon Root CA 1,O=Amazon,C=US | 2015-05-26 | 2038-01-17
+e35d28419ed02025cfa69038cd623962458da5c695fbdea3c22b0bfb25897092 | CN=Amazon Root CA 4,O=Amazon,C=US | 2015-05-26 | 2040-05-26
+5c58468d55f58e497e743982d2b50010b6d165374acf83a7d4a32db768c4408e | CN=Certum Trusted Network CA,OU=Certum Certification Authority,O=Unizeto Technologies S.A.,C=PL | 2008-10-22 | 2029-12-31
+b676f2eddae8775cd36cb0f63cd1d4603961f49e6265ba013a2f0307b6d0b804 | CN=Certum Trusted Network CA 2,OU=Certum Certification Authority,O=Unizeto Technologies S.A.,C=PL | 2011-10-06 | 2046-10-06
+f356bea244b7a91eb35d53ca9ad7864ace018e2d35d5f8f96ddf68a6f41aa474 | CN=Atos TrustedRoot 2011,O=Atos,C=DE | 2011-07-07 | 2030-12-31
+04048028bf1f2864d48f9ad4d83294366a828856553f3b14303f90147f5d40ef | CN=Autoridad de Certificacion Firmaprofesional CIF A62634068,C=ES | 2009-05-20 | 2030-12-31
+9a114025197c5bb95d94e63d55cd43790847b646b23cdf11ada4a00eff15fb48 | CN=Buypass Class 2 Root CA,O=Buypass AS-983163327,C=NO | 2010-10-26 | 2040-10-26
+edf7ebbca27a2a384d387b7d4010c666e2edb4843e4c29b4ae1d5b9332e6b24d | CN=Buypass Class 3 Root CA,O=Buypass AS-983163327,C=NO | 2010-10-26 | 2040-10-26
+657cfe2fa73faa38462571f332a2363a46fce7020951710702cdfbb6eeda3305 | OU=certSIGN ROOT CA G2,O=CERTSIGN SA,C=RO | 2017-02-06 | 2042-02-06
+eaa962c4fa4a6bafebe415196d351ccd888d4f53f3fa8ae6d7c466a94e6042bb | OU=certSIGN ROOT CA,O=certSIGN,C=RO | 2006-07-04 | 2031-07-04
+5cc3d78e4e1d5e45547a04e6873e64f90cf9536d1ccc2ef800f355c4c5fd70fd | CN=CFCA EV ROOT,O=China Financial Certification Authority,C=CN | 2012-08-08 | 2029-12-31
+c0a6f4dc63a24bfdcf54ef2a6a082a0a72de35803e2ff5ff527ae5d87206dfd5 | OU=ePKI Root Certification Authority,O=Chunghwa Telecom Co.\, Ltd.,C=TW | 2004-12-20 | 2034-12-20
+bf0feefb9e3a581ad5f9e9db7589985743d261085c4d314f6f5d7259aa421612 | CN=SecureSign RootCA11,O=Japan Certification Services\, Inc.,C=JP | 2009-04-08 | 2029-04-08
+49e7a442acf0ea6287050054b52564b650e4f49e42e348d6aa38e039e957b1c1 | CN=D-TRUST Root Class 3 CA 2 2009,O=D-Trust GmbH,C=DE | 2009-11-05 | 2029-11-05
+eec5496b988ce98625b934092eec2908bed0b0f316c2d4730c84eaf1f3d34881 | CN=D-TRUST Root Class 3 CA 2 EV 2009,O=D-Trust GmbH,C=DE | 2009-11-05 | 2029-11-05
+91e2f5788d5810eba7ba58737de1548a8ecacd014598bc0b143e041b17052552 | CN=T-TeleSec GlobalRoot Class 2,OU=T-Systems Trust Center,O=T-Systems Enterprise Services GmbH,C=DE | 2008-10-01 | 2033-10-01
+fd73dad31c644ff1b43bef0ccdda96710b9cd9875eca7e31707af3e96d522bbd | CN=T-TeleSec GlobalRoot Class 3,OU=T-Systems Trust Center,O=T-Systems Enterprise Services GmbH,C=DE | 2008-10-01 | 2033-10-01
+d48d3d23eedb50a459e55197601c27774b9d7b18c94d5a059511a10250b93168 | CN=Certigna Root CA,OU=0002 48146308100036,O=Dhimyotis,C=FR | 2013-10-01 | 2033-10-01
+e3b6a2db2ed7ce48842f7ac53241c7b71d54144bfb40c11f3f1d0b42f5eea12d | CN=Certigna,O=Dhimyotis,C=FR | 2007-06-29 | 2027-06-29
+16af57a9f676b0ab126095aa5ebadef22ab31119d644ac95cd4b93dbf3f26aeb | CN=Baltimore CyberTrust Root,OU=CyberTrust,O=Baltimore,C=IE | 2000-05-12 | 2025-05-12
+31ad6648f8104138c738f39ea4320133393e3a18cc02296ef97c2ac9ef6731d0 | CN=DigiCert Global Root G3,OU=www.digicert.com,O=DigiCert Inc,C=US | 2013-08-01 | 2038-01-15
+3e9099b5015e8f486c00bcea9d111ee721faba355a89bcf1df69561e3dc6325c | CN=DigiCert Assured ID Root CA,OU=www.digicert.com,O=DigiCert Inc,C=US | 2006-11-10 | 2031-11-10
+4348a0e9444c78cb265e058d5e8944b4d84f9662bd26db257f8934a443c70161 | CN=DigiCert Global Root CA,OU=www.digicert.com,O=DigiCert Inc,C=US | 2006-11-10 | 2031-11-10
+552f7bdcf1a7af9e6ce672017f4f12abf77240c78e761ac203d1d9d20ac89988 | CN=DigiCert Trusted Root G4,OU=www.digicert.com,O=DigiCert Inc,C=US | 2013-08-01 | 2038-01-15
+7431e5f4c3c1ce4690774f0b61e05440883ba9a01ed00ba6abd7806ed3b118cf | CN=DigiCert High Assurance EV Root CA,OU=www.digicert.com,O=DigiCert Inc,C=US | 2006-11-10 | 2031-11-10
+7d05ebb682339f8c9451ee094eebfefa7953a114edb2f44949452fab7d2fc185 | CN=DigiCert Assured ID Root G2,OU=www.digicert.com,O=DigiCert Inc,C=US | 2013-08-01 | 2038-01-15
+7e37cb8b4c47090cab36551ba6f45db840680fba166a952db100717f43053fc2 | CN=DigiCert Assured ID Root G3,OU=www.digicert.com,O=DigiCert Inc,C=US | 2013-08-01 | 2038-01-15
+cb3ccbb76031e5e0138f8dd39a23f9de47ffc35e43c1144cea27d46a5ab1cb5f | CN=DigiCert Global Root G2,OU=www.digicert.com,O=DigiCert Inc,C=US | 2013-08-01 | 2038-01-15
+e23d4a036d7b70e9f595b1422079d2b91edfbb1fb651a0633eaa8a9dc5f80703 | CN=CA Disig Root R2,O=Disig a.s.,L=Bratislava,C=SK | 2012-07-19 | 2042-07-19
+b0bfd52bb0d7d9bd92bf5d4dc13da255c02c542f378365ea893911f55e55f23c | CN=E-Tugra Certification Authority,OU=E-Tugra Sertifikasyon Merkezi,O=E-Tuğra EBG Bilişim Teknolojileri ve Hizmetleri A.Ş.,L=Ankara,C=TR | 2013-03-05 | 2023-03-03
+125609aa301da0a249b97a8239cb6a34216f44dcac9f3954b14292f2e8c8608f | CN=emSign Root CA - C1,OU=emSign PKI,O=eMudhra Inc,C=US | 2018-02-18 | 2043-02-18
+40f6af0346a99aa1cd1d555a4e9cce62c7f9634603ee406615833dc8c8d00367 | CN=emSign Root CA - G1,OU=emSign PKI,O=eMudhra Technologies Limited,C=IN | 2018-02-18 | 2043-02-18
+86a1ecba089c4a8d3bbe2734c612ba341d813e043cf9e8a862cd5c57a36bbe6b | CN=emSign ECC Root CA - G3,OU=emSign PKI,O=eMudhra Technologies Limited,C=IN | 2018-02-18 | 2043-02-18
+bc4d809b15189d78db3e1d8cf4f9726a795da1643ca5f1358e1ddb0edc0d7eb3 | CN=emSign ECC Root CA - C3,OU=emSign PKI,O=eMudhra Inc,C=US | 2018-02-18 | 2043-02-18
+02ed0eb28c14da45165c566791700d6451d7fb56f0b2ab1d3b8eb070e56edff5 | CN=Entrust Root Certification Authority - EC1,OU=See www.entrust.net/legal-terms+OU=(c) 2012 Entrust\, Inc. - for authorized use only,O=Entrust\, Inc.,C=US | 2012-12-18 | 2037-12-18
+0376ab1d54c5f9803ce4b2e201a0ee7eef7b57b636e8a93c9b8d4860c96f5fa7 | CN=AffirmTrust Commercial,O=AffirmTrust,C=US | 2010-01-29 | 2030-12-31
+0a81ec5a929777f145904af38d5d509f66b5e2c58fcdb531058b0e17f3f0b41b | CN=AffirmTrust Networking,O=AffirmTrust,C=US | 2010-01-29 | 2030-12-31
+43df5774b03e7fef5fe40d931a7bedf1bb2e6b42738c4e6d3841103d3aa7f339 | CN=Entrust Root Certification Authority - G2,OU=See www.entrust.net/legal-terms+OU=(c) 2009 Entrust\, Inc. - for authorized use only,O=Entrust\, Inc.,C=US | 2009-07-07 | 2030-12-07
+6dc47172e01cbcb0bf62580d895fe2b8ac9ad4f873801e0c10b9c837d21eb177 | CN=Entrust.net Certification Authority (2048),OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)+OU=(c) 1999 Entrust.net Limited,O=Entrust.net | 1999-12-24 | 2029-07-24
+70a73f7f376b60074248904534b11482d5bf0e698ecc498df52577ebf2e93b9a | CN=AffirmTrust Premium,O=AffirmTrust,C=US | 2010-01-29 | 2040-12-31
+73c176434f1bc6d5adf45b0e76e727287c8de57616c1e6e6141a2b2cbc7d8e4c | CN=Entrust Root Certification Authority,OU=www.entrust.net/CPS is incorporated by reference+OU=(c) 2006 Entrust\, Inc.,O=Entrust\, Inc.,C=US | 2006-11-27 | 2026-11-27
+bd71fdf6da97e4cf62d1647add2581b07d79adf8397eb4ecba9c5e8488821423 | CN=AffirmTrust Premium ECC,O=AffirmTrust,C=US | 2010-01-29 | 2040-12-31
+db3517d1f6732a2d5ab97c533ec70779ee3270a62fb4ac4238372460e6f01e88 | CN=Entrust Root Certification Authority - G4,OU=See www.entrust.net/legal-terms+OU=(c) 2015 Entrust\, Inc. - for authorized use only,O=Entrust\, Inc.,C=US | 2015-05-27 | 2037-12-27
+bfff8fd04433487d6a8aa60c1a29767a9fc2bbb05e420f713a13b992891d3893 | CN=GDCA TrustAUTH R5 ROOT,O=GUANG DONG CERTIFICATE AUTHORITY CO.\,LTD.,C=CN | 2014-11-26 | 2040-12-31
+179fbc148a3dd00fd24ea13458cc43bfa7f59c8182d783a513f6ebec100c8924 | CN=GlobalSign,OU=GlobalSign ECC Root CA - R5,O=GlobalSign | 2012-11-13 | 2038-01-19
+2cabeafe37d06ca22aba7391c0033d25982952c453647349763a3ab5ad6ccf69 | CN=GlobalSign,OU=GlobalSign Root CA - R6,O=GlobalSign | 2014-12-10 | 2034-12-10
+cbb522d7b7f127ad6a0113865bdf1cd4102e7d0759af635a7cf4720dc963c53b | CN=GlobalSign,OU=GlobalSign Root CA - R3,O=GlobalSign | 2009-03-18 | 2029-03-18
+ebd41040e4bb3ec742c9e381d31ef2a41a48b6685c96e7cef3c1df6cd4331c99 | CN=GlobalSign Root CA,OU=Root CA,O=GlobalSign nv-sa,C=BE | 1998-09-01 | 2028-01-28
+1465fa205397b876faa6f0a9958e5590e40fcc7faa4fb7c2c8677521fb5fb658 | OU=Starfield Class 2 Certification Authority,O=Starfield Technologies\, Inc.,C=US | 2004-06-29 | 2034-06-29
+2ce1cb0bf9d2f9e102993fbe215152c3b2dd0cabde1c68e5319b839154dbb7f5 | CN=Starfield Root Certificate Authority - G2,O=Starfield Technologies\, Inc.,L=Scottsdale,ST=Arizona,C=US | 2009-09-01 | 2037-12-31
+45140b3247eb9cc8c5b4f0d7b53091f73292089e6e5a63e2749dd3aca9198eda | CN=Go Daddy Root Certificate Authority - G2,O=GoDaddy.com\, Inc.,L=Scottsdale,ST=Arizona,C=US | 2009-09-01 | 2037-12-31
+c3846bf24b9e93ca64274c0ec67c1ecc5e024ffcacd2d74019350e81fe546ae4 | OU=Go Daddy Class 2 Certification Authority,O=The Go Daddy Group\, Inc.,C=US | 2004-06-29 | 2034-06-29
+34d8a73ee208d9bcdb0d956520934b4e40e69482596e8b6f73c8426b010a6f48 | CN=GTS Root R3,O=Google Trust Services LLC,C=US | 2016-06-22 | 2036-06-22
+d947432abde7b7fa90fc2e6b59101b1280e0e1c7e4e40fa3c6887fff57a7f4cf | CN=GTS Root R1,O=Google Trust Services LLC,C=US | 2016-06-22 | 2036-06-22
+349dfa4058c5e263123b398ae795573c4e1313c83fe68f93556cd5e8031b3c7d | CN=GTS Root R4,O=Google Trust Services LLC,C=US | 2016-06-22 | 2036-06-22
+bec94911c2955676db6c0a550986d76e3ba005667c442c9762b4fbb773de228c | CN=GlobalSign,OU=GlobalSign ECC Root CA - R4,O=GlobalSign | 2012-11-13 | 2038-01-19
+8d25cd97229dbf70356bda4eb3cc734031e24cf00fafcfd32dc76eb5841c7ea8 | CN=GTS Root R2,O=Google Trust Services LLC,C=US | 2016-06-22 | 2036-06-22
+5a2fc03f0c83b090bbfa40604b0988446c7636183df9846e17101a447fb8efd6 | CN=Hongkong Post Root CA 3,O=Hongkong Post,L=Hong Kong,ST=Hong Kong,C=HK | 2017-06-03 | 2042-06-03
+f9e67d336c51002ac054c632022d66dda2e7e3fff10ad061ed31d8bbb410cfb2 | CN=Hongkong Post Root CA 1,O=Hongkong Post,C=HK | 2003-05-15 | 2023-05-15
+9a6ec012e1a7da9dbe34194d478ad7c0db1822fb071df12981496ed104384113 | CN=ACCVRAIZ1,OU=PKIACCV,O=ACCV,C=ES | 2011-05-05 | 2030-12-31
+ebc5570c29018c4d67b1aa127baf12f703b4611ebc17b7dab5573894179b93fa | OU=AC RAIZ FNMT-RCM,O=FNMT-RCM,C=ES | 2008-10-29 | 2030-01-01
+4d2491414cfe956746ec4cefa6cf6f72e28a1329432f9d8a907ac4cb5dadc15a | CN=Staat der Nederlanden EV Root CA,O=Staat der Nederlanden,C=NL | 2010-12-08 | 2022-12-08
+46edc3689046d53a453fb3104ab80dcaec658b2660ea1629dd7e867990648716 | CN=TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1,OU=Kamu Sertifikasyon Merkezi - Kamu SM,O=Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK,L=Gebze - Kocaeli,C=TR | 2013-11-25 | 2043-10-25
+44b545aa8a25e65a73ca15dc27fc36d24c1cb9953a066539b11582dc487b4833 | CN=Hellenic Academic and Research Institutions ECC RootCA 2015,O=Hellenic Academic and Research Institutions Cert. Authority,L=Athens,C=GR | 2015-07-07 | 2040-06-30
+a040929a02ce53b4acf4f2ffc6981ce4496f755e6d45fe0b2a692bcd52523f36 | CN=Hellenic Academic and Research Institutions RootCA 2015,O=Hellenic Academic and Research Institutions Cert. Authority,L=Athens,C=GR | 2015-07-07 | 2040-06-30
+30d0895a9a448a262091635522d1f52010b5867acae12c78ef958fd4f4389f2f | CN=IdenTrust Public Sector Root CA 1,O=IdenTrust,C=US | 2014-01-16 | 2034-01-16
+5d56499be4d2e08bcfcad08a3e38723d50503bde706948e42f55603019e528ae | CN=IdenTrust Commercial Root CA 1,O=IdenTrust,C=US | 2014-01-16 | 2034-01-16
+96bcec06264976f37460779acf28c5a7cfe8a3c0aae11a8ffcee05c0bddf08c6 | CN=ISRG Root X1,O=Internet Security Research Group,C=US | 2015-06-04 | 2035-06-04
+2530cc8e98321502bad96f9b1fba1b099e2d299e0f4548bb914f363bc0d4531f | CN=Izenpe.com,O=IZENPE S.A.,C=ES | 2007-12-13 | 2037-12-13
+a1339d33281a0b56e557d3d32b1ce7f9367eb094bd5fa72a7e5004c8ded7cafe | CN=SZAFIR ROOT CA2,O=Krajowa Izba Rozliczeniowa S.A.,C=PL | 2015-10-19 | 2035-10-19
+3c5f81fea5fab82c64bfa2eaecafcde8e077fc8620a7cae537163df36edbf378 | CN=Microsec e-Szigno Root CA 2009,O=Microsec Ltd.,L=Budapest,C=HU,1.2.840.113549.1.9.1=#0c10696e666f40652d737a69676e6f2e6875 | 2009-06-16 | 2029-12-30
+beb00b30839b9bc32c32e4447905950641f26421b15ed089198b518ae2ea1b99 | CN=e-Szigno Root CA 2017,O=Microsec Ltd.,L=Budapest,C=HU,2.5.4.97=#130e56415448552d3233353834343937 | 2017-08-22 | 2042-08-22
+358df39d764af9e1b766e9c972df352ee15cfac227af6ad1d70e8e4a6edcba02 | CN=Microsoft ECC Root Certificate Authority 2017,O=Microsoft Corporation,C=US | 2019-12-18 | 2042-07-18
+c741f70f4b2a8d88bf2e71c14122ef53ef10eba0cfa5e64cfa20f418853073e0 | CN=Microsoft RSA Root Certificate Authority 2017,O=Microsoft Corporation,C=US | 2019-12-18 | 2042-07-18
+6c61dac3a2def031506be036d2a6fe401994fbd13df9c8d466599274c446ec98 | CN=NetLock Arany (Class Gold) Főtanúsítvány,OU=Tanúsítványkiadók (Certification Services),O=NetLock Kft.,L=Budapest,C=HU | 2008-12-11 | 2028-12-06
+6b9c08e86eb0f767cfad65cd98b62149e5494a67f5845e7bd1ed019f27b86bd6 | CN=OISTE WISeKey Global Root GB CA,OU=OISTE Foundation Endorsed,O=WISeKey,C=CH | 2014-12-01 | 2039-12-01
+8560f91c3624daba9570b5fea0dbe36ff11a8323be9486854fb3f34a5571198d | CN=OISTE WISeKey Global Root GC CA,OU=OISTE Foundation Endorsed,O=WISeKey,C=CH | 2017-05-09 | 2042-05-09
+18f1fc7f205df8adddeb7fe007dd57e3af375a9c4d8d73546bf4f1fed1e18d35 | CN=QuoVadis Root CA 3,O=QuoVadis Limited,C=BM | 2006-11-24 | 2031-11-24
+85a0dd7dd720adb7ff05f83d542b209dc7ff4528f7d677b18389fea5e5c49e86 | CN=QuoVadis Root CA 2,O=QuoVadis Limited,C=BM | 2006-11-24 | 2031-11-24
+88ef81de202eb018452e43f864725cea5fbd1fc2d9d205730709c5d8b8690f46 | CN=QuoVadis Root CA 3 G3,O=QuoVadis Limited,C=BM | 2012-01-12 | 2042-01-12
+8a866fd1b276b57e578e921c65828a2bed58e9f2f288054134b7f1f4bfc9cc74 | CN=QuoVadis Root CA 1 G3,O=QuoVadis Limited,C=BM | 2012-01-12 | 2042-01-12
+8fe4fb0af93a4d0d67db0bebb23e37c71bf325dcbcdd240ea04daf58b47e1840 | CN=QuoVadis Root CA 2 G3,O=QuoVadis Limited,C=BM | 2012-01-12 | 2042-01-12
+513b2cecb810d4cde5dd85391adfc6c2dd60d87bb736d2b521484aa47a0ebef6 | OU=Security Communication RootCA2,O=SECOM Trust Systems CO.\,LTD.,C=JP | 2009-05-29 | 2029-05-29
+e75e72ed9f560eec6eb4800073a43fc3ad19195a392282017895974a99026b6c | OU=Security Communication RootCA1,O=SECOM Trust.net,C=JP | 2003-09-30 | 2023-09-30
+1a0d20445de5ba1862d19ef880858cbce50102b36e8f0a040c3c69e74522fe6e | CN=COMODO Certification Authority,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB | 2011-01-01 | 2030-12-31
+1793927a0614549789adce2f8f34f7f0b66d0f3ae3a3b84d21ec15dbba4fadc7 | CN=COMODO ECC Certification Authority,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB | 2008-03-06 | 2038-01-18
+4ff460d54b9c86dabfbcfc5712e0400d2bed3fbc4d4fbdaa86e06adcd2a9ad7a | CN=USERTrust ECC Certification Authority,O=The USERTRUST Network,L=Jersey City,ST=New Jersey,C=US | 2010-02-01 | 2038-01-18
+52f0e1c4e58ec629291b60317f074671b85d7ea80d5b07273463534b32b40234 | CN=COMODO RSA Certification Authority,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB | 2010-01-19 | 2038-01-18
+d7a7a0fb5d7e2731d771e9484ebcdef71d5f0c3e0a2948782bc83ee0ea699ef4 | CN=AAA Certificate Services,O=Comodo CA Limited,L=Salford,ST=Greater Manchester,C=GB | 2004-01-01 | 2028-12-31
+e793c9b02fd8aa13e21c31228accb08119643b749c898964b1746d46c3d4cbd2 | CN=USERTrust RSA Certification Authority,O=The USERTRUST Network,L=Jersey City,ST=New Jersey,C=US | 2010-02-01 | 2038-01-18
+4200f5043ac8590ebb527d209ed1503029fbcbd41ca1b506ec27f15ade7dac69 | CN=Secure Global CA,O=SecureTrust Corporation,C=US | 2006-11-07 | 2029-12-31
+55903859c8c0c3ebb8759ece4e2557225ff5758bbd38ebd48276601e1bd58097 | CN=Trustwave Global ECC P384 Certification Authority,O=Trustwave Holdings\, Inc.,L=Chicago,ST=Illinois,C=US | 2017-08-23 | 2042-08-23
+945bbc825ea554f489d1fd51a73ddf2ea624ac7019a05205225c22a78ccfa8b4 | CN=Trustwave Global ECC P256 Certification Authority,O=Trustwave Holdings\, Inc.,L=Chicago,ST=Illinois,C=US | 2017-08-23 | 2042-08-23
+97552015f5ddfc3c8788c006944555408894450084f100867086bc1a2bb58dc8 | CN=Trustwave Global Certification Authority,O=Trustwave Holdings\, Inc.,L=Chicago,ST=Illinois,C=US | 2017-08-23 | 2042-08-23
+cecddc905099d8dadfc5b1d209b737cbe2c18cfb2c10c0ff0bcf0d3286fc1aa2 | CN=XRamp Global Certification Authority,OU=www.xrampsecurity.com,O=XRamp Security Services Inc,C=US | 2004-11-01 | 2035-01-01
+f1c1b50ae5a20dd8030ec9f6bc24823dd367b5255759b4e71b61fce9f7375d73 | CN=SecureTrust CA,O=SecureTrust Corporation,C=US | 2006-11-07 | 2029-12-31
+9bea11c976fe014764c1be56a6f914b5a560317abd9988393382e5161aa0493c | CN=UCA Global G2 Root,O=UniTrust,C=CN | 2016-03-11 | 2040-12-31
+d43af9b35473755c9684fc06d7d8cb70ee5c28e773fb294eb41ee71722924d24 | CN=UCA Extended Validation Root,O=UniTrust,C=CN | 2015-03-13 | 2038-12-31
+22a2c1f7bded704cc1e701b5f408c310880fe956b5de2a4a44f99c873a25a7c8 | CN=SSL.com EV Root Certification Authority ECC,O=SSL Corporation,L=Houston,ST=Texas,C=US | 2016-02-12 | 2041-02-12
+2e7bf16cc22485a7bbe2aa8696750761b0ae39be3b2fe9d0cc6d4ef73491425c | CN=SSL.com EV Root Certification Authority RSA R2,O=SSL Corporation,L=Houston,ST=Texas,C=US | 2017-05-31 | 2042-05-30
+3417bb06cc6007da1b961c920b8ab4ce3fad820e4aa30b9acbc4a74ebdcebc65 | CN=SSL.com Root Certification Authority ECC,O=SSL Corporation,L=Houston,ST=Texas,C=US | 2016-02-12 | 2041-02-12
+85666a562ee0be5ce925c1d8890a6f76a87ec16d4d7d5f29ea7419cf20123b69 | CN=SSL.com Root Certification Authority RSA,O=SSL Corporation,L=Houston,ST=Texas,C=US | 2016-02-12 | 2041-02-12
+62dd0be9b9f50a163ea0f8e75c053b1eca57ea55c8688f647c6881f2c8357b95 | CN=SwissSign Gold CA - G2,O=SwissSign AG,C=CH | 2006-10-25 | 2036-10-25
+be6c4da2bbb9ba59b6f3939768374246c3c005993fa98f020d1dedbed48a81d5 | CN=SwissSign Silver CA - G2,O=SwissSign AG,C=CH | 2006-10-25 | 2036-10-25
+59769007f7685d0fcd50872f9f95d5755a5b2b457d81f3692b610a98672f0e1b | CN=TWCA Global Root CA,OU=Root CA,O=TAIWAN-CA,C=TW | 2012-06-27 | 2030-12-31
+bfd88fe1101c41ae3e801bf8be56350ee9bad1a6b9bd515edc5c6d5b8711ac44 | CN=TWCA Root Certification Authority,OU=Root CA,O=TAIWAN-CA,C=TW | 2008-08-28 | 2030-12-31
+dd6936fe21f8f077c123a1a521c12224f72255b73e03a7260693e8a24b0fa389 | CN=TeliaSonera Root CA v1,O=TeliaSonera | 2007-10-18 | 2032-10-18
+0753e940378c1bd5e3836e395daea5cb839e5046f1bd0eae1951cf10fec7c965 | CN=TrustCor RootCert CA-2,OU=TrustCor Certificate Authority,O=TrustCor Systems S. de R.L.,L=Panama City,ST=Panama,C=PA | 2016-02-04 | 2034-12-31
+5a885db19c01d912c5759388938cafbbdf031ab2d48e91ee15589b42971d039c | CN=TrustCor ECA-1,OU=TrustCor Certificate Authority,O=TrustCor Systems S. de R.L.,L=Panama City,ST=Panama,C=PA | 2016-02-04 | 2029-12-31
+d40e9c86cd8fe468c1776959f49ea774fa548684b6c406f3909261f4dce2575c | CN=TrustCor RootCert CA-1,OU=TrustCor Certificate Authority,O=TrustCor Systems S. de R.L.,L=Panama City,ST=Panama,C=PA | 2016-02-04 | 2029-12-31
+001686cd181f83a1b1217d305b365c41e3470a78a1d37b134a98cd547b92dab3 | CN=Network Solutions Certificate Authority,O=Network Solutions L.L.C.,C=US | 2011-01-01 | 2030-12-31
+6b328085625318aa50d173c98d8bda09d57e27413d114cf787a0f5d06c030cf6 | CN=Certum EC-384 CA,OU=Certum Certification Authority,O=Asseco Data Systems S.A.,C=PL | 2018-03-26 | 2043-03-26
+fe7696573855773e37a95e7ad4d9cc96c30157c15d31765ba9b15704e1ae78fd | CN=Certum Trusted Root CA,OU=Certum Certification Authority,O=Asseco Data Systems S.A.,C=PL | 2018-03-16 | 2043-03-16
+e59aaa816009c22bff5b25bad37df306f049797c1f81d85ab089e657bd8f0044 | CN=D-TRUST BR Root CA 1 2020,O=D-Trust GmbH,C=DE | 2020-02-11 | 2035-02-11
+08170d1aa36453901a2f959245e347db0c8d37abaabc56b81aa100dc958970db | CN=D-TRUST EV Root CA 1 2020,O=D-Trust GmbH,C=DE | 2020-02-11 | 2035-02-11
+cbb9c44d84b8043e1050ea31a69f514955d7bfd2e2c6b49301019ad61d9f5058 | CN=GlobalSign Root E46,O=GlobalSign nv-sa,C=BE | 2019-03-20 | 2046-03-20
+4fa3126d8d3a11d1c4855a4f807cbad6cf919d3a5a88b03bea2c6372d93c40c9 | CN=GlobalSign Root R46,O=GlobalSign nv-sa,C=BE | 2019-03-20 | 2046-03-20
+9a296a5182d1d451a2e37f439b74daafa267523329f90f9a0d2007c334e23c9a | CN=GLOBALTRUST 2020,O=e-commerce monitoring GmbH,C=AT | 2020-02-10 | 2040-06-10
+3f99cc474acfce4dfed58794665e478d1547739f2e780f1bb4ca9b133097d401 | CN=HARICA TLS ECC Root CA 2021,O=Hellenic Academic and Research Institutions CA,C=GR | 2021-02-19 | 2045-02-13
+d95d0e8eda79525bf9beb11b14d2100d3294985f0c62d9fabd9cd999eccb7b1d | CN=HARICA TLS RSA Root CA 2021,O=Hellenic Academic and Research Institutions CA,C=GR | 2021-02-19 | 2045-02-13
+f015ce3cc239bfef064be9f1d2c417e1a0264a0a94be1f0c8d121864eb6949cc | CN=HiPKI Root CA - G1,O=Chunghwa Telecom Co.\, Ltd.,C=TW | 2019-02-22 | 2037-12-31
+69729b8e15a86efc177a57afb7171dfc64add28c2fca8cf1507e34453ccb1470 | CN=ISRG Root X2,O=Internet Security Research Group,C=US | 2020-09-04 | 2040-09-17
+88f438dcf8ffd1fa8f429115ffe5f82ae1e06e0c70c375faad717b34a49e7265 | CN=NAVER Global Root Certification Authority,O=NAVER BUSINESS PLATFORM Corp.,C=KR | 2017-08-18 | 2037-08-18
+242b69742fcb1e5b2abf98898b94572187544e5b4d9911786573621f6a74b82c | CN=Telia Root CA v2,O=Telia Finland Oyj,C=FI | 2018-11-29 | 2043-11-29
+018e13f0772532cf809bd1b17281867283fc48c6e13be9c69812854a490c1b05 | CN=DigiCert TLS ECC P384 Root G5,O=DigiCert\, Inc.,C=US | 2021-01-15 | 2046-01-14
+371a00dc0533b3721a7eeb40e8419e70799d2b0a0f2c1d80693165f7cec4ad75 | CN=DigiCert TLS RSA4096 Root G5,O=DigiCert\, Inc.,C=US | 2021-01-15 | 2046-01-14
+b4585f22e4ac756a4e8612a1361c5d9d031a93fd84febb778fa3068b0fc42dc2 | CN=Certainly Root E1,O=Certainly,C=US | 2021-04-01 | 2046-04-01
+77b82cd8644c4305f7acc5cb156b45675004033d51c60c6202a8e0c33467d3a0 | CN=Certainly Root R1,O=Certainly,C=US | 2021-04-01 | 2046-04-01
diff --git a/net/dns/mock_host_resolver.cc b/net/dns/mock_host_resolver.cc
index e4a0419e..6bf961a7 100644
--- a/net/dns/mock_host_resolver.cc
+++ b/net/dns/mock_host_resolver.cc
@@ -1116,10 +1116,10 @@
 //-----------------------------------------------------------------------------
 
 RuleBasedHostResolverProc::Rule::Rule(ResolverType resolver_type,
-                                      const std::string& host_pattern,
+                                      base::StringPiece host_pattern,
                                       AddressFamily address_family,
                                       HostResolverFlags host_resolver_flags,
-                                      const std::string& replacement,
+                                      base::StringPiece replacement,
                                       std::vector<std::string> dns_aliases,
                                       int latency_ms)
     : resolver_type(resolver_type),
@@ -1141,16 +1141,16 @@
     bool allow_fallback)
     : HostResolverProc(std::move(previous), allow_fallback) {}
 
-void RuleBasedHostResolverProc::AddRule(const std::string& host_pattern,
-                                        const std::string& replacement) {
+void RuleBasedHostResolverProc::AddRule(base::StringPiece host_pattern,
+                                        base::StringPiece replacement) {
   AddRuleForAddressFamily(host_pattern, ADDRESS_FAMILY_UNSPECIFIED,
                           replacement);
 }
 
 void RuleBasedHostResolverProc::AddRuleForAddressFamily(
-    const std::string& host_pattern,
+    base::StringPiece host_pattern,
     AddressFamily address_family,
-    const std::string& replacement) {
+    base::StringPiece replacement) {
   DCHECK(!replacement.empty());
   HostResolverFlags flags = HOST_RESOLVER_LOOPBACK_ONLY;
   Rule rule(Rule::kResolverTypeSystem, host_pattern, address_family, flags,
@@ -1159,8 +1159,8 @@
 }
 
 void RuleBasedHostResolverProc::AddRuleWithFlags(
-    const std::string& host_pattern,
-    const std::string& replacement,
+    base::StringPiece host_pattern,
+    base::StringPiece replacement,
     HostResolverFlags flags,
     std::vector<std::string> dns_aliases) {
   DCHECK(!replacement.empty());
@@ -1170,9 +1170,9 @@
 }
 
 void RuleBasedHostResolverProc::AddIPLiteralRule(
-    const std::string& host_pattern,
-    const std::string& ip_literal,
-    const std::string& canonical_name) {
+    base::StringPiece host_pattern,
+    base::StringPiece ip_literal,
+    base::StringPiece canonical_name) {
   // Literals are always resolved to themselves by HostResolverImpl,
   // consequently we do not support remapping them.
   IPAddress ip_address;
@@ -1191,8 +1191,8 @@
 }
 
 void RuleBasedHostResolverProc::AddIPLiteralRuleWithDnsAliases(
-    const std::string& host_pattern,
-    const std::string& ip_literal,
+    base::StringPiece host_pattern,
+    base::StringPiece ip_literal,
     std::vector<std::string> dns_aliases) {
   // Literals are always resolved to themselves by HostResolverImpl,
   // consequently we do not support remapping them.
@@ -1209,8 +1209,8 @@
 }
 
 void RuleBasedHostResolverProc::AddRuleWithLatency(
-    const std::string& host_pattern,
-    const std::string& replacement,
+    base::StringPiece host_pattern,
+    base::StringPiece replacement,
     int latency_ms) {
   DCHECK(!replacement.empty());
   HostResolverFlags flags = HOST_RESOLVER_LOOPBACK_ONLY;
@@ -1220,7 +1220,7 @@
 }
 
 void RuleBasedHostResolverProc::AllowDirectLookup(
-    const std::string& host_pattern) {
+    base::StringPiece host_pattern) {
   HostResolverFlags flags = HOST_RESOLVER_LOOPBACK_ONLY;
   Rule rule(Rule::kResolverTypeSystem, host_pattern, ADDRESS_FAMILY_UNSPECIFIED,
             flags, std::string(), /*dns_aliases=*/{}, 0);
@@ -1228,7 +1228,7 @@
 }
 
 void RuleBasedHostResolverProc::AddSimulatedFailure(
-    const std::string& host_pattern,
+    base::StringPiece host_pattern,
     HostResolverFlags flags) {
   Rule rule(Rule::kResolverTypeFail, host_pattern, ADDRESS_FAMILY_UNSPECIFIED,
             flags, std::string(), /*dns_aliases=*/{}, 0);
@@ -1236,7 +1236,7 @@
 }
 
 void RuleBasedHostResolverProc::AddSimulatedTimeoutFailure(
-    const std::string& host_pattern,
+    base::StringPiece host_pattern,
     HostResolverFlags flags) {
   Rule rule(Rule::kResolverTypeFailTimeout, host_pattern,
             ADDRESS_FAMILY_UNSPECIFIED, flags, std::string(),
diff --git a/net/dns/mock_host_resolver.h b/net/dns/mock_host_resolver.h
index 677fa77..5c69e1d 100644
--- a/net/dns/mock_host_resolver.h
+++ b/net/dns/mock_host_resolver.h
@@ -514,15 +514,15 @@
 
   // Any hostname matching the given pattern will be replaced with the given
   // |ip_literal|.
-  void AddRule(const std::string& host_pattern, const std::string& ip_literal);
+  void AddRule(base::StringPiece host_pattern, base::StringPiece ip_literal);
 
   // Same as AddRule(), but further restricts to |address_family|.
-  void AddRuleForAddressFamily(const std::string& host_pattern,
+  void AddRuleForAddressFamily(base::StringPiece host_pattern,
                                AddressFamily address_family,
-                               const std::string& ip_literal);
+                               base::StringPiece ip_literal);
 
-  void AddRuleWithFlags(const std::string& host_pattern,
-                        const std::string& ip_literal,
+  void AddRuleWithFlags(base::StringPiece host_pattern,
+                        base::StringPiece ip_literal,
                         HostResolverFlags flags,
                         std::vector<std::string> dns_aliases = {});
 
@@ -533,9 +533,9 @@
   // but does not impact DNS resolution.
   // |ip_literal| can be a single IP address like "192.168.1.1" or a comma
   // separated list of IP addresses, like "::1,192:168.1.2".
-  void AddIPLiteralRule(const std::string& host_pattern,
-                        const std::string& ip_literal,
-                        const std::string& canonical_name);
+  void AddIPLiteralRule(base::StringPiece host_pattern,
+                        base::StringPiece ip_literal,
+                        base::StringPiece canonical_name);
 
   // Same as AddIPLiteralRule, but with a parameter allowing multiple DNS
   // aliases, such as CNAME aliases, instead of only the canonical name. While
@@ -544,26 +544,26 @@
   // MockHostResolver who need to be able to obtain aliases and can be
   // agnostic about how the host resolution took place, as the alternative,
   // MockDnsClient, is not currently hooked up to MockHostResolver.
-  void AddIPLiteralRuleWithDnsAliases(const std::string& host_pattern,
-                                      const std::string& ip_literal,
+  void AddIPLiteralRuleWithDnsAliases(base::StringPiece host_pattern,
+                                      base::StringPiece ip_literal,
                                       std::vector<std::string> dns_aliases);
 
-  void AddRuleWithLatency(const std::string& host_pattern,
-                          const std::string& replacement,
+  void AddRuleWithLatency(base::StringPiece host_pattern,
+                          base::StringPiece replacement,
                           int latency_ms);
 
   // Make sure that |host| will not be re-mapped or even processed by underlying
   // host resolver procedures. It can also be a pattern.
-  void AllowDirectLookup(const std::string& host);
+  void AllowDirectLookup(base::StringPiece host);
 
   // Simulate a lookup failure for |host| (it also can be a pattern).
   void AddSimulatedFailure(
-      const std::string& host,
+      base::StringPiece host,
       HostResolverFlags flags = HOST_RESOLVER_LOOPBACK_ONLY);
 
   // Simulate a lookup timeout failure for |host| (it also can be a pattern).
   void AddSimulatedTimeoutFailure(
-      const std::string& host,
+      base::StringPiece host,
       HostResolverFlags flags = HOST_RESOLVER_LOOPBACK_ONLY);
 
   // Deletes all the rules that have been added.
@@ -593,10 +593,10 @@
     };
 
     Rule(ResolverType resolver_type,
-         const std::string& host_pattern,
+         base::StringPiece host_pattern,
          AddressFamily address_family,
          HostResolverFlags host_resolver_flags,
-         const std::string& replacement,
+         base::StringPiece replacement,
          std::vector<std::string> dns_aliases,
          int latency_ms);
     Rule(const Rule& other);
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc
index 3c544e42..1466f53 100644
--- a/net/spdy/spdy_session.cc
+++ b/net/spdy/spdy_session.cc
@@ -302,15 +302,15 @@
 
 base::Value NetLogSpdySendSettingsParams(const spdy::SettingsMap* settings) {
   base::Value::Dict dict;
-  std::vector<base::Value> settings_list;
+  base::Value::List settings_list;
   for (const auto& setting : *settings) {
     const spdy::SpdySettingsId id = setting.first;
     const uint32_t value = setting.second;
-    settings_list.emplace_back(
+    settings_list.Append(
         base::StringPrintf("[id:%u (%s) value:%u]", id,
                            spdy::SettingsIdToString(id).c_str(), value));
   }
-  dict.Set("settings", base::Value(std::move(settings_list)));
+  dict.Set("settings", std::move(settings_list));
   return base::Value(std::move(dict));
 }
 
diff --git a/services/viz/public/cpp/compositing/compositor_frame_metadata_mojom_traits.cc b/services/viz/public/cpp/compositing/compositor_frame_metadata_mojom_traits.cc
index 5c06a61..7c0e8a2 100644
--- a/services/viz/public/cpp/compositing/compositor_frame_metadata_mojom_traits.cc
+++ b/services/viz/public/cpp/compositing/compositor_frame_metadata_mojom_traits.cc
@@ -10,6 +10,7 @@
 #include "services/viz/public/cpp/compositing/selection_mojom_traits.h"
 #include "services/viz/public/cpp/compositing/surface_id_mojom_traits.h"
 #include "services/viz/public/cpp/crash_keys.h"
+#include "skia/public/mojom/skcolor4f_mojom_traits.h"
 #include "ui/gfx/geometry/mojom/geometry_mojom_traits.h"
 #include "ui/gfx/mojom/display_color_spaces_mojom_traits.h"
 #include "ui/gfx/mojom/selection_bound_mojom_traits.h"
@@ -41,13 +42,16 @@
 
   if (!data.ReadContentColorUsage(&out->content_color_usage))
     return false;
+
+  if (!data.ReadRootBackgroundColor(&out->root_background_color))
+    return false;
+
   out->may_contain_video = data.may_contain_video();
   out->may_throttle_if_undrawn_frames = data.may_throttle_if_undrawn_frames();
   out->has_shared_element_resources = data.has_shared_element_resources();
   out->is_resourceless_software_draw_with_scroll_or_animation =
       data.is_resourceless_software_draw_with_scroll_or_animation();
   out->send_frame_token_to_embedder = data.send_frame_token_to_embedder();
-  out->root_background_color = data.root_background_color();
   out->min_page_scale_factor = data.min_page_scale_factor();
   if (data.top_controls_visible_height_set()) {
     out->top_controls_visible_height.emplace(
diff --git a/services/viz/public/cpp/compositing/compositor_frame_metadata_mojom_traits.h b/services/viz/public/cpp/compositing/compositor_frame_metadata_mojom_traits.h
index be2f79d1..f3b5b76 100644
--- a/services/viz/public/cpp/compositing/compositor_frame_metadata_mojom_traits.h
+++ b/services/viz/public/cpp/compositing/compositor_frame_metadata_mojom_traits.h
@@ -71,7 +71,7 @@
     return metadata.is_resourceless_software_draw_with_scroll_or_animation;
   }
 
-  static uint32_t root_background_color(
+  static SkColor4f root_background_color(
       const viz::CompositorFrameMetadata& metadata) {
     return metadata.root_background_color;
   }
diff --git a/services/viz/public/cpp/compositing/mojom_traits_unittest.cc b/services/viz/public/cpp/compositing/mojom_traits_unittest.cc
index 632c8461..779ee3e 100644
--- a/services/viz/public/cpp/compositing/mojom_traits_unittest.cc
+++ b/services/viz/public/cpp/compositing/mojom_traits_unittest.cc
@@ -676,7 +676,7 @@
   const gfx::SizeF scrollable_viewport_size(1337.7f, 1234.5f);
   const bool may_contain_video = true;
   const bool is_resourceless_software_draw_with_scroll_or_animation = true;
-  const uint32_t root_background_color = 1337;
+  const SkColor4f root_background_color = {0.0f, 0.02f, 0.224f, 0.0f};
   ui::LatencyInfo latency_info;
   latency_info.set_trace_id(5);
   latency_info.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT);
diff --git a/services/viz/public/mojom/compositing/compositor_frame_metadata.mojom b/services/viz/public/mojom/compositing/compositor_frame_metadata.mojom
index f78225ce..2cfc0d64 100644
--- a/services/viz/public/mojom/compositing/compositor_frame_metadata.mojom
+++ b/services/viz/public/mojom/compositing/compositor_frame_metadata.mojom
@@ -13,6 +13,7 @@
 import "services/viz/public/mojom/compositing/selection.mojom";
 import "services/viz/public/mojom/compositing/surface_id.mojom";
 import "services/viz/public/mojom/compositing/surface_range.mojom";
+import "skia/public/mojom/skcolor4f.mojom";
 import "ui/gfx/geometry/mojom/geometry.mojom";
 import "ui/gfx/mojom/display_color_spaces.mojom";
 import "ui/gfx/mojom/overlay_transform.mojom";
@@ -30,7 +31,7 @@
   bool may_contain_video;
   bool may_throttle_if_undrawn_frames;
   bool is_resourceless_software_draw_with_scroll_or_animation;
-  uint32 root_background_color;
+  skia.mojom.SkColor4f root_background_color;
   array<ui.mojom.LatencyInfo> latency_info;
   array<SurfaceRange> referenced_surfaces;
   FrameDeadline deadline;
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index 4e07eac5..959608e 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -1061,7 +1061,7 @@
           "--browser=cros-chrome",
           "--passthrough",
           "-v",
-          "--extra-browser-args=--log-level=0 --js-flags=--expose-gc --force_high_performance_gpu --disable-features=BackgroundVideoPauseOptimization",
+          "--extra-browser-args=--log-level=0 --js-flags=--expose-gc --force_high_performance_gpu",
           "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl_conformance_tests_output.json",
           "--remote=127.0.0.1",
           "--remote-ssh-port=9222"
@@ -5755,21 +5755,21 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5177.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5178.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5177.0",
+        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5178.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5177.0",
-              "revision": "version:105.0.5177.0"
+              "location": "lacros_version_skew_tests_v105.0.5178.0",
+              "revision": "version:105.0.5178.0"
             }
           ],
           "dimension_sets": [
@@ -5782,7 +5782,7 @@
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 105.0.5177.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5178.0"
       },
       {
         "isolate_profile_data": true,
@@ -5920,21 +5920,21 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5177.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5178.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5177.0",
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5178.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5177.0",
-              "revision": "version:105.0.5177.0"
+              "location": "lacros_version_skew_tests_v105.0.5178.0",
+              "revision": "version:105.0.5178.0"
             }
           ],
           "dimension_sets": [
@@ -5946,7 +5946,7 @@
         },
         "test": "lacros_chrome_browsertests",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
-        "variant_id": "Lacros version skew testing ash 105.0.5177.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5178.0"
       },
       {
         "args": [
@@ -6066,21 +6066,21 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5177.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5178.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5177.0",
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5178.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5177.0",
-              "revision": "version:105.0.5177.0"
+              "location": "lacros_version_skew_tests_v105.0.5178.0",
+              "revision": "version:105.0.5178.0"
             }
           ],
           "dimension_sets": [
@@ -6092,7 +6092,7 @@
         },
         "test": "lacros_chrome_browsertests_run_in_series",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
-        "variant_id": "Lacros version skew testing ash 105.0.5177.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5178.0"
       },
       {
         "isolate_profile_data": true,
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index e2b7a1e..62f30b7 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -93240,21 +93240,21 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5177.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5178.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5177.0",
+        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5178.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5177.0",
-              "revision": "version:105.0.5177.0"
+              "location": "lacros_version_skew_tests_v105.0.5178.0",
+              "revision": "version:105.0.5178.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -93262,7 +93262,7 @@
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 105.0.5177.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5178.0"
       },
       {
         "isolate_profile_data": true,
@@ -93375,28 +93375,28 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5177.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5178.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5177.0",
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5178.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5177.0",
-              "revision": "version:105.0.5177.0"
+              "location": "lacros_version_skew_tests_v105.0.5178.0",
+              "revision": "version:105.0.5178.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "lacros_chrome_browsertests",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
-        "variant_id": "Lacros version skew testing ash 105.0.5177.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5178.0"
       },
       {
         "args": [
@@ -93496,28 +93496,28 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5177.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5178.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5177.0",
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5178.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5177.0",
-              "revision": "version:105.0.5177.0"
+              "location": "lacros_version_skew_tests_v105.0.5178.0",
+              "revision": "version:105.0.5178.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "lacros_chrome_browsertests_run_in_series",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
-        "variant_id": "Lacros version skew testing ash 105.0.5177.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5178.0"
       },
       {
         "isolate_profile_data": true,
@@ -94855,20 +94855,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5177.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5178.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5177.0",
+        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5178.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5177.0",
-              "revision": "version:105.0.5177.0"
+              "location": "lacros_version_skew_tests_v105.0.5178.0",
+              "revision": "version:105.0.5178.0"
             }
           ],
           "dimension_sets": [
@@ -94882,7 +94882,7 @@
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 105.0.5177.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5178.0"
       },
       {
         "merge": {
@@ -95020,20 +95020,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5177.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5178.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5177.0",
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5178.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5177.0",
-              "revision": "version:105.0.5177.0"
+              "location": "lacros_version_skew_tests_v105.0.5178.0",
+              "revision": "version:105.0.5178.0"
             }
           ],
           "dimension_sets": [
@@ -95046,7 +95046,7 @@
         },
         "test": "lacros_chrome_browsertests",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
-        "variant_id": "Lacros version skew testing ash 105.0.5177.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5178.0"
       },
       {
         "args": [
@@ -95166,20 +95166,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5177.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5178.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5177.0",
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5178.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5177.0",
-              "revision": "version:105.0.5177.0"
+              "location": "lacros_version_skew_tests_v105.0.5178.0",
+              "revision": "version:105.0.5178.0"
             }
           ],
           "dimension_sets": [
@@ -95192,7 +95192,7 @@
         },
         "test": "lacros_chrome_browsertests_run_in_series",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
-        "variant_id": "Lacros version skew testing ash 105.0.5177.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5178.0"
       },
       {
         "merge": {
@@ -96688,20 +96688,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5177.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5178.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5177.0",
+        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5178.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5177.0",
-              "revision": "version:105.0.5177.0"
+              "location": "lacros_version_skew_tests_v105.0.5178.0",
+              "revision": "version:105.0.5178.0"
             }
           ],
           "dimension_sets": [
@@ -96715,7 +96715,7 @@
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 105.0.5177.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5178.0"
       },
       {
         "merge": {
@@ -96853,20 +96853,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5177.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5178.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5177.0",
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5178.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5177.0",
-              "revision": "version:105.0.5177.0"
+              "location": "lacros_version_skew_tests_v105.0.5178.0",
+              "revision": "version:105.0.5178.0"
             }
           ],
           "dimension_sets": [
@@ -96879,7 +96879,7 @@
         },
         "test": "lacros_chrome_browsertests",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
-        "variant_id": "Lacros version skew testing ash 105.0.5177.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5178.0"
       },
       {
         "args": [
@@ -96999,20 +96999,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5177.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5178.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5177.0",
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5178.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5177.0",
-              "revision": "version:105.0.5177.0"
+              "location": "lacros_version_skew_tests_v105.0.5178.0",
+              "revision": "version:105.0.5178.0"
             }
           ],
           "dimension_sets": [
@@ -97025,7 +97025,7 @@
         },
         "test": "lacros_chrome_browsertests_run_in_series",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
-        "variant_id": "Lacros version skew testing ash 105.0.5177.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5178.0"
       },
       {
         "merge": {
@@ -97760,20 +97760,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5177.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5178.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5177.0",
+        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5178.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5177.0",
-              "revision": "version:105.0.5177.0"
+              "location": "lacros_version_skew_tests_v105.0.5178.0",
+              "revision": "version:105.0.5178.0"
             }
           ],
           "dimension_sets": [
@@ -97786,7 +97786,7 @@
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 105.0.5177.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5178.0"
       }
     ]
   },
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index 8c710bc..c1ac4be 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -11958,7 +11958,7 @@
               "cpu": "arm64",
               "display_attached": "1",
               "mac_model": "Macmini9,1",
-              "os": "Mac-11",
+              "os": "Mac-11|Mac-12",
               "pool": "chromium.tests"
             }
           ],
@@ -11988,7 +11988,7 @@
               "cpu": "arm64",
               "display_attached": "1",
               "mac_model": "Macmini9,1",
-              "os": "Mac-11",
+              "os": "Mac-11|Mac-12",
               "pool": "chromium.tests"
             }
           ],
@@ -12016,7 +12016,7 @@
               "cpu": "arm64",
               "display_attached": "1",
               "mac_model": "Macmini9,1",
-              "os": "Mac-11",
+              "os": "Mac-11|Mac-12",
               "pool": "chromium.tests"
             }
           ],
@@ -12042,7 +12042,7 @@
               "cpu": "arm64",
               "display_attached": "1",
               "mac_model": "Macmini9,1",
-              "os": "Mac-11",
+              "os": "Mac-11|Mac-12",
               "pool": "chromium.tests"
             }
           ],
@@ -12067,7 +12067,7 @@
               "cpu": "arm64",
               "display_attached": "1",
               "mac_model": "Macmini9,1",
-              "os": "Mac-11",
+              "os": "Mac-11|Mac-12",
               "pool": "chromium.tests"
             }
           ],
@@ -12089,7 +12089,7 @@
               "cpu": "arm64",
               "display_attached": "1",
               "mac_model": "Macmini9,1",
-              "os": "Mac-11",
+              "os": "Mac-11|Mac-12",
               "pool": "chromium.tests"
             }
           ],
@@ -12115,7 +12115,7 @@
               "cpu": "arm64",
               "display_attached": "1",
               "mac_model": "Macmini9,1",
-              "os": "Mac-11",
+              "os": "Mac-11|Mac-12",
               "pool": "chromium.tests"
             }
           ],
@@ -12154,7 +12154,7 @@
               "cpu": "arm64",
               "display_attached": "1",
               "mac_model": "Macmini9,1",
-              "os": "Mac-11",
+              "os": "Mac-11|Mac-12",
               "pool": "chromium.tests"
             }
           ],
@@ -12191,7 +12191,7 @@
               "cpu": "arm64",
               "display_attached": "1",
               "mac_model": "Macmini9,1",
-              "os": "Mac-11",
+              "os": "Mac-11|Mac-12",
               "pool": "chromium.tests"
             }
           ],
@@ -12228,7 +12228,7 @@
               "cpu": "arm64",
               "display_attached": "1",
               "mac_model": "Macmini9,1",
-              "os": "Mac-11",
+              "os": "Mac-11|Mac-12",
               "pool": "chromium.tests"
             }
           ],
@@ -12269,7 +12269,7 @@
               "cpu": "arm64",
               "display_attached": "1",
               "mac_model": "Macmini9,1",
-              "os": "Mac-11",
+              "os": "Mac-11|Mac-12",
               "pool": "chromium.tests"
             }
           ],
@@ -12315,7 +12315,7 @@
               "cpu": "arm64",
               "display_attached": "1",
               "mac_model": "Macmini9,1",
-              "os": "Mac-11",
+              "os": "Mac-11|Mac-12",
               "pool": "chromium.tests"
             }
           ],
@@ -12352,7 +12352,7 @@
               "cpu": "arm64",
               "display_attached": "1",
               "mac_model": "Macmini9,1",
-              "os": "Mac-11",
+              "os": "Mac-11|Mac-12",
               "pool": "chromium.tests"
             }
           ],
@@ -12398,7 +12398,7 @@
               "cpu": "arm64",
               "display_attached": "1",
               "mac_model": "Macmini9,1",
-              "os": "Mac-11",
+              "os": "Mac-11|Mac-12",
               "pool": "chromium.tests"
             }
           ],
@@ -12436,7 +12436,7 @@
               "cpu": "arm64",
               "display_attached": "1",
               "mac_model": "Macmini9,1",
-              "os": "Mac-11",
+              "os": "Mac-11|Mac-12",
               "pool": "chromium.tests"
             }
           ],
@@ -12473,7 +12473,7 @@
               "cpu": "arm64",
               "display_attached": "1",
               "mac_model": "Macmini9,1",
-              "os": "Mac-11",
+              "os": "Mac-11|Mac-12",
               "pool": "chromium.tests"
             }
           ],
@@ -12506,7 +12506,7 @@
               "cpu": "arm64",
               "display_attached": "1",
               "mac_model": "Macmini9,1",
-              "os": "Mac-11",
+              "os": "Mac-11|Mac-12",
               "pool": "chromium.tests"
             }
           ],
@@ -12545,7 +12545,7 @@
               "cpu": "arm64",
               "display_attached": "1",
               "mac_model": "Macmini9,1",
-              "os": "Mac-11",
+              "os": "Mac-11|Mac-12",
               "pool": "chromium.tests"
             }
           ],
@@ -12585,7 +12585,7 @@
               "cpu": "arm64",
               "display_attached": "1",
               "mac_model": "Macmini9,1",
-              "os": "Mac-11",
+              "os": "Mac-11|Mac-12",
               "pool": "chromium.tests"
             }
           ],
@@ -12624,7 +12624,7 @@
               "cpu": "arm64",
               "display_attached": "1",
               "mac_model": "Macmini9,1",
-              "os": "Mac-11",
+              "os": "Mac-11|Mac-12",
               "pool": "chromium.tests"
             }
           ],
@@ -12663,7 +12663,7 @@
               "cpu": "arm64",
               "display_attached": "1",
               "mac_model": "Macmini9,1",
-              "os": "Mac-11",
+              "os": "Mac-11|Mac-12",
               "pool": "chromium.tests"
             }
           ],
@@ -12702,7 +12702,7 @@
               "cpu": "arm64",
               "display_attached": "1",
               "mac_model": "Macmini9,1",
-              "os": "Mac-11",
+              "os": "Mac-11|Mac-12",
               "pool": "chromium.tests"
             }
           ],
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index 13d03715..dca03f9 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -20875,21 +20875,21 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5177.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5178.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5177.0",
+        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5178.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5177.0",
-              "revision": "version:105.0.5177.0"
+              "location": "lacros_version_skew_tests_v105.0.5178.0",
+              "revision": "version:105.0.5178.0"
             }
           ],
           "dimension_sets": [
@@ -20902,7 +20902,7 @@
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 105.0.5177.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5178.0"
       },
       {
         "isolate_profile_data": true,
@@ -21040,21 +21040,21 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5177.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5178.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5177.0",
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5178.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5177.0",
-              "revision": "version:105.0.5177.0"
+              "location": "lacros_version_skew_tests_v105.0.5178.0",
+              "revision": "version:105.0.5178.0"
             }
           ],
           "dimension_sets": [
@@ -21066,7 +21066,7 @@
         },
         "test": "lacros_chrome_browsertests",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
-        "variant_id": "Lacros version skew testing ash 105.0.5177.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5178.0"
       },
       {
         "args": [
@@ -21186,21 +21186,21 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5177.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5178.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5177.0",
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5178.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5177.0",
-              "revision": "version:105.0.5177.0"
+              "location": "lacros_version_skew_tests_v105.0.5178.0",
+              "revision": "version:105.0.5178.0"
             }
           ],
           "dimension_sets": [
@@ -21212,7 +21212,7 @@
         },
         "test": "lacros_chrome_browsertests_run_in_series",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
-        "variant_id": "Lacros version skew testing ash 105.0.5177.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5178.0"
       },
       {
         "isolate_profile_data": true,
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl
index 3171fe8..ba6cb4e 100644
--- a/testing/buildbot/mixins.pyl
+++ b/testing/buildbot/mixins.pyl
@@ -839,7 +839,7 @@
       'dimensions': {
         'cpu': 'arm64',
         'mac_model': 'Macmini9,1',
-        'os': 'Mac-11',
+        'os': 'Mac-11|Mac-12',
         'pool': 'chromium.tests',
         'display_attached': '1',
       },
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 72ae133..33d70f4 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -3741,10 +3741,6 @@
     ],
     'modifications': {
       'chromeos-amd64-generic-rel': {
-        'args': [
-          # Added to debug crbug.com/1293967.
-          '--extra-browser-args=--disable-features=BackgroundVideoPauseOptimization',
-        ],
         'swarming': {
           'quickrun_shards': 40,
         },
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 96a4f35..2e4ea65 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -22,15 +22,15 @@
   },
   'LACROS_VERSION_SKEW_CANARY': {
     'args': [
-      '--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5177.0/test_ash_chrome',
+      '--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5178.0/test_ash_chrome',
     ],
-    'identifier': 'Lacros version skew testing ash 105.0.5177.0',
+    'identifier': 'Lacros version skew testing ash 105.0.5178.0',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip',
-          'location': 'lacros_version_skew_tests_v105.0.5177.0',
-          'revision': 'version:105.0.5177.0',
+          'location': 'lacros_version_skew_tests_v105.0.5178.0',
+          'revision': 'version:105.0.5178.0',
         },
       ],
     },
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 97a87ab..9611bb3a 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -3125,6 +3125,29 @@
             ]
         }
     ],
+    "DelayAsyncScriptExecution": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "params": {
+                        "delay_type": "first_paint_or_finished_parsing"
+                    },
+                    "enable_features": [
+                        "DelayAsyncScriptExecution"
+                    ]
+                }
+            ]
+        }
+    ],
     "DelayFirstParkingOfStrings": [
         {
             "platforms": [
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 0363153f..d0da0a7 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -1478,6 +1478,21 @@
 const base::Feature kDecodeScriptSourceOffThread{
     "DecodeScriptSourceOffThread", base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kDelayAsyncScriptExecution{
+    "DelayAsyncScriptExecution", base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::FeatureParam<DelayAsyncScriptDelayType>::Option
+    delay_async_script_execution_delay_types[] = {
+        {DelayAsyncScriptDelayType::kFinishedParsing, "finished_parsing"},
+        {DelayAsyncScriptDelayType::kFirstPaintOrFinishedParsing,
+         "first_paint_or_finished_parsing"}};
+
+const base::FeatureParam<DelayAsyncScriptDelayType>
+    kDelayAsyncScriptExecutionDelayParam{
+        &kDelayAsyncScriptExecution, "delay_type",
+        DelayAsyncScriptDelayType::kFinishedParsing,
+        &delay_async_script_execution_delay_types};
+
 const base::Feature kAllowSourceSwitchOnPausedVideoMediaStream{
     "AllowSourceSwitchOnPausedVideoMediaStream",
     base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index 25834af..9da701c6 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -724,6 +724,16 @@
 // thread.
 BLINK_COMMON_EXPORT extern const base::Feature kDecodeScriptSourceOffThread;
 
+// If enabled, async script execution will be delayed than usual.
+// See https://crbug.com/1340837.
+BLINK_COMMON_EXPORT extern const base::Feature kDelayAsyncScriptExecution;
+enum class DelayAsyncScriptDelayType {
+  kFinishedParsing,
+  kFirstPaintOrFinishedParsing,
+};
+BLINK_COMMON_EXPORT extern const base::FeatureParam<DelayAsyncScriptDelayType>
+    kDelayAsyncScriptExecutionDelayParam;
+
 // If enabled, allows MediaStreamVideoSource objects to be restarted by a
 // successful source switch. Normally, switching the source would only allowed
 // on streams that are in started state. However, changing the source also first
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
index 174745f..8002c19d 100644
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -7109,6 +7109,7 @@
       screen-wake-lock
       serial
       shared-autofill
+      shared-storage
       storage-access-api
       sync-xhr
       trust-token-redemption
diff --git a/third_party/blink/public/mojom/annotation/annotation.mojom b/third_party/blink/public/mojom/annotation/annotation.mojom
index 7993856..abf1a74 100644
--- a/third_party/blink/public/mojom/annotation/annotation.mojom
+++ b/third_party/blink/public/mojom/annotation/annotation.mojom
@@ -4,6 +4,7 @@
 
 module blink.mojom;
 
+import "mojo/public/mojom/base/string16.mojom";
 import "ui/gfx/geometry/mojom/geometry.mojom";
 
 enum AnnotationType {
@@ -42,15 +43,51 @@
   // Instantiates a new agent in the renderer. The new agent will immediately
   // attempt attachment. Currently, an AnnotationAgent can only perform
   // attachment on creation and if it fails it cannot retry.
-  // `host_remote` - A binding to an object in the caller which will receive
-  //                 events for the agent.
-  // `agent_receiver` - A binding that the caller will use to send messages to
-  //                    and control the life time of the agent.
-  // `type` - The use case the annotation agent is being used for.
-  // `serialized_selector` - A selector that will be used to attach the agent
-  //                         to a Range of DOM in the Document.
+  //
+  // `host_remote`: A binding to an object in the caller which will receive
+  //    events for the agent.
+  // `agent_receiver`: A binding that the caller will use to send messages to
+  //    and control the life time of the agent.
+  // `type`: The use case the annotation agent is being used for.
+  // `serialized_selector`: A selector that will be used to attach the agent to
+  //    a Range of DOM in the Document.
   CreateAgent(pending_remote<blink.mojom.AnnotationAgentHost> host_remote,
               pending_receiver<blink.mojom.AnnotationAgent> agent_receiver,
               AnnotationType type,
               string serialized_selector);
+
+  // Creates an agent from the document's current text selection and returns
+  // the bindings as well as a selector which can be used in the future to
+  // target the same content. Performs attachment immediately after returning.
+  // If successful, clears the current text selection.
+  //
+  // Selector creation may fail for a few reasons:
+  //    * If there is no text selection in the renderer
+  //    * If the selection is degenerate (contains no text).
+  //    * The selected text cannot be uniquely identified using a text
+  //      selector.
+  //    * Generating the selector took too long and timed out.
+  //
+  // `type`: The use case the annotation agent is being used for.
+  //
+  // Returns:
+  // `host_receiver`: The receiver for the AnnotationAgentHost in the browser
+  //    that the agent will send messages to, or NullReceiver if a selector
+  //    could not be created.
+  // `agent_remote`: The remote that can be used from the browser to interact
+  //    with the agent, or NullRemote if a selector could not be created.
+  // `serialized_selector`: A string representing a selector that can be used
+  //    in the future (via `CreateAgent`) to target the same content. Empty
+  //    string if a selector could not be created (e.g. because no text
+  //    selection exists, or the selection is too ambiguous).
+  // `selected_text`: The text that's selected by the returned selector. Empty
+  //    string if a selector could not be created. This is in UTF-16 since
+  //    it'll be displayed in UI which requires UTF-16 encoding.
+  // TODO(bokan): Package the reply arguments into a single nullable result
+  // struct to simplify consistency checking on the browser side.
+  CreateAgentFromSelection(AnnotationType type) => (
+      pending_receiver<blink.mojom.AnnotationAgentHost>? host_receiver,
+      pending_remote<blink.mojom.AnnotationAgent>? agent_remote,
+      string serialized_selector,
+      mojo_base.mojom.String16 selected_text);
 };
diff --git a/third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom b/third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom
index 1296a93..7ad10b3 100644
--- a/third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom
+++ b/third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom
@@ -214,6 +214,10 @@
   // Controls use of FedCM API.
   kFederatedCredentials = 102,
 
+  // "shared-storage" permissions policy that controls the use of Shared Storage
+  // API.
+  kSharedStorage = 103,
+
   // Don't change assigned numbers of any item, and don't reuse removed slots.
   // Add new features at the end of the enum.
   // Also, run update_permissions_policy_enum.py in
diff --git a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
index 2e0664b..7aa9a3c 100644
--- a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
+++ b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
@@ -3617,6 +3617,7 @@
   kParentOfDisabledFormControlRespondsToMouseEvents = 4296,
   kUnhandledExceptionCountInMainThread = 4297,
   kUnhandledExceptionCountInWorker = 4298,
+  kWebCodecsImageDecoderPremultiplyAlphaDeprecation = 4299,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/renderer/core/annotation/DEPS b/third_party/blink/renderer/core/annotation/DEPS
new file mode 100644
index 0000000..b295480
--- /dev/null
+++ b/third_party/blink/renderer/core/annotation/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+    "+components/shared_highlighting/core/common"
+]
diff --git a/third_party/blink/renderer/core/annotation/annotation_agent_container_impl.cc b/third_party/blink/renderer/core/annotation/annotation_agent_container_impl.cc
index ce6cab9..e0903fd 100644
--- a/third_party/blink/renderer/core/annotation/annotation_agent_container_impl.cc
+++ b/third_party/blink/renderer/core/annotation/annotation_agent_container_impl.cc
@@ -7,8 +7,12 @@
 #include "base/callback.h"
 #include "third_party/blink/renderer/core/annotation/annotation_agent_impl.h"
 #include "third_party/blink/renderer/core/annotation/annotation_selector.h"
+#include "third_party/blink/renderer/core/annotation/text_annotation_selector.h"
+#include "third_party/blink/renderer/core/editing/frame_selection.h"
+#include "third_party/blink/renderer/core/editing/visible_selection.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/fragment_directive/text_fragment_handler.h"
+#include "third_party/blink/renderer/core/fragment_directive/text_fragment_selector.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 
 namespace blink {
@@ -130,4 +134,97 @@
   agent_impl->Attach();
 }
 
+void AnnotationAgentContainerImpl::CreateAgentFromSelection(
+    mojom::blink::AnnotationType type,
+    CreateAgentFromSelectionCallback callback) {
+  // Both Document and LocalFrame must be non-null since the mojo connections
+  // are closed when the Document shuts down its execution context.
+  Document* document = GetSupplementable();
+  DCHECK(document);
+
+  LocalFrame* frame = document->GetFrame();
+  DCHECK(frame);
+
+  VisibleSelectionInFlatTree selection =
+      frame->Selection().ComputeVisibleSelectionInFlatTree();
+  if (selection.IsNone() || !selection.IsRange()) {
+    std::move(callback).Run(mojo::NullReceiver(), mojo::NullRemote(),
+                            /*serialized_selector=*/"", /*selected_text=*/"");
+    return;
+  }
+
+  EphemeralRangeInFlatTree selection_range(selection.Start(), selection.End());
+
+  if (selection_range.IsNull() || selection_range.IsCollapsed()) {
+    std::move(callback).Run(mojo::NullReceiver(), mojo::NullRemote(),
+                            /*serialized_selector=*/"", /*selected_text=*/"");
+    return;
+  }
+
+  RangeInFlatTree* current_selection_range =
+      MakeGarbageCollected<RangeInFlatTree>(selection_range.StartPosition(),
+                                            selection_range.EndPosition());
+
+  // TODO(crbug.com/1313967): We may be able to reduce the latency of adding a
+  // new note by starting the generator when the context menu is opened so that
+  // by the time the user selects "add a note" the selector is already
+  // generated. We already do this for shared-highlighting so we could just
+  // generalize that code, see
+  // TextFragmentHandler::OpenedContextMenuOverSelection.
+  auto* generator = MakeGarbageCollected<TextFragmentSelectorGenerator>(frame);
+
+  // The generator is kept alive by the callback.
+  generator->Generate(
+      *current_selection_range,
+      WTF::Bind(&AnnotationAgentContainerImpl::DidFinishSelectorGeneration,
+                WrapWeakPersistent(this), WrapPersistent(generator), type,
+                std::move(callback)));
+}
+
+void AnnotationAgentContainerImpl::DidFinishSelectorGeneration(
+    TextFragmentSelectorGenerator* generator,
+    mojom::blink::AnnotationType type,
+    CreateAgentFromSelectionCallback callback,
+    const TextFragmentSelector& selector,
+    shared_highlighting::LinkGenerationError error) {
+  if (error != shared_highlighting::LinkGenerationError::kNone) {
+    std::move(callback).Run(mojo::NullReceiver(), mojo::NullRemote(),
+                            /*serialized_selector=*/"", /*selected_text=*/"");
+    return;
+  }
+
+  // TODO(bokan): Should we clear the frame selection?
+  {
+    // If the document were detached selector generation will return above with
+    // an error.
+    Document* document = GetSupplementable();
+    DCHECK(document);
+
+    LocalFrame* frame = document->GetFrame();
+    DCHECK(frame);
+
+    frame->Selection().Clear();
+  }
+
+  mojo::PendingRemote<mojom::blink::AnnotationAgentHost> pending_host_remote;
+  mojo::PendingReceiver<mojom::blink::AnnotationAgent> pending_agent_receiver;
+
+  // TODO(bokan): This replies with the selector before performing attachment
+  // (i.e. before the highlight is shown). If we'd prefer to guarantee the
+  // highlight is showing before the creation flow begins we can swap these.
+  auto* annotation_selector =
+      MakeGarbageCollected<TextAnnotationSelector>(selector);
+  std::move(callback).Run(pending_host_remote.InitWithNewPipeAndPassReceiver(),
+                          pending_agent_receiver.InitWithNewPipeAndPassRemote(),
+                          annotation_selector->Serialize(),
+                          generator->GetSelectorTargetText());
+
+  AnnotationAgentImpl* agent_impl =
+      CreateUnboundAgent(type, *annotation_selector);
+  agent_impl->Bind(std::move(pending_host_remote),
+                   std::move(pending_agent_receiver));
+
+  agent_impl->Attach();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/annotation/annotation_agent_container_impl.h b/third_party/blink/renderer/core/annotation/annotation_agent_container_impl.h
index 42c57c2..5cc1a663 100644
--- a/third_party/blink/renderer/core/annotation/annotation_agent_container_impl.h
+++ b/third_party/blink/renderer/core/annotation/annotation_agent_container_impl.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_ANNOTATION_ANNOTATION_AGENT_CONTAINER_IMPL_H_
 
 #include "base/types/pass_key.h"
+#include "components/shared_highlighting/core/common/shared_highlighting_metrics.h"
 #include "third_party/blink/public/mojom/annotation/annotation.mojom-blink.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/dom/document.h"
@@ -21,6 +22,8 @@
 class AnnotationAgentImpl;
 class AnnotationSelector;
 class LocalFrame;
+class TextFragmentSelector;
+class TextFragmentSelectorGenerator;
 
 // This class provides a per-Document container for AnnotationAgents. It is
 // used primarily as an entrypoint to allow clients to create an
@@ -85,10 +88,20 @@
       mojo::PendingReceiver<mojom::blink::AnnotationAgent> agent_receiver,
       mojom::blink::AnnotationType type,
       const String& serialized_selector) override;
+  void CreateAgentFromSelection(
+      mojom::blink::AnnotationType type,
+      CreateAgentFromSelectionCallback callback) override;
 
  private:
   friend AnnotationAgentContainerImplTest;
 
+  void DidFinishSelectorGeneration(
+      TextFragmentSelectorGenerator* generator,
+      mojom::blink::AnnotationType type,
+      CreateAgentFromSelectionCallback callback,
+      const TextFragmentSelector& selector,
+      shared_highlighting::LinkGenerationError error);
+
   HeapMojoReceiverSet<mojom::blink::AnnotationAgentContainer,
                       AnnotationAgentContainerImpl>
       receivers_;
diff --git a/third_party/blink/renderer/core/annotation/annotation_agent_container_impl_test.cc b/third_party/blink/renderer/core/annotation/annotation_agent_container_impl_test.cc
index 213afb1..1193eca 100644
--- a/third_party/blink/renderer/core/annotation/annotation_agent_container_impl_test.cc
+++ b/third_party/blink/renderer/core/annotation/annotation_agent_container_impl_test.cc
@@ -13,6 +13,11 @@
 #include "third_party/blink/public/mojom/annotation/annotation.mojom-blink.h"
 #include "third_party/blink/renderer/core/annotation/annotation_agent_impl.h"
 #include "third_party/blink/renderer/core/annotation/annotation_test_utils.h"
+#include "third_party/blink/renderer/core/editing/finder/async_find_buffer.h"
+#include "third_party/blink/renderer/core/editing/frame_selection.h"
+#include "third_party/blink/renderer/core/editing/iterators/text_iterator.h"
+#include "third_party/blink/renderer/core/editing/selection_template.h"
+#include "third_party/blink/renderer/core/html/html_element.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_request.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_test.h"
 
@@ -303,4 +308,221 @@
   EXPECT_FALSE(AnnotationAgentContainerImpl::From(first_document));
 }
 
+// When the document has no selection, calling CreateAgentFromSelection must
+// not create an agent and it must return empty null bindings back to the
+// caller.
+TEST_F(AnnotationAgentContainerImplTest,
+       CreateAgentFromSelectionWithNoSelection) {
+  SimRequest request("https://example.com/test.html", "text/html");
+  LoadURL("https://example.com/test.html");
+  request.Complete(R"HTML(
+    <!DOCTYPE html>
+    <body>TEST PAGE</body>
+  )HTML");
+
+  auto* container = AnnotationAgentContainerImpl::From(GetDocument());
+
+  bool did_reply = false;
+  container->CreateAgentFromSelection(
+      mojom::blink::AnnotationType::kUserNote,
+      base::BindLambdaForTesting(
+          [&did_reply](
+              mojo::PendingReceiver<mojom::blink::AnnotationAgentHost>
+                  host_receiver,
+              mojo::PendingRemote<mojom::blink::AnnotationAgent> agent_remote,
+              const String& serialized_selector, const String& selected_text) {
+            did_reply = true;
+
+            EXPECT_EQ(selected_text, "");
+            EXPECT_EQ(serialized_selector, "");
+            EXPECT_FALSE(host_receiver.is_valid());
+            EXPECT_FALSE(agent_remote.is_valid());
+          }));
+
+  EXPECT_TRUE(did_reply);
+  EXPECT_EQ(GetAgentCount(*container), 0ul);
+}
+
+// CreateAgentFromSelection must create an agent and return a selector for the
+// selected text and bindings to the agent. It should also attach the agent to
+// show the highlight.
+TEST_F(AnnotationAgentContainerImplTest,
+       CreateAgentFromSelectionWithCollapsedSelection) {
+  SimRequest request("https://example.com/test.html", "text/html");
+  LoadURL("https://example.com/test.html");
+  request.Complete(R"HTML(
+    <!DOCTYPE html>
+    <body>TEST PAGE</body>
+  )HTML");
+
+  auto* container = AnnotationAgentContainerImpl::From(GetDocument());
+
+  FrameSelection& frame_selection = GetDocument().GetFrame()->Selection();
+
+  Element* body = GetDocument().body();
+  frame_selection.SetSelection(SelectionInDOMTree::Builder()
+                                   .Collapse(Position(body->firstChild(), 0))
+                                   .Build(),
+                               SetSelectionOptions());
+
+  bool did_reply = false;
+  container->CreateAgentFromSelection(
+      mojom::blink::AnnotationType::kUserNote,
+      base::BindLambdaForTesting(
+          [&did_reply](
+              mojo::PendingReceiver<mojom::blink::AnnotationAgentHost>
+                  host_receiver,
+              mojo::PendingRemote<mojom::blink::AnnotationAgent> agent_remote,
+              const String& serialized_selector, const String& selected_text) {
+            did_reply = true;
+
+            EXPECT_EQ(selected_text, "");
+            EXPECT_EQ(serialized_selector, "");
+            EXPECT_FALSE(host_receiver.is_valid());
+            EXPECT_FALSE(agent_remote.is_valid());
+          }));
+
+  EXPECT_TRUE(did_reply);
+  EXPECT_EQ(GetAgentCount(*container), 0ul);
+}
+
+// When the document has a collapsed selection, calling
+// CreateAgentFromSelection must not create an agent and it must return empty
+// null bindings back to the caller.
+TEST_F(AnnotationAgentContainerImplTest, CreateAgentFromSelection) {
+  SimRequest request("https://example.com/test.html", "text/html");
+  LoadURL("https://example.com/test.html");
+  request.Complete(R"HTML(
+    <!DOCTYPE html>
+    <body>TEST PAGE</body>
+  )HTML");
+
+  auto* container = AnnotationAgentContainerImpl::From(GetDocument());
+
+  FrameSelection& frame_selection = GetDocument().GetFrame()->Selection();
+
+  Element* body = GetDocument().body();
+  EphemeralRange range = EphemeralRange(Position(body->firstChild(), 0),
+                                        Position(body->firstChild(), 5));
+  ASSERT_EQ("TEST ", PlainText(range));
+
+  frame_selection.SetSelection(
+      SelectionInDOMTree::Builder().SetBaseAndExtent(range).Build(),
+      SetSelectionOptions());
+
+  MockAnnotationAgentHost host;
+
+  base::RunLoop run_loop;
+  container->CreateAgentFromSelection(
+      mojom::blink::AnnotationType::kUserNote,
+      base::BindLambdaForTesting(
+          [&run_loop, &host](
+              mojo::PendingReceiver<mojom::blink::AnnotationAgentHost>
+                  host_receiver,
+              mojo::PendingRemote<mojom::blink::AnnotationAgent> agent_remote,
+              const String& serialized_selector, const String& selected_text) {
+            run_loop.Quit();
+
+            EXPECT_EQ(selected_text, "TEST");
+            EXPECT_EQ(serialized_selector, "TEST,-PAGE");
+            EXPECT_TRUE(host_receiver.is_valid());
+            EXPECT_TRUE(agent_remote.is_valid());
+
+            host.Bind(std::move(host_receiver), std::move(agent_remote));
+          }));
+  run_loop.Run();
+
+  EXPECT_TRUE(host.agent_.is_connected());
+  host.FlushForTesting();
+
+  // Creating an agent from selection should automatically start attachment.
+  EXPECT_TRUE(host.did_finish_attachment_rect_);
+
+  EXPECT_EQ(GetAgentCount(*container), 1ul);
+}
+
+// Test that an in-progress, asynchronous generation is canceled gracefully if
+// a new document is navigated.
+TEST_F(AnnotationAgentContainerImplTest, ShutdownDocumentWhileGenerating) {
+  SimRequest request("https://example.com/test.html", "text/html");
+  SimRequest request_next("https://example.com/next.html", "text/html");
+  LoadURL("https://example.com/test.html");
+  request.Complete(R"HTML(
+    <!DOCTYPE html>
+    <body>
+      <p>Multiple blocks</p>
+      <p>Multiple blocks</p>
+      <p>Multiple blocks</p>
+      <p>Multiple blocks</p>
+      <p>Multiple blocks</p>
+      <p id="target">TARGET</p>
+      <p>Multiple blocks</p>
+    </body>
+  )HTML");
+
+  // Set a tiny timeout so that the generator takes many tasks to finish its
+  // work.
+  auto auto_reset_timeout =
+      AsyncFindBuffer::OverrideTimeoutForTesting(base::TimeDelta::Min());
+
+  auto* container = AnnotationAgentContainerImpl::From(GetDocument());
+
+  FrameSelection& frame_selection = GetDocument().GetFrame()->Selection();
+
+  Element* target = GetDocument().getElementById("target");
+  EphemeralRange range =
+      EphemeralRange(Position(target, 0), Position(target, 1));
+  ASSERT_EQ("TARGET", PlainText(range));
+
+  frame_selection.SetSelection(
+      SelectionInDOMTree::Builder().SetBaseAndExtent(range).Build(),
+      SetSelectionOptions());
+
+  base::RunLoop run_loop;
+  bool did_finish = false;
+
+  container->CreateAgentFromSelection(
+      mojom::blink::AnnotationType::kUserNote,
+      base::BindLambdaForTesting(
+          [&did_finish](
+              mojo::PendingReceiver<mojom::blink::AnnotationAgentHost>
+                  host_receiver,
+              mojo::PendingRemote<mojom::blink::AnnotationAgent> agent_remote,
+              const String& serialized_selector, const String& selected_text) {
+            did_finish = true;
+            EXPECT_EQ(selected_text, "");
+            EXPECT_EQ(serialized_selector, "");
+            EXPECT_FALSE(host_receiver.is_valid());
+            EXPECT_FALSE(agent_remote.is_valid());
+          }));
+
+  // The above will have posted the first generator task to
+  // kInternalFindInPage. Post a task after it to exit back to test code after
+  // that task runs.
+  GetDocument()
+      .GetTaskRunner(TaskType::kInternalFindInPage)
+      ->PostTask(FROM_HERE, run_loop.QuitClosure());
+  run_loop.Run();
+
+  // The generator should still not have completed. Navigate the page to a new
+  // document.
+  EXPECT_FALSE(did_finish);
+  LoadURL("https://example.com/next.html");
+  request_next.Complete(R"HTML(
+    <!DOCTYPE html>
+    NEXT PAGE
+  )HTML");
+
+  // The generation should complete but return failure, the agent should not
+  // have been created.
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(did_finish);
+  EXPECT_EQ(GetAgentCount(*container), 0ul);
+
+  // Ensure the new document doesn't somehow get involved.
+  auto* new_container = AnnotationAgentContainerImpl::From(GetDocument());
+  ASSERT_NE(new_container, container);
+  EXPECT_EQ(GetAgentCount(*new_container), 0ul);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/annotation/annotation_test_utils.h b/third_party/blink/renderer/core/annotation/annotation_test_utils.h
index fd04809..ed88500 100644
--- a/third_party/blink/renderer/core/annotation/annotation_test_utils.h
+++ b/third_party/blink/renderer/core/annotation/annotation_test_utils.h
@@ -98,6 +98,13 @@
     agent.Bind(std::move(pending_remote), agent_.BindNewPipeAndPassReceiver());
   }
 
+  void Bind(
+      mojo::PendingReceiver<mojom::blink::AnnotationAgentHost> host_receiver,
+      mojo::PendingRemote<mojom::blink::AnnotationAgent> agent_remote) {
+    agent_.Bind(std::move(agent_remote));
+    receiver_.Bind(std::move(host_receiver));
+  }
+
   using RemoteHostReceiverAgentPair =
       std::pair<mojo::PendingRemote<mojom::blink::AnnotationAgentHost>,
                 mojo::PendingReceiver<mojom::blink::AnnotationAgent>>;
diff --git a/third_party/blink/renderer/core/css/css_custom_font_data.h b/third_party/blink/renderer/core/css/css_custom_font_data.h
index a23ffeb1..1226321 100644
--- a/third_party/blink/renderer/core/css/css_custom_font_data.h
+++ b/third_party/blink/renderer/core/css/css_custom_font_data.h
@@ -69,9 +69,15 @@
 
   // TODO(Oilpan): consider moving (Custom)FontFace hierarchy to the heap,
   // thereby making this reference a Member<>.
+#if defined(USE_PARALLEL_TEXT_SHAPING)
+  CrossThreadWeakPersistent<CSSFontFaceSource> font_face_source_;
+  std::atomic<FallbackVisibility> fallback_visibility_;
+  mutable std::atomic<bool> is_loading_{false};
+#else
   WeakPersistent<CSSFontFaceSource> font_face_source_;
   FallbackVisibility fallback_visibility_;
   mutable bool is_loading_ = false;
+#endif
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_font_selector.cc b/third_party/blink/renderer/core/css/css_font_selector.cc
index c4cb3ff3f..9ca049a 100644
--- a/third_party/blink/renderer/core/css/css_font_selector.cc
+++ b/third_party/blink/renderer/core/css/css_font_selector.cc
@@ -40,7 +40,11 @@
 namespace blink {
 
 CSSFontSelector::CSSFontSelector(const TreeScope& tree_scope)
-    : tree_scope_(&tree_scope) {
+    : CSSFontSelectorBase(
+          tree_scope.GetDocument().GetExecutionContext()->GetTaskRunner(
+              TaskType::kInternalDefault)),
+      tree_scope_(&tree_scope) {
+  DCHECK(tree_scope.GetDocument().GetExecutionContext()->IsContextThread());
   DCHECK(tree_scope.GetDocument().GetFrame());
   generic_font_family_settings_ = tree_scope.GetDocument()
                                       .GetFrame()
@@ -57,7 +61,8 @@
 CSSFontSelector::~CSSFontSelector() = default;
 
 UseCounter* CSSFontSelector::GetUseCounter() const {
-  return GetExecutionContext();
+  auto* const context = GetExecutionContext();
+  return context && context->IsContextThread() ? context : nullptr;
 }
 
 void CSSFontSelector::RegisterForInvalidationCallbacks(
@@ -148,7 +153,7 @@
       FontCache::Get().GetFontData(request_description, settings_family_name);
 
   ReportFontLookupByUniqueOrFamilyName(settings_family_name,
-                                       request_description, font_data.get());
+                                       request_description, font_data);
 
   return font_data;
 }
@@ -165,6 +170,10 @@
   return GetDocument().GetFontMatchingMetrics();
 }
 
+bool CSSFontSelector::IsAlive() const {
+  return tree_scope_;
+}
+
 void CSSFontSelector::Trace(Visitor* visitor) const {
   visitor->Trace(tree_scope_);
   visitor->Trace(clients_);
diff --git a/third_party/blink/renderer/core/css/css_font_selector.h b/third_party/blink/renderer/core/css/css_font_selector.h
index 74b7ee2..c568a0e 100644
--- a/third_party/blink/renderer/core/css/css_font_selector.h
+++ b/third_party/blink/renderer/core/css/css_font_selector.h
@@ -82,6 +82,7 @@
   void DispatchInvalidationCallbacks(FontInvalidationReason);
 
   // `CSSFontSelectorBase` overrides
+  bool IsAlive() const override;
   FontMatchingMetrics* GetFontMatchingMetrics() const override;
   UseCounter* GetUseCounter() const override;
 
diff --git a/third_party/blink/renderer/core/css/css_font_selector_base.cc b/third_party/blink/renderer/core/css/css_font_selector_base.cc
index aeec9b2..104ac309 100644
--- a/third_party/blink/renderer/core/css/css_font_selector_base.cc
+++ b/third_party/blink/renderer/core/css/css_font_selector_base.cc
@@ -16,17 +16,58 @@
 
 namespace blink {
 
+CSSFontSelectorBase::CSSFontSelectorBase(
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+#if defined(USE_PARALLEL_TEXT_SHAPING)
+    : task_runner_(task_runner)
+#endif
+{
+  DCHECK(IsContextThread());
+}
+
 void CSSFontSelectorBase::CountUse(WebFeature feature) const {
-  return UseCounter::Count(GetUseCounter(), feature);
+#if defined(USE_PARALLEL_TEXT_SHAPING)
+  if (!IsAlive())
+    return;
+  if (IsContextThread())
+    return UseCounter::Count(GetUseCounter(), feature);
+  PostCrossThreadTask(
+      *task_runner_, FROM_HERE,
+      CrossThreadBindOnce(&CSSFontSelectorBase::CountUse,
+                          WrapCrossThreadPersistent(this), feature));
+#endif
 }
 
 AtomicString CSSFontSelectorBase::FamilyNameFromSettings(
     const FontDescription& font_description,
     const FontFamily& generic_family_name) {
+#if defined(USE_PARALLEL_TEXT_SHAPING)
+  if (!IsContextThread()) {
+    if (IsWebkitBodyFamily(font_description)) {
+      PostCrossThreadTask(
+          *task_runner_, FROM_HERE,
+          CrossThreadBindOnce(
+              &CSSFontSelectorBase::CountUse, WrapCrossThreadPersistent(this),
+              WebFeature::kFontSelectorCSSFontFamilyWebKitPrefixBody));
+    }
+    return FontSelector::FamilyNameFromSettings(generic_font_family_settings_,
+                                                font_description,
+                                                generic_family_name, nullptr);
+  }
+#endif
   return FontSelector::FamilyNameFromSettings(
       generic_font_family_settings_, font_description, generic_family_name,
       GetUseCounter());
 }
+
+bool CSSFontSelectorBase::IsContextThread() const {
+#if defined(USE_PARALLEL_TEXT_SHAPING)
+  return task_runner_->RunsTasksInCurrentSequence();
+#else
+  return true;
+#endif
+}
+
 bool CSSFontSelectorBase::IsPlatformFamilyMatchAvailable(
     const FontDescription& font_description,
     const FontFamily& passed_family) {
@@ -40,6 +81,19 @@
 void CSSFontSelectorBase::ReportEmojiSegmentGlyphCoverage(
     unsigned num_clusters,
     unsigned num_broken_clusters) {
+#if defined(USE_PARALLEL_TEXT_SHAPING)
+  if (!IsAlive())
+    return;
+  if (!IsContextThread()) {
+    PostCrossThreadTask(
+        *task_runner_, FROM_HERE,
+        CrossThreadBindOnce(
+            &CSSFontSelectorBase::ReportEmojiSegmentGlyphCoverage,
+            WrapCrossThreadPersistent(this), num_clusters,
+            num_broken_clusters));
+    return;
+  }
+#endif
   GetFontMatchingMetrics()->ReportEmojiSegmentGlyphCoverage(
       num_clusters, num_broken_clusters);
 }
@@ -49,6 +103,19 @@
     UScriptCode script,
     FontDescription::GenericFamilyType generic_family_type,
     const AtomicString& resulting_font_name) {
+#if defined(USE_PARALLEL_TEXT_SHAPING)
+  if (!IsAlive())
+    return;
+  if (!IsContextThread()) {
+    PostCrossThreadTask(
+        *task_runner_, FROM_HERE,
+        CrossThreadBindOnce(
+            &CSSFontSelectorBase::ReportFontFamilyLookupByGenericFamily,
+            WrapCrossThreadPersistent(this), generic_font_family_name, script,
+            generic_family_type, resulting_font_name));
+    return;
+  }
+#endif
   GetFontMatchingMetrics()->ReportFontFamilyLookupByGenericFamily(
       generic_font_family_name, script, generic_family_type,
       resulting_font_name);
@@ -56,56 +123,153 @@
 
 void CSSFontSelectorBase::ReportSuccessfulFontFamilyMatch(
     const AtomicString& font_family_name) {
+#if defined(USE_PARALLEL_TEXT_SHAPING)
+  if (!IsAlive())
+    return;
+  if (!IsContextThread()) {
+    PostCrossThreadTask(
+        *task_runner_, FROM_HERE,
+        CrossThreadBindOnce(&CSSFontSelectorBase::ReportFailedFontFamilyMatch,
+                            WrapCrossThreadPersistent(this), font_family_name));
+    return;
+  }
+#endif
   GetFontMatchingMetrics()->ReportSuccessfulFontFamilyMatch(font_family_name);
 }
 
 void CSSFontSelectorBase::ReportFailedFontFamilyMatch(
     const AtomicString& font_family_name) {
+#if defined(USE_PARALLEL_TEXT_SHAPING)
+  if (!IsAlive())
+    return;
+  if (!IsContextThread()) {
+    PostCrossThreadTask(
+        *task_runner_, FROM_HERE,
+        CrossThreadBindOnce(&CSSFontSelectorBase::ReportFailedFontFamilyMatch,
+                            WrapCrossThreadPersistent(this), font_family_name));
+    return;
+  }
+#endif
   GetFontMatchingMetrics()->ReportFailedFontFamilyMatch(font_family_name);
 }
 
 void CSSFontSelectorBase::ReportSuccessfulLocalFontMatch(
     const AtomicString& font_name) {
+#if defined(USE_PARALLEL_TEXT_SHAPING)
+  if (!IsAlive())
+    return;
+  if (!IsContextThread()) {
+    PostCrossThreadTask(
+        *task_runner_, FROM_HERE,
+        CrossThreadBindOnce(
+            &CSSFontSelectorBase::ReportSuccessfulLocalFontMatch,
+            WrapCrossThreadPersistent(this), font_name));
+    return;
+  }
+#endif
   GetFontMatchingMetrics()->ReportSuccessfulLocalFontMatch(font_name);
 }
 
 void CSSFontSelectorBase::ReportFailedLocalFontMatch(
     const AtomicString& font_name) {
+#if defined(USE_PARALLEL_TEXT_SHAPING)
+  if (!IsAlive())
+    return;
+  if (!IsContextThread()) {
+    PostCrossThreadTask(
+        *task_runner_, FROM_HERE,
+        CrossThreadBindOnce(&CSSFontSelectorBase::ReportFailedLocalFontMatch,
+                            WrapCrossThreadPersistent(this), font_name));
+    return;
+  }
+#endif
   GetFontMatchingMetrics()->ReportFailedLocalFontMatch(font_name);
 }
 
 void CSSFontSelectorBase::ReportFontLookupByUniqueOrFamilyName(
     const AtomicString& name,
     const FontDescription& font_description,
-    SimpleFontData* resulting_font_data) {
+    scoped_refptr<SimpleFontData> resulting_font_data) {
+#if defined(USE_PARALLEL_TEXT_SHAPING)
+  if (!IsAlive())
+    return;
+  if (!IsContextThread()) {
+    PostCrossThreadTask(
+        *task_runner_, FROM_HERE,
+        CrossThreadBindOnce(
+            &CSSFontSelectorBase::ReportFontLookupByUniqueOrFamilyName,
+            WrapCrossThreadPersistent(this), name, font_description,
+            resulting_font_data));
+    return;
+  }
+#endif
   GetFontMatchingMetrics()->ReportFontLookupByUniqueOrFamilyName(
-      name, font_description, resulting_font_data);
+      name, font_description, resulting_font_data.get());
 }
 
 void CSSFontSelectorBase::ReportFontLookupByUniqueNameOnly(
     const AtomicString& name,
     const FontDescription& font_description,
-    SimpleFontData* resulting_font_data,
+    scoped_refptr<SimpleFontData> resulting_font_data,
     bool is_loading_fallback) {
+#if defined(USE_PARALLEL_TEXT_SHAPING)
+  if (!IsAlive())
+    return;
+  if (!IsContextThread()) {
+    PostCrossThreadTask(
+        *task_runner_, FROM_HERE,
+        CrossThreadBindOnce(
+            &CSSFontSelectorBase::ReportFontLookupByUniqueNameOnly,
+            WrapCrossThreadPersistent(this), name, font_description,
+            resulting_font_data, is_loading_fallback));
+    return;
+  }
+#endif
   GetFontMatchingMetrics()->ReportFontLookupByUniqueNameOnly(
-      name, font_description, resulting_font_data, is_loading_fallback);
+      name, font_description, resulting_font_data.get(), is_loading_fallback);
 }
 
 void CSSFontSelectorBase::ReportFontLookupByFallbackCharacter(
     UChar32 fallback_character,
     FontFallbackPriority fallback_priority,
     const FontDescription& font_description,
-    SimpleFontData* resulting_font_data) {
+    scoped_refptr<SimpleFontData> resulting_font_data) {
+#if defined(USE_PARALLEL_TEXT_SHAPING)
+  if (!IsAlive())
+    return;
+  if (!IsContextThread()) {
+    PostCrossThreadTask(
+        *task_runner_, FROM_HERE,
+        CrossThreadBindOnce(
+            &CSSFontSelectorBase::ReportFontLookupByFallbackCharacter,
+            WrapCrossThreadPersistent(this), fallback_character,
+            fallback_priority, font_description, resulting_font_data));
+    return;
+  }
+#endif
   GetFontMatchingMetrics()->ReportFontLookupByFallbackCharacter(
       fallback_character, fallback_priority, font_description,
-      resulting_font_data);
+      resulting_font_data.get());
 }
 
 void CSSFontSelectorBase::ReportLastResortFallbackFontLookup(
     const FontDescription& font_description,
-    SimpleFontData* resulting_font_data) {
+    scoped_refptr<SimpleFontData> resulting_font_data) {
+#if defined(USE_PARALLEL_TEXT_SHAPING)
+  if (!IsAlive())
+    return;
+  if (!IsContextThread()) {
+    PostCrossThreadTask(
+        *task_runner_, FROM_HERE,
+        CrossThreadBindOnce(
+            &CSSFontSelectorBase::ReportLastResortFallbackFontLookup,
+            WrapCrossThreadPersistent(this), font_description,
+            resulting_font_data));
+    return;
+  }
+#endif
   GetFontMatchingMetrics()->ReportLastResortFallbackFontLookup(
-      font_description, resulting_font_data);
+      font_description, resulting_font_data.get());
 }
 
 void CSSFontSelectorBase::ReportNotDefGlyph() const {
@@ -114,11 +278,33 @@
 
 void CSSFontSelectorBase::ReportSystemFontFamily(
     const AtomicString& font_family_name) {
+#if defined(USE_PARALLEL_TEXT_SHAPING)
+  if (!IsAlive())
+    return;
+  if (!IsContextThread()) {
+    PostCrossThreadTask(
+        *task_runner_, FROM_HERE,
+        CrossThreadBindOnce(&CSSFontSelectorBase::ReportSystemFontFamily,
+                            WrapCrossThreadPersistent(this), font_family_name));
+    return;
+  }
+#endif
   GetFontMatchingMetrics()->ReportSystemFontFamily(font_family_name);
 }
 
 void CSSFontSelectorBase::ReportWebFontFamily(
     const AtomicString& font_family_name) {
+#if defined(USE_PARALLEL_TEXT_SHAPING)
+  if (!IsAlive())
+    return;
+  if (!IsContextThread()) {
+    PostCrossThreadTask(
+        *task_runner_, FROM_HERE,
+        CrossThreadBindOnce(&CSSFontSelectorBase::ReportWebFontFamily,
+                            WrapCrossThreadPersistent(this), font_family_name));
+    return;
+  }
+#endif
   GetFontMatchingMetrics()->ReportWebFontFamily(font_family_name);
 }
 
@@ -127,7 +313,7 @@
     const FontFamily& family,
     const String& text) {
   if (family.FamilyIsGeneric()) {
-    if (family.IsPrewarmed())
+    if (family.IsPrewarmed() || UNLIKELY(family.FamilyName().IsEmpty()))
       return;
     family.SetIsPrewarmed();
     // |FamilyNameFromSettings| has a visible impact on the load performance.
@@ -135,9 +321,13 @@
     // only when the |Font| is shared across elements, and therefore it can't
     // help when e.g., the font size is different, check once more if this
     // generic family is already prewarmed.
-    const auto result = prewarmed_generic_families_.insert(family.FamilyName());
-    if (!result.is_new_entry)
-      return;
+    {
+      AutoLockForParallelTextShaping guard(prewarmed_generic_families_lock_);
+      const auto result =
+          prewarmed_generic_families_.insert(family.FamilyName());
+      if (!result.is_new_entry)
+        return;
+    }
     const AtomicString& family_name =
         FamilyNameFromSettings(font_description, family);
     if (!family_name.IsEmpty())
@@ -151,7 +341,7 @@
     return;
   }
 
-  if (family.IsPrewarmed())
+  if (family.IsPrewarmed() || UNLIKELY(family.FamilyName().IsEmpty()))
     return;
   family.SetIsPrewarmed();
   FontCache::PrewarmFamily(family.FamilyName());
diff --git a/third_party/blink/renderer/core/css/css_font_selector_base.h b/third_party/blink/renderer/core/css/css_font_selector_base.h
index 00eea28..5c4047d0 100644
--- a/third_party/blink/renderer/core/css/css_font_selector_base.h
+++ b/third_party/blink/renderer/core/css/css_font_selector_base.h
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/core/css/font_face_cache.h"
 #include "third_party/blink/renderer/platform/fonts/font_selector.h"
 #include "third_party/blink/renderer/platform/fonts/generic_font_family_settings.h"
+#include "third_party/blink/renderer/platform/fonts/lock_for_parallel_text_shaping.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
 
 namespace blink {
@@ -46,23 +47,23 @@
   void ReportFontLookupByUniqueOrFamilyName(
       const AtomicString& name,
       const FontDescription& font_description,
-      SimpleFontData* resulting_font_data) override;
+      scoped_refptr<SimpleFontData> resulting_font_data) override;
 
   void ReportFontLookupByUniqueNameOnly(
       const AtomicString& name,
       const FontDescription& font_description,
-      SimpleFontData* resulting_font_data,
+      scoped_refptr<SimpleFontData> resulting_font_data,
       bool is_loading_fallback = false) override;
 
   void ReportFontLookupByFallbackCharacter(
       UChar32 fallback_character,
       FontFallbackPriority fallback_priority,
       const FontDescription& font_description,
-      SimpleFontData* resulting_font_data) override;
+      scoped_refptr<SimpleFontData> resulting_font_data) override;
 
   void ReportLastResortFallbackFontLookup(
       const FontDescription& font_description,
-      SimpleFontData* resulting_font_data) override;
+      scoped_refptr<SimpleFontData> resulting_font_data) override;
 
   void ReportFontFamilyLookupByGenericFamily(
       const AtomicString& generic_font_family_name,
@@ -75,9 +76,18 @@
   void ReportEmojiSegmentGlyphCoverage(unsigned num_clusters,
                                        unsigned num_broken_clusters) override;
 
+  bool IsContextThread() const override;
+
   void Trace(Visitor*) const override;
 
  protected:
+  explicit CSSFontSelectorBase(
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+
+  // TODO(crbug.com/383860): We should get rid of `IsAlive()` once lifetime
+  // issue of `CSSFontSelector` is solved. It will be alive after `TreeScope`
+  // is dead.
+  virtual bool IsAlive() const { return true; }
   virtual FontMatchingMetrics* GetFontMatchingMetrics() const = 0;
   virtual UseCounter* GetUseCounter() const = 0;
 
@@ -89,7 +99,12 @@
 
   Member<FontFaceCache> font_face_cache_;
   GenericFontFamilySettings generic_font_family_settings_;
-  HashSet<AtomicString> prewarmed_generic_families_;
+  LockForParallelTextShaping prewarmed_generic_families_lock_;
+  HashSet<AtomicString> prewarmed_generic_families_
+      GUARDED_BY(prewarmed_generic_families_lock_);
+#if defined(USE_PARALLEL_TEXT_SHAPING)
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+#endif
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/font_face.cc b/third_party/blink/renderer/core/css/font_face.cc
index 2c8bcf69..f89037b 100644
--- a/third_party/blink/renderer/core/css/font_face.cc
+++ b/third_party/blink/renderer/core/css/font_face.cc
@@ -870,7 +870,8 @@
         RemoteFontFaceSource* source =
             MakeGarbageCollected<RemoteFontFaceSource>(
                 css_font_face_, font_selector,
-                CSSValueToFontDisplay(display_.Get()));
+                CSSValueToFontDisplay(display_.Get()),
+                context->GetTaskRunner(TaskType::kFontLoading));
         item.Fetch(context, source);
         css_font_face_->AddSource(source);
       }
diff --git a/third_party/blink/renderer/core/css/offscreen_font_selector.cc b/third_party/blink/renderer/core/css/offscreen_font_selector.cc
index fa1b592f..efae34d 100644
--- a/third_party/blink/renderer/core/css/offscreen_font_selector.cc
+++ b/third_party/blink/renderer/core/css/offscreen_font_selector.cc
@@ -12,7 +12,8 @@
 namespace blink {
 
 OffscreenFontSelector::OffscreenFontSelector(WorkerGlobalScope* worker)
-    : worker_(worker) {
+    : CSSFontSelectorBase(worker->GetTaskRunner(TaskType::kInternalDefault)),
+      worker_(worker) {
   DCHECK(worker);
   font_face_cache_ = MakeGarbageCollected<FontFaceCache>();
   FontCache::Get().AddClient(this);
diff --git a/third_party/blink/renderer/core/css/remote_font_face_source.cc b/third_party/blink/renderer/core/css/remote_font_face_source.cc
index 11d1ef1..cf944c61 100644
--- a/third_party/blink/renderer/core/css/remote_font_face_source.cc
+++ b/third_party/blink/renderer/core/css/remote_font_face_source.cc
@@ -28,6 +28,10 @@
 #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_load_priority.h"
 #include "third_party/blink/renderer/platform/network/network_state_notifier.h"
+#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
+#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
+#include "third_party/blink/renderer/platform/wtf/cross_thread_copier.h"
+#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
 
 namespace blink {
 
@@ -133,11 +137,16 @@
   return kSwapPeriod;
 }
 
-RemoteFontFaceSource::RemoteFontFaceSource(CSSFontFace* css_font_face,
-                                           FontSelector* font_selector,
-                                           FontDisplay display)
+RemoteFontFaceSource::RemoteFontFaceSource(
+    CSSFontFace* css_font_face,
+    FontSelector* font_selector,
+    FontDisplay display,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
     : face_(css_font_face),
       font_selector_(font_selector),
+#if defined(USE_PARALLEL_TEXT_SHAPING)
+      task_runner_(task_runner),
+#endif
       // No need to report the violation here since the font is not loaded yet
       display_(
           GetFontDisplayWithDocumentPolicyCheck(display,
@@ -176,6 +185,7 @@
   ExecutionContext* execution_context = font_selector_->GetExecutionContext();
   if (!execution_context)
     return;
+  DCHECK(execution_context->IsContextThread());
   // Prevent promise rejection while shutting down the document.
   // See crbug.com/960290
   auto* window = DynamicTo<LocalDOMWindow>(execution_context);
@@ -372,8 +382,29 @@
 }
 
 void RemoteFontFaceSource::BeginLoadIfNeeded() {
-  if (IsLoaded() || !font_selector_->GetExecutionContext())
+  if (IsLoaded())
     return;
+  ExecutionContext* const execution_context =
+      font_selector_->GetExecutionContext();
+  if (!execution_context)
+    return;
+#if defined(USE_PARALLEL_TEXT_SHAPING)
+  if (!execution_context->IsContextThread()) {
+    // Following tests reache here.
+    //  * fast/css3-text/css3-text-decoration/text-decoration-skip-ink-links.html
+    //  * fast/css3-text/css3-word-break/word-break-break-all-in-span.html
+    //  * virtual/text-antialias/justify-vertical.html
+    //  * virtual/text-antialias/line-break-8bit-after-16bit.html
+    // Note: |ExecutionContext::GetTaskRunner()| works only for context
+    // thread, so we ask main thread to handle |BeginLoadIfNeeded()|.
+    PostCrossThreadTask(
+        *task_runner_, FROM_HERE,
+        CrossThreadBindOnce(&RemoteFontFaceSource::BeginLoadIfNeeded,
+                            WrapCrossThreadPersistent(this)));
+    return;
+  }
+#endif
+
   DCHECK(GetResource());
 
   SetDisplay(face_->GetFontFace()->GetFontDisplay());
@@ -381,20 +412,19 @@
   auto* font = To<FontResource>(GetResource());
   if (font->StillNeedsLoad()) {
     if (font->IsLowPriorityLoadingAllowedForRemoteFont()) {
-      font_selector_->GetExecutionContext()->AddConsoleMessage(
-          MakeGarbageCollected<ConsoleMessage>(
-              mojom::ConsoleMessageSource::kIntervention,
-              mojom::ConsoleMessageLevel::kInfo,
-              "Slow network is detected. See "
-              "https://www.chromestatus.com/feature/5636954674692096 for more "
-              "details. Fallback font will be used while loading: " +
-                  font->Url().ElidedString()));
+      execution_context->AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
+          mojom::blink::ConsoleMessageSource::kIntervention,
+          mojom::blink::ConsoleMessageLevel::kInfo,
+          "Slow network is detected. See "
+          "https://www.chromestatus.com/feature/5636954674692096 for more "
+          "details. Fallback font will be used while loading: " +
+              font->Url().ElidedString()));
 
       // Set the loading priority to VeryLow only when all other clients agreed
       // that this font is not required for painting the text.
       font->DidChangePriority(ResourceLoadPriority::kVeryLow, 0);
     }
-    if (font_selector_->GetExecutionContext()->Fetcher()->StartLoad(font))
+    if (execution_context->Fetcher()->StartLoad(font))
       histograms_.LoadStarted();
   }
 
@@ -402,9 +432,7 @@
   // Note that <link rel=preload> may have initiated loading without kicking
   // off the timers.
   font->StartLoadLimitTimersIfNecessary(
-      font_selector_->GetExecutionContext()
-          ->GetTaskRunner(TaskType::kInternalLoading)
-          .get());
+      execution_context->GetTaskRunner(TaskType::kInternalLoading).get());
 
   face_->DidBeginLoad();
 }
diff --git a/third_party/blink/renderer/core/css/remote_font_face_source.h b/third_party/blink/renderer/core/css/remote_font_face_source.h
index 7744db3..6fe503d 100644
--- a/third_party/blink/renderer/core/css/remote_font_face_source.h
+++ b/third_party/blink/renderer/core/css/remote_font_face_source.h
@@ -23,7 +23,10 @@
  public:
   enum Phase { kNoLimitExceeded, kShortLimitExceeded, kLongLimitExceeded };
 
-  RemoteFontFaceSource(CSSFontFace*, FontSelector*, FontDisplay);
+  RemoteFontFaceSource(CSSFontFace*,
+                       FontSelector*,
+                       FontDisplay,
+                       scoped_refptr<base::SingleThreadTaskRunner>);
   ~RemoteFontFaceSource() override;
 
   bool IsLoading() const override;
@@ -148,6 +151,11 @@
   Member<CSSFontFace> face_;
   Member<FontSelector> font_selector_;
 
+#if defined(USE_PARALLEL_TEXT_SHAPING)
+  // Post `BeginLoadIfNeeded()` unless context thread.
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+#endif
+
   // |nullptr| if font is not loaded or failed to decode.
   scoped_refptr<FontCustomPlatformData> custom_font_data_;
   // |nullptr| if font is not loaded or failed to decode.
diff --git a/third_party/blink/renderer/core/css/resolver/style_adjuster.cc b/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
index 651cf780..0547ecda 100644
--- a/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
@@ -553,7 +553,10 @@
   DCHECK(style.OverflowX() != EOverflow::kVisible ||
          style.OverflowY() != EOverflow::kVisible);
 
-  if (style.IsDisplayTableBox()) {
+  bool overflow_is_clip_or_visible =
+      IsOverflowClipOrVisible(style.OverflowY()) &&
+      IsOverflowClipOrVisible(style.OverflowX());
+  if (!overflow_is_clip_or_visible && style.IsDisplayTableBox()) {
     // Tables only support overflow:hidden and overflow:visible and ignore
     // anything else, see https://drafts.csswg.org/css2/visufx.html#overflow. As
     // a table is not a block container box the rules for resolving conflicting
@@ -566,7 +569,6 @@
     // If we are left with conflicting overflow values for the x and y axes on a
     // table then resolve both to OverflowVisible. This is interoperable
     // behaviour but is not specced anywhere.
-    // TODO(https://crbug.com/966283): figure out how 'clip' should be handled.
     if (style.OverflowX() == EOverflow::kVisible)
       style.SetOverflowY(EOverflow::kVisible);
     else if (style.OverflowY() == EOverflow::kVisible)
diff --git a/third_party/blink/renderer/core/css/style_environment_variables_test.cc b/third_party/blink/renderer/core/css/style_environment_variables_test.cc
index c6a485d..aa9e556 100644
--- a/third_party/blink/renderer/core/css/style_environment_variables_test.cc
+++ b/third_party/blink/renderer/core/css/style_environment_variables_test.cc
@@ -682,4 +682,93 @@
                                GetCSSPropertyBackgroundColor()));
 }
 
+#if !BUILDFLAG(IS_ANDROID)
+TEST_F(StyleEnvironmentVariablesTest, TitlebarArea_AfterLoad) {
+  // This test asserts that the titlebar area environment variables should be
+  // loaded when UpdateWindowControlsOverlay is invoked in LocalFrame when the
+  // WindowControlsOverlay runtime flag is set for PWAs with display_override
+  // "window-controls-overlay".
+  ScopedWebAppWindowControlsOverlayForTest scoped_feature(true);
+
+  // Simulate browser sending the titlebar area bounds.
+  GetFrame().UpdateWindowControlsOverlay(gfx::Rect(0, 0, 100, 10));
+  String env_contents("titlebar-area-x");
+  InitializeTestPageWithVariableNamed(GetFrame(), env_contents);
+
+  // Validate the data is set.
+  DocumentStyleEnvironmentVariables& vars =
+      GetDocument().GetStyleEngine().EnsureEnvironmentVariables();
+
+  CSSVariableData* data =
+      vars.ResolveVariable(StyleEnvironmentVariables::GetVariableName(
+                               UADefinedVariable::kTitlebarAreaX,
+                               /*feature_context=*/nullptr),
+                           {});
+  EXPECT_TRUE(data);
+  EXPECT_EQ(data->Serialize(), "0px");
+  data = vars.ResolveVariable(
+      StyleEnvironmentVariables::GetVariableName(
+          UADefinedVariable::kTitlebarAreaY, /*feature_context=*/nullptr),
+      {});
+  EXPECT_TRUE(data);
+  EXPECT_EQ(data->Serialize(), "0px");
+  data = vars.ResolveVariable(StyleEnvironmentVariables::GetVariableName(
+                                  UADefinedVariable::kTitlebarAreaWidth,
+                                  /*feature_context=*/nullptr),
+                              {});
+  EXPECT_TRUE(data);
+  EXPECT_EQ(data->Serialize(), "100px");
+  data = vars.ResolveVariable(
+      StyleEnvironmentVariables::GetVariableName(
+          UADefinedVariable::kTitlebarAreaHeight, /*feature_context=*/nullptr),
+      {});
+  EXPECT_TRUE(data);
+  EXPECT_EQ(data->Serialize(), "10px");
+}
+
+TEST_F(StyleEnvironmentVariablesTest, TitlebarArea_AfterNavigation) {
+  // This test asserts that the titlebar area environment variables should be
+  // set after a navigation when the WindowControlsOverlay runtime flag is set
+  // for PWAs with display_override "window-controls-overlay".
+  ScopedWebAppWindowControlsOverlayForTest scoped_feature(true);
+
+  // Simulate browser sending the titlebar area bounds.
+  GetFrame().UpdateWindowControlsOverlay(gfx::Rect(0, 0, 100, 10));
+  String env_contents("titlebar-area-x");
+  InitializeTestPageWithVariableNamed(GetFrame(), env_contents);
+
+  SimulateNavigation();
+
+  // Validate the data is set after navigation.
+  DocumentStyleEnvironmentVariables& vars =
+      GetDocument().GetStyleEngine().EnsureEnvironmentVariables();
+
+  CSSVariableData* data =
+      vars.ResolveVariable(StyleEnvironmentVariables::GetVariableName(
+                               UADefinedVariable::kTitlebarAreaX,
+                               /*feature_context=*/nullptr),
+                           {});
+  EXPECT_TRUE(data);
+  EXPECT_EQ(data->Serialize(), "0px");
+  data = vars.ResolveVariable(
+      StyleEnvironmentVariables::GetVariableName(
+          UADefinedVariable::kTitlebarAreaY, /*feature_context=*/nullptr),
+      {});
+  EXPECT_TRUE(data);
+  EXPECT_EQ(data->Serialize(), "0px");
+  data = vars.ResolveVariable(StyleEnvironmentVariables::GetVariableName(
+                                  UADefinedVariable::kTitlebarAreaWidth,
+                                  /*feature_context=*/nullptr),
+                              {});
+  EXPECT_TRUE(data);
+  EXPECT_EQ(data->Serialize(), "100px");
+  data = vars.ResolveVariable(
+      StyleEnvironmentVariables::GetVariableName(
+          UADefinedVariable::kTitlebarAreaHeight, /*feature_context=*/nullptr),
+      {});
+  EXPECT_TRUE(data);
+  EXPECT_EQ(data->Serialize(), "10px");
+}
+#endif  // !BUILDFLAG(IS_ANDROID)
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index d786c39..0bb6bd0d 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -6743,12 +6743,40 @@
       val ? AllowState::kAllow : AllowState::kDeny;
 }
 
+void Document::MaybeExecuteDelayedAsyncScripts(
+    MilestoneForDelayedAsyncScript milestone) {
+  if (!base::FeatureList::IsEnabled(features::kDelayAsyncScriptExecution))
+    return;
+
+  switch (features::kDelayAsyncScriptExecutionDelayParam.Get()) {
+    case features::DelayAsyncScriptDelayType::kFirstPaintOrFinishedParsing:
+      // Notify the ScriptRunner if the first paint has been recorded and
+      // we're delaying async scripts until first paint or finished parsing
+      // (whichever comes first).
+      script_runner_->NotifyDelayedAsyncScriptsMilestoneReached();
+      break;
+    case features::DelayAsyncScriptDelayType::kFinishedParsing:
+      // Notify the ScriptRunner if we're finished parsing and we're delaying
+      // async scripts until finished parsing occurs.
+      if (milestone == MilestoneForDelayedAsyncScript::kFinishedParsing)
+        script_runner_->NotifyDelayedAsyncScriptsMilestoneReached();
+      break;
+  }
+}
+
+void Document::MarkFirstPaint() {
+  MaybeExecuteDelayedAsyncScripts(MilestoneForDelayedAsyncScript::kFirstPaint);
+}
+
 void Document::FinishedParsing() {
   DCHECK(!GetScriptableDocumentParser() || !parser_->IsParsing());
   DCHECK(!GetScriptableDocumentParser() || ready_state_ != kLoading);
   SetParsingState(kInDOMContentLoaded);
   DocumentParserTiming::From(*this).MarkParserStop();
 
+  MaybeExecuteDelayedAsyncScripts(
+      MilestoneForDelayedAsyncScript::kFinishedParsing);
+
   // FIXME: DOMContentLoaded is dispatched synchronously, but this should be
   // dispatched in a queued task, see https://crbug.com/425790
   if (document_timing_.DomContentLoadedEventStart().is_null())
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index cbdfcaf5..629f3d3 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -1331,6 +1331,7 @@
   bool IsDNSPrefetchEnabled() const { return is_dns_prefetch_enabled_; }
   void ParseDNSPrefetchControlHeader(const String&);
 
+  void MarkFirstPaint();
   void FinishedParsing();
 
   void SetEncodingData(const DocumentEncodingData& new_data);
@@ -2017,6 +2018,9 @@
   void ExecuteScriptsWaitingForResources();
   void ExecuteJavaScriptUrls();
 
+  enum class MilestoneForDelayedAsyncScript { kFirstPaint, kFinishedParsing };
+  void MaybeExecuteDelayedAsyncScripts(MilestoneForDelayedAsyncScript);
+
   void LoadEventDelayTimerFired(TimerBase*);
   void PluginLoadingTimerFired(TimerBase*);
 
diff --git a/third_party/blink/renderer/core/dom/node.cc b/third_party/blink/renderer/core/dom/node.cc
index 5ab77509..ca093c10 100644
--- a/third_party/blink/renderer/core/dom/node.cc
+++ b/third_party/blink/renderer/core/dom/node.cc
@@ -549,6 +549,8 @@
     return;
   LayoutBox* box_to_scroll = scrollable_area->GetLayoutBox();
 
+  auto& visual_viewport = GetDocument().GetPage()->GetVisualViewport();
+
   // TODO(bokan): This is a hack to fix https://crbug.com/977954. If we have a
   // non-default root scroller, scrolling from one of its siblings or a fixed
   // element will chain up to the root node without passing through the root
@@ -558,8 +560,8 @@
   // thread is awkward since we assume only Nodes are scrollable but the
   // VisualViewport isn't a Node. See LTHI::ApplyScroll for the equivalent
   // behavior in CC.
-  bool also_scroll_visual_viewport = GetDocument().GetFrame() &&
-                                     GetDocument().GetFrame()->IsMainFrame() &&
+  bool also_scroll_visual_viewport = GetDocument().IsInMainFrame() &&
+                                     visual_viewport.IsActiveViewport() &&
                                      IsA<LayoutView>(box_to_scroll);
   DCHECK(!also_scroll_visual_viewport ||
          !box_to_scroll->IsGlobalRootScroller());
@@ -571,9 +573,8 @@
   // Also try scrolling the visual viewport if we're at the end of the scroll
   // chain.
   if (!result.DidScroll() && also_scroll_visual_viewport) {
-    result = GetDocument().GetPage()->GetVisualViewport().UserScroll(
-        scroll_state.delta_granularity(), delta,
-        ScrollableArea::ScrollCallback());
+    result = visual_viewport.UserScroll(scroll_state.delta_granularity(), delta,
+                                        ScrollableArea::ScrollCallback());
   }
 
   if (!result.DidScroll())
diff --git a/third_party/blink/renderer/core/editing/finder/async_find_buffer.cc b/third_party/blink/renderer/core/editing/finder/async_find_buffer.cc
index 4ea74cd..3d895ee 100644
--- a/third_party/blink/renderer/core/editing/finder/async_find_buffer.cc
+++ b/third_party/blink/renderer/core/editing/finder/async_find_buffer.cc
@@ -11,9 +11,19 @@
 
 namespace {
 // Indicates how long FindBuffer task should run before pausing the work.
-constexpr base::TimeDelta kFindBufferTaskTimeoutMs = base::Milliseconds(100);
+constexpr base::TimeDelta kFindBufferTaskTimeout = base::Milliseconds(100);
+
+// Global static to allow tests to override the timeout.
+base::TimeDelta g_find_buffer_timeout = kFindBufferTaskTimeout;
 }  // namespace
 
+// static
+std::unique_ptr<base::AutoReset<base::TimeDelta>>
+AsyncFindBuffer::OverrideTimeoutForTesting(base::TimeDelta timeout_override) {
+  return std::make_unique<base::AutoReset<base::TimeDelta>>(
+      &g_find_buffer_timeout, timeout_override);
+}
+
 void AsyncFindBuffer::FindMatchInRange(RangeInFlatTree* search_range,
                                        String search_text,
                                        FindOptions options,
@@ -40,9 +50,9 @@
   search_range->StartPosition().GetDocument()->UpdateStyleAndLayout(
       DocumentUpdateReason::kFindInPage);
 
-  EphemeralRangeInFlatTree range = FindBuffer::FindMatchInRange(
-      search_range->ToEphemeralRange(), search_text, options,
-      kFindBufferTaskTimeoutMs);
+  EphemeralRangeInFlatTree range =
+      FindBuffer::FindMatchInRange(search_range->ToEphemeralRange(),
+                                   search_text, options, g_find_buffer_timeout);
 
   if (range.IsNotNull() && range.IsCollapsed()) {
     // FindBuffer reached time limit - Start/End of range is last checked
diff --git a/third_party/blink/renderer/core/editing/finder/async_find_buffer.h b/third_party/blink/renderer/core/editing/finder/async_find_buffer.h
index a744b28..6e48e9f3 100644
--- a/third_party/blink/renderer/core/editing/finder/async_find_buffer.h
+++ b/third_party/blink/renderer/core/editing/finder/async_find_buffer.h
@@ -5,19 +5,26 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_FINDER_ASYNC_FIND_BUFFER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_FINDER_ASYNC_FIND_BUFFER_H_
 
+#include "base/auto_reset.h"
 #include "base/callback.h"
 #include "base/time/time.h"
+#include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/editing/finder/find_buffer_runner.h"
 #include "third_party/blink/renderer/core/editing/finder/find_options.h"
 #include "third_party/blink/renderer/core/editing/forward.h"
 #include "third_party/blink/renderer/core/editing/range_in_flat_tree.h"
 #include "third_party/blink/renderer/platform/scheduler/public/post_cancellable_task.h"
 
+#include <memory>
+
 namespace blink {
 // This is used as an asynchronous wrapper around FindBuffer to provide a
 // callback-based interface.
-class AsyncFindBuffer final : public FindBufferRunner {
+class CORE_EXPORT AsyncFindBuffer final : public FindBufferRunner {
  public:
+  static std::unique_ptr<base::AutoReset<base::TimeDelta>>
+  OverrideTimeoutForTesting(base::TimeDelta timeout_override);
+
   explicit AsyncFindBuffer() = default;
   ~AsyncFindBuffer() = default;
 
diff --git a/third_party/blink/renderer/core/fragment_directive/text_fragment_selector_generator.cc b/third_party/blink/renderer/core/fragment_directive/text_fragment_selector_generator.cc
index 32752e70..e1f3749 100644
--- a/third_party/blink/renderer/core/fragment_directive/text_fragment_selector_generator.cc
+++ b/third_party/blink/renderer/core/fragment_directive/text_fragment_selector_generator.cc
@@ -184,6 +184,13 @@
                                 state_);
 }
 
+String TextFragmentSelectorGenerator::GetSelectorTargetText() const {
+  if (!range_)
+    return g_empty_string;
+
+  return PlainText(range_->ToEphemeralRange()).StripWhiteSpace();
+}
+
 void TextFragmentSelectorGenerator::DidFindMatch(const RangeInFlatTree& match,
                                                  bool is_unique) {
   if (is_unique &&
@@ -678,16 +685,10 @@
 
   RecordAllMetrics(selector);
   if (pending_generate_selector_callback_) {
-    NotifyClientSelectorReady(selector);
+    std::move(pending_generate_selector_callback_).Run(selector, error_);
   }
 }
 
-void TextFragmentSelectorGenerator::NotifyClientSelectorReady(
-    const TextFragmentSelector& selector) {
-  DCHECK(pending_generate_selector_callback_);
-  std::move(pending_generate_selector_callback_).Run(selector, error_);
-}
-
 // static
 void TextFragmentSelectorGenerator::OverrideExactTextMaxCharsForTesting(
     int value) {
diff --git a/third_party/blink/renderer/core/fragment_directive/text_fragment_selector_generator.h b/third_party/blink/renderer/core/fragment_directive/text_fragment_selector_generator.h
index 7ceae62f..8bfa937 100644
--- a/third_party/blink/renderer/core/fragment_directive/text_fragment_selector_generator.h
+++ b/third_party/blink/renderer/core/fragment_directive/text_fragment_selector_generator.h
@@ -28,9 +28,9 @@
 //
 // TextFragmentSelectorGenerator works by starting with a candidate selector
 // and repeatedly trying it against the page content to ensure the correct and
-// unique match. While we don't have a unique match, we repeatedly adding
-// context/range to the selector until the correct match is uniquely identified
-// or no new context/range can be added.
+// unique match. While the match isn't unique, repeatedly add context/range to
+// the selector until the correct match is uniquely identified or no new
+// context/range can be added.
 class CORE_EXPORT TextFragmentSelectorGenerator final
     : public GarbageCollected<TextFragmentSelectorGenerator>,
       public TextFragmentFinder::Client {
@@ -65,6 +65,11 @@
 
   LocalFrame* GetFrame() { return frame_; }
 
+  // Returns the text value of the range this generator is attempting to
+  // generate a selector for. Returns empty string if the range is invalid or
+  // if called before calling Generate().
+  String GetSelectorTargetText() const;
+
  private:
   friend class TextFragmentSelectorGeneratorTest;
 
@@ -162,9 +167,6 @@
   // Called when selector generation is complete.
   void OnSelectorReady(const TextFragmentSelector& selector);
 
-  // Called to notify clients of the result of |Generate|.
-  void NotifyClientSelectorReady(const TextFragmentSelector& selector);
-
   // Called by tests to change default parameters. A negative value will reset
   // the override.
   static void OverrideExactTextMaxCharsForTesting(int value);
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index 4730c85..5fccf35 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -712,6 +712,23 @@
   GetEventHandler().Clear();
   Selection().DidAttachDocument(document);
   notified_color_scheme_ = false;
+
+#if !BUILDFLAG(IS_ANDROID)
+  // For PWAs with display_override "window-controls-overlay", titlebar area
+  // rect bounds sent from the browser need to persist on navigation to keep the
+  // UI consistent. The titlebar area rect values are set in |LocalFrame| before
+  // the new document is attached. The css environment variables are needed to
+  // be set for the new document.
+  if (is_window_controls_overlay_visible_) {
+    DocumentStyleEnvironmentVariables& vars =
+        GetDocument()->GetStyleEngine().EnsureEnvironmentVariables();
+    DCHECK(!vars.ResolveVariable(
+        StyleEnvironmentVariables::GetVariableName(
+            UADefinedVariable::kTitlebarAreaX, document->GetExecutionContext()),
+        {}, false /* record_metrics */));
+    SetTitlebarAreaDocumentStyleEnvironmentVariables();
+  }
+#endif
 }
 
 void LocalFrame::OnFirstPaint(bool text_painted, bool image_painted) {
@@ -2803,18 +2820,7 @@
       GetDocument()->GetStyleEngine().EnsureEnvironmentVariables();
 
   if (is_window_controls_overlay_visible_) {
-    vars.SetVariable(
-        UADefinedVariable::kTitlebarAreaX,
-        StyleEnvironmentVariables::FormatPx(window_controls_overlay_rect_.x()));
-    vars.SetVariable(
-        UADefinedVariable::kTitlebarAreaY,
-        StyleEnvironmentVariables::FormatPx(window_controls_overlay_rect_.y()));
-    vars.SetVariable(UADefinedVariable::kTitlebarAreaWidth,
-                     StyleEnvironmentVariables::FormatPx(
-                         window_controls_overlay_rect_.width()));
-    vars.SetVariable(UADefinedVariable::kTitlebarAreaHeight,
-                     StyleEnvironmentVariables::FormatPx(
-                         window_controls_overlay_rect_.height()));
+    SetTitlebarAreaDocumentStyleEnvironmentVariables();
   } else {
     const UADefinedVariable vars_to_remove[] = {
         UADefinedVariable::kTitlebarAreaX,
@@ -3238,4 +3244,24 @@
            IsCrossOriginToOutermostMainFrame());
 }
 
+#if !BUILDFLAG(IS_ANDROID)
+void LocalFrame::SetTitlebarAreaDocumentStyleEnvironmentVariables() const {
+  DCHECK(is_window_controls_overlay_visible_);
+  DocumentStyleEnvironmentVariables& vars =
+      GetDocument()->GetStyleEngine().EnsureEnvironmentVariables();
+  vars.SetVariable(
+      UADefinedVariable::kTitlebarAreaX,
+      StyleEnvironmentVariables::FormatPx(window_controls_overlay_rect_.x()));
+  vars.SetVariable(
+      UADefinedVariable::kTitlebarAreaY,
+      StyleEnvironmentVariables::FormatPx(window_controls_overlay_rect_.y()));
+  vars.SetVariable(UADefinedVariable::kTitlebarAreaWidth,
+                   StyleEnvironmentVariables::FormatPx(
+                       window_controls_overlay_rect_.width()));
+  vars.SetVariable(UADefinedVariable::kTitlebarAreaHeight,
+                   StyleEnvironmentVariables::FormatPx(
+                       window_controls_overlay_rect_.height()));
+}
+#endif
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h
index fcddbad..de97f47 100644
--- a/third_party/blink/renderer/core/frame/local_frame.h
+++ b/third_party/blink/renderer/core/frame/local_frame.h
@@ -832,6 +832,10 @@
                                     String& clip_html,
                                     gfx::Rect& clip_rect);
 
+#if !BUILDFLAG(IS_ANDROID)
+  void SetTitlebarAreaDocumentStyleEnvironmentVariables() const;
+#endif
+
   std::unique_ptr<FrameScheduler> frame_scheduler_;
 
   // Holds all PauseSubresourceLoadingHandles allowing either |this| to delete
diff --git a/third_party/blink/renderer/core/input/scroll_manager.cc b/third_party/blink/renderer/core/input/scroll_manager.cc
index 123bc0f..42afe22 100644
--- a/third_party/blink/renderer/core/input/scroll_manager.cc
+++ b/third_party/blink/renderer/core/input/scroll_manager.cc
@@ -269,15 +269,17 @@
   if (scrolling_box->IsGlobalRootScroller() && !for_autoscroll)
     return true;
 
-  // If this is the main LayoutView, and it's not the root scroller, that means
-  // we have a non-default root scroller on the page. In this case, attempts to
-  // scroll the LayoutView should cause panning of the visual viewport as well
-  // so ensure it gets added to the scroll chain. See LTHI::ApplyScroll for the
-  // equivalent behavior in CC. Node::NativeApplyScroll contains a special
+  // If this is the main LayoutView of an active viewport (outermost main
+  // frame, portal), and it's not the root scroller, that means we have a
+  // non-default root scroller on the page.  In this case, attempts to scroll
+  // the LayoutView should cause panning of the visual viewport as well so
+  // ensure it gets added to the scroll chain.  See LTHI::ApplyScroll for the
+  // equivalent behavior in CC.  Node::NativeApplyScroll contains a special
   // handler for this case. If autoscrolling, ignore this condition because we
   // latch on to the deepest autoscrollable node.
   if (IsA<LayoutView>(scrolling_box) &&
-      current_node.GetDocument().GetFrame()->IsMainFrame() && !for_autoscroll) {
+      current_node.GetDocument().IsInMainFrame() &&
+      GetPage()->GetVisualViewport().IsActiveViewport() && !for_autoscroll) {
     return true;
   }
 
diff --git a/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc b/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
index ab7c1dd8..c32f4951 100644
--- a/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
@@ -52,6 +52,7 @@
 #include "third_party/blink/renderer/core/dom/dom_node_ids.h"
 #include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/dom/flat_tree_traversal.h"
+#include "third_party/blink/renderer/core/dom/layout_tree_builder_traversal.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/dom/shadow_root.h"
@@ -2550,6 +2551,10 @@
   if (!node->isConnected())
     return Response::ServerError("Node is detached from document");
   LayoutObject* layout_object = node->GetLayoutObject();
+  if (!layout_object) {
+    node = LayoutTreeBuilderTraversal::FirstLayoutChild(*node);
+    layout_object = node->GetLayoutObject();
+  }
   if (!layout_object)
     return Response::ServerError("Node does not have a layout object");
   PhysicalRect rect_to_scroll =
diff --git a/third_party/blink/renderer/core/inspector/inspector_highlight.cc b/third_party/blink/renderer/core/inspector/inspector_highlight.cc
index 84d6c09c..2a6d4398 100644
--- a/third_party/blink/renderer/core/inspector/inspector_highlight.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_highlight.cc
@@ -1456,7 +1456,9 @@
                        isPrimary);
 }
 
-void CollectQuadsRecursive(Node* node, Vector<gfx::QuadF>& out_quads) {
+void CollectQuads(Node* node,
+                  bool adjust_for_absolute_zoom,
+                  Vector<gfx::QuadF>& out_quads) {
   LayoutObject* layout_object = node->GetLayoutObject();
   // For inline elements, absoluteQuads will return a line box based on the
   // line-height and font metrics, which is technically incorrect as replaced
@@ -1472,20 +1474,18 @@
       LayoutTreeBuilderTraversal::FirstChild(*node)) {
     for (Node* child = LayoutTreeBuilderTraversal::FirstChild(*node); child;
          child = LayoutTreeBuilderTraversal::NextSibling(*child))
-      CollectQuadsRecursive(child, out_quads);
+      CollectQuads(child, adjust_for_absolute_zoom, out_quads);
   } else if (layout_object) {
+    wtf_size_t old_size = out_quads.size();
     layout_object->AbsoluteQuads(out_quads);
-  }
-}
-
-void CollectQuads(Node* node, Vector<gfx::QuadF>& out_quads) {
-  CollectQuadsRecursive(node, out_quads);
-  LocalFrameView* containing_view =
-      node->GetLayoutObject() ? node->GetLayoutObject()->GetFrameView()
-                              : nullptr;
-  if (containing_view) {
-    for (gfx::QuadF& quad : out_quads)
-      FrameQuadToViewport(containing_view, quad);
+    wtf_size_t new_size = out_quads.size();
+    LocalFrameView* containing_view = layout_object->GetFrameView();
+    for (wtf_size_t i = old_size; i < new_size; i++) {
+      if (containing_view)
+        FrameQuadToViewport(containing_view, out_quads[i]);
+      if (adjust_for_absolute_zoom)
+        AdjustForAbsoluteZoom::AdjustQuad(out_quads[i], *layout_object);
+    }
   }
 }
 
@@ -2101,7 +2101,7 @@
   if (!layout_object->GetNode() || !layout_object->GetNode()->IsSVGElement() ||
       layout_object->IsSVGRoot())
     return false;
-  CollectQuads(node, quads);
+  CollectQuads(node, false /* adjust_for_absolute_zoom */, quads);
   return true;
 }
 
@@ -2109,17 +2109,14 @@
 bool InspectorHighlight::GetContentQuads(
     Node* node,
     std::unique_ptr<protocol::Array<protocol::Array<double>>>* result) {
-  LayoutObject* layout_object = node->GetLayoutObject();
   LocalFrameView* view = node->GetDocument().View();
-  if (!layout_object || !view)
+  if (!view)
     return false;
   Vector<gfx::QuadF> quads;
-  CollectQuads(node, quads);
+  CollectQuads(node, true /* adjust_for_absolute_zoom */, quads);
   float scale = PageScaleFromFrameView(view);
-  for (gfx::QuadF& quad : quads) {
-    AdjustForAbsoluteZoom::AdjustQuad(quad, *layout_object);
+  for (gfx::QuadF& quad : quads)
     quad.Scale(scale, scale);
-  }
 
   *result = std::make_unique<protocol::Array<protocol::Array<double>>>();
   for (gfx::QuadF& quad : quads)
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
index 598d0f6..80d5109d 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
@@ -423,7 +423,7 @@
       LayoutTrackCollection(placement_data, kForColumns, &grid_items),
       LayoutTrackCollection(placement_data, kForRows, &grid_items));
 
-  for (auto& grid_item : grid_items) {
+  for (auto& grid_item : grid_items.item_data) {
     grid_item.ComputeSetIndices(*layout_data.Columns());
     grid_item.ComputeSetIndices(*layout_data.Rows());
   }
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_placement.cc b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_placement.cc
index 566e793..c0f254c 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_placement.cc
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_placement.cc
@@ -210,8 +210,8 @@
                                  : item_minor_span.EndLine());
 
     if (!has_indefinite_major_span && !has_indefinite_minor_span) {
-      auto* placed_item =
-          new PlacedGridItem(position, major_direction_, minor_direction_);
+      auto placed_item = std::make_unique<PlacedGridItem>(
+          position, major_direction_, minor_direction_);
 
       // We will need to sort the item vector if the new placed item should be
       // inserted to the ordered list before the last item in the vector.
@@ -219,7 +219,7 @@
           !non_auto_placed_items.IsEmpty() &&
           *placed_item < *non_auto_placed_items.back();
 
-      non_auto_placed_items.emplace_back(placed_item);
+      non_auto_placed_items.emplace_back(std::move(placed_item));
     } else {
       if (has_indefinite_major_span)
         positions_not_locked_to_major_axis->emplace_back(&position);
@@ -332,16 +332,17 @@
     AutoPlacementCursor* placement_cursor) const {
   DCHECK(placed_items && placement_cursor);
 
-  auto* new_placed_item =
-      new PlacedGridItem(position, major_direction_, minor_direction_);
-  placed_items->item_vector.emplace_back(new_placed_item);
-
+  auto new_placed_item = std::make_unique<PlacedGridItem>(
+      position, major_direction_, minor_direction_);
   const auto* next_placed_item = placement_cursor->NextPlacedItem();
-  placed_items->ordered_list.InsertAfter(
-      new_placed_item, next_placed_item ? next_placed_item->Prev()
-                                        : placed_items->ordered_list.Tail());
 
-  placement_cursor->InsertPlacedItemAtCurrentPosition(new_placed_item);
+  placed_items->ordered_list.InsertAfter(
+      new_placed_item.get(), next_placed_item
+                                 ? next_placed_item->Prev()
+                                 : placed_items->ordered_list.Tail());
+
+  placement_cursor->InsertPlacedItemAtCurrentPosition(new_placed_item.get());
+  placed_items->item_vector.emplace_back(std::move(new_placed_item));
 }
 
 void NGGridPlacement::ClampGridItemsToFitSubgridArea(
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc
index b415bd8..76a1f6f5 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.cc
+++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -1062,6 +1062,44 @@
     extra_data = document_loader_->TakeExtraData();
   }
 
+  // Fenced frame reporting metadata persists across same-origin navigations
+  // initiated from inside the fenced frame. Embedder-initiated navigations
+  // use a unique origin (in `FencedFrame::Navigate`), so the requestor is
+  // always considered cross-origin by the check (in MPArch).
+  bool is_requestor_same_origin =
+      !navigation_params->requestor_origin.IsNull() &&
+      navigation_params->requestor_origin.IsSameOriginWith(
+          WebSecurityOrigin::Create(navigation_params->url));
+  if (is_requestor_same_origin) {
+    for (const WebNavigationParams::RedirectInfo& redirect :
+         navigation_params->redirects) {
+      is_requestor_same_origin &=
+          navigation_params->requestor_origin.IsSameOriginWith(
+              WebSecurityOrigin::Create(redirect.new_url));
+    }
+  }
+  if (is_requestor_same_origin) {
+    const mojom::blink::FencedFrameReportingPtr& old_fenced_frame_reporting =
+        document_loader_->FencedFrameReporting();
+    // TODO(crbug.com/1342301): When we disable FF self urn navigations, add
+    // this DCHECK:
+    // DCHECK(!navigation_params->fenced_frame_reporting);
+    // and remove the condition from the `if` below.
+    if (old_fenced_frame_reporting &&
+        !navigation_params->fenced_frame_reporting) {
+      navigation_params->fenced_frame_reporting.emplace();
+      for (const auto& [destination, event_type_url] :
+           old_fenced_frame_reporting->metadata) {
+        base::flat_map<WebString, WebURL> data;
+        for (const auto& [event_type, url] : event_type_url) {
+          data.emplace(event_type, url);
+        }
+        navigation_params->fenced_frame_reporting->metadata.emplace(
+            destination, std::move(data));
+      }
+    }
+  }
+
   // Create the OldDocumentInfoForCommit for the old document (that might be in
   // another FrameLoader) and save it in ScopedOldDocumentInfoForCommitCapturer,
   // so that the old document can access it and fill in the information as it
diff --git a/third_party/blink/renderer/core/paint/paint_timing.cc b/third_party/blink/renderer/core/paint/paint_timing.cc
index d7db2e4..7c819a9 100644
--- a/third_party/blink/renderer/core/paint/paint_timing.cc
+++ b/third_party/blink/renderer/core/paint/paint_timing.cc
@@ -241,6 +241,11 @@
 
   first_paint_ = stamp;
   RegisterNotifyPresentationTime(PaintEvent::kFirstPaint);
+
+  LocalFrame* frame = GetFrame();
+  if (frame && frame->GetDocument()) {
+    frame->GetDocument()->MarkFirstPaint();
+  }
 }
 
 void PaintTiming::SetFirstContentfulPaint(base::TimeTicks stamp) {
diff --git a/third_party/blink/renderer/core/permissions_policy/permissions_policy_features.json5 b/third_party/blink/renderer/core/permissions_policy/permissions_policy_features.json5
index 879491e..16ba1fe 100644
--- a/third_party/blink/renderer/core/permissions_policy/permissions_policy_features.json5
+++ b/third_party/blink/renderer/core/permissions_policy/permissions_policy_features.json5
@@ -342,6 +342,12 @@
       depends_on: ["SharedAutofill"],
     },
     {
+      name: "SharedStorage",
+      permissions_policy_name: "shared-storage",
+      feature_default: "EnableForAll",
+      depends_on: ["SharedStorageAPI"],
+    },
+    {
       name: "StorageAccessAPI",
       feature_default: "EnableForAll",
       permissions_policy_name: "storage-access-api",
diff --git a/third_party/blink/renderer/core/script/script_runner.cc b/third_party/blink/renderer/core/script/script_runner.cc
index 922cfff..6658d7b 100644
--- a/third_party/blink/renderer/core/script/script_runner.cc
+++ b/third_party/blink/renderer/core/script/script_runner.cc
@@ -72,6 +72,20 @@
   pending_script->WatchForLoad(this);
 }
 
+void ScriptRunner::NotifyDelayedAsyncScriptsMilestoneReached() {
+  delay_async_script_milestone_reached_ = true;
+  while (!pending_delayed_async_scripts_.IsEmpty()) {
+    PendingScript* pending_script = pending_delayed_async_scripts_.TakeFirst();
+    DCHECK_EQ(pending_script->GetSchedulingType(),
+              ScriptSchedulingType::kAsync);
+
+    task_runner_->PostTask(
+        FROM_HERE,
+        WTF::Bind(&ScriptRunner::ExecutePendingScript, WrapWeakPersistent(this),
+                  WrapPersistent(pending_script)));
+  }
+}
+
 void ScriptRunner::PendingScriptFinished(PendingScript* pending_script) {
   pending_script->StopWatchingForLoad();
 
@@ -80,6 +94,17 @@
       CHECK(pending_async_scripts_.Contains(pending_script));
       pending_async_scripts_.erase(pending_script);
 
+      if (pending_script->IsEligibleForDelay() &&
+          !pending_script->GetElement()->IsPotentiallyRenderBlocking() &&
+          !delay_async_script_milestone_reached_ &&
+          base::FeatureList::IsEnabled(features::kDelayAsyncScriptExecution)) {
+        // When the ScriptRunner is notified via
+        // |NotifyDelayedAsyncScriptsMilestoneReached()|, the scripts in
+        // |pending_delayed_async_scripts_| will be scheduled for execution.
+        pending_delayed_async_scripts_.push_back(pending_script);
+        return;
+      }
+
       task_runner_->PostTask(
           FROM_HERE,
           WTF::Bind(&ScriptRunner::ExecutePendingScript,
@@ -118,6 +143,7 @@
   visitor->Trace(document_);
   visitor->Trace(pending_in_order_scripts_);
   visitor->Trace(pending_async_scripts_);
+  visitor->Trace(pending_delayed_async_scripts_);
   PendingScriptClient::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/core/script/script_runner.h b/third_party/blink/renderer/core/script/script_runner.h
index d532292..b4a7ca73 100644
--- a/third_party/blink/renderer/core/script/script_runner.h
+++ b/third_party/blink/renderer/core/script/script_runner.h
@@ -50,6 +50,8 @@
 
   void QueueScriptForExecution(PendingScript*);
 
+  void NotifyDelayedAsyncScriptsMilestoneReached();
+
   void SetTaskRunnerForTesting(base::SingleThreadTaskRunner* task_runner) {
     task_runner_ = task_runner;
   }
@@ -70,8 +72,17 @@
   HeapDeque<Member<PendingScript>> pending_in_order_scripts_;
   // https://html.spec.whatwg.org/C/#set-of-scripts-that-will-execute-as-soon-as-possible
   HeapHashSet<Member<PendingScript>> pending_async_scripts_;
+  HeapDeque<Member<PendingScript>> pending_delayed_async_scripts_;
 
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+  // Scripts in |pending_delayed_async_scripts_| are delayed until the
+  // |NotifyDelayedAsyncScriptsMilestoneReached()| is called. After this point,
+  // the ScriptRunner no longer delays async scripts. This bool is used to
+  // ensure we don't continue delaying async scripts after this point. See the
+  // design doc:
+  // https://docs.google.com/document/u/1/d/1G-IUrT4enARZlsIrFQ4d4cRVe9MRTJASfWwolV09JZE/edit.
+  bool delay_async_script_milestone_reached_ = false;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/shared_storage/BUILD.gn b/third_party/blink/renderer/modules/shared_storage/BUILD.gn
index 9eb4f6b..68b3c07 100644
--- a/third_party/blink/renderer/modules/shared_storage/BUILD.gn
+++ b/third_party/blink/renderer/modules/shared_storage/BUILD.gn
@@ -10,6 +10,8 @@
     "shared_storage.h",
     "shared_storage_worklet.cc",
     "shared_storage_worklet.h",
+    "util.cc",
+    "util.h",
     "window_shared_storage.cc",
     "window_shared_storage.h",
   ]
diff --git a/third_party/blink/renderer/modules/shared_storage/shared_storage.cc b/third_party/blink/renderer/modules/shared_storage/shared_storage.cc
index 640c595..6b4e2c11 100644
--- a/third_party/blink/renderer/modules/shared_storage/shared_storage.cc
+++ b/third_party/blink/renderer/modules/shared_storage/shared_storage.cc
@@ -30,6 +30,7 @@
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/probe/core_probes.h"
 #include "third_party/blink/renderer/modules/shared_storage/shared_storage_worklet.h"
+#include "third_party/blink/renderer/modules/shared_storage/util.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
@@ -150,16 +151,18 @@
   ExecutionContext* execution_context = ExecutionContext::From(script_state);
   CHECK(execution_context->IsWindow());
 
-  if (!script_state->ContextIsValid()) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidAccessError,
-                                      "A browsing context is required.");
+  if (!CheckBrowsingContextIsValid(*script_state, exception_state))
     return ScriptPromise();
-  }
 
   ScriptPromiseResolver* resolver =
       MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   ScriptPromise promise = resolver->Promise();
 
+  if (!CheckSharedStoragePermissionsPolicy(*script_state, *execution_context,
+                                           *resolver)) {
+    return promise;
+  }
+
   if (!IsValidSharedStorageKeyStringLength(key.length())) {
     resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
         script_state->GetIsolate(), DOMExceptionCode::kDataError,
@@ -192,16 +195,18 @@
   ExecutionContext* execution_context = ExecutionContext::From(script_state);
   CHECK(execution_context->IsWindow());
 
-  if (!script_state->ContextIsValid()) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidAccessError,
-                                      "A browsing context is required.");
+  if (!CheckBrowsingContextIsValid(*script_state, exception_state))
     return ScriptPromise();
-  }
 
   ScriptPromiseResolver* resolver =
       MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   ScriptPromise promise = resolver->Promise();
 
+  if (!CheckSharedStoragePermissionsPolicy(*script_state, *execution_context,
+                                           *resolver)) {
+    return promise;
+  }
+
   if (!IsValidSharedStorageKeyStringLength(key.length())) {
     resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
         script_state->GetIsolate(), DOMExceptionCode::kDataError,
@@ -231,16 +236,18 @@
   ExecutionContext* execution_context = ExecutionContext::From(script_state);
   CHECK(execution_context->IsWindow());
 
-  if (!script_state->ContextIsValid()) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidAccessError,
-                                      "A browsing context is required.");
+  if (!CheckBrowsingContextIsValid(*script_state, exception_state))
     return ScriptPromise();
-  }
 
   ScriptPromiseResolver* resolver =
       MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   ScriptPromise promise = resolver->Promise();
 
+  if (!CheckSharedStoragePermissionsPolicy(*script_state, *execution_context,
+                                           *resolver)) {
+    return promise;
+  }
+
   if (!IsValidSharedStorageKeyStringLength(key.length())) {
     resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
         script_state->GetIsolate(), DOMExceptionCode::kDataError,
@@ -261,16 +268,18 @@
   ExecutionContext* execution_context = ExecutionContext::From(script_state);
   CHECK(execution_context->IsWindow());
 
-  if (!script_state->ContextIsValid()) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidAccessError,
-                                      "A browsing context is required.");
+  if (!CheckBrowsingContextIsValid(*script_state, exception_state))
     return ScriptPromise();
-  }
 
   ScriptPromiseResolver* resolver =
       MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   ScriptPromise promise = resolver->Promise();
 
+  if (!CheckSharedStoragePermissionsPolicy(*script_state, *execution_context,
+                                           *resolver)) {
+    return promise;
+  }
+
   GetSharedStorageDocumentService(execution_context)
       ->SharedStorageClear(WTF::Bind(&OnVoidOperationFinished,
                                      WrapPersistent(resolver),
@@ -298,11 +307,8 @@
   ExecutionContext* execution_context = ExecutionContext::From(script_state);
   CHECK(execution_context->IsWindow());
 
-  if (!script_state->ContextIsValid()) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidAccessError,
-                                      "A browsing context is required.");
+  if (!CheckBrowsingContextIsValid(*script_state, exception_state))
     return ScriptPromise();
-  }
 
   LocalFrame* frame = To<LocalDOMWindow>(execution_context)->GetFrame();
   DCHECK(frame);
@@ -319,6 +325,16 @@
     return promise;
   }
 
+  // For `selectURL()` to succeed, it is currently enforced in the browser side
+  // that `addModule()` must be called beforehand that passed the early
+  // permission checks. Thus the permissions-policy check here isn't strictly
+  // needed. But here we still check the permissions-policy for consistency and
+  // consider this a higher priority error.
+  if (!CheckSharedStoragePermissionsPolicy(*script_state, *execution_context,
+                                           *resolver)) {
+    return promise;
+  }
+
   if (!IsValidSharedStorageURLsArrayLength(urls.size())) {
     resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
         script_state->GetIsolate(), DOMExceptionCode::kDataError,
@@ -460,11 +476,8 @@
   ExecutionContext* execution_context = ExecutionContext::From(script_state);
   CHECK(execution_context->IsWindow());
 
-  if (!script_state->ContextIsValid()) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidAccessError,
-                                      "A browsing context is required.");
+  if (!CheckBrowsingContextIsValid(*script_state, exception_state))
     return ScriptPromise();
-  }
 
   Vector<uint8_t> serialized_data;
   if (!Serialize(script_state, options, exception_state, serialized_data))
@@ -474,6 +487,11 @@
       MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   ScriptPromise promise = resolver->Promise();
 
+  if (!CheckSharedStoragePermissionsPolicy(*script_state, *execution_context,
+                                           *resolver)) {
+    return promise;
+  }
+
   GetSharedStorageDocumentService(execution_context)
       ->RunOperationOnWorklet(
           name, std::move(serialized_data),
diff --git a/third_party/blink/renderer/modules/shared_storage/shared_storage_worklet.cc b/third_party/blink/renderer/modules/shared_storage/shared_storage_worklet.cc
index a8e6d61..4c6d839c5 100644
--- a/third_party/blink/renderer/modules/shared_storage/shared_storage_worklet.cc
+++ b/third_party/blink/renderer/modules/shared_storage/shared_storage_worklet.cc
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/modules/shared_storage/shared_storage.h"
+#include "third_party/blink/renderer/modules/shared_storage/util.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 
@@ -29,17 +30,19 @@
   ExecutionContext* execution_context = ExecutionContext::From(script_state);
   CHECK(execution_context->IsWindow());
 
-  if (!script_state->ContextIsValid()) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidAccessError,
-                                      "A browsing context is required.");
+  if (!CheckBrowsingContextIsValid(*script_state, exception_state))
     return ScriptPromise();
-  }
 
   KURL script_source_url = execution_context->CompleteURL(module_url);
 
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   ScriptPromise promise = resolver->Promise();
 
+  if (!CheckSharedStoragePermissionsPolicy(*script_state, *execution_context,
+                                           *resolver)) {
+    return promise;
+  }
+
   if (!script_source_url.IsValid()) {
     resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
         script_state->GetIsolate(), DOMExceptionCode::kDataError,
diff --git a/third_party/blink/renderer/modules/shared_storage/util.cc b/third_party/blink/renderer/modules/shared_storage/util.cc
new file mode 100644
index 0000000..cf8bf1f
--- /dev/null
+++ b/third_party/blink/renderer/modules/shared_storage/util.cc
@@ -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.
+
+#include "third_party/blink/renderer/modules/shared_storage/util.h"
+
+#include "third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom-blink.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/platform/bindings/exception_state.h"
+#include "third_party/blink/renderer/platform/bindings/script_state.h"
+
+namespace blink {
+
+bool CheckBrowsingContextIsValid(ScriptState& script_state,
+                                 ExceptionState& exception_state) {
+  if (!script_state.ContextIsValid()) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidAccessError,
+                                      "A browsing context is required.");
+    return false;
+  }
+
+  return true;
+}
+
+bool CheckSharedStoragePermissionsPolicy(ScriptState& script_state,
+                                         ExecutionContext& execution_context,
+                                         ScriptPromiseResolver& resolver) {
+  if (!execution_context.IsFeatureEnabled(
+          mojom::blink::PermissionsPolicyFeature::kSharedStorage)) {
+    resolver.Reject(V8ThrowDOMException::CreateOrEmpty(
+        script_state.GetIsolate(), DOMExceptionCode::kInvalidAccessError,
+        "The \"shared-storage\" Permissions Policy denied the method on "
+        "window.sharedStorage."));
+
+    return false;
+  }
+
+  return true;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/shared_storage/util.h b/third_party/blink/renderer/modules/shared_storage/util.h
new file mode 100644
index 0000000..664c3c47
--- /dev/null
+++ b/third_party/blink/renderer/modules/shared_storage/util.h
@@ -0,0 +1,28 @@
+// 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_MODULES_SHARED_STORAGE_UTIL_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_SHARED_STORAGE_UTIL_H_
+
+namespace blink {
+
+class ExecutionContext;
+class ExceptionState;
+class ScriptState;
+class ScriptPromiseResolver;
+
+// Return if there is a valid browsing context associated with `script_state`.
+// Throw an error via `exception_state` if invalid.
+bool CheckBrowsingContextIsValid(ScriptState& script_state,
+                                 ExceptionState& exception_state);
+
+// Return if the shared-storage permissions policy is allowed in
+// `execution_context`. Reject the `resolver` with an error if disallowed.
+bool CheckSharedStoragePermissionsPolicy(ScriptState& script_state,
+                                         ExecutionContext& execution_context,
+                                         ScriptPromiseResolver& resolver);
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_SHARED_STORAGE_UTIL_H_
diff --git a/third_party/blink/renderer/modules/webcodecs/image_decoder_external.cc b/third_party/blink/renderer/modules/webcodecs/image_decoder_external.cc
index cbbbdc2..8dd1fe0 100644
--- a/third_party/blink/renderer/modules/webcodecs/image_decoder_external.cc
+++ b/third_party/blink/renderer/modules/webcodecs/image_decoder_external.cc
@@ -147,6 +147,14 @@
   if (init->premultiplyAlpha() == kNoneOption)
     alpha_option = ImageDecoder::kAlphaNotPremultiplied;
 
+  // TODO(crbug.com/1340190): We want to deprecate premultiplied alpha, so
+  // record whenever a client has explicitly required that.
+  if (init->premultiplyAlpha() == "premultiply") {
+    UseCounter::Count(
+        GetExecutionContext(),
+        WebFeature::kWebCodecsImageDecoderPremultiplyAlphaDeprecation);
+  }
+
   auto desired_size = SkISize::MakeEmpty();
   if (init->hasDesiredWidth() && init->hasDesiredHeight())
     desired_size = SkISize::Make(init->desiredWidth(), init->desiredHeight());
diff --git a/third_party/blink/renderer/platform/fonts/font_selector.cc b/third_party/blink/renderer/platform/fonts/font_selector.cc
index 0df0d4f..4d996e6 100644
--- a/third_party/blink/renderer/platform/fonts/font_selector.cc
+++ b/third_party/blink/renderer/platform/fonts/font_selector.cc
@@ -33,10 +33,13 @@
     return g_empty_atom;
 
   if (IsWebkitBodyFamily(font_description)) {
-    // TODO(crbug.com/1065468): Remove this counter when it's no longer
-    // necessary.
-    UseCounter::Count(use_counter,
-                      WebFeature::kFontSelectorCSSFontFamilyWebKitPrefixBody);
+    // TODO(yosin): We should make |use_counter| available for font threads.
+    if (use_counter) {
+      // TODO(crbug.com/1065468): Remove this counter when it's no longer
+      // necessary.
+      UseCounter::Count(use_counter,
+                        WebFeature::kFontSelectorCSSFontFamilyWebKitPrefixBody);
+    }
   } else if (generic_family_name == font_family_names::kWebkitStandard &&
              !generic_family.FamilyIsGeneric()) {
     // -webkit-standard is set internally only with a kGenericFamily type in
diff --git a/third_party/blink/renderer/platform/fonts/font_selector.h b/third_party/blink/renderer/platform/fonts/font_selector.h
index 66a14e4..ff22b4e6 100644
--- a/third_party/blink/renderer/platform/fonts/font_selector.h
+++ b/third_party/blink/renderer/platform/fonts/font_selector.h
@@ -92,7 +92,7 @@
   virtual void ReportFontLookupByUniqueOrFamilyName(
       const AtomicString& name,
       const FontDescription& font_description,
-      SimpleFontData* resulting_font_data) = 0;
+      scoped_refptr<SimpleFontData> resulting_font_data) = 0;
 
   // Called whenever a page attempts to find a local font based on a name. This
   // only includes lookups where the name is allowed to match PostScript names
@@ -100,7 +100,7 @@
   virtual void ReportFontLookupByUniqueNameOnly(
       const AtomicString& name,
       const FontDescription& font_description,
-      SimpleFontData* resulting_font_data,
+      scoped_refptr<SimpleFontData> resulting_font_data,
       bool is_loading_fallback = false) = 0;
 
   // Called whenever a page attempts to find a local font based on a fallback
@@ -109,12 +109,12 @@
       UChar32 fallback_character,
       FontFallbackPriority fallback_priority,
       const FontDescription& font_description,
-      SimpleFontData* resulting_font_data) = 0;
+      scoped_refptr<SimpleFontData> resulting_font_data) = 0;
 
   // Called whenever a page attempts to find a last-resort font.
   virtual void ReportLastResortFallbackFontLookup(
       const FontDescription& font_description,
-      SimpleFontData* resulting_font_data) = 0;
+      scoped_refptr<SimpleFontData> resulting_font_data) = 0;
 
   virtual void ReportNotDefGlyph() const = 0;
 
@@ -137,6 +137,8 @@
       const FontDescription&,
       const FontFamily& passed_family) = 0;
 
+  virtual bool IsContextThread() const = 0;
+
   FontFallbackMap& GetFontFallbackMap();
 
   void Trace(Visitor* visitor) const override;
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 a46eb2f..b8f9f2c5 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
@@ -53,6 +53,7 @@
   MOCK_CONST_METHOD0(getDeviceClipBounds, SkIRect());
   MOCK_CONST_METHOD1(getDeviceClipBounds, bool(SkIRect* bounds));
   MOCK_METHOD2(drawColor, void(SkColor color, SkBlendMode mode));
+  MOCK_METHOD2(drawColor, void(SkColor4f color, SkBlendMode mode));
   MOCK_METHOD1(clear, void(SkColor color));
   MOCK_METHOD1(clear, void(SkColor4f color));
   MOCK_METHOD5(drawLine,
diff --git a/third_party/blink/renderer/platform/testing/font_test_helpers.cc b/third_party/blink/renderer/platform/testing/font_test_helpers.cc
index f6a7a47f..49d6857 100644
--- a/third_party/blink/renderer/platform/testing/font_test_helpers.cc
+++ b/third_party/blink/renderer/platform/testing/font_test_helpers.cc
@@ -79,24 +79,25 @@
   void ReportFontLookupByUniqueOrFamilyName(
       const AtomicString& name,
       const FontDescription& font_description,
-      SimpleFontData* resulting_font_data) override {}
+      scoped_refptr<SimpleFontData> resulting_font_data) override {}
   void ReportFontLookupByUniqueNameOnly(
       const AtomicString& name,
       const FontDescription& font_description,
-      SimpleFontData* resulting_font_data,
+      scoped_refptr<SimpleFontData> resulting_font_data,
       bool is_loading_fallback = false) override {}
   void ReportFontLookupByFallbackCharacter(
       UChar32 hint,
       FontFallbackPriority fallback_priority,
       const FontDescription& font_description,
-      SimpleFontData* resulting_font_data) override {}
+      scoped_refptr<SimpleFontData> resulting_font_data) override {}
   void ReportLastResortFallbackFontLookup(
       const FontDescription& font_description,
-      SimpleFontData* resulting_font_data) override {}
+      scoped_refptr<SimpleFontData> resulting_font_data) override {}
   void ReportNotDefGlyph() const override {}
   void ReportEmojiSegmentGlyphCoverage(unsigned, unsigned) override {}
   ExecutionContext* GetExecutionContext() const override { return nullptr; }
   FontFaceCache* GetFontFaceCache() override { return nullptr; }
+  bool IsContextThread() const override { return true; }
 
   void RegisterForInvalidationCallbacks(FontSelectorClient*) override {}
   void UnregisterForInvalidationCallbacks(FontSelectorClient*) override {}
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 bf38c6a..f6b1282 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
@@ -759,6 +759,14 @@
         ],
     },
     {
+        'paths': ['third_party/blink/renderer/core/annotation'],
+        'allowed': [
+            # AnnotationAgentContainerImpl reuses TextFragmentSelectorGenerator
+            # and the callback must accept this type as the result code.
+            'shared_highlighting::LinkGenerationError',
+        ],
+    },
+    {
         'paths': ['third_party/blink/renderer/core/offscreencanvas'],
         'allowed': [
             # Flags to be used to set up sharedImage
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 29d45c6..4b7843a 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -261,6 +261,7 @@
 # These tests require shared-storage and fenced-frames
 # Keep this in sync with VirtualTestSuites.
 crbug.com/1218540 wpt_internal/shared_storage/* [ Skip ]
+crbug.com/1218540 external/wpt/shared-storage/* [ Skip ]
 crbug.com/1218540 virtual/shared-storage-fenced-frame-mparch/* [ Pass ]
 crbug.com/1218540 virtual/shared-storage-fenced-frame-shadow-dom/* [ Pass ]
 
@@ -3475,13 +3476,21 @@
 crbug.com/626703 [ Win ] virtual/partitioned-cookies/http/tests/inspector-protocol/network/disabled-cache-navigation.js [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 external/wpt/html/canvas/element/manual/drawing-text-to-the-canvas/canvas.2d.disconnected.html [ Failure ]
+crbug.com/626703 virtual/no-alloc-direct-call/external/wpt/html/canvas/element/manual/drawing-text-to-the-canvas/canvas.2d.disconnected.html [ Failure ]
 crbug.com/626703 [ Mac11-arm64 ] virtual/fenced-frame-mparch/wpt_internal/fenced_frame/key-scrolling.https.html [ Failure Timeout ]
 crbug.com/626703 external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-038.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-039.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-042.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-043.html [ Failure ]
 crbug.com/626703 [ Mac11 ] virtual/off-main-thread-css-paint/external/wpt/css/css-paint-api/paint2d-roundRect.https.html [ Failure ]
-crbug.com/626703 [ Mac11 ] virtual/conversions-debug-mode/wpt_internal/attribution-reporting/trigger-registration.sub.https.html?method=xhr&eligible=trigger [ Timeout ]
+crbug.com/626703 virtual/conversions-debug-mode/wpt_internal/attribution-reporting/trigger-registration.sub.https.html?method=xhr [ Pass Timeout ]
+crbug.com/626703 virtual/conversions-debug-mode/wpt_internal/attribution-reporting/trigger-registration.sub.https.html?method=xhr&eligible=trigger [ Pass Timeout ]
+crbug.com/626703 virtual/conversions-debug-mode/wpt_internal/attribution-reporting/trigger-registration.sub.https.html?method=fetch&eligible=trigger [ Pass Timeout ]
+crbug.com/626703 virtual/conversions-debug-mode/wpt_internal/attribution-reporting/trigger-registration.sub.https.html?method=img [ Pass Timeout ]
+crbug.com/626703 virtual/conversions-debug-mode/wpt_internal/attribution-reporting/trigger-registration.sub.https.html?method=img&eligible [ Pass Timeout ]
+crbug.com/626703 virtual/conversions-debug-mode/wpt_internal/attribution-reporting/trigger-registration.sub.https.html?method=script [ Pass Timeout ]
+crbug.com/626703 virtual/conversions-debug-mode/wpt_internal/attribution-reporting/trigger-registration.sub.https.html?method=script&eligible [ Pass Timeout ]
 crbug.com/626703 [ Mac10.15 ] virtual/off-main-thread-css-paint/external/wpt/css/css-paint-api/registered-property-interpolation-001.https.html [ Failure ]
 crbug.com/626703 external/wpt/shadow-dom/shadow-style-invalidation-vw-units.html [ Failure ]
 crbug.com/626703 virtual/offsetparent-old-behavior/external/wpt/shadow-dom/shadow-style-invalidation-vw-units.html [ Failure ]
@@ -7177,19 +7186,20 @@
 # Sheriff 2022-07-12
 crbug.com/1343674 [ Linux ] editing/deleting/460938.html [ Failure Pass ]
 crbug.com/1343664 external/wpt/dom/events/non-cancelable-when-passive/non-passive-mousewheel-event-listener-on-document.html [ Failure Pass ]
-crbug.com/1343651 external/wpt/credential-management/fedcm-network-requests.sub.https.html [ Crash Failure Pass ]
+crbug.com/1343651 external/wpt/credential-management/fedcm-network-requests.sub.https.html [ Crash Pass ]
 crbug.com/1343698 [ Mac Release ] external/wpt/webmessaging/postMessage_asterisk_xorigin.sub.htm [ Failure Pass ]
 crbug.com/1343698 [ Release Win10.20h2 ] external/wpt/webmessaging/postMessage_asterisk_xorigin.sub.htm [ Failure Pass ]
 
 # Tests that are exercising the tentative feature COOP: restrict-properties.
 # These are tests which are implemented before the feature is complete and
 # flakily crash.
-crbug.com/1221127 virtual/coop-restrict-properties/external/wpt/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-un.https.html?1-2 [ Failure Crash ]
-crbug.com/1221127 virtual/coop-restrict-properties/external/wpt/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-un.https.html?3-4 [ Failure Crash ]
-crbug.com/1221127 virtual/coop-restrict-properties/external/wpt/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-un.https.html?5-6 [ Failure Crash ]
-crbug.com/1221127 virtual/coop-restrict-properties/external/wpt/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-un.https.html?7-8 [ Failure Crash ]
-crbug.com/1221127 virtual/coop-restrict-properties/external/wpt/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-un.https.html?9-last [ Failure Crash ]
+crbug.com/1221127 virtual/coop-restrict-properties/external/wpt/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-un.https.html?1-2 [ Crash Failure ]
+crbug.com/1221127 virtual/coop-restrict-properties/external/wpt/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-un.https.html?3-4 [ Crash Failure ]
+crbug.com/1221127 virtual/coop-restrict-properties/external/wpt/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-un.https.html?5-6 [ Crash Failure ]
+crbug.com/1221127 virtual/coop-restrict-properties/external/wpt/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-un.https.html?7-8 [ Crash Failure ]
+crbug.com/1221127 virtual/coop-restrict-properties/external/wpt/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-un.https.html?9-last [ Crash Failure ]
 
 # Sheriff 2022-07-13
-crbug.com/1344128 [ Mac10.14 ] external/wpt/html/webappapis/scripting/events/event-handler-attributes-body-window.html [ Failure ]
 crbug.com/1204176 [ Linux ] fast/scrolling/overflow-scrollability.html [ Failure Pass ]
+crbug.com/1344210 [ Linux Debug ] http/tests/devtools/elements/styles-2/page-reload-update-sidebar.js [ Failure Pass ]
+crbug.com/1344175 [ Mac12 ] external/wpt/dom/events/non-cancelable-when-passive/non-passive-mousewheel-event-listener-on-root.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 49e9b65..34da545 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -337,7 +337,7 @@
     "prefix": "percent-based-scrolling",
     "platforms": ["Linux", "Mac", "Win"],
     "bases": ["fast/scrolling/scrollbars"],
-    "args": ["--enable-features=PercentBasedScrolling",
+    "args": ["--enable-features=WindowsScrollingPersonality",
              "--enable-threaded-compositing",
              "--enable-prefer-compositing-to-lcd-text",
              "--disable-features=CompositorThreadedScrollbarScrolling"]
@@ -348,7 +348,7 @@
     "bases": ["fast/events/wheel",
               "fast/scrolling",
               "virtual/percent-based-scrolling"],
-    "args": ["--enable-features=PercentBasedScrolling"]
+    "args": ["--enable-features=WindowsScrollingPersonality"]
   },
   {
     "prefix": "compositor-threaded-percent-based-scrolling",
@@ -356,7 +356,7 @@
     "bases": ["fast/events/wheel",
               "fast/scrolling",
               "virtual/percent-based-scrolling"],
-    "args": ["--enable-features=PercentBasedScrolling",
+    "args": ["--enable-features=WindowsScrollingPersonality",
              "--enable-threaded-compositing",
              "--enable-prefer-compositing-to-lcd-text"]
   },
@@ -365,7 +365,7 @@
     "platforms": ["Linux", "Mac", "Win"],
     "bases": ["fast/scrolling/scrollbars/dsf-ready",
               "virtual/percent-based-scrolling"],
-    "args": ["--enable-features=PercentBasedScrolling",
+    "args": ["--enable-features=WindowsScrollingPersonality",
              "--enable-threaded-compositing",
              "--enable-prefer-compositing-to-lcd-text",
              "--force-device-scale-factor=2"]
@@ -915,7 +915,7 @@
     "platforms": ["Linux", "Mac", "Win"],
     "bases": ["fast/scrolling/scrollbars"],
     "args": ["--enable-features=ScrollUnification",
-             "--enable-features=PercentBasedScrolling",
+             "--enable-features=WindowsScrollingPersonality",
              "--enable-threaded-compositing",
              "--enable-prefer-compositing-to-lcd-text",
              "--disable-features=CompositorThreadedScrollbarScrolling"]
@@ -1074,6 +1074,7 @@
       "http/tests/inspector-protocol/target/auto-attach-auction-worklet.js",
       "http/tests/inspector-protocol/target/target-auction-worklet.js",
       "http/tests/inspector-protocol/timeline/auction-worklet.js",
+      "http/tests/inspector-protocol/timeline/auction-worklet-network.js",
       "http/tests/inspector-protocol/timeline/auction-worklet-frame.js",
       "http/tests/inspector-protocol/storage/interest-groups.js"
     ],
@@ -1097,6 +1098,24 @@
     "args": ["--enable-features=AutomaticLazyFrameLoadingToAds:timeout/1000"]
   },
   {
+    "prefix": "async-script-scheduling-disabled",
+    "platforms": ["Linux", "Mac", "Win"],
+    "bases": ["wpt_internal/async-script-scheduling"],
+    "args": ["--disable-features=DelayAsyncScriptExecution"]
+  },
+  {
+    "prefix": "async-script-scheduling-finished-parsing",
+    "platforms": ["Linux", "Mac", "Win"],
+    "bases": ["wpt_internal/async-script-scheduling"],
+    "args": ["--enable-features=DelayAsyncScriptExecution:delay_type/finished_parsing"]
+  },
+  {
+    "prefix": "async-script-scheduling-first-paint-or-finished-parsing",
+    "platforms": ["Linux", "Mac", "Win"],
+    "bases": ["wpt_internal/async-script-scheduling"],
+    "args": ["--enable-features=DelayAsyncScriptExecution:delay_type/first_paint_or_finished_parsing"]
+  },
+  {
     "prefix": "initially-invisible-images-lcp",
     "platforms": ["Linux", "Mac", "Win"],
     "bases": ["external/wpt/largest-contentful-paint/initially-invisible-images.html"],
@@ -1123,7 +1142,8 @@
     "prefix": "shared-storage-fenced-frame-mparch",
     "platforms": ["Linux", "Mac", "Win"],
     "bases": [
-      "wpt_internal/shared_storage"
+      "wpt_internal/shared_storage",
+      "external/wpt/shared-storage"
     ],
     "args": ["--enable-features=SharedStorageAPI,FencedFrames:implementation_type/mparch,PrivacySandboxAdsAPIsOverride"]
   },
@@ -1131,7 +1151,8 @@
     "prefix": "shared-storage-fenced-frame-shadow-dom",
     "platforms": ["Linux", "Mac", "Win"],
     "bases": [
-      "wpt_internal/shared_storage"
+      "wpt_internal/shared_storage",
+      "external/wpt/shared-storage"
     ],
     "args": ["--enable-features=SharedStorageAPI,FencedFrames:implementation_type/shadow_dom,PrivacySandboxAdsAPIsOverride"]
   },
diff --git a/third_party/blink/web_tests/external/Version b/third_party/blink/web_tests/external/Version
index a4fa35f7..2f5c352 100644
--- a/third_party/blink/web_tests/external/Version
+++ b/third_party/blink/web_tests/external/Version
@@ -1 +1 @@
-Version: 08628d867c1421e398b4a47e1b1890b736e956f8
+Version: f5a2d3ec1ce8fecf1eef456969ca1aee87138c40
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 9fdeb4e0..6ea5e75 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
@@ -15749,6 +15749,35 @@
      ]
     },
     "printing": {
+     "background-image-print.html": [
+      "8d294107406e902bae7b368a8e34100221cea2d2",
+      [
+       null,
+       [
+        [
+         "/css/printing/background-image-print-ref.html",
+         "=="
+        ]
+       ],
+       {
+        "fuzzy": [
+         [
+          null,
+          [
+           [
+            0,
+            3
+           ],
+           [
+            0,
+            400
+           ]
+          ]
+         ]
+        ]
+       }
+      ]
+     ],
      "emoji-print.html": [
       "689c337acba6f84d088b3e8ed5cffe9c3ef6dd5c",
       [
@@ -143980,6 +144009,19 @@
        {}
       ]
      ],
+     "nested-oofs-in-relative-multicol.html": [
+      "2130e72e6fe5275af732c18209f5861180b6984d",
+      [
+       null,
+       [
+        [
+         "/css/css-multicol/reference/nested-oofs-in-relative-multicol-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "nested-past-fragmentation-line.html": [
       "77f91f4a23d802bdfe1d94072825cf25b5aeb10f",
       [
@@ -144058,19 +144100,6 @@
        {}
       ]
      ],
-     "oof-in-nested-multicol.html": [
-      "44c28696b3002af9887c3d6ad6c161069f7db911",
-      [
-       null,
-       [
-        [
-         "/css/css-multicol/reference/oof-in-nested-multicol-ref.html",
-         "=="
-        ]
-       ],
-       {}
-      ]
-     ],
      "oof-nested-in-single-column.html": [
       "d888a0692f262a7aa6df424dca07ea6c6f837b66",
       [
@@ -231920,6 +231949,35 @@
         }
        },
        "drawing-text-to-the-canvas": {
+        "canvas.2d.disconnected.html": [
+         "a1715f6663c96289adac6db909035020b6640d1c",
+         [
+          null,
+          [
+           [
+            "/html/canvas/element/manual/drawing-text-to-the-canvas/canvas.2d.disconnected-ref.html",
+            "=="
+           ]
+          ],
+          {
+           "fuzzy": [
+            [
+             null,
+             [
+              [
+               0,
+               23
+              ],
+              [
+               0,
+               829
+              ]
+             ]
+            ]
+           ]
+          }
+         ]
+        ],
         "canvas.2d.fontStretch.condensed.html": [
          "2bda32082e3577757ca58dd460d0f0a478a7d647",
          [
@@ -281040,8 +281098,8 @@
        "d19ec1b93a14cc030c5b97de775438d8cebb94c9",
        []
       ],
-      "oof-in-nested-multicol-ref.html": [
-       "5d8208e167aec63290a1eef697ab61cfccefcd83",
+      "nested-oofs-in-relative-multicol-ref.html": [
+       "01956bf492c825d73271d528d74f4a6053764e0a",
        []
       ]
      },
@@ -297073,6 +297131,10 @@
      ]
     },
     "printing": {
+     "background-image-print-ref.html": [
+      "305aa2fce2f490ef161c803558cc7a17bf7aa117",
+      []
+     ],
      "fragmented-inline-block-002-print-ref.html": [
       "dcfa3a1385628a90cb2d1be17e8bf6da09cde20e",
       []
@@ -306600,6 +306662,10 @@
         }
        },
        "drawing-text-to-the-canvas": {
+        "canvas.2d.disconnected-ref.html": [
+         "b36d29b97f5e3b0065ea13ca008ce0054835efdc",
+         []
+        ],
         "canvas.2d.fontStretch-ref.html": [
          "4b3b28baf9dcb1dea96611bfdbfbce0a0aba7c3c",
          []
@@ -308101,7 +308167,7 @@
        []
       ],
       "iframe-test.js": [
-       "3d887449840351c8f91a9f9845bcaa639f194d1f",
+       "84b065fab7676be7f3de3061c143ef7dd83de449",
        []
       ],
       "popup-test.js": [
@@ -308139,6 +308205,26 @@
         "b3c24c3f82d2dbf07eefa9fa2e88fcf3817473d2",
         []
        ],
+       "iframe-popup-to-so.https.html.headers": [
+        "d5c99062d2bb8f9660b68c172754867b598ed43f",
+        []
+       ],
+       "iframe-popup-to-soap.https.html.headers": [
+        "d5c99062d2bb8f9660b68c172754867b598ed43f",
+        []
+       ],
+       "iframe-popup-to-un.https.html.headers": [
+        "d5c99062d2bb8f9660b68c172754867b598ed43f",
+        []
+       ],
+       "iframe-popup.https.html.headers": [
+        "d5c99062d2bb8f9660b68c172754867b598ed43f",
+        []
+       ],
+       "named_targeting.https.html.headers": [
+        "d5c99062d2bb8f9660b68c172754867b598ed43f",
+        []
+       ],
        "popup-so.https.html.headers": [
         "46ad58d83bf6e98913ca4c564b7acb8f19fa0093",
         []
@@ -327316,7 +327402,13 @@
       []
      ],
      "testcommon.js": [
-      "0eadb54a2feedf9d1a2677fc7e44a428b00927b9",
+      "97e81f494cc6f0d40b293ade0db8e65cc53424e6",
+      []
+     ]
+    },
+    "view-timelines": {
+     "testcommon.js": [
+      "c49caf1583058051071087c008efd8b06d390c59",
       []
      ]
     }
@@ -329021,7 +329113,7 @@
        []
       ],
       "fetch-event-test-worker.js": [
-       "bb59bc928651acd5274431e6d31711c4288523ba",
+       "813f79d1b07ba9bb9e21acb5a2274cfd4029c62b",
        []
       ],
       "fetch-event-within-sw-worker.js": [
@@ -464372,6 +464464,145 @@
      ],
      "tentative": {
       "restrict-properties": {
+       "iframe-popup-to-so.https.html": [
+        "740ff2595aa5891cfaf99a04cd281e71e4d4bc74",
+        [
+         "html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-so.https.html?1-2",
+         {
+          "timeout": "long"
+         }
+        ],
+        [
+         "html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-so.https.html?3-4",
+         {
+          "timeout": "long"
+         }
+        ],
+        [
+         "html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-so.https.html?5-6",
+         {
+          "timeout": "long"
+         }
+        ],
+        [
+         "html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-so.https.html?7-8",
+         {
+          "timeout": "long"
+         }
+        ],
+        [
+         "html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-so.https.html?9-last",
+         {
+          "timeout": "long"
+         }
+        ]
+       ],
+       "iframe-popup-to-soap.https.html": [
+        "f3af3ca7db1bad6506fef6b519c8f244d2080d0e",
+        [
+         "html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-soap.https.html?1-2",
+         {
+          "timeout": "long"
+         }
+        ],
+        [
+         "html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-soap.https.html?3-4",
+         {
+          "timeout": "long"
+         }
+        ],
+        [
+         "html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-soap.https.html?5-6",
+         {
+          "timeout": "long"
+         }
+        ],
+        [
+         "html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-soap.https.html?7-8",
+         {
+          "timeout": "long"
+         }
+        ],
+        [
+         "html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-soap.https.html?9-last",
+         {
+          "timeout": "long"
+         }
+        ]
+       ],
+       "iframe-popup-to-un.https.html": [
+        "560dfd90511dca11a8b5f4fb30d88beb093c538a",
+        [
+         "html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-un.https.html?1-2",
+         {
+          "timeout": "long"
+         }
+        ],
+        [
+         "html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-un.https.html?3-4",
+         {
+          "timeout": "long"
+         }
+        ],
+        [
+         "html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-un.https.html?5-6",
+         {
+          "timeout": "long"
+         }
+        ],
+        [
+         "html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-un.https.html?7-8",
+         {
+          "timeout": "long"
+         }
+        ],
+        [
+         "html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-un.https.html?9-last",
+         {
+          "timeout": "long"
+         }
+        ]
+       ],
+       "iframe-popup.https.html": [
+        "2c6f0b62822f61bc4aac8a9bdd50cdbd724c07eb",
+        [
+         "html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup.https.html?1-2",
+         {
+          "timeout": "long"
+         }
+        ],
+        [
+         "html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup.https.html?3-4",
+         {
+          "timeout": "long"
+         }
+        ],
+        [
+         "html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup.https.html?5-6",
+         {
+          "timeout": "long"
+         }
+        ],
+        [
+         "html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup.https.html?7-8",
+         {
+          "timeout": "long"
+         }
+        ],
+        [
+         "html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup.https.html?9-last",
+         {
+          "timeout": "long"
+         }
+        ]
+       ],
+       "named_targeting.https.html": [
+        "00eb6506e9b9b18bfb385bc4a0dd64dcd48a8b38",
+        [
+         null,
+         {}
+        ]
+       ],
        "popup-so.https.html": [
         "e5313a6e222d5b7671747516b8d056fda7e9db8b",
         [
@@ -521238,6 +521469,34 @@
      ]
     },
     "view-timelines": {
+     "block-view-timeline-current-time-vertical-rl.tentative.html": [
+      "2c2e02be6bb98a274bcb53bd65d9138c4e49a4f9",
+      [
+       null,
+       {}
+      ]
+     ],
+     "block-view-timeline-current-time.tentative.html": [
+      "6e60db2768b6a9cb2801d3beb51eb907b0e48c11",
+      [
+       null,
+       {}
+      ]
+     ],
+     "block-view-timeline-nested-subject.tentative.html": [
+      "720b33aad0d29c7771720280345af532144fb7c9",
+      [
+       null,
+       {}
+      ]
+     ],
+     "inline-view-timeline-current-time.tentative.html": [
+      "4cd6394e33f19f1995ae29560ee6c4248988f20a",
+      [
+       null,
+       {}
+      ]
+     ],
      "view-timeline-source.tentative.html": [
       "15fbdb5785543e739e5c0b1ff44cc6396cfb09ca",
       [
@@ -524148,8 +524407,17 @@
        }
       ]
      ],
+     "fetch-event.https.h2.html": [
+      "5cd381ec98dbee28586f4da68f3656706b81b03a",
+      [
+       null,
+       {
+        "timeout": "long"
+       }
+      ]
+     ],
      "fetch-event.https.html": [
-      "59a0988b490f6e7378baecc98a0b44dd20315e5d",
+      "ce53f3c9bff3f7b4bc1377891adfdfd6a322f2d5",
       [
        null,
        {
diff --git a/third_party/blink/web_tests/external/wpt/.well-known/web-identity b/third_party/blink/web_tests/external/wpt/.well-known/web-identity
index 5481479..6f503637 100644
--- a/third_party/blink/web_tests/external/wpt/.well-known/web-identity
+++ b/third_party/blink/web_tests/external/wpt/.well-known/web-identity
@@ -1,5 +1,6 @@
 def main(request, response):
-  host = request.headers.get(b"Host").decode("utf-8")
+  config = request.server.config
+  host = config.browser_host + ":" + str(config.ports["https"][0])
   return """
 {{
   "provider_urls": [
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-img-display-table-ref.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-img-display-table-ref.html
new file mode 100644
index 0000000..4552cb06
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-img-display-table-ref.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Verifies img elements are clipped with display:table</title>
+<link rel="help" href="https://drafts.csswg.org/css-overflow/#propdef-overflow">
+<link rel="author" title="Khushal Sagar" href="mailto:khushalsagar@chromium.org">
+<style>
+  .default {
+    width: 25px;
+    height: 50px;
+    border-radius: 2px;
+    overflow: clip;
+    border-radius: 2px;
+  }
+</style>
+<body>
+  <div class="default">
+    <img src="../css-images/support/exif-orientation-6-ru.jpg"></img>
+  </div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-img-display-table.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-img-display-table.html
new file mode 100644
index 0000000..b7e19aa
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-img-display-table.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Verifies img elements are clipped with display:table</title>
+<link rel="help" href="https://drafts.csswg.org/css-overflow/#propdef-overflow">
+<link rel="author" title="Khushal Sagar" href="mailto:khushalsagar@chromium.org">
+<link rel="match" href="overflow-img-display-table-ref.html">
+<style>
+  .default {
+    width: 25px;
+    height: 50px;
+    object-fit: none;
+    object-position: 0% 0%;
+    border-radius: 2px;
+    display: table;
+  }
+</style>
+<body>
+<img class=default src="../css-images/support/exif-orientation-6-ru.jpg"></img>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/printing/background-image-print-ref.html b/third_party/blink/web_tests/external/wpt/css/printing/background-image-print-ref.html
new file mode 100644
index 0000000..305aa2f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/printing/background-image-print-ref.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1769429">
+<style>
+  body {
+    font-family: century;
+    font-weight: bold;
+    color: black;
+    print-color-adjust: exact;
+  }
+  .tile {
+    width: 16px;
+    height: 16px;
+    background-repeat: no-repeat;
+  }
+  .white   { background: white }
+  .magenta { background: magenta }
+  .lime    { background: lime }
+  .cyan    { background: cyan }
+  .blue    { background: blue }
+  .red     { background: red }
+  .green   { background: green }
+  .gray    { background: gray }
+  .yellow  { background: yellow }
+</style>
+</head>
+<body>
+  <div class="tile white"></div>
+  <div class="tile magenta"></div>
+  <div class="tile lime"></div>
+  <div class="tile cyan"></div>
+  <div class="tile blue"></div>
+  <div class="tile red"></div>
+  <div class="tile green"></div>
+  <div class="tile gray"></div>
+  <div class="tile yellow"></div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/printing/background-image-print.html b/third_party/blink/web_tests/external/wpt/css/printing/background-image-print.html
new file mode 100644
index 0000000..8d2941074
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/printing/background-image-print.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>test-case.html</title>
+<link rel="match" href="background-image-print-ref.html">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1769429">
+<meta name="fuzzy" content="maxDifference=0-3;totalPixels=0-400">
+<style>
+  body {
+    font-family: century;
+    font-weight: bold;
+    color: black;
+    print-color-adjust: exact;
+  }
+  .tile {
+    width: 16px;
+    height: 16px;
+    background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH5gcMEDohgdlcwAAAAL1JREFUeNrt29ENgCAMRdFi2Ns6eR1CkxfjuQtAegJfsGZmKtmq7PKT3cBRAgBAAAAIAAABACAAAAQAgAAAEAAAAgBAAAAIAAABACAAAAQAgAAA0OP2+vkAwt8DnABXEAABACAAAAQAgAAAEAAAAgBAAAAIAAABACAAAAQAgAAAEAAAAgBAb7Tryj6Q7+7sBE4nwBUkAAAEAIAAABAAAAIAQAAACAAAAQAgAAAEAIAAABAAAAIAQAAACMDnuwGcQwlEUW+eJgAAAABJRU5ErkJggg==");
+    background-repeat: no-repeat;
+  }
+  .white   { background-position:   -2px   -2px }
+  .magenta { background-position: -34px   -2px }
+  .lime    { background-position: -66px   -2px }
+  .cyan    { background-position:   -2px -34px }
+  .blue    { background-position: -34px -34px }
+  .red     { background-position: -66px -34px }
+  .green   { background-position:   -2px -66px }
+  .gray    { background-position: -34px -66px }
+  .yellow  { background-position: -66px -66px }
+</style>
+</head>
+<body>
+  <div class="tile white"></div>
+  <div class="tile magenta"></div>
+  <div class="tile lime"></div>
+  <div class="tile cyan"></div>
+  <div class="tile blue"></div>
+  <div class="tile red"></div>
+  <div class="tile green"></div>
+  <div class="tile gray"></div>
+  <div class="tile yellow"></div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/drawing-text-to-the-canvas/canvas.2d.disconnected-ref.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/drawing-text-to-the-canvas/canvas.2d.disconnected-ref.html
new file mode 100644
index 0000000..b36d29b9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/drawing-text-to-the-canvas/canvas.2d.disconnected-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>HTML Canvas reference</title>
+<body>
+<canvas id=c>
+</body>
+<script>
+var c = document.getElementById("c");
+var ctx = c.getContext("2d");
+ctx.font = "50px monospace";
+ctx.fillText("Hello", 50, 75);
+ctx.font = "25px serif";
+ctx.fillText("World", 100, 100);
+c.style.border = "3px solid cyan";
+</script>
+
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/drawing-text-to-the-canvas/canvas.2d.disconnected.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/drawing-text-to-the-canvas/canvas.2d.disconnected.html
new file mode 100644
index 0000000..a1715f6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/drawing-text-to-the-canvas/canvas.2d.disconnected.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>HTML Canvas testcase: canvas element not in document</title>
+<link rel=match href="canvas.2d.disconnected-ref.html">
+<meta name=fuzzy content="maxDifference=0-23;totalPixels=0-829">
+<body>
+</body>
+<script>
+var d = new Document();
+var c = d.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
+var ctx = c.getContext("2d");
+ctx.font = "50px monospace";
+ctx.fillText("Hello", 50, 75);
+ctx.font = "25px serif";
+ctx.fillText("World", 100, 100);
+c.toBlob((blob) => {
+  var img = document.createElement("img");
+  const url = URL.createObjectURL(blob);
+  img.src = url;
+  img.style.border = "3px solid cyan";
+  document.body.appendChild(img);
+});
+</script>
+
diff --git a/third_party/blink/web_tests/external/wpt/shared-storage/permissions-policy-default.tentative.https.sub.html b/third_party/blink/web_tests/external/wpt/shared-storage/permissions-policy-default.tentative.https.sub.html
new file mode 100644
index 0000000..f055ae0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shared-storage/permissions-policy-default.tentative.https.sub.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src=/permissions-policy/resources/permissions-policy.js></script>
+  <script src="/shared-storage/resources/util.js"></script>
+  <script>
+    'use strict';
+    const same_origin_src = '/shared-storage/resources/permissions-policy-helper.html';
+    const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
+      same_origin_src;
+    const header = 'Default permissions policy';
+
+    promise_test(async t => {
+      const allowed = await AreSharedStorageMethodsAllowedByPermissionsPolicy();
+      assert_true(allowed);
+    }, header + ' allows sharedStorage in the current page.');
+
+    async_test(t => {
+      test_feature_availability('shared-storage', t, same_origin_src,
+          expect_feature_available_default);
+    }, header + ' allows sharedStorage in same-origin iframes.');
+
+    async_test(t => {
+      test_feature_availability('shared-storage', t, cross_origin_src,
+          expect_feature_available_default);
+    }, header + ' allows sharedStorage in cross-origin iframes.');
+  </script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/shared-storage/permissions-policy-none.tentative.https.sub.html b/third_party/blink/web_tests/external/wpt/shared-storage/permissions-policy-none.tentative.https.sub.html
new file mode 100644
index 0000000..7154061
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shared-storage/permissions-policy-none.tentative.https.sub.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src=/permissions-policy/resources/permissions-policy.js></script>
+  <script src="/shared-storage/resources/util.js"></script>
+  <script>
+    'use strict';
+    const same_origin_src = '/shared-storage/resources/permissions-policy-helper.html';
+    const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
+      same_origin_src;
+    const header = 'permissions policy header shared-storage=()';
+
+    promise_test(async t => {
+      const allowed = await AreSharedStorageMethodsAllowedByPermissionsPolicy();
+      assert_false(allowed);
+    }, header + ' disallows sharedStorage in the current page.');
+
+    async_test(t => {
+      test_feature_availability('shared-storage', t, same_origin_src,
+          expect_feature_unavailable_default);
+    }, header + ' disallows sharedStorage in same-origin iframes.');
+
+    async_test(t => {
+      test_feature_availability('shared-storage', t, cross_origin_src,
+          expect_feature_unavailable_default);
+    }, header + ' disallows sharedStorage in cross-origin iframes.');
+  </script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/shared-storage/permissions-policy-none.tentative.https.sub.html.headers b/third_party/blink/web_tests/external/wpt/shared-storage/permissions-policy-none.tentative.https.sub.html.headers
new file mode 100644
index 0000000..9903f7c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shared-storage/permissions-policy-none.tentative.https.sub.html.headers
@@ -0,0 +1 @@
+Permissions-Policy: shared-storage=()
diff --git a/third_party/blink/web_tests/external/wpt/shared-storage/permissions-policy-self.tentative.https.sub.html b/third_party/blink/web_tests/external/wpt/shared-storage/permissions-policy-self.tentative.https.sub.html
new file mode 100644
index 0000000..bde32a5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shared-storage/permissions-policy-self.tentative.https.sub.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src=/permissions-policy/resources/permissions-policy.js></script>
+  <script src="/shared-storage/resources/util.js"></script>
+  <script>
+    'use strict';
+    const same_origin_src = '/shared-storage/resources/permissions-policy-helper.html';
+    const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
+      same_origin_src;
+    const header = 'permissions policy header shared-storage=(self)';
+
+    promise_test(async t => {
+      const allowed = await AreSharedStorageMethodsAllowedByPermissionsPolicy();
+      assert_true(allowed);
+    }, header + ' allows sharedStorage in the current page.');
+
+    async_test(t => {
+      test_feature_availability('shared-storage', t, same_origin_src,
+          expect_feature_available_default);
+    }, header + ' allows sharedStorage in same-origin iframes.');
+
+    async_test(t => {
+      test_feature_availability('shared-storage', t, cross_origin_src,
+          expect_feature_unavailable_default);
+    }, header + ' disallows sharedStorage in cross-origin iframes.');
+  </script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/shared-storage/permissions-policy-self.tentative.https.sub.html.headers b/third_party/blink/web_tests/external/wpt/shared-storage/permissions-policy-self.tentative.https.sub.html.headers
new file mode 100644
index 0000000..36c95f2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shared-storage/permissions-policy-self.tentative.https.sub.html.headers
@@ -0,0 +1 @@
+Permissions-Policy: shared-storage=(self)
diff --git a/third_party/blink/web_tests/external/wpt/shared-storage/resources/permissions-policy-helper.html b/third_party/blink/web_tests/external/wpt/shared-storage/resources/permissions-policy-helper.html
new file mode 100644
index 0000000..d87092a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shared-storage/resources/permissions-policy-helper.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<body>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script src="/shared-storage/resources/util.js"></script>
+  <script>
+  'use strict';
+
+  window.onload = async function() {
+    if (await AreSharedStorageMethodsAllowedByPermissionsPolicy()) {
+      parent.postMessage({ type: 'availability-result', enabled: true }, '*');
+      return;
+    }
+
+    parent.postMessage({ type: 'availability-result', enabled: false }, '*');
+  }
+  </script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/shared-storage/resources/simple-module.js b/third_party/blink/web_tests/external/wpt/shared-storage/resources/simple-module.js
new file mode 100644
index 0000000..ad9a93a7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shared-storage/resources/simple-module.js
@@ -0,0 +1 @@
+'use strict';
diff --git a/third_party/blink/web_tests/external/wpt/shared-storage/resources/util.js b/third_party/blink/web_tests/external/wpt/shared-storage/resources/util.js
new file mode 100644
index 0000000..4dea983
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shared-storage/resources/util.js
@@ -0,0 +1,69 @@
+'use strict';
+
+// Execute all shared storage methods and capture their errors. Return true if
+// the permissions policy allows all of them; return false if the permissions
+// policy disallows all of them. Precondition: only these two outcomes are
+// possible.
+async function AreSharedStorageMethodsAllowedByPermissionsPolicy() {
+  let permissionsPolicyDeniedCount = 0;
+  const errorMessage = 'The \"shared-storage\" Permissions Policy denied the method on window.sharedStorage.';
+
+  try {
+    await window.sharedStorage.worklet.addModule('/shared-storage/resources/simple-module.js');
+  } catch (e) {
+    assert_equals(e.message, errorMessage);
+    ++permissionsPolicyDeniedCount;
+  }
+
+  try {
+    await window.sharedStorage.run('operation');
+  } catch (e) {
+    assert_equals(e.message, errorMessage);
+    ++permissionsPolicyDeniedCount;
+  }
+
+  try {
+    // Run selectURL() with without addModule() and this should always fail.
+    // Check the error message to distinguish between the permissions policy
+    // error and the missing addModule() error.
+    await sharedStorage.selectURL("operation", [{url: "1.html"}]);
+    assert_unreached("did not fail");
+  } catch (e) {
+    if (e.message === errorMessage) {
+      ++permissionsPolicyDeniedCount;
+    }
+  }
+
+  try {
+    await window.sharedStorage.set('a', 'b');
+  } catch (e) {
+    assert_equals(e.message, errorMessage);
+    ++permissionsPolicyDeniedCount;
+  }
+
+  try {
+    await window.sharedStorage.append('a', 'b');
+  } catch (e) {
+    assert_equals(e.message, errorMessage);
+    ++permissionsPolicyDeniedCount;
+  }
+
+  try {
+    await window.sharedStorage.clear();
+  } catch (e) {
+    assert_equals(e.message, errorMessage);
+    ++permissionsPolicyDeniedCount;
+  }
+
+  try {
+    await window.sharedStorage.delete('a');
+  } catch (e) {
+    assert_equals(e.message, errorMessage);
+    ++permissionsPolicyDeniedCount;
+  }
+
+  if (permissionsPolicyDeniedCount === 0)
+    return true;
+
+  return false;
+}
\ No newline at end of file
diff --git a/third_party/blink/web_tests/flag-specific/highdpi/inspector-protocol/dom/dom-scrollIntoViewIfNeeded-expected.txt b/third_party/blink/web_tests/flag-specific/highdpi/inspector-protocol/dom/dom-scrollIntoViewIfNeeded-expected.txt
index 9da26ea..2385a11 100644
--- a/third_party/blink/web_tests/flag-specific/highdpi/inspector-protocol/dom/dom-scrollIntoViewIfNeeded-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/highdpi/inspector-protocol/dom/dom-scrollIntoViewIfNeeded-expected.txt
@@ -5,4 +5,5 @@
 201.3333282470703;1901.3333740234375
 528.6666870117188;2222.666748046875
 348.6666564941406;2156
+0;0
 
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/timeline/auction-worklet-network.js b/third_party/blink/web_tests/http/tests/inspector-protocol/timeline/auction-worklet-network.js
new file mode 100644
index 0000000..bcfc14c3
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/timeline/auction-worklet-network.js
@@ -0,0 +1,98 @@
+(async function(testRunner) {
+  const base = 'https://a.test:8443/inspector-protocol/resources/'
+  const bp = testRunner.browserP();
+  const {page, session, dp} = await testRunner.startBlank(
+      'Tracing of network activity FLEDGE worklets.',
+      {url: base + 'fledge_join.html?40'});
+  const testStart = Date.now();
+  const testLimit = 5 * 60 * 1000;  // Way longer than the test may take.
+
+  const TracingHelper =
+      await testRunner.loadScript('../resources/tracing-test.js');
+  const tracingHelper = new TracingHelper(testRunner, session);
+  await tracingHelper.startTracing('devtools.timeline');
+
+  function handleUrn(input) {
+    if (typeof input === 'string' && input.startsWith('urn:uuid:'))
+      return 'urn:uuid:(randomized)';
+    return input;
+  }
+
+  function validateAbsoluteMs(object, field) {
+    // The ms absolute format is from epoch, same as JS.
+    if (field in object) {
+      const val = object[field];
+      if (val >= testStart && val < (testStart + testLimit))
+        object[field] = '<absolute timestamp>';
+    }
+  }
+
+  function validateRelativeMs(object, field) {
+    if (field in object) {
+      const val = object[field];
+      if (val === -1 || (val >= 0.0 && val < testLimit))
+        object[field] = '<relative timestamp>';
+    }
+  }
+
+  const auctionJs = `
+    navigator.runAdAuction({
+      decisionLogicUrl: "${base}fledge_decision_logic.js.php",
+      seller: "https://a.test:8443",
+      interestGroupBuyers: ["https://a.test:8443"]})`;
+
+
+  const winner = await session.evaluateAsync(auctionJs);
+  testRunner.log('Auction winner:' + handleUrn(winner));
+
+  const devtoolsEvents = await tracingHelper.stopTracing(/devtools.timeline/);
+
+  const requestIdToInfo = new Map();
+  for (ev of devtoolsEvents) {
+    if (ev.name === 'ResourceSendRequest') {
+      requestIdToInfo.set(ev.args.data.requestId, {
+        url: ev.args.data.url,
+        events: [{name: ev.name, data: ev.args.data}]
+      });
+    }
+    if (ev.name === 'ResourceReceiveResponse' || ev.name === 'ResourceFinish') {
+      requestIdToInfo.get(ev.args.data.requestId)
+          .events.push({name: ev.name, data: ev.args.data});
+    }
+  }
+
+  const sortedRequestInfo = [...requestIdToInfo.values()].sort((a, b) => {
+    if (a.url < b.url)
+      return -1;
+    else if (a.url === b.url)
+      return 0;
+    return +1;
+  });
+
+  for (let requestInfo of sortedRequestInfo) {
+    testRunner.log(requestInfo.url + ':');
+    for (let ev of requestInfo.events) {
+      const data = ev.data;
+      validateAbsoluteMs(data, 'responseTime');
+
+      if ('timing' in data) {
+        validateRelativeMs(data.timing, 'connectEnd');
+        validateRelativeMs(data.timing, 'connectStart');
+        validateRelativeMs(data.timing, 'dnsEnd');
+        validateRelativeMs(data.timing, 'dnsStart');
+        validateRelativeMs(data.timing, 'receiveHeadersEnd');
+        validateRelativeMs(data.timing, 'sendEnd');
+        validateRelativeMs(data.timing, 'sendStart');
+        validateRelativeMs(data.timing, 'sslEnd');
+        validateRelativeMs(data.timing, 'sslStart');
+      }
+      // requestTime and finishTime are in TimeTicks, so their absolute values
+      // can't be interpreted.
+      testRunner.log(
+          data, ev.name + ' ', ['requestId', 'requestTime', 'finishTime']);
+    }
+    testRunner.log('\n');
+  }
+
+  testRunner.completeTest();
+})
diff --git a/third_party/blink/web_tests/inspector-protocol/dom/dom-getContentQuads-display-contents.js b/third_party/blink/web_tests/inspector-protocol/dom/dom-getContentQuads-display-contents.js
index 9ba106327..2c3121f 100644
--- a/third_party/blink/web_tests/inspector-protocol/dom/dom-getContentQuads-display-contents.js
+++ b/third_party/blink/web_tests/inspector-protocol/dom/dom-getContentQuads-display-contents.js
@@ -5,6 +5,8 @@
   await dp.DOM.enable();
   const aLinkQuads = await quadsFor(`document.querySelector('a')`);
   testRunner.log('Returned quads count: ' + aLinkQuads.length);
+  const bQuads = await quadsFor(`document.querySelector('b')`);
+  testRunner.log('Returned quads count: ' + bQuads.length);
 
   testRunner.completeTest();
 
diff --git a/third_party/blink/web_tests/inspector-protocol/dom/dom-scrollIntoViewIfNeeded.js b/third_party/blink/web_tests/inspector-protocol/dom/dom-scrollIntoViewIfNeeded.js
index 841668e9..73ab69b 100644
--- a/third_party/blink/web_tests/inspector-protocol/dom/dom-scrollIntoViewIfNeeded.js
+++ b/third_party/blink/web_tests/inspector-protocol/dom/dom-scrollIntoViewIfNeeded.js
@@ -24,7 +24,7 @@
       .child {
         width: 500px;
         height: 500px;
-        margin-top: 700px;
+        margin-top: 680px;
         margin-left: 300px;
         background: red;
       }
@@ -36,6 +36,9 @@
     }
     </script>
     <div class=container>
+      <div style="height:20px;width:200px">
+        <button style="display:contents">a button</button>
+      </div>
       <div class=child>
       </div>
     </div>
@@ -54,7 +57,7 @@
 
   // Ensure that we update layout before scrolling.
   session.evaluate(
-      `document.querySelector('.child').style.marginTop = '2000px'`);
+      `document.querySelector('.child').style.marginTop = '1980px'`);
   dp.DOM.scrollIntoViewIfNeeded({objectId});
   testRunner.log(await session.evaluate(`getScroll()`));
 
@@ -73,5 +76,10 @@
       {objectId, rect: {x: 123, y: 234, width: 200, height: 200}});
   testRunner.log(await session.evaluate(`getScroll()`));
 
+  // display:contents element should be scrolled into view as well.
+  const buttonObjectId = (await dp.Runtime.evaluate({expression: `document.querySelector('button')`})).result.result.objectId;
+  dp.DOM.scrollIntoViewIfNeeded({objectId: buttonObjectId});
+  testRunner.log(await session.evaluate(`getScroll()`));
+
   testRunner.completeTest();
 })
diff --git a/third_party/blink/web_tests/inspector-protocol/dom/resources/display-contents.html b/third_party/blink/web_tests/inspector-protocol/dom/resources/display-contents.html
index feb004e3..7034f89 100644
--- a/third_party/blink/web_tests/inspector-protocol/dom/resources/display-contents.html
+++ b/third_party/blink/web_tests/inspector-protocol/dom/resources/display-contents.html
@@ -1,9 +1,10 @@
+<!DOCTYPE html>
 <html>
 <head>
 <style>
 b {
   display: contents;
-}  
+}
 </style>
 </head>
 <body>
diff --git a/third_party/blink/web_tests/platform/generic/http/tests/inspector-protocol/timeline/auction-worklet-network-expected.txt b/third_party/blink/web_tests/platform/generic/http/tests/inspector-protocol/timeline/auction-worklet-network-expected.txt
new file mode 100644
index 0000000..cfd7fcc
--- /dev/null
+++ b/third_party/blink/web_tests/platform/generic/http/tests/inspector-protocol/timeline/auction-worklet-network-expected.txt
@@ -0,0 +1,5 @@
+Tracing of network activity FLEDGE worklets.
+Recording started
+Auction winner:undefined
+Tracing complete
+
diff --git a/third_party/blink/web_tests/platform/generic/inspector-protocol/dom/dom-getContentQuads-display-contents-expected.txt b/third_party/blink/web_tests/platform/generic/inspector-protocol/dom/dom-getContentQuads-display-contents-expected.txt
index 2aae990..fd990e8d 100644
--- a/third_party/blink/web_tests/platform/generic/inspector-protocol/dom/dom-getContentQuads-display-contents-expected.txt
+++ b/third_party/blink/web_tests/platform/generic/inspector-protocol/dom/dom-getContentQuads-display-contents-expected.txt
@@ -9,4 +9,14 @@
     }
 }
 Returned quads count: 1
+{
+    result : {
+        className : HTMLElement
+        description : b
+        objectId : <string>
+        subtype : node
+        type : object
+    }
+}
+Returned quads count: 1
 
diff --git a/third_party/blink/web_tests/platform/generic/inspector-protocol/dom/dom-scrollIntoViewIfNeeded-expected.txt b/third_party/blink/web_tests/platform/generic/inspector-protocol/dom/dom-scrollIntoViewIfNeeded-expected.txt
index c88d2099..14afc37 100644
--- a/third_party/blink/web_tests/platform/generic/inspector-protocol/dom/dom-scrollIntoViewIfNeeded-expected.txt
+++ b/third_party/blink/web_tests/platform/generic/inspector-protocol/dom/dom-scrollIntoViewIfNeeded-expected.txt
@@ -5,4 +5,5 @@
 202;1902
 600;2300
 423;2234
+0;0
 
diff --git a/third_party/blink/web_tests/platform/generic/virtual/async-script-scheduling-disabled/wpt_internal/async-script-scheduling/execution-order-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/async-script-scheduling-disabled/wpt_internal/async-script-scheduling/execution-order-expected.txt
new file mode 100644
index 0000000..542616ff
--- /dev/null
+++ b/third_party/blink/web_tests/platform/generic/virtual/async-script-scheduling-disabled/wpt_internal/async-script-scheduling/execution-order-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Async Script Execution Order Uncaught Error: assert_equals: expected "async_preload;async_blocking_render;inline1;external;inline2;defer;DOMContentLoaded;async" but got "async_preload;async_blocking_render;async;inline1;external;inline2;defer;DOMContentLoaded"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/generic/virtual/fledge/http/tests/inspector-protocol/timeline/auction-worklet-network-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/fledge/http/tests/inspector-protocol/timeline/auction-worklet-network-expected.txt
new file mode 100644
index 0000000..3d3bb99e
--- /dev/null
+++ b/third_party/blink/web_tests/platform/generic/virtual/fledge/http/tests/inspector-protocol/timeline/auction-worklet-network-expected.txt
@@ -0,0 +1,128 @@
+Tracing of network activity FLEDGE worklets.
+Recording started
+Auction winner:urn:uuid:(randomized)
+Tracing complete
+https://a.test:8443/inspector-protocol/resources/fledge_bidding_logic.js.php:
+ResourceSendRequest {
+    requestId : <string>
+    url : https://a.test:8443/inspector-protocol/resources/fledge_bidding_logic.js.php
+}
+ResourceReceiveResponse {
+    encodedDataLength : 229
+    fromCache : false
+    fromServiceWorker : false
+    mimeType : application/javascript
+    requestId : <string>
+    responseTime : <absolute timestamp>
+    statusCode : 200
+    timing : {
+        connectEnd : <relative timestamp>
+        connectStart : <relative timestamp>
+        dnsEnd : <relative timestamp>
+        dnsStart : <relative timestamp>
+        proxyEnd : -1
+        proxyStart : -1
+        pushEnd : 0
+        pushStart : 0
+        receiveHeadersEnd : <relative timestamp>
+        requestTime : <number>
+        sendEnd : <relative timestamp>
+        sendStart : <relative timestamp>
+        sslEnd : <relative timestamp>
+        sslStart : <relative timestamp>
+        workerReady : -1
+        workerStart : -1
+    }
+}
+ResourceFinish {
+    decodedBodyLength : 620
+    didFail : false
+    encodedDataLength : 229
+    finishTime : <number>
+    requestId : <string>
+}
+
+
+https://a.test:8443/inspector-protocol/resources/fledge_bidding_logic.js.php:
+ResourceSendRequest {
+    requestId : <string>
+    url : https://a.test:8443/inspector-protocol/resources/fledge_bidding_logic.js.php
+}
+ResourceReceiveResponse {
+    encodedDataLength : 229
+    fromCache : false
+    fromServiceWorker : false
+    mimeType : application/javascript
+    requestId : <string>
+    responseTime : <absolute timestamp>
+    statusCode : 200
+    timing : {
+        connectEnd : <relative timestamp>
+        connectStart : <relative timestamp>
+        dnsEnd : <relative timestamp>
+        dnsStart : <relative timestamp>
+        proxyEnd : -1
+        proxyStart : -1
+        pushEnd : 0
+        pushStart : 0
+        receiveHeadersEnd : <relative timestamp>
+        requestTime : <number>
+        sendEnd : <relative timestamp>
+        sendStart : <relative timestamp>
+        sslEnd : <relative timestamp>
+        sslStart : <relative timestamp>
+        workerReady : -1
+        workerStart : -1
+    }
+}
+ResourceFinish {
+    decodedBodyLength : 620
+    didFail : false
+    encodedDataLength : 229
+    finishTime : <number>
+    requestId : <string>
+}
+
+
+https://a.test:8443/inspector-protocol/resources/fledge_decision_logic.js.php:
+ResourceSendRequest {
+    requestId : <string>
+    url : https://a.test:8443/inspector-protocol/resources/fledge_decision_logic.js.php
+}
+ResourceReceiveResponse {
+    encodedDataLength : 229
+    fromCache : false
+    fromServiceWorker : false
+    mimeType : application/javascript
+    requestId : <string>
+    responseTime : <absolute timestamp>
+    statusCode : 200
+    timing : {
+        connectEnd : <relative timestamp>
+        connectStart : <relative timestamp>
+        dnsEnd : <relative timestamp>
+        dnsStart : <relative timestamp>
+        proxyEnd : -1
+        proxyStart : -1
+        pushEnd : 0
+        pushStart : 0
+        receiveHeadersEnd : <relative timestamp>
+        requestTime : <number>
+        sendEnd : <relative timestamp>
+        sendStart : <relative timestamp>
+        sslEnd : <relative timestamp>
+        sslStart : <relative timestamp>
+        workerReady : -1
+        workerStart : -1
+    }
+}
+ResourceFinish {
+    decodedBodyLength : 544
+    didFail : false
+    encodedDataLength : 229
+    finishTime : <number>
+    requestId : <string>
+}
+
+
+
diff --git a/third_party/blink/web_tests/virtual/async-script-scheduling-disabled/README.md b/third_party/blink/web_tests/virtual/async-script-scheduling-disabled/README.md
new file mode 100644
index 0000000..a223b55
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/async-script-scheduling-disabled/README.md
@@ -0,0 +1,5 @@
+# DelayAsyncScriptExecution
+This suite runs the tests in wpt_internal/async-script-scheduling/ with
+`--disable-features=DelayAsyncScriptExecution`.
+
+See crbug.com/1340837.
diff --git a/third_party/blink/web_tests/virtual/async-script-scheduling-finished-parsing/README.md b/third_party/blink/web_tests/virtual/async-script-scheduling-finished-parsing/README.md
new file mode 100644
index 0000000..8e350aa2
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/async-script-scheduling-finished-parsing/README.md
@@ -0,0 +1,5 @@
+# DelayAsyncScriptExecution
+This suite runs the tests in wpt_internal/async-script-scheduling/ with
+`--enable-features=DelayAsyncScriptExecution:delay_type/finished_parsing`.
+
+See crbug.com/1340837.
diff --git a/third_party/blink/web_tests/virtual/async-script-scheduling-first-paint-or-finished-parsing/README.md b/third_party/blink/web_tests/virtual/async-script-scheduling-first-paint-or-finished-parsing/README.md
new file mode 100644
index 0000000..8450b1d7
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/async-script-scheduling-first-paint-or-finished-parsing/README.md
@@ -0,0 +1,5 @@
+# DelayAsyncScriptExecution
+This suite runs the tests in wpt_internal/async-script-scheduling/ with
+`--enable-features=DelayAsyncScriptExecution:delay_type/first_paint_or_finished_parsing`.
+
+See crbug.com/1340837.
diff --git a/third_party/blink/web_tests/wpt_internal/async-script-scheduling/execution-order.html b/third_party/blink/web_tests/wpt_internal/async-script-scheduling/execution-order.html
index 94374eba..8274eedf 100644
--- a/third_party/blink/web_tests/wpt_internal/async-script-scheduling/execution-order.html
+++ b/third_party/blink/web_tests/wpt_internal/async-script-scheduling/execution-order.html
@@ -3,7 +3,9 @@
   <title>Async Script Execution Order</title>
   <script src="/resources/testharness.js"></script>
   <script src="/resources/testharnessreport.js"></script>
-  <link rel="preload" as="script" href="resources/async-script.js?preload">
+  <link rel="preload" as="script" href="resources/script.js?4&preload">
+  <script async blocking=render src="resources/script.js?1&pipe=trickle(d1)"
+          data-label="async_blocking_render"></script>
 </head>
 <body>
   <!-- Note: This test serves mainly to document the current scheduling behavior
@@ -19,28 +21,30 @@
     }
     window.onload = e => {
       const actualOrder = logs.join(";");
-      const expectedOrder = "Async;Async;Async;inline1;external;inline2;DOMContentLoaded";
-      scriptlog.textContent += actualOrder;
+      // The following expectedOrder expects that the
+      // "DelayAsyncScriptExecution" feature is enabled with
+      // "delay_type:first_paint_or_finished_parsing" feature parameter.
+      const expectedOrder =
+        "async_preload;async_blocking_render;inline1;external;inline2;defer;DOMContentLoaded;async";
       assert_equals(actualOrder, expectedOrder);
       done();
     };
     document.addEventListener('DOMContentLoaded', e => { logScript('DOMContentLoaded'); });
   </script>
 
-  <script async src="resources/async-script.js?1"></script>
-  <script async src="resources/async-script.js?2"></script>
-  <script async src="resources/async-script.js?preload"></script>
-  <link rel=stylesheet href=resources/main.css?pipe=trickle(d1)>
-  <pre id="scriptlog"></pre>
+  <script defer src="resources/script.js?2" data-label="defer"></script>
+  <script async src="resources/script.js?3&pipe=trickle(d2)" data-label="async"></script>
+  <script async src="resources/script.js?4&preload" data-label="async_preload"></script>
+  <link rel=stylesheet href=resources/main.css?pipe=trickle(d3)>
   <script>
     logScript('inline1');
+    assert_equals('rgb(255, 255, 0)', getComputedStyle(document.body)['backgroundColor']);
   </script>
-  <h1>
-    Fast finished parsing, slow first paint
-  </h1>
-
-  <script src="resources/external.js?first&pipe=trickle(d2)"></script>
+  <script src="resources/script.js?5&pipe=trickle(d1)" data-label="external"></script>
   <script>
     logScript('inline2');
   </script>
+  <h1>
+    This is the first (contentful) paint.
+  </h1>
 </body>
diff --git a/third_party/blink/web_tests/wpt_internal/async-script-scheduling/resources/async-script.js b/third_party/blink/web_tests/wpt_internal/async-script-scheduling/resources/async-script.js
deleted file mode 100644
index ada8c251..0000000
--- a/third_party/blink/web_tests/wpt_internal/async-script-scheduling/resources/async-script.js
+++ /dev/null
@@ -1 +0,0 @@
-logScript("Async")
diff --git a/third_party/blink/web_tests/wpt_internal/async-script-scheduling/resources/external.js b/third_party/blink/web_tests/wpt_internal/async-script-scheduling/resources/external.js
deleted file mode 100644
index a79151bf..0000000
--- a/third_party/blink/web_tests/wpt_internal/async-script-scheduling/resources/external.js
+++ /dev/null
@@ -1 +0,0 @@
-logScript("external")
diff --git a/third_party/blink/web_tests/wpt_internal/async-script-scheduling/resources/script.js b/third_party/blink/web_tests/wpt_internal/async-script-scheduling/resources/script.js
new file mode 100644
index 0000000..d34c5f52
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/async-script-scheduling/resources/script.js
@@ -0,0 +1 @@
+logScript(document.currentScript.getAttribute("data-label"))
diff --git a/third_party/blink/web_tests/wpt_internal/attribution-reporting/simple-event-level-report.sub.https.html b/third_party/blink/web_tests/wpt_internal/attribution-reporting/simple-event-level-report.sub.https.html
index 0826560..fc5279b1 100644
--- a/third_party/blink/web_tests/wpt_internal/attribution-reporting/simple-event-level-report.sub.https.html
+++ b/third_party/blink/web_tests/wpt_internal/attribution-reporting/simple-event-level-report.sub.https.html
@@ -4,6 +4,8 @@
 <script src="/resources/testharnessreport.js"></script>
 <script src="resources/helpers.js"></script>
 <script>
+const uuidPattern = /^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$/;
+
 attribution_reporting_promise_test(async t => {
   const source = {
     source_event_id: '9153495207648170476',
@@ -16,20 +18,25 @@
 
   const payload = await pollEventLevelReports();
   assert_equals(payload.reports.length, 1);
+
   const report = JSON.parse(payload.reports[0].body);
-  const headers = payload.reports[0].headers;
+
   // The trigger data is sanitized to "0" because event sources are limited to 1
   // bit.
   assert_equals(report.trigger_data, '0');
   assert_equals(report.source_event_id, source.source_event_id);
   assert_equals(report.source_type, 'event');
   assert_equals(report.attribution_destination, source.destination);
-  assert_greater_than(report.randomized_trigger_rate, 0.0);
-  assert_less_than(report.randomized_trigger_rate, 1.0);
+
+  assert_between_inclusive(report.randomized_trigger_rate, 0.0, 1.0);
+
   assert_own_property(report, 'report_id');
   assert_equals(typeof report.report_id, 'string');
+  assert_regexp_match(report.report_id, uuidPattern);
+
   assert_not_own_property(report, 'source_debug_key');
   assert_not_own_property(report, 'trigger_debug_key');
-  validateReportHeaders(headers);
+
+  validateReportHeaders(payload.reports[0].headers);
 }, 'Ensure attribution report is received.');
 </script>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index aa5060e..122abfe 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -16943,6 +16943,11 @@
   <int value="3" label="Web Smart Paste"/>
 </enum>
 
+<enum name="ClipboardHistoryReorderType">
+  <int value="0" label="On copy"/>
+  <int value="1" label="On paste"/>
+</enum>
+
 <enum name="ClipboardHistoryTriggerType">
   <int value="0" label="Accelerator"/>
   <int value="1" label="Render View Context Menu"/>
@@ -22734,13 +22739,63 @@
 
 <enum name="CryptohomeErrorLocation">
 <!--
-  This enum is intended to be populated automatically by platform2/src/cryptohome/error/tool/location_db.py. It populates all values found in the cryptohome code base.
+  This enum is intended to be populated automatically by
+  platform2/cryptohome/error/tool/location_db.py. It populates all values
+  found in the cryptohome code base.
 
-  The labels are the Cryptohome Error Location enum defined in platform2/cryptohome/error/locations.h
+  It is allowed to manually add <int> error location to this file, and the
+  added error location will continue to be updated when the script runs the
+  next time. However, the label, and all changes to the label field may be
+  overwritten by the tool. Furthermore, removal of <int> may be added back
+  if that bucket is still observed in the field.
+
+  The labels are the Cryptohome Error Location enum defined in
+  platform2/cryptohome/error/locations.h
   -->
 
+  <int value="100" label="kLocUserSessionMountEphemeralFailed"/>
+  <int value="103" label="kLocUserDataAuthMountGuestSessionMountFailed"/>
+  <int value="105" label="kLocUserDataAuthEphemeralMountWithoutCreate"/>
+  <int value="138" label="kLocTpmNotBoundToPcrAuthBlockDecryptFailedInDerive"/>
+  <int value="144" label="kLocTpmNotBoundToPcrAuthBlockDecryptFailedInDecrypt"/>
+  <int value="154" label="kLocPinWeaverAuthBlockCheckCredFailedInDerive"/>
+  <int value="160" label="kLocAuthBlockUtilDeriveFailedInDeriveKeyBlobs"/>
+  <int value="197" label="kLocTpmAuthBlockUtilsHashIncorrectInPubkeyHash"/>
+  <int value="201" label="kLocTpmAuthBlockUtilsCHKeyMismatchInCheckReadiness"/>
+  <int value="217" label="kLocTpmBoundToPcrAuthBlockTpmNotReadyInDerive"/>
+  <int value="219" label="kLocTpmBoundToPcrAuthBlockDecryptFailedInDerive"/>
+  <int value="222" label="kLocTpmBoundToPcrAuthBlockUnsealFailedInDecrypt"/>
+  <int value="245" label="kLocTpmEccAuthBlockTpmNotReadyInDerive"/>
+  <int value="246" label="kLocTpmEccAuthBlockCantDeriveVKKInDerive"/>
+  <int value="247" label="kLocTpmEccAuthBlockPreloadFailedInDeriveVKK"/>
+  <int value="250" label="kLocTpmEccAuthBlockDeriveHVKKMFailedInDeriveVKK"/>
+  <int value="256" label="kLocTpmEccAuthBlockUnsealFailedInDeriveHVKKM"/>
+  <int value="280" label="kLocLECredManTpmFailedInCheckSecret"/>
+  <int value="284" label="kLocLECredManConvertTpmError"/>
   <int value="286" label="kLocUserDataAuthSessionNotFoundInAuthAuthSession"/>
   <int value="288" label="kLocUserDataAuthSessionNotFoundInExtendAuthSession"/>
+  <int value="326" label="kLocAuthSessionInvalidBlockTypeInAuthViaVaultKey"/>
+  <int value="329" label="kLocAuthSessionDeriveFailedInLoadVaultKeyset"/>
+  <int value="330"
+      label="kLocAuthSessionGetValidKeysetFailedInLoadVaultKeyset"/>
+  <int value="371" label="kLocVaultKeysetKeysetDecryptFailedInUnwrapVKK"/>
+  <int value="375" label="kLocVaultKeysetKeysetDecryptFailedInUnwrapScrypt"/>
+  <int value="390" label="kLocVaultKeysetUnwrapVKKFailedInUnwrapVK"/>
+  <int value="391" label="kLocVaultKeysetUnwrapScryptFailedInUnwrapVK"/>
+  <int value="427" label="kLocKeysetManagementDecryptFailedInGetValidKeyset"/>
+  <int value="449" label="kLocUserDataAuthLoadVKFailedInAttemptUserMountCred"/>
+  <int value="455" label="kLocUserSessionMountFailedInMountVault"/>
+  <int value="476"
+      label="kLocUserDataAuthSessionAlreadyMountedInGetMountableUS"/>
+  <int value="478"
+      label="kLocUserDataAuthOtherSessionActiveInPrepareGuestVault"/>
+  <int value="485"
+      label="kLocUserDataAuthGetSessionFailedInPreparePersistentVault"/>
+  <int value="486" label="kLocUserDataAuthMountFailedInPreparePersistentVault"/>
+  <int value="532" label="kLocUserDataAuthNotConfiguredInStartAuthSession"/>
+  <int value="572"
+      label="kLocUserDataAuthAccountNotFoundInContinueMountWithCred"/>
+  <int value="579" label="kLocUserDataAuthMountFailedInContinueMountWithCred"/>
 </enum>
 
 <enum name="CryptohomeErrorLocationWithTPMError">
@@ -35050,6 +35105,9 @@
   <int value="1683" label="OFFSCREEN_CLOSEDOCUMENT"/>
   <int value="1684" label="WMDESKSPRIVATE_SETWINDOWPROPERTIES"/>
   <int value="1685" label="AUTOTESTPRIVATE_LAUNCHFILESAPPTOPATH"/>
+  <int value="1686" label="SIDEPANEL_GETOPTIONS"/>
+  <int value="1687" label="SIDEPANEL_SETOPTIONS"/>
+  <int value="1688" label="ENTERPRISEREPORTINGPRIVATE_GETFILESYSTEMINFO"/>
 </enum>
 
 <enum name="ExtensionIconState">
@@ -35680,6 +35738,7 @@
   <int value="238" label="kSharedStoragePrivate"/>
   <int value="239" label="kEnterpriseRemoteApps"/>
   <int value="240" label="kOffscreen"/>
+  <int value="241" label="kSidePanel"/>
 </enum>
 
 <enum name="ExtensionPointEnableState">
@@ -40734,6 +40793,7 @@
   <int value="4296" label="ParentOfDisabledFormControlRespondsToMouseEvents"/>
   <int value="4297" label="UnhandledExceptionCountInMainThread"/>
   <int value="4298" label="UnhandledExceptionCountInWorker"/>
+  <int value="4299" label="WebCodecsImageDecoderPremultiplyAlphaDeprecation"/>
 </enum>
 
 <enum name="FeaturePolicyAllowlistType">
@@ -40856,6 +40916,7 @@
   <int value="100" label="LocalFonts"/>
   <int value="101" label="Bluetooth"/>
   <int value="102" label="FederatedCredentials"/>
+  <int value="103" label="SharedStorage"/>
 </enum>
 
 <enum name="FeaturePolicyImageCompressionFormat">
@@ -56018,6 +56079,8 @@
   <int value="-1643933608" label="SyncAutofillWalletOfferData:enabled"/>
   <int value="-1643227464"
       label="AutofillEnableUpdateVirtualCardEnrollment:disabled"/>
+  <int value="-1643224308"
+      label="OmniboxMostVisitedTilesTitleWrapAround:disabled"/>
   <int value="-1641832607" label="DragToPinTabs:enabled"/>
   <int value="-1641656402"
       label="RTCDisallowPlanBOutsideDeprecationTrial:disabled"/>
@@ -56532,11 +56595,14 @@
   <int value="-1344375439" label="ServiceWorkerPaymentApps:disabled"/>
   <int value="-1343259222" label="RegionalLocalesAsDisplayUI:disabled"/>
   <int value="-1342961844" label="InlineUpdateFlow:disabled"/>
+  <int value="-1342536303" label="WindowsScrollingPersonality:enabled"/>
   <int value="-1342039126" label="AshNewSystemMenu:enabled"/>
   <int value="-1341685799" label="AutofillAssistantDirectActions:enabled"/>
   <int value="-1341092934" label="enable-accelerated-overflow-scroll"/>
   <int value="-1340460869" label="ShutdownSupportForKeepalive:enabled"/>
   <int value="-1340055960" label="enable-streamlined-hosted-apps"/>
+  <int value="-1338701593"
+      label="OmniboxMostVisitedTilesTitleWrapAround:enabled"/>
   <int value="-1338306372" label="BrowserTouchBar:enabled"/>
   <int value="-1338237485" label="HardwareSecureDecryptionExperiment:disabled"/>
   <int value="-1337185440" label="enable-webvr"/>
@@ -57361,6 +57427,7 @@
   <int value="-822712881"
       label="ContextualSearchThinWebViewImplementation:enabled"/>
   <int value="-822237807" label="UnifiedSidePanel:enabled"/>
+  <int value="-822103041" label="SeamlessRefreshRateSwitching:disabled"/>
   <int value="-821635312" label="EyeDropper:enabled"/>
   <int value="-820676825" label="MessagesPreinstall:enabled"/>
   <int value="-820041355" label="enable-transition-compositing"/>
@@ -57461,6 +57528,7 @@
   <int value="-754726495" label="WebUITabStripContextMenuAfterTap:enabled"/>
   <int value="-752321357" label="AutofillEnableGoogleIssuedCard:enabled"/>
   <int value="-750175757" label="ClientLoFi:enabled"/>
+  <int value="-749550261" label="SeamlessRefreshRateSwitching:enabled"/>
   <int value="-749048160" label="enable-panels"/>
   <int value="-748571227"
       label="AssistantEnableMediaSessionIntegration:enabled"/>
@@ -58467,6 +58535,8 @@
   <int value="-86243376" label="LayoutNG:enabled"/>
   <int value="-85706353" label="VirtualDesksGestures:enabled"/>
   <int value="-82530769" label="WebXRPlaneDetection:enabled"/>
+  <int value="-82328138"
+      label="AutofillEnableRemadeDownstreamMetrics:disabled"/>
   <int value="-82266557" label="SafeBrowsingSecuritySectionUIAndroid:disabled"/>
   <int value="-81940476"
       label="OverrideSimilarLanguagesForHrefTranslate:disabled"/>
@@ -58597,6 +58667,7 @@
   <int value="-9701088" label="ScanAppMediaLink:disabled"/>
   <int value="-9599490" label="KernelnextVMs:disabled"/>
   <int value="-9322265" label="ShowBluetoothDebugLogToggle:enabled"/>
+  <int value="-8063095" label="AutofillEnableRemadeDownstreamMetrics:enabled"/>
   <int value="-5492723" label="CCTIncognito:enabled"/>
   <int value="-5305648" label="OmniboxClosePopupWithEscape:enabled"/>
   <int value="-5052940" label="enable-simplified-fullscreen"/>
@@ -61431,6 +61502,7 @@
   <int value="1839005510" label="MutingCompromisedCredentials:enabled"/>
   <int value="1839740266" label="LocationHardReload:disabled"/>
   <int value="1840087820" label="NtpRealboxMatchSearchboxTheme:enabled"/>
+  <int value="1841051176" label="WindowsScrollingPersonality:disabled"/>
   <int value="1841793150" label="TwoPanesStartSurfaceAndroid:enabled"/>
   <int value="1841976850" label="FeedLoadingPlaceholder:enabled"/>
   <int value="1842219851" label="enable-incognito-window-counter"/>
@@ -70730,6 +70802,23 @@
   <int value="3" label="Odd width and height"/>
 </enum>
 
+<enum name="OffersSuggestionsEvent">
+  <int value="0" label="Undefined"/>
+  <int value="1" label="Suggestion shown in popup"/>
+  <int value="2" label="Suggestion shown in popup (once)"/>
+  <int value="3" label="Suggestion selected in popup"/>
+  <int value="4" label="Suggestion selected in popup (once)"/>
+  <int value="5" label="Footer suggestion see offer details selected in popup"/>
+  <int value="6"
+      label="Footer suggestion see offer details selected in popup (once)"/>
+</enum>
+
+<enum name="OffersSuggestionsPopupEvent">
+  <int value="0" label="Undefined"/>
+  <int value="1" label="Suggestions popup shown"/>
+  <int value="2" label="Suggestions popup shown (once)"/>
+</enum>
+
 <enum name="OfficeFileHandler">
   <int value="0" label="Other"/>
   <int value="1" label="Web Drive Office"/>
diff --git a/tools/metrics/histograms/metadata/ash/histograms.xml b/tools/metrics/histograms/metadata/ash/histograms.xml
index be6b6085..0edeae9 100644
--- a/tools/metrics/histograms/metadata/ash/histograms.xml
+++ b/tools/metrics/histograms/metadata/ash/histograms.xml
@@ -1490,6 +1490,16 @@
   </summary>
 </histogram>
 
+<histogram name="Ash.ClipboardHistory.ReorderType"
+    enum="ClipboardHistoryReorderType" expires_after="2022-10-25">
+  <owner>ckincaid@chromium.org</owner>
+  <owner>multipaste@google.com</owner>
+  <summary>
+    Each time the clipboard history list is reordered, the operation (copy or
+    paste) initiating that reorder is emitted here.
+  </summary>
+</histogram>
+
 <histogram name="Ash.DarkTheme.Settings.IsDarkModeEnabled" enum="Boolean"
     expires_after="2023-03-01">
   <obsolete>
diff --git a/tools/metrics/histograms/metadata/autofill/histograms.xml b/tools/metrics/histograms/metadata/autofill/histograms.xml
index 07b487b6..9eed5c3 100644
--- a/tools/metrics/histograms/metadata/autofill/histograms.xml
+++ b/tools/metrics/histograms/metadata/autofill/histograms.xml
@@ -2151,6 +2151,31 @@
   </summary>
 </histogram>
 
+<histogram name="Autofill.Offer.Suggestion.{OfferType}"
+    enum="OffersSuggestionsEvent" expires_after="2023-07-01">
+  <owner>vinnypersky@google.com</owner>
+  <owner>jsaul@google.com</owner>
+  <summary>
+    This metric is recorded every time an event related to an individual offer
+    suggestion in the offers suggestions popup gets triggered. The offers
+    suggestions popup gets displayed when a form's field that can be autofilled
+    with an offer is selected.
+  </summary>
+  <token key="OfferType" variants="Autofill.OfferNotification.Type"/>
+</histogram>
+
+<histogram name="Autofill.Offer.SuggestionsPopupShown"
+    enum="OffersSuggestionsPopupEvent" expires_after="2023-07-01">
+  <owner>vinnypersky@google.com</owner>
+  <owner>jsaul@google.com</owner>
+  <summary>
+    This metric records two enums, one for every time the offers suggestions
+    popup is shown, and another for that same event, but only one per unique
+    field. The offers suggestions popup is shown when a form's field that can be
+    autofilled with an offer is selected.
+  </summary>
+</histogram>
+
 <histogram name="Autofill.Offer.SyncedOfferDataBeingValid" units="offers"
     expires_after="2022-12-04">
   <owner>siyua@chromium.org</owner>
@@ -3648,7 +3673,7 @@
 </histogram>
 
 <histogram name="Autofill.VirtualCard.Enroll.Attempt.{Source}"
-    enum="BooleanAttempted" expires_after="2022-08-07">
+    enum="BooleanAttempted" expires_after="2023-07-01">
   <owner>siyua@chromium.org</owner>
   <owner>payments-autofill-team@google.com</owner>
   <summary>
@@ -3659,7 +3684,7 @@
 </histogram>
 
 <histogram name="Autofill.VirtualCard.Enroll.Result.{Source}"
-    enum="BooleanSuccess" expires_after="2022-08-07">
+    enum="BooleanSuccess" expires_after="2023-07-01">
   <owner>siyua@chromium.org</owner>
   <owner>payments-autofill-team@google.com</owner>
   <summary>
@@ -3671,7 +3696,7 @@
 </histogram>
 
 <histogram name="Autofill.VirtualCard.GetDetailsForEnrollment.Attempt.{Source}"
-    enum="BooleanAttempted" expires_after="2022-08-07">
+    enum="BooleanAttempted" expires_after="2023-07-01">
   <owner>siyua@chromium.org</owner>
   <owner>payments-autofill-team@google.com</owner>
   <summary>
@@ -3684,7 +3709,7 @@
 
 <histogram
     name="Autofill.VirtualCard.GetDetailsForEnrollment.Latency.{Source}.{Result}"
-    units="ms" expires_after="2022-08-07">
+    units="ms" expires_after="2023-07-01">
   <owner>siyua@chromium.org</owner>
   <owner>payments-autofill-team@google.com</owner>
   <summary>
@@ -3697,7 +3722,7 @@
 </histogram>
 
 <histogram name="Autofill.VirtualCard.GetDetailsForEnrollment.Result.{Source}"
-    enum="BooleanSuccess" expires_after="2022-08-07">
+    enum="BooleanSuccess" expires_after="2023-07-01">
   <owner>siyua@chromium.org</owner>
   <owner>payments-autofill-team@google.com</owner>
   <summary>
@@ -3709,7 +3734,7 @@
 </histogram>
 
 <histogram name="Autofill.VirtualCard.MetadataSynced" enum="BooleanExists"
-    expires_after="2022-08-07">
+    expires_after="2023-07-01">
   <owner>siyua@chromium.org</owner>
   <owner>payments-autofill-team@google.com</owner>
   <summary>
@@ -3720,7 +3745,7 @@
 </histogram>
 
 <histogram name="Autofill.VirtualCard.Unenroll.Attempt.{Source}"
-    enum="BooleanAttempted" expires_after="2022-08-07">
+    enum="BooleanAttempted" expires_after="2023-07-01">
   <owner>siyua@chromium.org</owner>
   <owner>payments-autofill-team@google.com</owner>
   <summary>
@@ -3732,7 +3757,7 @@
 </histogram>
 
 <histogram name="Autofill.VirtualCard.Unenroll.Result.{Source}"
-    enum="BooleanSuccess" expires_after="2022-08-07">
+    enum="BooleanSuccess" expires_after="2023-07-01">
   <owner>siyua@chromium.org</owner>
   <owner>payments-autofill-team@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/blink/histograms.xml b/tools/metrics/histograms/metadata/blink/histograms.xml
index e65f155..e1fbd41 100644
--- a/tools/metrics/histograms/metadata/blink/histograms.xml
+++ b/tools/metrics/histograms/metadata/blink/histograms.xml
@@ -514,7 +514,7 @@
 </histogram>
 
 <histogram name="Blink.Clipboard.HasTransientUserActivation" enum="Boolean"
-    expires_after="2022-08-01">
+    expires_after="2023-08-01">
   <owner>asully@chromium.org</owner>
   <owner>snianu@microsoft.com</owner>
   <owner>chrome-owp-storage@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/cross_device/histograms.xml b/tools/metrics/histograms/metadata/cross_device/histograms.xml
index 9ce12e4..57e7740 100644
--- a/tools/metrics/histograms/metadata/cross_device/histograms.xml
+++ b/tools/metrics/histograms/metadata/cross_device/histograms.xml
@@ -1112,15 +1112,18 @@
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
-    Upon a successful Smart Lock unlock or signin, records whether the user's
-    phone was locked during any point while the lock screen was up. This can be
-    used to get a sense for whether users are using Smart Lock for Chromebook
-    without something like Smart Lock for Android to keep their phones unlocked.
+    Upon a successful Smart Lock unlock, records whether the user's phone was
+    locked during any point while the lock screen was up. This can be used to
+    get a sense for whether users are using Smart Lock for Chromebook without
+    something like Smart Lock for Android to keep their phones unlocked.
   </summary>
 </histogram>
 
 <histogram name="EasyUnlock.AuthEvent.SignIn" enum="EasyUnlockAuthEvent"
     expires_after="2023-02-01">
+  <obsolete>
+    Sign in with Smart Lock deprecated.
+  </obsolete>
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1133,6 +1136,9 @@
 
 <histogram name="EasyUnlock.AuthEvent.SignIn.Duration" units="ms"
     expires_after="2023-02-01">
+  <obsolete>
+    Sign in with Smart Lock deprecated.
+  </obsolete>
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -2083,6 +2089,9 @@
 
 <histogram name="SmartLock.AuthMethodChoice.SignIn"
     enum="SmartLockAuthMethodChoice" expires_after="2023-02-01">
+  <obsolete>
+    Sign in with Smart Lock deprecated.
+  </obsolete>
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>Records the user's sign in method choice.</summary>
@@ -2090,6 +2099,9 @@
 
 <histogram name="SmartLock.AuthMethodChoice.SignIn.PasswordState"
     enum="SmartLockAuthEventPasswordState" expires_after="2023-02-01">
+  <obsolete>
+    Sign in with Smart Lock deprecated.
+  </obsolete>
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -2127,6 +2139,9 @@
 
 <histogram name="SmartLock.AuthResult.SignIn" enum="BooleanSuccess"
     expires_after="2023-02-01">
+  <obsolete>
+    Sign in with Smart Lock deprecated.
+  </obsolete>
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -2137,6 +2152,9 @@
 
 <histogram name="SmartLock.AuthResult.SignIn.Failure"
     enum="SmartLockAuthResultFailureReason" expires_after="2023-02-01">
+  <obsolete>
+    Sign in with Smart Lock deprecated.
+  </obsolete>
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -2147,6 +2165,9 @@
 
 <histogram name="SmartLock.AuthResult.SignIn.Failure.UserControllerAuth"
     enum="LoginFailureReason" expires_after="2023-02-01">
+  <obsolete>
+    Sign in with Smart Lock deprecated.
+  </obsolete>
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -2201,6 +2222,9 @@
 
 <histogram name="SmartLock.FindAndConnectToHostResult.SignIn"
     enum="SmartLockFindAndConnectToHostResult" expires_after="2023-02-01">
+  <obsolete>
+    Sign in with Smart Lock deprecated.
+  </obsolete>
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -2232,6 +2256,9 @@
 
 <histogram name="SmartLock.GetRemoteStatus.SignIn" enum="BooleanSuccess"
     expires_after="2023-02-01">
+  <obsolete>
+    Sign in with Smart Lock deprecated.
+  </obsolete>
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -2243,6 +2270,9 @@
 <histogram name="SmartLock.GetRemoteStatus.SignIn.Failure"
     enum="SmartLockGetRemoteStatusResultFailureReason"
     expires_after="2023-02-01">
+  <obsolete>
+    Sign in with Smart Lock deprecated.
+  </obsolete>
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/cryptohome/histograms.xml b/tools/metrics/histograms/metadata/cryptohome/histograms.xml
index da930e9..1b71681 100644
--- a/tools/metrics/histograms/metadata/cryptohome/histograms.xml
+++ b/tools/metrics/histograms/metadata/cryptohome/histograms.xml
@@ -686,6 +686,19 @@
   </summary>
 </histogram>
 
+<histogram name="Cryptohome.TimeToCreatePersistentUser" units="ms"
+    expires_after="2023-05-01">
+  <owner>thomascedeno@google.com</owner>
+  <owner>cros-hwsec+uma@chromium.org</owner>
+  <summary>
+    AuthSession is a DBus API for various operations dealing with credential
+    storage, processing, and authentication. This metric records the amount of
+    time (ms) to create a persistent user. This is recorded when the AuthSession
+    is requested to create a persistent user in cryptohomed, through the DBUS
+    API.
+  </summary>
+</histogram>
+
 <histogram name="Cryptohome.TimeToGenerateEccAuthValue" units="ms"
     expires_after="2022-12-04">
   <owner>yich@google.com</owner>
@@ -889,6 +902,65 @@
   </token>
 </histogram>
 
+<histogram name="Cryptohome.{AuthSessionFunction}.{AuthBlockType}" units="ms"
+    expires_after="2023-05-01">
+  <owner>thomascedeno@google.com</owner>
+  <owner>cros-hwsec+uma@chromium.org</owner>
+  <summary>
+    AuthSession is a DBus API for various operations dealing with credential
+    storage, processing, and authentication. These metrics record the time an
+    {AuthSessionFunction} takes to complete. Each function is parameterized by
+    an {AuthBlockType}, or credential backend, which significantly influences
+    results. The metric is emitted when the respective {AuthSessionFunction} is
+    requested through the DBus API.
+  </summary>
+  <token key="AuthSessionFunction">
+    <variant name="TimeToAuthSessionAddAuthFactorUSS"/>
+    <variant name="TimeToAuthSessionAddAuthFactorVK"/>
+    <variant name="TimetoAuthSessionAddCredentials"/>
+    <variant name="TimeToAuthSessionAuthenticate"/>
+    <variant name="TimeToAuthSessionAuthenticateAuthFactorUSS"/>
+    <variant name="TimeToAuthSessionAuthenticateAuthFactorVK"/>
+    <variant name="TimeToAuthSessionRemoveAuthFactorUSS"/>
+    <variant name="TimeToAuthSessionRemoveAuthFactorVK"/>
+    <variant name="TimeToAuthSessionUpdateAuthFactorUSS"/>
+    <variant name="TimeToAuthSessionUpdateAuthFactorVK"/>
+    <variant name="TimeToAuthSessionUpdateCredentials"/>
+  </token>
+  <token key="AuthBlockType">
+    <variant name="ChallengeCredential"/>
+    <variant name="CryptohomeRecovery"/>
+    <variant name="DoubleWrappedCompat"/>
+    <variant name="LibScryptCompat"/>
+    <variant name="PinWeaver"/>
+    <variant name="Scrypt"/>
+    <variant name="TpmBoundToPcr"/>
+    <variant name="TpmEcc"/>
+    <variant name="TpmNotBoundToPcr"/>
+  </token>
+</histogram>
+
+<histogram name="Cryptohome.{AuthSessionLifetime}.{Type}" units="ms"
+    expires_after="2023-05-01">
+  <owner>thomascedeno@google.com</owner>
+  <owner>cros-hwsec+uma@chromium.org</owner>
+  <summary>
+    The amount of time an AuthSession lives for after authentication and
+    overall. This is 5 minutes by default, but can be extended through the
+    ExtendAuthSession API call. This metric is emitted when the AuthSession is
+    destroyed, by either manually invalidating through the DBus API or through
+    an automatic timeout.
+  </summary>
+  <token key="AuthSessionLifetime">
+    <variant name="AuthSessionAuthenticatedLifetime"/>
+    <variant name="AuthSessionTotalLifetime"/>
+  </token>
+  <token key="Type">
+    <variant name="Ephemeral"/>
+    <variant name="Persistent"/>
+  </token>
+</histogram>
+
 <histogram name="CryptohomeClient" units="ms" expires_after="2022-07-24">
   <owner>zuan@chromium.org</owner>
   <owner>cros-hwsec+uma@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/media/histograms.xml b/tools/metrics/histograms/metadata/media/histograms.xml
index 51f8b92a..1ce68ea 100644
--- a/tools/metrics/histograms/metadata/media/histograms.xml
+++ b/tools/metrics/histograms/metadata/media/histograms.xml
@@ -2932,6 +2932,22 @@
   </summary>
 </histogram>
 
+<histogram name="Media.HasAcceleratedVideoDecode.{CodecType}"
+    enum="BooleanSupported" expires_after="2023-12-01">
+  <owner>dalecurtis@chromium.org</owner>
+  <owner>media-dev@chromium.org</owner>
+  <summary>
+    Tracks accelerated video decoding support for a given codec. Recorded once
+    per GPU process after an attempt to use accelerated video decoding.
+  </summary>
+  <token key="CodecType">
+    <variant name="AV1"/>
+    <variant name="H264"/>
+    <variant name="H265"/>
+    <variant name="VP9"/>
+  </token>
+</histogram>
+
 <histogram name="Media.HasEverPlayed" enum="BooleanHasPlayed"
     expires_after="never">
 <!-- expires-never: Media pipeline health metric. -->
diff --git a/tools/metrics/histograms/metadata/profile/histograms.xml b/tools/metrics/histograms/metadata/profile/histograms.xml
index 7953067..5d8a6ca5 100644
--- a/tools/metrics/histograms/metadata/profile/histograms.xml
+++ b/tools/metrics/histograms/metadata/profile/histograms.xml
@@ -329,7 +329,7 @@
 </histogram>
 
 <histogram name="Profile.LacrosPrimaryProfileFirstRunEntryPoint"
-    enum="LacrosFirstRunEntryPoint" expires_after="2022-09-16">
+    enum="LacrosFirstRunEntryPoint" expires_after="2022-11-13">
   <owner>dgn@chromium.org</owner>
   <owner>droger@chromium.org</owner>
   <summary>
@@ -342,7 +342,7 @@
 </histogram>
 
 <histogram name="Profile.LacrosPrimaryProfileFirstRunOutcome"
-    enum="ProfileSignedInFlowOutcome" expires_after="2022-09-16">
+    enum="ProfileSignedInFlowOutcome" expires_after="2022-11-13">
   <owner>dgn@chromium.org</owner>
   <owner>droger@chromium.org</owner>
   <summary>
@@ -740,7 +740,7 @@
 </histogram>
 
 <histogram name="ProfilePicker.FirstProfileTime.FirstWebContentsFinishReason"
-    enum="StartupProfilingFinishReason" expires_after="2022-07-24">
+    enum="StartupProfilingFinishReason" expires_after="2022-11-27">
   <owner>dgn@chromium.org</owner>
   <owner>chrome-signin-team@google.com</owner>
   <summary>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 577531e..8481f2a 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,24 +5,24 @@
             "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm64/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "win": {
-            "hash": "101c003beb6acc9a1bbbe51d73b109d956353274",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/7a33ae4f5f16f74f70b52d8abe5e6a4d902c9279/trace_processor_shell.exe"
+            "hash": "3162010876755fbd8e9b80ccf166b309a6b002e6",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/00a6e13924cea678f0054f8e3ba096b6c6d91e5e/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "58893933be305d3bfe0a72ebebcacde2ac3ca893",
             "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "mac": {
-            "hash": "3f0fe47f2f63c679ad030b946be0613b33f1ff03",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/7a33ae4f5f16f74f70b52d8abe5e6a4d902c9279/trace_processor_shell"
+            "hash": "ae563edbf92aa307557259cb9086feb36a121bb5",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/eefeab864b684a100003599b2d453c8d918c8ab7/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "e1ad4861384b06d911a65f035317914b8cc975c6",
             "full_remote_path": "perfetto-luci-artifacts/v25.0/mac-arm64/trace_processor_shell"
         },
         "linux": {
-            "hash": "4488911135aedfc4ad34d6c6a035ba7b5cf3eee9",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/7a33ae4f5f16f74f70b52d8abe5e6a4d902c9279/trace_processor_shell"
+            "hash": "98cb9ac4384528bcf68df8cd398db0941ff5362b",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/90d4b83dcd36530dd729f1db23def5c149ed1fbe/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/accessibility/platform/ax_platform_node_cocoa.mm b/ui/accessibility/platform/ax_platform_node_cocoa.mm
index 84fa418..426b6ce73 100644
--- a/ui/accessibility/platform/ax_platform_node_cocoa.mm
+++ b/ui/accessibility/platform/ax_platform_node_cocoa.mm
@@ -301,6 +301,7 @@
     case ax::mojom::Role::kGenericContainer:
     case ax::mojom::Role::kGroup:
     case ax::mojom::Role::kRadioGroup:
+    case ax::mojom::Role::kTabPanel:
       return true;
     default:
       break;
diff --git a/ui/android/BUILD.gn b/ui/android/BUILD.gn
index 226bc9c..dec9de7 100644
--- a/ui/android/BUILD.gn
+++ b/ui/android/BUILD.gn
@@ -373,6 +373,7 @@
     "//base:jni_java",
     "//build/android:build_java",
     "//components/url_formatter/android:url_formatter_java",
+    "//third_party/android_deps:com_google_code_findbugs_jsr305_java",
     "//third_party/androidx:androidx_activity_activity_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
     "//third_party/androidx:androidx_appcompat_appcompat_java",
diff --git a/ui/android/java/src/org/chromium/ui/base/ViewAndroidDelegate.java b/ui/android/java/src/org/chromium/ui/base/ViewAndroidDelegate.java
index 6fba2fb..01dbebb 100644
--- a/ui/android/java/src/org/chromium/ui/base/ViewAndroidDelegate.java
+++ b/ui/android/java/src/org/chromium/ui/base/ViewAndroidDelegate.java
@@ -16,6 +16,7 @@
 import android.view.ViewGroup.MarginLayoutParams;
 import android.view.inputmethod.InputConnection;
 
+import androidx.annotation.CallSuper;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.VisibleForTesting;
@@ -56,6 +57,21 @@
     private ObserverList<ContainerViewObserver> mContainerViewObservers = new ObserverList<>();
 
     /**
+     * Notifies the listener of vertical scroll direction changes.
+     */
+    public interface VerticalScrollDirectionChangeListener {
+        /**
+         * Called when the vertical scroll direction changes.
+         * @param directionUp Whether the scroll direction is up, i.e. swiping down.
+         * @param currentScrollRatio The current scroll ratio of the page.
+         */
+        void onVerticalScrollDirectionChanged(boolean directionUp, float currentScrollRatio);
+    }
+
+    private final ObserverList<VerticalScrollDirectionChangeListener>
+            mVerticalScrollDirectionChangeListeners = new ObserverList<>();
+
+    /**
      * Create and return a basic implementation of {@link ViewAndroidDelegate}.
      * @param containerView {@link ViewGroup} to be used as a container view.
      * @return a new instance of {@link ViewAndroidDelegate}.
@@ -80,6 +96,18 @@
         mContainerViewObservers.addObserver(observer);
     }
 
+    /** Adds the provided {@link VerticalScrollDirectionChangeListener}. */
+    public final void addVerticalScrollDirectionChangeListener(
+            VerticalScrollDirectionChangeListener listener) {
+        mVerticalScrollDirectionChangeListeners.addObserver(listener);
+    }
+
+    /** Removes the provided {@link VerticalScrollDirectionChangeListener}. */
+    public final void removeVerticalScrollDirectionChangeListener(
+            VerticalScrollDirectionChangeListener listener) {
+        mVerticalScrollDirectionChangeListeners.removeObserver(listener);
+    }
+
     /**
      * Updates the current container view to which this class delegates.
      *
@@ -394,7 +422,9 @@
      * if page is not scrollable, though this should not be called in that case.
      */
     @CalledByNative
+    @CallSuper
     protected void onVerticalScrollDirectionChanged(boolean directionUp, float currentScrollRatio) {
+        notifyVerticalScrollDirectionChangeListeners(directionUp, currentScrollRatio);
     }
 
     /**
@@ -513,6 +543,14 @@
         return null;
     }
 
+    private void notifyVerticalScrollDirectionChangeListeners(
+            boolean directionUp, float currentScrollRatio) {
+        for (VerticalScrollDirectionChangeListener listener :
+                mVerticalScrollDirectionChangeListeners) {
+            listener.onVerticalScrollDirectionChanged(directionUp, currentScrollRatio);
+        }
+    }
+
     /** Destroy and clean up dependencies (e.g. drag state tracker if set). */
     public void destroy() {
         // TODO(https://crbug.com/1297354): Call this in when destroying WebContents.
diff --git a/ui/android/java/src/org/chromium/ui/resources/dynamics/HardwareDraw.java b/ui/android/java/src/org/chromium/ui/resources/dynamics/HardwareDraw.java
index fe79047d..d2548d2 100644
--- a/ui/android/java/src/org/chromium/ui/resources/dynamics/HardwareDraw.java
+++ b/ui/android/java/src/org/chromium/ui/resources/dynamics/HardwareDraw.java
@@ -20,18 +20,17 @@
 import androidx.annotation.IntDef;
 import androidx.annotation.RequiresApi;
 
+import org.chromium.base.Callback;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.TraceEvent;
 import org.chromium.base.task.PostTask;
 import org.chromium.base.task.SequencedTaskRunner;
 import org.chromium.base.task.TaskTraits;
 import org.chromium.ui.resources.dynamics.ViewResourceAdapter.CaptureMechanism;
-import org.chromium.ui.resources.dynamics.ViewResourceAdapter.CaptureResult;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.nio.ByteBuffer;
-import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Uses a {@link RenderNode} to perform bitmap capture of a java View. This should typically walk
@@ -56,17 +55,28 @@
         int RUNNING = 3;
     }
 
-    private ThreadUtils.ThreadChecker mThreadChecker = new ThreadUtils.ThreadChecker();
-    // When using Hardware Acceleration the conversion from canvas to Bitmap occurs on a different
-    // thread.
-    private static Handler sHandler;
+    private static class RequestState {
+        // Track the last BitmapRequestId so we only return one image per request (in case of
+        // animations during that draw).
+        public final int requestId;
 
-    private AcceleratedImageReader mReader;
-    private boolean mDebugViewAttachedToWindowListenerAdded;
-    // Incremented each time we enqueue a Hardware drawn Bitmap. Only used if
-    // |mUseHardwareBitmapDraw| is true.
-    private AtomicInteger mCurrentBitmapRequestId;
-    private Bitmap mBitmap;
+        public final View view;
+        public final float scale;
+        public final Callback<Bitmap> onBitmapCapture;
+
+        private RequestState(
+                int requestId, View view, float scale, Callback<Bitmap> onBitmapCapture) {
+            this.requestId = requestId;
+            this.view = view;
+            this.scale = scale;
+            this.onBitmapCapture = onBitmapCapture;
+        }
+        public static RequestState next(
+                View view, float scale, Callback<Bitmap> onBitmapCapture, RequestState previous) {
+            int nextId = previous == null ? 1 : previous.requestId + 1;
+            return new RequestState(nextId, view, scale, onBitmapCapture);
+        }
+    }
 
     // RenderNode was added in API level 29 (Android 10). So restrict AcceleratedImageReader as
     // well.
@@ -108,7 +118,7 @@
             init(width, height);
         }
 
-        // This needs PixelFormat.RGBA_8888, because the |mBitmap| uses Bitmap.Config.ARGB_888
+        // This needs PixelFormat.RGBA_8888, because the bitmap uses Bitmap.Config.ARGB_888
         // this is supported by the android docs which states " This must be one of the
         // ImageFormat or PixelFormat constants.". It does state that not all formats are
         // supported, but this seems to work and has worked for quite awhile. This comment
@@ -154,7 +164,8 @@
         // Should only be called directly after calling currentState() and seeing |requestNeeded|
         // being true. By requiring this we can avoid taking a lock to check the state before
         // posting the renderNode.
-        private void requestDraw(RenderNode renderNode) {
+        private void requestDraw(
+                RenderNode renderNode, View view, float scale, Callback<Bitmap> onBitmapCapture) {
             mThreadChecker.assertOnValidThread();
             switch (mImageReaderStatus) {
                 case ImageReaderStatus.NEW:
@@ -172,7 +183,8 @@
             mTaskRunner.postTask(() -> {
                 HardwareRenderer mRenderer = new HardwareRenderer();
                 try (TraceEvent e = TraceEvent.scoped("AcceleratedImageReader::requestDraw")) {
-                    mCurrentBitmapRequestId.incrementAndGet();
+                    mCurrentRequestState =
+                            RequestState.next(view, scale, onBitmapCapture, mCurrentRequestState);
                     Surface s = mReaderDelegate.getSurface();
                     mRenderer.setContentRoot(renderNode);
                     mRenderer.setSurface(s);
@@ -182,6 +194,7 @@
             });
         }
 
+        // This method is run on the sHandler.
         @Override
         public void onImageAvailable(ImageReader reader) {
             try (TraceEvent e = TraceEvent.scoped("AcceleratedImageReader::onImageAvailable")) {
@@ -192,8 +205,8 @@
                     return;
                 }
 
-                int request = mCurrentBitmapRequestId.get();
-                if (request == mLastBitmapRequestId) {
+                final RequestState requestState = mCurrentRequestState;
+                if (requestState.requestId == mLastBitmapRequestId) {
                     // If there was an animation when we requested a draw, we will receive each
                     // frame of the animation. For now we just take the first one (though the last
                     // would be better there is no good way to know when its the last of the frame).
@@ -205,7 +218,7 @@
                     image.close();
                     return;
                 }
-                mLastBitmapRequestId = request;
+                mLastBitmapRequestId = requestState.requestId;
 
                 android.media.Image.Plane[] planes = image.getPlanes();
                 assert planes.length != 0;
@@ -227,8 +240,21 @@
                                 CaptureUtils.createBitmap(width + rowPaddingInPixels, height);
                         snapshot.copyPixelsFromBuffer(buffer);
                         image.close();
-                        // Update the bitmap the UI reads.
-                        mState = new State(width, height, rowPaddingInPixels, snapshot);
+
+                        final State currentState =
+                                new State(width, height, rowPaddingInPixels, snapshot);
+                        mState = currentState;
+                        requestState.view.getHandler().post(() -> {
+                            int scaledWidth =
+                                    (int) (requestState.view.getWidth() * requestState.scale);
+                            int scaledHeight =
+                                    (int) (requestState.view.getHeight() * requestState.scale);
+                            if (mReader.validateCurrentBitmap(
+                                        currentState, scaledWidth, scaledHeight)
+                                    && currentState.mHardwareBitmap != null) {
+                                requestState.onBitmapCapture.onResult(currentState.mHardwareBitmap);
+                            }
+                        });
                     }
                 });
             }
@@ -259,21 +285,30 @@
         }
     }
 
+    // When using Hardware Acceleration the conversion from canvas to Bitmap occurs on a different
+    // thread.
+    private static Handler sHandler;
+
+    private final ThreadUtils.ThreadChecker mThreadChecker = new ThreadUtils.ThreadChecker();
+
+    private AcceleratedImageReader mReader;
+    private boolean mDebugViewAttachedToWindowListenerAdded;
+    // Set each time we enqueue a Hardware drawn Bitmap.
+    private RequestState mCurrentRequestState;
+
     /**
      * Each instance should be called by external clients only on the thread it is created. The
      * first instance created will also create a thread to do the actual rendering on.
      */
     public HardwareDraw() {
         mThreadChecker.assertOnValidThread();
-
         if (sHandler == null) {
             HandlerThread thread = new HandlerThread("HardwareDrawThread");
             thread.start();
             sHandler = new Handler(thread.getLooper());
         }
-        mCurrentBitmapRequestId = new AtomicInteger(0);
-        // We can't set up |mReader| here because |mView| might not have had its first layout
-        // yet and image reader needs to know the width and the height.
+        // We couldn't set up |mReader| even if we had a View, because the view might not have had
+        // its first layout yet and image reader needs to know the width and the height.
     }
 
     private boolean captureHardware(Canvas canvas, View view, Rect dirtyRect, float scale,
@@ -282,8 +317,7 @@
                     /*drawWhileDetached*/ drawWhileDetached, observer)) {
             return true;
         }
-        // TODO(crbug/1318009): remove this code or promote it to default once we determine if this
-        // is the proper fix.
+        // TODO(https://crbug/1318009): Remove this code or promote it to default once we determine
         TraceEvent.instant("HardwareDraw::DrawAttemptedWhileDetached");
         if (!mDebugViewAttachedToWindowListenerAdded) {
             mDebugViewAttachedToWindowListenerAdded = true;
@@ -304,11 +338,14 @@
         return false;
     }
 
-    // This uses a RecordingNode to store all the required draw instructions without doing
-    // them upfront. And then on a threadpool task we grab a hardware canvas (required to use a
-    // RenderNode) and draw it using the hardware accelerated canvas.
-    private boolean captureWithHardwareDraw(
-            View view, Rect dirtyRect, float scale, CaptureObserver observer) {
+    /**
+     * This uses a RecordingNode to store all the required draw instructions without doing them
+     * upfront. And then on a threadpool task we grab a hardware canvas (required to use a
+     * RenderNode) and draw it using the hardware accelerated canvas.
+     * @return If draw instructions were recorded and the dirty rect can be reset.
+     */
+    private boolean captureWithHardwareDraw(View view, Rect dirtyRect, float scale,
+            CaptureObserver observer, Callback<Bitmap> onBitmapCapture) {
         try (TraceEvent e = TraceEvent.scoped("HardwareDraw:captureWithHardwareDraw")) {
             if (view.getWidth() == 0 || view.getHeight() == 0) {
                 // We haven't actually laid out this view yet no point in requesting a screenshot.
@@ -321,18 +358,6 @@
             // modify this state object is the UI thread. So grab it all up front.
             AcceleratedImageReader.State currentState = mReader.currentState();
 
-            // If we have a new Bitmap update our |mBitmap| to the newest version. Since we update
-            // one check late we might serve a slightly stale result but not for long. If the bitmap
-            // is not there we'll end up just showing a blank toolbar without any icons or text.
-            // However this is preferred over blocking the main thread waiting for the image
-            // potentially during user input.
-            int scaledWidth = (int) (view.getWidth() * scale);
-            int scaledHeight = (int) (view.getHeight() * scale);
-            if (mReader.validateCurrentBitmap(currentState, scaledWidth, scaledHeight)
-                    && currentState.mHardwareBitmap != null) {
-                mBitmap = currentState.mHardwareBitmap;
-            }
-
             // If we didn't have a bitmap to return and there isn't an ongoing request already we
             // will start a bitmap copy which will be done Async on a different thread.
             RenderNode renderNode = null;
@@ -352,7 +377,8 @@
                         canvas, view, dirtyRect, scale, /*drawWhileDetached*/ false, observer);
                 renderNode.endRecording();
                 if (captureSuccess) {
-                    onDrawInstructionsAvailable(renderNode, currentState);
+                    onDrawInstructionsAvailable(
+                            renderNode, currentState, view, scale, onBitmapCapture);
                 }
                 return captureSuccess;
             }
@@ -361,10 +387,11 @@
     }
 
     @SuppressWarnings("NewApi")
-    private void onDrawInstructionsAvailable(
-            RenderNode renderNode, AcceleratedImageReader.State currentState) {
+    private void onDrawInstructionsAvailable(RenderNode renderNode,
+            AcceleratedImageReader.State currentState, View view, float scale,
+            Callback<Bitmap> onBitmapCapture) {
         currentState.mRequestNewDraw = false;
-        mReader.requestDraw(renderNode);
+        mReader.requestDraw(renderNode, view, scale, onBitmapCapture);
     }
 
     @Override
@@ -373,19 +400,7 @@
     }
 
     @Override
-    public boolean shouldPretendIsDirty() {
-        // The bitmap is dirty if we're waiting for the results of a previous request (null mBitmap
-        // or RUNNING), so continue to trigger captures.
-        return mBitmap == null || mReader.currentStatus() == ImageReaderStatus.RUNNING;
-    }
-
-    @Override
     public void onViewSizeChange(View view, float scale) {
-        // Wait for a new image before returning anything.
-        if (mBitmap != null) {
-            mBitmap.recycle();
-            mBitmap = null;
-        }
         int scaledWidth = (int) (view.getWidth() * scale);
         int scaledHeight = (int) (view.getHeight() * scale);
         if (mReader == null) {
@@ -397,15 +412,17 @@
 
     @Override
     public void dropCachedBitmap() {
-        mBitmap = null;
+        AcceleratedImageReader.State state = mReader.mState;
+        if (state != null) {
+            state.mHardwareBitmap = null;
+        }
     }
 
     @Override
-    public CaptureResult syncCaptureBitmap(
-            View view, Rect dirtyRect, float scale, CaptureObserver observer) {
+    public boolean startBitmapCapture(View view, Rect dirtyRect, float scale,
+            CaptureObserver observer, Callback<Bitmap> onBitmapCapture) {
         try (TraceEvent e = TraceEvent.scoped("HardwareDraw:syncCaptureBitmap")) {
-            boolean captureSuccess = captureWithHardwareDraw(view, dirtyRect, scale, observer);
-            return new CaptureResult(mBitmap, captureSuccess);
+            return captureWithHardwareDraw(view, dirtyRect, scale, observer, onBitmapCapture);
         }
     }
 }
\ No newline at end of file
diff --git a/ui/android/java/src/org/chromium/ui/resources/dynamics/SoftwareDraw.java b/ui/android/java/src/org/chromium/ui/resources/dynamics/SoftwareDraw.java
index 20878b627..e5d951c 100644
--- a/ui/android/java/src/org/chromium/ui/resources/dynamics/SoftwareDraw.java
+++ b/ui/android/java/src/org/chromium/ui/resources/dynamics/SoftwareDraw.java
@@ -10,8 +10,8 @@
 import android.graphics.Rect;
 import android.view.View;
 
+import org.chromium.base.Callback;
 import org.chromium.base.TraceEvent;
-import org.chromium.ui.resources.dynamics.ViewResourceAdapter.CaptureResult;
 
 /** Simple bitmap capture approach simply calling {@link View#draw(Canvas)}. */
 public class SoftwareDraw implements ViewResourceAdapter.CaptureMechanism {
@@ -23,11 +23,6 @@
     }
 
     @Override
-    public boolean shouldPretendIsDirty() {
-        return false;
-    }
-
-    @Override
     public void onViewSizeChange(View view, float scale) {}
 
     @Override
@@ -36,8 +31,8 @@
     }
 
     @Override
-    public CaptureResult syncCaptureBitmap(
-            View view, Rect dirtyRect, float scale, CaptureObserver observer) {
+    public boolean startBitmapCapture(View view, Rect dirtyRect, float scale,
+            CaptureObserver observer, Callback<Bitmap> onBitmapCapture) {
         try (TraceEvent e = TraceEvent.scoped("SoftwareDraw:syncCaptureBitmap")) {
             int scaledWidth = (int) (view.getWidth() * scale);
             int scaledHeight = (int) (view.getHeight() * scale);
@@ -65,7 +60,9 @@
                 assert mBitmap.getWidth() == 1 && mBitmap.getHeight() == 1;
                 mBitmap.setPixel(0, 0, Color.TRANSPARENT);
             }
-            return new CaptureResult(mBitmap, !isEmpty);
+
+            onBitmapCapture.onResult(mBitmap);
+            return !isEmpty;
         }
     }
 }
\ No newline at end of file
diff --git a/ui/android/java/src/org/chromium/ui/resources/dynamics/ViewResourceAdapter.java b/ui/android/java/src/org/chromium/ui/resources/dynamics/ViewResourceAdapter.java
index 9ce2ea9..b05eb48 100644
--- a/ui/android/java/src/org/chromium/ui/resources/dynamics/ViewResourceAdapter.java
+++ b/ui/android/java/src/org/chromium/ui/resources/dynamics/ViewResourceAdapter.java
@@ -29,41 +29,26 @@
  */
 public class ViewResourceAdapter
         implements DynamicResource, OnLayoutChangeListener, CaptureObserver {
-    /** A plain object to hold the return values from {@link CaptureMechanism#syncCaptureBitmap}. */
-    public static class CaptureResult {
-        public final Bitmap bitmap;
-        public final boolean clearDirtyRect;
-
-        /**
-         * @param bitmap The drawn pixels for the view being captured.
-         * @param clearDirtyRect If the dirty rect can be cleared, because a capture happened.
-         */
-        public CaptureResult(Bitmap bitmap, boolean clearDirtyRect) {
-            this.bitmap = bitmap;
-            this.clearDirtyRect = clearDirtyRect;
-        }
-    }
-
     /** Abstraction around the mechanism for actually capturing bitmaps.  */
     public interface CaptureMechanism {
         /** See {@link Resource#shouldRemoveResourceOnNullBitmap()}. */
         boolean shouldRemoveResourceOnNullBitmap();
-        /** If a capture should be taken based on state of the capture mechanism. */
-        boolean shouldPretendIsDirty();
         /** Called when the size of the view changes. */
-        void onViewSizeChange(View view, float scale);
+        default void onViewSizeChange(View view, float scale) {}
         /** Called to drop any cached bitmaps to free up memory. */
         void dropCachedBitmap();
+
         /**
          * Called to trigger the actual bitmap capture.
          * @param view The view being captured.
          * @param dirtyRect The area that has changed since last capture.
          * @param scale Scalar to apply to width and height when capturing a bitmap.
          * @param observer To be notified before and after the capture happens.
-         * @return The result of the capture.
+         * @param onBitmapCapture The callback to return the recorded image.
+         * @return If the dirty rect can be cleared on a successful capture.
          */
-        CaptureResult syncCaptureBitmap(
-                View view, Rect dirtyRect, float scale, CaptureObserver observer);
+        boolean startBitmapCapture(View view, Rect dirtyRect, float scale, CaptureObserver observer,
+                Callback<Bitmap> onBitmapCapture);
     }
 
     private final View mView;
@@ -112,25 +97,28 @@
     }
 
     /**
-     * Typically called when ({@link #isDirty()} returned {@code true}), to return a new
-     * {@link Bitmap} and clear out the dirty rect, resulting in a non-dirty view. Depending on the
-     * draw mechanism, this may return a null bitmap. In such a case, on the next frame, isDirty()
-     * should still be used to decide whether to call this.
-     * @return A {@link Bitmap} representing the {@link View}.
+     * Triggers a bitmap capture. Depending on this mechanism, it may do some or all of the work,
+     * and may be sync or async.
      */
     @SuppressWarnings("NewApi")
-    public Bitmap getBitmap() {
+    public void triggerBitmapCapture() {
         mThreadChecker.assertOnValidThread();
         try (TraceEvent e = TraceEvent.scoped("ViewResourceAdapter:getBitmap")) {
-            CaptureResult result =
-                    mCaptureMechanism.syncCaptureBitmap(mView, new Rect(mDirtyRect), mScale, this);
-            if (result.clearDirtyRect) {
+            if (mCaptureMechanism.startBitmapCapture(
+                        mView, new Rect(mDirtyRect), mScale, this, this::onCapture)) {
                 mDirtyRect.setEmpty();
             }
-            return result.bitmap;
         }
     }
 
+    private void onCapture(Bitmap bitmap) {
+        mThreadChecker.assertOnValidThread();
+        Resource resource = new DynamicResourceSnapshot(bitmap,
+                mCaptureMechanism.shouldRemoveResourceOnNullBitmap(), mViewSize,
+                createNativeResource());
+        mOnResourceReady.onResult(resource);
+    }
+
     /**
      * Set the downsampling scale. The rendered size is not affected.
      * @param scale The scale to use. <1 means the Bitmap is smaller than the View.
@@ -156,30 +144,18 @@
     /**
      * On every render frame, this resources will check to see if it is dirty. If so, it will take
      * a bitmap capture and push it to the renderer. Subclasses can override this method to suppress
-     * when captures are/can be taken. Although this will likely run into problems with hardware
-     * draws because the bitmap they return lags behind by a capture. Should ultimately be fixed as
-     * part of https://crbug.com/1338202.
+     * when captures are/can be taken.
      * @return If a bitmap capture should be taken/started.
      */
     @CallSuper
     protected boolean isDirty() {
-        // The bitmap is dirty if some part of it has changed, or the capture mode wants to return a
-        // new bitmap.
-        return !mDirtyRect.isEmpty() || mCaptureMechanism.shouldPretendIsDirty();
+        return !mDirtyRect.isEmpty();
     }
 
     @Override
     public void onResourceRequested() {
-        // TODO(skym): The hardware capture approach should be pushing bitmaps when they're ready,
-        // and avoid relying on isDirty and/or onResourceRequested signals. However this is an
-        // intermediate state during refactoring, and is intentionally keeping the old behavior for
-        // now.
         if (mOnResourceReady != null && isDirty()) {
-            Bitmap bitmap = getBitmap();
-            boolean removeOnNull = mCaptureMechanism.shouldRemoveResourceOnNullBitmap();
-            Resource resource = new DynamicResourceSnapshot(
-                    bitmap, removeOnNull, mViewSize, createNativeResource());
-            mOnResourceReady.onResult(resource);
+            triggerBitmapCapture();
         }
     }
 
diff --git a/ui/android/junit/src/org/chromium/ui/resources/dynamics/ViewResourceAdapterTest.java b/ui/android/junit/src/org/chromium/ui/resources/dynamics/ViewResourceAdapterTest.java
index 2c7d064..0a863dc 100644
--- a/ui/android/junit/src/org/chromium/ui/resources/dynamics/ViewResourceAdapterTest.java
+++ b/ui/android/junit/src/org/chromium/ui/resources/dynamics/ViewResourceAdapterTest.java
@@ -87,9 +87,15 @@
         return DynamicResourceTestUtils.getBitmapSizeSync(mAdapter);
     }
 
+    private Bitmap getBitmap() {
+        // Need to mark dirty before requesting, otherwise it will no-op.
+        mAdapter.invalidate(null);
+        return DynamicResourceTestUtils.getBitmapSync(mAdapter);
+    }
+
     @Test
     public void testGetBitmap() {
-        Bitmap bitmap = mAdapter.getBitmap();
+        Bitmap bitmap = getBitmap();
         assertNotNull(bitmap);
         assertEquals(mViewWidth, bitmap.getWidth());
         assertEquals(mViewHeight, bitmap.getHeight());
@@ -97,8 +103,9 @@
 
     @Test
     public void testGetBitmapSize() {
-        Bitmap bitmap = mAdapter.getBitmap();
+        Bitmap bitmap = getBitmap();
         Rect rect = getBitmapSize();
+
         assertEquals(bitmap.getWidth(), rect.width());
         assertEquals(bitmap.getHeight(), rect.height());
     }
@@ -107,7 +114,7 @@
     public void testSetDownsamplingSize() {
         float scale = 0.5f;
         mAdapter.setDownsamplingScale(scale);
-        Bitmap bitmap = mAdapter.getBitmap();
+        Bitmap bitmap = getBitmap();
         assertEquals(mViewWidth * scale, bitmap.getWidth(), 1);
         assertEquals(mViewHeight * scale, bitmap.getHeight(), 1);
 
@@ -120,13 +127,13 @@
     public void testIsDirty() {
         assertTrue(mAdapter.isDirty());
 
-        mAdapter.getBitmap();
+        getBitmap();
         assertFalse(mAdapter.isDirty());
     }
 
     @Test
     public void testOnLayoutChange() {
-        mAdapter.getBitmap();
+        getBitmap();
         assertFalse(mAdapter.isDirty());
 
         mAdapter.onLayoutChange(mView, 0, 0, 1, 2, 0, 0, mViewWidth, mViewHeight);
@@ -141,7 +148,7 @@
     public void testOnLayoutChangeDownsampled() {
         mAdapter.setDownsamplingScale(0.5f);
 
-        mAdapter.getBitmap();
+        getBitmap();
         assertFalse(mAdapter.isDirty());
 
         mAdapter.onLayoutChange(mView, 0, 0, 1, 2, 0, 0, mViewWidth, mViewHeight);
@@ -154,7 +161,7 @@
 
     @Test
     public void testInvalidate() {
-        mAdapter.getBitmap();
+        getBitmap();
         assertFalse(mAdapter.isDirty());
 
         mAdapter.invalidate(null);
@@ -167,7 +174,7 @@
 
     @Test
     public void testInvalidateRect() {
-        mAdapter.getBitmap();
+        getBitmap();
         assertFalse(mAdapter.isDirty());
 
         Rect dirtyRect = new Rect(1, 2, 3, 4);
@@ -180,7 +187,7 @@
     public void testInvalidateRectDownsampled() {
         mAdapter.setDownsamplingScale(0.5f);
 
-        mAdapter.getBitmap();
+        getBitmap();
         assertFalse(mAdapter.isDirty());
 
         Rect dirtyRect = new Rect(1, 2, 3, 4);
@@ -191,7 +198,7 @@
 
     @Test
     public void testInvalidateRectUnion() {
-        mAdapter.getBitmap();
+        getBitmap();
         assertFalse(mAdapter.isDirty());
 
         mAdapter.invalidate(new Rect(1, 2, 3, 4));
@@ -203,7 +210,7 @@
 
     @Test
     public void testGetBitmapResized() {
-        Bitmap bitmap = mAdapter.getBitmap();
+        Bitmap bitmap = getBitmap();
         assertNotNull(bitmap);
         assertEquals(mViewWidth, bitmap.getWidth());
         assertEquals(mViewHeight, bitmap.getHeight());
@@ -211,7 +218,7 @@
         mViewWidth = 10;
         mViewHeight = 20;
         mAdapter.invalidate(null);
-        Bitmap bitmap2 = mAdapter.getBitmap();
+        Bitmap bitmap2 = getBitmap();
         assertNotNull(bitmap2);
         assertEquals(mViewWidth, bitmap2.getWidth());
         assertEquals(mViewHeight, bitmap2.getHeight());
@@ -220,39 +227,39 @@
 
     @Test
     public void testBitmapReused() {
-        Bitmap bitmap = mAdapter.getBitmap();
+        Bitmap bitmap = getBitmap();
         assertNotNull(bitmap);
 
         mAdapter.invalidate(null);
         assertTrue(mAdapter.isDirty());
-        assertEquals(bitmap, mAdapter.getBitmap());
+        assertEquals(bitmap, getBitmap());
     }
 
     @Test
     public void testDropCachedBitmap() {
-        Bitmap bitmap = mAdapter.getBitmap();
+        Bitmap bitmap = getBitmap();
         assertNotNull(bitmap);
 
         mAdapter.invalidate(null);
         assertTrue(mAdapter.isDirty());
-        assertEquals(bitmap, mAdapter.getBitmap());
+        assertEquals(bitmap, getBitmap());
 
         mAdapter.dropCachedBitmap();
         mAdapter.invalidate(null);
         assertTrue(mAdapter.isDirty());
-        assertNotEquals(bitmap, mAdapter.getBitmap());
+        assertNotEquals(bitmap, getBitmap());
     }
 
     @Test
     public void testDropCachedBitmapNotDirty() {
-        mAdapter.getBitmap();
+        getBitmap();
         mAdapter.dropCachedBitmap();
         assertFalse(mAdapter.isDirty());
     }
 
     @Test
     public void testDropCachedBitmapGCed() {
-        WeakReference<Bitmap> bitmapWeakReference = new WeakReference<>(mAdapter.getBitmap());
+        WeakReference<Bitmap> bitmapWeakReference = new WeakReference<>(getBitmap());
         assertNotNull(bitmapWeakReference.get());
         assertFalse(canBeGarbageCollected(bitmapWeakReference));
 
@@ -262,19 +269,19 @@
 
     @Test
     public void testResizeGCed() {
-        WeakReference<Bitmap> bitmapWeakReference = new WeakReference<>(mAdapter.getBitmap());
+        WeakReference<Bitmap> bitmapWeakReference = new WeakReference<>(getBitmap());
         assertNotNull(bitmapWeakReference.get());
         assertFalse(canBeGarbageCollected(bitmapWeakReference));
 
         mViewWidth += 10;
         mAdapter.invalidate(null);
-        mAdapter.getBitmap();
+        getBitmap();
         assertTrue(canBeGarbageCollected(bitmapWeakReference));
     }
 
     @Test
     public void testGetDirtyRect() {
-        mAdapter.getBitmap();
+        getBitmap();
         Rect rect = mAdapter.getDirtyRect();
         assertTrue(rect.isEmpty());
 
@@ -288,7 +295,7 @@
     public void testGetDirtyRectDownsampled() {
         mAdapter.setDownsamplingScale(0.5f);
 
-        mAdapter.getBitmap();
+        getBitmap();
         Rect rect = mAdapter.getDirtyRect();
         assertTrue(rect.isEmpty());
 
diff --git a/ui/base/ui_base_features.cc b/ui/base/ui_base_features.cc
index d35ff2a..2b69a00 100644
--- a/ui/base/ui_base_features.cc
+++ b/ui/base/ui_base_features.cc
@@ -105,12 +105,21 @@
     "InputMethodSettingsUiUpdate", base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Enables percent-based scrolling for mousewheel and keyboard initiated
-// scrolls.
-const base::Feature kPercentBasedScrolling = {
-    "PercentBasedScrolling", base::FEATURE_DISABLED_BY_DEFAULT};
+// scrolls and impulse curve animations.
+const enum base::FeatureState kWindowsScrollingPersonalityDefaultStatus =
+    base::FEATURE_DISABLED_BY_DEFAULT;
+static_assert(!BUILDFLAG(IS_MAC) ||
+                  (BUILDFLAG(IS_MAC) &&
+                   kWindowsScrollingPersonalityDefaultStatus ==
+                       base::FEATURE_DISABLED_BY_DEFAULT),
+              "Do not enable this on the Mac. The animation does not match the "
+              "system scroll animation curve to such an extent that it makes "
+              "Chromium stand out in a bad way.");
+const base::Feature kWindowsScrollingPersonality = {
+    "WindowsScrollingPersonality", kWindowsScrollingPersonalityDefaultStatus};
 
 bool IsPercentBasedScrollingEnabled() {
-  return base::FeatureList::IsEnabled(features::kPercentBasedScrolling);
+  return base::FeatureList::IsEnabled(features::kWindowsScrollingPersonality);
 }
 
 // Allows requesting unadjusted movement when entering pointerlock.
diff --git a/ui/base/ui_base_features.h b/ui/base/ui_base_features.h
index 0735a721..b190c0d 100644
--- a/ui/base/ui_base_features.h
+++ b/ui/base/ui_base_features.h
@@ -28,7 +28,7 @@
 COMPONENT_EXPORT(UI_BASE_FEATURES)
 extern const base::Feature kInputMethodSettingsUiUpdate;
 COMPONENT_EXPORT(UI_BASE_FEATURES)
-extern const base::Feature kPercentBasedScrolling;
+extern const base::Feature kWindowsScrollingPersonality;
 COMPONENT_EXPORT(UI_BASE_FEATURES) bool IsPercentBasedScrollingEnabled();
 COMPONENT_EXPORT(UI_BASE_FEATURES)
 extern const base::Feature kPointerLockOptions;
diff --git a/ui/compositor/compositor.cc b/ui/compositor/compositor.cc
index dd358b3..9c98780 100644
--- a/ui/compositor/compositor.cc
+++ b/ui/compositor/compositor.cc
@@ -658,8 +658,8 @@
                            weak_ptr_factory_.GetWeakPtr());
 }
 
-uint32_t Compositor::GetAverageThroughput() const {
-  return host_->GetAverageThroughput();
+double Compositor::GetPercentDroppedFrames() const {
+  return host_->GetPercentDroppedFrames();
 }
 
 std::unique_ptr<cc::EventsMetricsManager::ScopedMonitor>
diff --git a/ui/compositor/compositor.h b/ui/compositor/compositor.h
index e0345f59..5dee593 100644
--- a/ui/compositor/compositor.h
+++ b/ui/compositor/compositor.h
@@ -337,8 +337,8 @@
   // Creates a ThroughputTracker for tracking this Compositor.
   ThroughputTracker RequestNewThroughputTracker();
 
-  // Returns a percentage representing average throughput of last X seconds.
-  uint32_t GetAverageThroughput() const;
+  // Returns a percentage of dropped frames of the last second.
+  double GetPercentDroppedFrames() const;
 
   // Activates a scoped monitor for the current event to track its metrics.
   // `done_callback` is called when the monitor goes out of scope.
diff --git a/ui/events/blink/web_input_event_builders_win_unittest.cc b/ui/events/blink/web_input_event_builders_win_unittest.cc
index 9013363b..4345611 100644
--- a/ui/events/blink/web_input_event_builders_win_unittest.cc
+++ b/ui/events/blink/web_input_event_builders_win_unittest.cc
@@ -54,7 +54,8 @@
 
 TEST(WebInputEventBuilderTest, TestPercentMouseWheelScroll) {
   base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(features::kPercentBasedScrolling);
+  scoped_feature_list.InitAndEnableFeature(
+      features::kWindowsScrollingPersonality);
 
   // We must discount the system scroll settings from the test, as we don't them
   // failing if the test machine has different settings.
diff --git a/ui/events/blink/web_input_event_unittest.cc b/ui/events/blink/web_input_event_unittest.cc
index 59788b2..f49908eb 100644
--- a/ui/events/blink/web_input_event_unittest.cc
+++ b/ui/events/blink/web_input_event_unittest.cc
@@ -382,7 +382,8 @@
 #if !BUILDFLAG(IS_MAC)
 TEST(WebInputEventTest, TestPercentMouseWheelScroll) {
   base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(features::kPercentBasedScrolling);
+  scoped_feature_list.InitAndEnableFeature(
+      features::kWindowsScrollingPersonality);
 
   base::TimeTicks timestamp = EventTimeForNow();
   MouseWheelEvent ui_event(gfx::Vector2d(0, -MouseWheelEvent::kWheelDelta),
@@ -428,7 +429,8 @@
 
 TEST(WebInputEventTest, WheelEvent) {
   base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndDisableFeature(features::kPercentBasedScrolling);
+  scoped_feature_list.InitAndDisableFeature(
+      features::kWindowsScrollingPersonality);
   const int kDeltaX = 14;
   const int kDeltaY = -3;
   ui::MouseWheelEvent ui_event(
diff --git a/ui/ozone/platform/wayland/host/wayland_window.cc b/ui/ozone/platform/wayland/host/wayland_window.cc
index f37ecd7c..d94bfc9 100644
--- a/ui/ozone/platform/wayland/host/wayland_window.cc
+++ b/ui/ozone/platform/wayland/host/wayland_window.cc
@@ -950,7 +950,7 @@
     // window has been applied.
     SetWindowGeometry(pending_bounds_dip_);
     AckConfigure(serial);
-    connection()->ScheduleFlush();
+    root_surface()->Commit();
   } else if (!pending_configures_.empty() &&
              pending_bounds_dip_.size() ==
                  pending_configures_.back().bounds_dip.size()) {
diff --git a/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc b/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc
index cfbbfec..53debc8 100644
--- a/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc
+++ b/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc
@@ -1101,7 +1101,9 @@
     EXPECT_CALL(*xdg_surface, AckConfigure(_)).Times(1);
     EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(1);
     EXPECT_CALL(*mock_surface, Frame(_)).Times(1);
-    EXPECT_CALL(*mock_surface, Commit()).Times(1);
+    // Commit() can be called a second time as part of the configure -> ack
+    // sequence.
+    EXPECT_CALL(*mock_surface, Commit()).Times(testing::Between(1, 2));
 
     ActivateSurface(mock_surface->xdg_surface());
     Sync();
diff --git a/weblayer/browser/autofill_client_impl.cc b/weblayer/browser/autofill_client_impl.cc
index 35ff94f..6c20c13 100644
--- a/weblayer/browser/autofill_client_impl.cc
+++ b/weblayer/browser/autofill_client_impl.cc
@@ -335,7 +335,7 @@
   NOTREACHED();
 }
 
-void AutofillClientImpl::OnPromoCodeSuggestionsFooterSelected(const GURL& url) {
+void AutofillClientImpl::OpenPromoCodeOfferDetailsURL(const GURL& url) {
   NOTREACHED();
 }
 
diff --git a/weblayer/browser/autofill_client_impl.h b/weblayer/browser/autofill_client_impl.h
index b55b3ac2..b4b70fc9 100644
--- a/weblayer/browser/autofill_client_impl.h
+++ b/weblayer/browser/autofill_client_impl.h
@@ -136,7 +136,7 @@
   bool ShouldShowSigninPromo() override;
   bool AreServerCardsSupported() const override;
   void ExecuteCommand(int id) override;
-  void OnPromoCodeSuggestionsFooterSelected(const GURL& url) override;
+  void OpenPromoCodeOfferDetailsURL(const GURL& url) override;
 
   // RiskDataLoader:
   void LoadRiskData(
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/BrowserViewController.java b/weblayer/browser/java/org/chromium/weblayer_private/BrowserViewController.java
index c1faa3b..f3eb434 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/BrowserViewController.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/BrowserViewController.java
@@ -190,7 +190,8 @@
 
         mBottomSheetController = BottomSheetControllerFactory.createBottomSheetController(
                 () -> mScrim, (v) -> {}, ContextUtils.activityFromContext(context).getWindow(),
-                KeyboardVisibilityDelegate.getInstance(), () -> mBottomSheetContainer);
+                KeyboardVisibilityDelegate.getInstance(), () -> mBottomSheetContainer,
+                () -> mContentViewRenderView.getHeight());
         BottomSheetControllerFactory.attach(mWindowAndroid, mBottomSheetController);
 
         mPwaBottomSheetController = PwaBottomSheetControllerFactory.createPwaBottomSheetController(
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/TabImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/TabImpl.java
index 3723120..c444f56c 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/TabImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/TabImpl.java
@@ -227,6 +227,7 @@
         @Override
         protected void onVerticalScrollDirectionChanged(
                 boolean directionUp, float currentScrollRatio) {
+            super.onVerticalScrollDirectionChanged(directionUp, currentScrollRatio);
             try {
                 mClient.onScrollNotification(directionUp
                                 ? ScrollNotificationType.DIRECTION_CHANGED_UP