diff --git a/DEPS b/DEPS
index ba4cabfb..3fd40c5f 100644
--- a/DEPS
+++ b/DEPS
@@ -199,11 +199,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'bc2fa2b526327f68a26d9a9a17356a3837cf8563',
+  'skia_revision': 'c616e1c1791c79a8cdec0d6c23dcb3b172228b96',
   # 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': '00ea60a40e748000e049a0551eb8d3f46a0db714',
+  'v8_revision': '358aa1391624d156e3a358fae69b15a0dcee52b9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -278,7 +278,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': '7f3180d06de0d7034c6b5442cea66a7bd0d9fdfd',
+  'devtools_frontend_revision': '18c05620760284f6910f413fdc9f89b1285a7511',
   # 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.
@@ -318,7 +318,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': 'e9f42997b213f1b287aa31698e22b2bca6976853',
+  'dawn_revision': 'd44159c23c104746dd12b35d251922d89d45f4e8',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -357,7 +357,7 @@
   'ukey2_revision': '0275885d8e6038c39b8a8ca55e75d1d4d1727f47',
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'tint_revision': 'c2118b0dcb2a11d93267dc0877584f9d6855e796',
+  'tint_revision': 'dc9d3e0cd5f24928d2ca11c825ef60fad1b8ac37',
 
   # TODO(crbug.com/941824): The values below need to be kept in sync
   # between //DEPS and //buildtools/DEPS, so if you're updating one,
@@ -529,7 +529,7 @@
     Var('chromium_git') + '/external/github.com/toji/webvr.info.git' + '@' + 'c58ae99b9ff9e2aa4c524633519570bf33536248',
 
   'src/ios/third_party/earl_grey2/src': {
-      'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + '8e677c10bb9ab2e29dcbb3e08f99452ad0572548',
+      'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + '3bde011ae34e1db6cee941d4844aaf1eaa98621e',
       'condition': 'checkout_ios',
   },
 
@@ -554,7 +554,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '6171ea2840fb0901a49c0a886c54bf048ff0e8f4',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '6028ffe8792402eb137f9197e0521439bdf999b9',
       'condition': 'checkout_ios',
   },
 
@@ -678,7 +678,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'zXeCxmhPbNT770qhdvfCQxK-TvzbsmCTISBgo4ahJXUC',
+          'version': 'hF9dL0GeFAftrouf8BezMHHVnB3KIDD1V1YgH3_c6IwC',
       },
     ],
     'condition': 'checkout_android',
@@ -911,7 +911,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '6551036772d64afb7a15e844273683123c9d501a',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '398091b1938bf1ddb42a9eef0f42de7a5e632d17',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1276,7 +1276,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'c0182a50034c0c17a444c40aec96ee54239bc269',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'd5c3eb6a5fe1b920ce5833cd8511d2b7810882fc',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1354,7 +1354,7 @@
       'packages': [
           {
               'package': 'fuchsia/third_party/aemu/linux-amd64',
-              'version': 'ze82f_wfveFwoydF7LkeHv0d-sI8F7BLasfX6xmLVM0C'
+              'version': 'zN4Wm-IY1Nd2ZF2IFXt3kLaxI1cDiwANew4sQzpVRSgC'
           },
       ],
       'condition': 'host_os == "linux" and checkout_fuchsia',
@@ -1584,7 +1584,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@fdd04c5e2c18006c2e9376f8248d00c575532710',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@27d0f9d57d30ee8609ea978e9af7ea12edf1f9e5',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/nonembedded/BUILD.gn b/android_webview/nonembedded/BUILD.gn
index 7c5119cd..568461c 100644
--- a/android_webview/nonembedded/BUILD.gn
+++ b/android_webview/nonembedded/BUILD.gn
@@ -109,9 +109,29 @@
   ]
 }
 
+source_set("component_updater") {
+  sources = [
+    "component_updater/aw_component_updater_configurator.cc",
+    "component_updater/aw_component_updater_configurator.h",
+  ]
+  deps = [
+    "//base",
+    "//components/component_updater",
+    "//components/prefs",
+    "//components/services/patch/content",
+    "//components/services/unzip/content",
+    "//components/update_client",
+    "//components/update_client:patch_impl",
+    "//components/update_client:unzip_impl",
+    "//components/version_info",
+    "//components/version_info/android:channel_getter",
+  ]
+}
+
 source_set("nonembedded") {
   sources = [ "webview_apk_application.cc" ]
   deps = [
+    ":component_updater",
     ":nonembedded_jni_headers",
     "//android_webview/common",
     "//base",
diff --git a/android_webview/nonembedded/component_updater/DEPS b/android_webview/nonembedded/component_updater/DEPS
new file mode 100644
index 0000000..c0d2102
--- /dev/null
+++ b/android_webview/nonembedded/component_updater/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+  "+components/component_updater",
+  "+components/services/patch",
+  "+components/services/unzip",
+  "+components/update_client",
+]
diff --git a/android_webview/nonembedded/component_updater/aw_component_updater_configurator.cc b/android_webview/nonembedded/component_updater/aw_component_updater_configurator.cc
new file mode 100644
index 0000000..14fe5a13
--- /dev/null
+++ b/android_webview/nonembedded/component_updater/aw_component_updater_configurator.cc
@@ -0,0 +1,243 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "android_webview/nonembedded/component_updater/aw_component_updater_configurator.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/version.h"
+#include "components/component_updater/component_updater_command_line_config_policy.h"
+#include "components/component_updater/configurator_impl.h"
+#include "components/prefs/pref_service.h"
+#include "components/services/patch/content/patch_service.h"
+#include "components/services/unzip/content/unzip_service.h"
+#include "components/update_client/activity_data_service.h"
+#include "components/update_client/crx_downloader_factory.h"
+#include "components/update_client/network.h"
+#include "components/update_client/patch/patch_impl.h"
+#include "components/update_client/protocol_handler.h"
+#include "components/update_client/unzip/unzip_impl.h"
+#include "components/update_client/unzipper.h"
+#include "components/update_client/update_query_params.h"
+#include "components/version_info/android/channel_getter.h"
+#include "components/version_info/version_info.h"
+#include "components/version_info/version_info_values.h"
+
+namespace android_webview {
+
+namespace {
+
+class AwConfigurator : public update_client::Configurator {
+ public:
+  AwConfigurator(const base::CommandLine* cmdline, PrefService* pref_service);
+
+  // update_client::Configurator overrides.
+  double InitialDelay() const override;
+  int NextCheckDelay() const override;
+  int OnDemandDelay() const override;
+  int UpdateDelay() const override;
+  std::vector<GURL> UpdateUrl() const override;
+  std::vector<GURL> PingUrl() const override;
+  std::string GetProdId() const override;
+  base::Version GetBrowserVersion() const override;
+  std::string GetChannel() const override;
+  std::string GetBrand() const override;
+  std::string GetLang() const override;
+  std::string GetOSLongName() const override;
+  base::flat_map<std::string, std::string> ExtraRequestParams() const override;
+  std::string GetDownloadPreference() const override;
+  scoped_refptr<update_client::NetworkFetcherFactory> GetNetworkFetcherFactory()
+      override;
+  scoped_refptr<update_client::CrxDownloaderFactory> GetCrxDownloaderFactory()
+      override;
+  scoped_refptr<update_client::UnzipperFactory> GetUnzipperFactory() override;
+  scoped_refptr<update_client::PatcherFactory> GetPatcherFactory() override;
+  bool EnabledDeltas() const override;
+  bool EnabledComponentUpdates() const override;
+  bool EnabledBackgroundDownloader() const override;
+  bool EnabledCupSigning() const override;
+  PrefService* GetPrefService() const override;
+  update_client::ActivityDataService* GetActivityDataService() const override;
+  bool IsPerUserInstall() const override;
+  std::unique_ptr<update_client::ProtocolHandlerFactory>
+  GetProtocolHandlerFactory() const override;
+
+ private:
+  friend class base::RefCountedThreadSafe<AwConfigurator>;
+
+  component_updater::ConfiguratorImpl configurator_impl_;
+  PrefService* pref_service_;  // This member is not owned by this class.
+  scoped_refptr<update_client::NetworkFetcherFactory> network_fetcher_factory_;
+  scoped_refptr<update_client::CrxDownloaderFactory> crx_downloader_factory_;
+  scoped_refptr<update_client::UnzipperFactory> unzip_factory_;
+  scoped_refptr<update_client::PatcherFactory> patch_factory_;
+
+  ~AwConfigurator() override = default;
+};
+
+AwConfigurator::AwConfigurator(const base::CommandLine* cmdline,
+                               PrefService* pref_service)
+    : configurator_impl_(
+          component_updater::ComponentUpdaterCommandLineConfigPolicy(cmdline),
+          false),
+      pref_service_(pref_service) {}
+
+double AwConfigurator::InitialDelay() const {
+  // We want the update to start immediately.
+  return 0;
+}
+
+int AwConfigurator::NextCheckDelay() const {
+  return configurator_impl_.NextCheckDelay();
+}
+
+int AwConfigurator::OnDemandDelay() const {
+  return configurator_impl_.OnDemandDelay();
+}
+
+int AwConfigurator::UpdateDelay() const {
+  // No need to have any delays between components updates. In WebView this
+  // doesn't run in a browser and shouldn't affect user's experience.
+  // Furthermore, this will be a background service that is scheduled by
+  // JobScheduler, so we want to do as much work in as little time as possible.
+  // However, if we ever invoked installation on-demand, we should be less
+  // aggressive here.
+  return 0;
+}
+
+std::vector<GURL> AwConfigurator::UpdateUrl() const {
+  return configurator_impl_.UpdateUrl();
+}
+
+std::vector<GURL> AwConfigurator::PingUrl() const {
+  return configurator_impl_.PingUrl();
+}
+
+std::string AwConfigurator::GetProdId() const {
+  // TODO(crbug.com/1176894) add WebView to the ProdId enum. This would allow us
+  // to distinguish requests from this stack but it's fine to use CRX for now.
+  return update_client::UpdateQueryParams::GetProdIdString(
+      update_client::UpdateQueryParams::ProdId::CRX);
+}
+
+base::Version AwConfigurator::GetBrowserVersion() const {
+  return configurator_impl_.GetBrowserVersion();
+}
+
+std::string AwConfigurator::GetChannel() const {
+  return version_info::GetChannelString(version_info::android::GetChannel());
+}
+
+std::string AwConfigurator::GetBrand() const {
+  // WebView isn't branded.
+  return std::string();
+}
+
+std::string AwConfigurator::GetLang() const {
+  // WebView uses the locale of the embedding app. Components are shared with
+  // WebView instances across apps so we don't set a locale.
+  return std::string();
+}
+
+std::string AwConfigurator::GetOSLongName() const {
+  return configurator_impl_.GetOSLongName();
+}
+
+base::flat_map<std::string, std::string> AwConfigurator::ExtraRequestParams()
+    const {
+  return configurator_impl_.ExtraRequestParams();
+}
+
+std::string AwConfigurator::GetDownloadPreference() const {
+  // Hints for the server about caching URLs, "" means let the server decide the
+  // best URLs to return according to its policies.
+  return configurator_impl_.GetDownloadPreference();
+}
+
+scoped_refptr<update_client::NetworkFetcherFactory>
+AwConfigurator::GetNetworkFetcherFactory() {
+  if (!network_fetcher_factory_) {
+    // TODO(crbug.com/1174140) create network fetcher factory.
+  }
+  return network_fetcher_factory_;
+}
+
+scoped_refptr<update_client::CrxDownloaderFactory>
+AwConfigurator::GetCrxDownloaderFactory() {
+  if (!crx_downloader_factory_) {
+    crx_downloader_factory_ =
+        update_client::MakeCrxDownloaderFactory(GetNetworkFetcherFactory());
+  }
+  return crx_downloader_factory_;
+}
+
+scoped_refptr<update_client::UnzipperFactory>
+AwConfigurator::GetUnzipperFactory() {
+  if (!unzip_factory_) {
+    unzip_factory_ = base::MakeRefCounted<update_client::UnzipChromiumFactory>(
+        base::BindRepeating(&unzip::LaunchUnzipper));
+  }
+  return unzip_factory_;
+}
+
+scoped_refptr<update_client::PatcherFactory>
+AwConfigurator::GetPatcherFactory() {
+  if (!patch_factory_) {
+    patch_factory_ = base::MakeRefCounted<update_client::PatchChromiumFactory>(
+        base::BindRepeating(&patch::LaunchFilePatcher));
+  }
+  return patch_factory_;
+}
+
+bool AwConfigurator::EnabledDeltas() const {
+  return configurator_impl_.EnabledDeltas();
+}
+
+bool AwConfigurator::EnabledComponentUpdates() const {
+  // Always enabled.
+  return configurator_impl_.EnabledComponentUpdates();
+}
+
+bool AwConfigurator::EnabledBackgroundDownloader() const {
+  return configurator_impl_.EnabledBackgroundDownloader();
+}
+
+bool AwConfigurator::EnabledCupSigning() const {
+  return configurator_impl_.EnabledCupSigning();
+}
+
+PrefService* AwConfigurator::GetPrefService() const {
+  return pref_service_;
+}
+
+update_client::ActivityDataService* AwConfigurator::GetActivityDataService()
+    const {
+  // This tracks user's activity using the component, doesn't apply to
+  // components and safe to be null.
+  return nullptr;
+}
+
+bool AwConfigurator::IsPerUserInstall() const {
+  // Android doesn't have per user updaters.
+  NOTREACHED();
+  return true;
+}
+
+std::unique_ptr<update_client::ProtocolHandlerFactory>
+AwConfigurator::GetProtocolHandlerFactory() const {
+  return configurator_impl_.GetProtocolHandlerFactory();
+}
+
+}  // namespace
+
+scoped_refptr<update_client::Configurator> MakeAwComponentUpdaterConfigurator(
+    const base::CommandLine* cmdline,
+    PrefService* pref_service) {
+  return base::MakeRefCounted<AwConfigurator>(cmdline, pref_service);
+}
+
+}  // namespace android_webview
diff --git a/android_webview/nonembedded/component_updater/aw_component_updater_configurator.h b/android_webview/nonembedded/component_updater/aw_component_updater_configurator.h
new file mode 100644
index 0000000..605e8b4
--- /dev/null
+++ b/android_webview/nonembedded/component_updater/aw_component_updater_configurator.h
@@ -0,0 +1,25 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ANDROID_WEBVIEW_NONEMBEDDED_COMPONENT_UPDATER_AW_COMPONENT_UPDATER_CONFIGURATOR_H_
+#define ANDROID_WEBVIEW_NONEMBEDDED_COMPONENT_UPDATER_AW_COMPONENT_UPDATER_CONFIGURATOR_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "components/update_client/configurator.h"
+
+class PrefService;
+
+namespace base {
+class CommandLine;
+}
+
+namespace android_webview {
+
+scoped_refptr<update_client::Configurator> MakeAwComponentUpdaterConfigurator(
+    const base::CommandLine* cmdline,
+    PrefService* pref_service);
+
+}  // namespace android_webview
+
+#endif  // ANDROID_WEBVIEW_NONEMBEDDED_COMPONENT_UPDATER_AW_COMPONENT_UPDATER_CONFIGURATOR_H_
diff --git a/android_webview/nonembedded/component_updater/aw_component_updater_configurator_unittest.cc b/android_webview/nonembedded/component_updater/aw_component_updater_configurator_unittest.cc
new file mode 100644
index 0000000..d617e6d
--- /dev/null
+++ b/android_webview/nonembedded/component_updater/aw_component_updater_configurator_unittest.cc
@@ -0,0 +1,123 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "android_webview/nonembedded/component_updater/aw_component_updater_configurator.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/memory/scoped_refptr.h"
+#include "components/component_updater/component_updater_command_line_config_policy.h"
+#include "components/component_updater/component_updater_switches.h"
+#include "components/component_updater/component_updater_url_constants.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/update_client/configurator.h"
+#include "components/update_client/update_query_params.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+using component_updater::ComponentUpdaterCommandLineConfigPolicy;
+using update_client::Configurator;
+
+namespace android_webview {
+
+class AwComponentUpdaterConfiguratorTest : public testing::Test {
+ public:
+  AwComponentUpdaterConfiguratorTest() = default;
+  ~AwComponentUpdaterConfiguratorTest() override = default;
+
+  AwComponentUpdaterConfiguratorTest(
+      const AwComponentUpdaterConfiguratorTest&) = delete;
+  void operator=(const AwComponentUpdaterConfiguratorTest&) = delete;
+
+  // Overrides from testing::Test.
+  void SetUp() override;
+
+ protected:
+  TestingPrefServiceSimple* GetPrefService() { return pref_service_.get(); }
+  base::CommandLine* GetCommandLine() { return cmdline_.get(); }
+
+ private:
+  std::unique_ptr<TestingPrefServiceSimple> pref_service_;
+  std::unique_ptr<base::CommandLine> cmdline_;
+};
+
+void AwComponentUpdaterConfiguratorTest::SetUp() {
+  pref_service_ = std::make_unique<TestingPrefServiceSimple>();
+  cmdline_ = std::make_unique<base::CommandLine>(
+      *base::CommandLine::ForCurrentProcess());
+}
+
+TEST_F(AwComponentUpdaterConfiguratorTest, TestDelays) {
+  scoped_refptr<update_client::Configurator> config =
+      MakeAwComponentUpdaterConfigurator(GetCommandLine(), GetPrefService());
+
+  CHECK_EQ(config->InitialDelay(), 0);
+  CHECK_EQ(config->NextCheckDelay(), 5 * 60 * 60);
+  CHECK_EQ(config->OnDemandDelay(), 30 * 60);
+  CHECK_EQ(config->UpdateDelay(), 0);
+}
+
+TEST_F(AwComponentUpdaterConfiguratorTest, TestDelaysWithFastUpdate) {
+  base::CommandLine* cmdline = GetCommandLine();
+  cmdline->AppendSwitchASCII(switches::kComponentUpdater, "fast-update");
+  scoped_refptr<update_client::Configurator> config =
+      MakeAwComponentUpdaterConfigurator(cmdline, GetPrefService());
+
+  CHECK_EQ(config->InitialDelay(), 0);
+  CHECK_EQ(config->NextCheckDelay(), 5 * 60 * 60);
+  CHECK_EQ(config->OnDemandDelay(), 2);
+  CHECK_EQ(config->UpdateDelay(), 0);
+}
+
+TEST_F(AwComponentUpdaterConfiguratorTest, TestDefaultImpl) {
+  // Test default implementation pumped from ConfiguratorImpl
+  scoped_refptr<update_client::Configurator> config =
+      MakeAwComponentUpdaterConfigurator(GetCommandLine(), GetPrefService());
+
+  std::vector<GURL> urls = config->UpdateUrl();
+  ASSERT_EQ(urls.size(), 2u);
+  EXPECT_TRUE(urls.front().SchemeIsCryptographic());
+  ASSERT_STREQ(component_updater::kUpdaterJSONDefaultUrl,
+               urls[0].spec().c_str());
+  ASSERT_STREQ(component_updater::kUpdaterJSONFallbackUrl,
+               urls[1].spec().c_str());
+
+  ASSERT_EQ(config->UpdateUrl(), config->PingUrl());
+
+  EXPECT_TRUE(config->ExtraRequestParams().empty());
+  EXPECT_TRUE(config->GetDownloadPreference().empty());
+
+  EXPECT_TRUE(config->EnabledCupSigning());
+  EXPECT_TRUE(config->EnabledDeltas());
+  EXPECT_TRUE(config->EnabledComponentUpdates());
+  EXPECT_FALSE(config->EnabledBackgroundDownloader());
+}
+
+TEST_F(AwComponentUpdaterConfiguratorTest, TestCustomImpl) {
+  // Test custom value specific for WebView.
+  scoped_refptr<update_client::Configurator> config =
+      MakeAwComponentUpdaterConfigurator(GetCommandLine(), GetPrefService());
+
+  EXPECT_STREQ(update_client::UpdateQueryParams::GetProdIdString(
+                   update_client::UpdateQueryParams::ProdId::CRX),
+               config->GetProdId().c_str());
+
+  EXPECT_TRUE(config->GetBrand().empty());
+  EXPECT_TRUE(config->GetLang().empty());
+}
+
+TEST_F(AwComponentUpdaterConfiguratorTest, TestSwitchRequestParam) {
+  base::CommandLine* cmdline = GetCommandLine();
+  cmdline->AppendSwitchASCII(switches::kComponentUpdater, "test-request");
+
+  scoped_refptr<update_client::Configurator> config =
+      MakeAwComponentUpdaterConfigurator(cmdline, GetPrefService());
+
+  EXPECT_FALSE(config->ExtraRequestParams().empty());
+}
+
+}  // namespace android_webview
diff --git a/android_webview/test/BUILD.gn b/android_webview/test/BUILD.gn
index 8417ce48..58c630e 100644
--- a/android_webview/test/BUILD.gn
+++ b/android_webview/test/BUILD.gn
@@ -450,12 +450,15 @@
     "//android_webview/browser/lifecycle",
     "//android_webview/browser/metrics",
     "//android_webview/common",
+    "//android_webview/nonembedded:component_updater",
     "//base/test:test_support",
     "//components/autofill/core/browser",
+    "//components/component_updater",
     "//components/embedder_support/android:util",
     "//components/metrics",
     "//components/prefs:prefs",
     "//components/prefs:test_support",
+    "//components/update_client",
     "//content:content",
     "//content/test:test_support",
     "//mojo/core/embedder",
@@ -492,6 +495,9 @@
     "../browser/scoped_add_feature_flags_unittests.cc",
     "../browser/state_serializer_unittest.cc",
     "../lib/webview_tests.cc",
+
+    # TODO(crbug.com/1173887) move to nonembedded unit tests suite.
+    "../nonembedded/component_updater/aw_component_updater_configurator_unittest.cc",
   ]
 
   deps += [ "//v8:v8_external_startup_data_assets" ]
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 28b9293..7b998baf 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1493,8 +1493,6 @@
     "wallpaper/wallpaper_widget_controller.h",
     "wallpaper/wallpaper_window_state_manager.cc",
     "wallpaper/wallpaper_window_state_manager.h",
-    "window_factory.cc",
-    "window_factory.h",
     "window_tree_host_lookup.cc",
     "window_user_data.h",
     "wm/always_on_top_controller.cc",
diff --git a/ash/accessibility/accessibility_controller_impl.cc b/ash/accessibility/accessibility_controller_impl.cc
index b7d2cb0..e851726c 100644
--- a/ash/accessibility/accessibility_controller_impl.cc
+++ b/ash/accessibility/accessibility_controller_impl.cc
@@ -1121,13 +1121,15 @@
 
 void AccessibilityControllerImpl::StartPointScan() {
   if (::switches::IsSwitchAccessPointScanningEnabled()) {
-    point_scan_controller_ = std::make_unique<PointScanController>();
-    point_scan_controller_->StartHorizontalRangeScan();
+    if (!point_scan_controller_)
+      point_scan_controller_ = std::make_unique<PointScanController>();
+    point_scan_controller_->Start();
   }
 }
 
 void AccessibilityControllerImpl::StopPointScan() {
-  point_scan_controller_.reset();
+  if (point_scan_controller_)
+    point_scan_controller_->HideAll();
 }
 
 void AccessibilityControllerImpl::
diff --git a/ash/accessibility/point_scan_controller.cc b/ash/accessibility/point_scan_controller.cc
index b5bc521..44978ec 100644
--- a/ash/accessibility/point_scan_controller.cc
+++ b/ash/accessibility/point_scan_controller.cc
@@ -31,45 +31,58 @@
 
 PointScanController::~PointScanController() = default;
 
+void PointScanController::Start() {
+  HideAll();
+  ResetAnimation();
+  StartHorizontalRangeScan();
+}
+
 void PointScanController::StartHorizontalRangeScan() {
   state_ = PointScanState::kHorizontalRangeScanning;
-  horizontal_range_layer_.reset(new PointScanLayer(this));
+  horizontal_range_layer_.reset(
+      new PointScanLayer(this, PointScanLayer::Orientation::HORIZONTAL,
+                         PointScanLayer::Type::RANGE));
   horizontal_range_layer_info_.offset_bound =
       horizontal_range_layer_->bounds().width() - kDefaultRangeWidthDips;
-  horizontal_range_layer_->StartHorizontalRangeScanning();
+  horizontal_range_layer_->Start();
 }
 
 void PointScanController::StartHorizontalLineScan() {
   state_ = PointScanState::kHorizontalScanning;
   horizontal_range_layer_->Pause();
-  horizontal_line_layer_.reset(new PointScanLayer(this));
+  horizontal_line_layer_.reset(
+      new PointScanLayer(this, PointScanLayer::Orientation::HORIZONTAL,
+                         PointScanLayer::Type::LINE));
   horizontal_line_layer_info_.offset = horizontal_range_layer_info_.offset;
   horizontal_line_layer_info_.offset_start =
       horizontal_range_layer_info_.offset;
   horizontal_line_layer_info_.offset_bound =
       horizontal_range_layer_info_.offset + kDefaultRangeWidthDips;
-  horizontal_line_layer_->StartHorizontalScanning();
+  horizontal_line_layer_->Start();
 }
 
 void PointScanController::StartVerticalRangeScan() {
   state_ = PointScanState::kVerticalRangeScanning;
   horizontal_line_layer_->Pause();
   horizontal_range_layer_->SetOpacity(0);
-  vertical_range_layer_.reset(new PointScanLayer(this));
+  vertical_range_layer_.reset(
+      new PointScanLayer(this, PointScanLayer::Orientation::VERTICAL,
+                         PointScanLayer::Type::RANGE));
   vertical_range_layer_info_.offset_bound =
       vertical_range_layer_->bounds().height() - kDefaultRangeHeightDips;
-  vertical_range_layer_->StartVerticalRangeScanning();
+  vertical_range_layer_->Start();
 }
 
 void PointScanController::StartVerticalLineScan() {
   state_ = PointScanState::kVerticalScanning;
   vertical_range_layer_->Pause();
-  vertical_line_layer_.reset(new PointScanLayer(this));
+  vertical_line_layer_.reset(new PointScanLayer(
+      this, PointScanLayer::Orientation::VERTICAL, PointScanLayer::Type::LINE));
   vertical_line_layer_info_.offset = vertical_range_layer_info_.offset;
   vertical_line_layer_info_.offset_start = vertical_range_layer_info_.offset;
   vertical_line_layer_info_.offset_bound =
       vertical_range_layer_info_.offset + kDefaultRangeHeightDips;
-  vertical_line_layer_->StartVerticalScanning();
+  vertical_line_layer_->Start();
 }
 
 void PointScanController::Stop() {
@@ -78,6 +91,43 @@
   vertical_range_layer_->SetOpacity(0);
 }
 
+void PointScanController::HideAll() {
+  if (horizontal_range_layer_) {
+    horizontal_range_layer_->Pause();
+    horizontal_range_layer_->SetOpacity(0);
+  }
+  if (horizontal_line_layer_) {
+    horizontal_line_layer_->Pause();
+    horizontal_line_layer_->SetOpacity(0);
+  }
+  if (vertical_range_layer_) {
+    vertical_range_layer_->Pause();
+    vertical_range_layer_->SetOpacity(0);
+  }
+  if (vertical_line_layer_) {
+    vertical_line_layer_->Pause();
+    vertical_line_layer_->SetOpacity(0);
+  }
+}
+
+void PointScanController::ResetAnimation() {
+  horizontal_range_layer_info_.Clear();
+  if (horizontal_range_layer_)
+    horizontal_range_layer_->SetSubpixelPositionOffset(gfx::Vector2dF(0, 0));
+
+  horizontal_line_layer_info_.Clear();
+  if (horizontal_line_layer_)
+    horizontal_line_layer_->SetSubpixelPositionOffset(gfx::Vector2dF(0, 0));
+
+  vertical_range_layer_info_.Clear();
+  if (vertical_range_layer_)
+    vertical_range_layer_->SetSubpixelPositionOffset(gfx::Vector2dF(0, 0));
+
+  vertical_line_layer_info_.Clear();
+  if (vertical_line_layer_)
+    vertical_line_layer_->SetSubpixelPositionOffset(gfx::Vector2dF(0, 0));
+}
+
 base::Optional<gfx::PointF> PointScanController::OnPointSelect() {
   switch (state_) {
     case PointScanState::kHorizontalRangeScanning:
diff --git a/ash/accessibility/point_scan_controller.h b/ash/accessibility/point_scan_controller.h
index 0cdace2..0b8193d 100644
--- a/ash/accessibility/point_scan_controller.h
+++ b/ash/accessibility/point_scan_controller.h
@@ -38,13 +38,16 @@
     kOff,
   };
 
-  // Starts point scanning, by sweeping a line across the screen and waiting for
-  // user input.
+  // Starts point scanning, by sweeping a range across the screen and waiting
+  // for user input.
+  void Start();
   void StartHorizontalRangeScan();
   void StartHorizontalLineScan();
   void StartVerticalRangeScan();
   void StartVerticalLineScan();
   void Stop();
+  void HideAll();
+  void ResetAnimation();
   base::Optional<gfx::PointF> OnPointSelect();
   bool IsPointScanEnabled();
 
diff --git a/ash/accessibility/point_scan_layer.cc b/ash/accessibility/point_scan_layer.cc
index 166418d..8f9ecca 100644
--- a/ash/accessibility/point_scan_layer.cc
+++ b/ash/accessibility/point_scan_layer.cc
@@ -30,53 +30,42 @@
 }
 }  // namespace
 
-PointScanLayer::PointScanLayer(AccessibilityLayerDelegate* delegate)
-    : AccessibilityLayer(delegate) {
+PointScanLayer::PointScanLayer(AccessibilityLayerDelegate* delegate,
+                               PointScanLayer::Orientation orientation,
+                               PointScanLayer::Type type)
+    : AccessibilityLayer(delegate), orientation_(orientation), type_(type) {
   aura::Window* root_window =
       Shell::GetRootWindowForDisplayId(GetPrimaryDisplay().id());
   CreateOrUpdateLayer(root_window, "PointScanning", gfx::Rect(),
                       /*stack_at_top=*/true);
   SetOpacity(1.0);
-  bounds_ = GetPrimaryDisplay().bounds();
-  layer()->SetBounds(bounds_);
+  layer()->SetBounds(GetPrimaryDisplay().bounds());
 }
 
-void PointScanLayer::StartHorizontalRangeScanning() {
-  is_range_scan_ = true;
-  gfx::Point start(kDefaultPaddingDips, 0);
-  gfx::Point end(bounds_.bottom_left().x() + kDefaultPaddingDips,
-                 bounds_.bottom_left().y());
+void PointScanLayer::Start() {
+  gfx::Point start;
+  gfx::Point end;
+
+  // Set the end point, based on the orientation.
+  if (orientation_ == PointScanLayer::Orientation::HORIZONTAL)
+    end = bounds().bottom_left();
+  else if (orientation_ == PointScanLayer::Orientation::VERTICAL)
+    end = bounds().top_right();
+
+  // Ranges need to offset |line_| by the range width.
+  if (type_ == PointScanLayer::Type::RANGE) {
+    if (orientation_ == PointScanLayer::Orientation::HORIZONTAL) {
+      start.Offset(kDefaultPaddingDips, 0);
+      end.Offset(kDefaultPaddingDips, 0);
+    } else if (orientation_ == PointScanLayer::Orientation::VERTICAL) {
+      start.Offset(0, kDefaultPaddingDips);
+      end.Offset(0, kDefaultPaddingDips);
+    }
+  }
+
   line_.start = start;
   line_.end = end;
   is_moving_ = true;
-  is_horizontal_range_ = true;
-}
-
-void PointScanLayer::StartHorizontalScanning() {
-  is_range_scan_ = false;
-  gfx::Point end = bounds_.bottom_left();
-  bounds_.set_origin(line_.start);
-  line_.end = end;
-  is_moving_ = true;
-  is_horizontal_range_ = false;
-}
-
-void PointScanLayer::StartVerticalRangeScanning() {
-  is_range_scan_ = true;
-  gfx::Point start(0, kDefaultPaddingDips);
-  gfx::Point end(bounds_.top_right().x(),
-                 bounds_.top_right().y() + kDefaultPaddingDips);
-  line_.start = start;
-  line_.end = end;
-  is_moving_ = true;
-}
-
-void PointScanLayer::StartVerticalScanning() {
-  is_range_scan_ = false;
-  gfx::Point end = bounds_.top_right();
-  bounds_.set_origin(line_.start);
-  line_.end = end;
-  is_moving_ = true;
 }
 
 void PointScanLayer::Pause() {
@@ -107,11 +96,11 @@
 
   SkPath path;
 
-  if (is_range_scan_) {
-    if (is_horizontal_range_) {
+  if (type_ == PointScanLayer::Type::RANGE) {
+    if (orientation_ == PointScanLayer::Orientation::HORIZONTAL) {
       path.moveTo(line_.start.x() + kDefaultRangeWidthDips, line_.start.y());
       path.lineTo(line_.end.x() + kDefaultRangeWidthDips, line_.end.y());
-    } else {
+    } else if (orientation_ == PointScanLayer::Orientation::VERTICAL) {
       path.moveTo(line_.start.x(), line_.start.y() + kDefaultRangeHeightDips);
       path.lineTo(line_.end.x(), line_.end.y() + kDefaultRangeHeightDips);
     }
diff --git a/ash/accessibility/point_scan_layer.h b/ash/accessibility/point_scan_layer.h
index c44fe1b..971b617 100644
--- a/ash/accessibility/point_scan_layer.h
+++ b/ash/accessibility/point_scan_layer.h
@@ -8,27 +8,35 @@
 #include "ash/accessibility/accessibility_layer.h"
 #include "ash/accessibility/point_scan_layer_animation_info.h"
 #include "ash/ash_export.h"
+#include "ui/compositor/layer.h"
 
 namespace ash {
 
 class PointScanLayer : public AccessibilityLayer {
  public:
-  explicit PointScanLayer(AccessibilityLayerDelegate* delegate);
+  enum Orientation {
+    HORIZONTAL = 0,
+    VERTICAL,
+  };
+
+  enum Type {
+    LINE = 0,
+    RANGE,
+  };
+
+  explicit PointScanLayer(AccessibilityLayerDelegate* delegate,
+                          Orientation orientation,
+                          Type type);
   ~PointScanLayer() override = default;
 
   PointScanLayer(const PointScanLayer&) = delete;
   PointScanLayer& operator=(const PointScanLayer&) = delete;
 
-  // Begins sweeping a line horizontally across the screen, for the user to pick
-  // an x-coordinate.
-  void StartHorizontalRangeScanning();
-  void StartHorizontalScanning();
-  void StartVerticalRangeScanning();
-  void StartVerticalScanning();
+  void Start();
   void Pause();
 
   bool IsMoving() const;
-  gfx::Rect bounds() const { return bounds_; }
+  gfx::Rect bounds() { return layer()->bounds(); }
 
   // AccessibilityLayer overrides:
   bool CanAnimate() const override;
@@ -45,17 +53,10 @@
     gfx::Point end;
   };
 
-  // The bounds within which we are scanning.
-  gfx::Rect bounds_;
-
-  // The line currently being drawn.
+  Orientation orientation_;
+  Type type_;
   Line line_;
-
   bool is_moving_ = false;
-
-  bool is_range_scan_ = false;
-
-  bool is_horizontal_range_ = false;
 };
 
 }  // namespace ash
diff --git a/ash/accessibility/point_scan_layer_animation_info.cc b/ash/accessibility/point_scan_layer_animation_info.cc
index bb4c21d..613be15e 100644
--- a/ash/accessibility/point_scan_layer_animation_info.cc
+++ b/ash/accessibility/point_scan_layer_animation_info.cc
@@ -6,6 +6,14 @@
 
 namespace ash {
 
+void PointScanLayerAnimationInfo::Clear() {
+  start_time = base::TimeTicks();
+  change_time = base::TimeTicks();
+  offset = 0;
+  offset_bound = 0;
+  offset_start = 0;
+}
+
 void ComputeOffset(PointScanLayerAnimationInfo* animation_info,
                    base::TimeTicks timestamp) {
   if (timestamp < animation_info->start_time)
@@ -26,4 +34,4 @@
   animation_info->offset += offset_delta;
 }
 
-}  // namespace ash
\ No newline at end of file
+}  // namespace ash
diff --git a/ash/accessibility/point_scan_layer_animation_info.h b/ash/accessibility/point_scan_layer_animation_info.h
index 33a8174..c9fb4ce 100644
--- a/ash/accessibility/point_scan_layer_animation_info.h
+++ b/ash/accessibility/point_scan_layer_animation_info.h
@@ -16,6 +16,8 @@
   float offset_bound = 0;
   float offset_start = 0;
   float animation_rate = 0;
+
+  void Clear();
 };
 
 void ComputeOffset(PointScanLayerAnimationInfo* animation_info,
@@ -23,4 +25,4 @@
 
 }  // namespace ash
 
-#endif  // ASH_ACCESSIBILITY_POINT_SCAN_LAYER_ANIMATION_INFO_H_
\ No newline at end of file
+#endif  // ASH_ACCESSIBILITY_POINT_SCAN_LAYER_ANIMATION_INFO_H_
diff --git a/ash/ambient/ui/ambient_info_view.cc b/ash/ambient/ui/ambient_info_view.cc
index 76140653..fa3afc1 100644
--- a/ash/ambient/ui/ambient_info_view.cc
+++ b/ash/ambient/ui/ambient_info_view.cc
@@ -25,7 +25,6 @@
 constexpr int kSpacingDip = 8;
 
 // Typography
-constexpr SkColor kTextColor = SK_ColorWHITE;
 constexpr int kDefaultFontSizeDip = 64;
 constexpr int kDetailsFontSizeDip = 13;
 
@@ -74,7 +73,8 @@
 
   details_label_ = AddChildView(std::make_unique<views::Label>());
   details_label_->SetAutoColorReadabilityEnabled(false);
-  details_label_->SetEnabledColor(kTextColor);
+  details_label_->SetEnabledColor(ambient::util::GetContentLayerColor(
+      AshColorProvider::ContentLayerType::kTextColorSecondary));
   details_label_->SetFontList(
       ambient::util::GetDefaultFontlist().DeriveWithSizeDelta(
           kDetailsFontSizeDip - kDefaultFontSizeDip));
diff --git a/ash/ambient/ui/glanceable_info_view.cc b/ash/ambient/ui/glanceable_info_view.cc
index 52bfce2..50fb2c15 100644
--- a/ash/ambient/ui/glanceable_info_view.cc
+++ b/ash/ambient/ui/glanceable_info_view.cc
@@ -43,7 +43,6 @@
 constexpr int kWeatherIconSizeDip = 32;
 
 // Typography.
-constexpr SkColor kTextColor = SK_ColorWHITE;
 constexpr int kDefaultFontSizeDip = 64;
 constexpr int kWeatherTemperatureFontSizeDip = 32;
 
@@ -141,8 +140,10 @@
       ash::tray::TimeView::ClockLayout::HORIZONTAL_CLOCK,
       Shell::Get()->system_tray_model()->clock()));
   time_view_->SetTextFont(GetTimeFontList());
-  time_view_->SetTextColor(kTextColor,
-                           /*auto_color_readability_enabled=*/false);
+  time_view_->SetTextColor(
+      ambient::util::GetContentLayerColor(
+          AshColorProvider::ContentLayerType::kTextColorPrimary),
+      /*auto_color_readability_enabled=*/false);
   time_view_->SetTextShadowValues(text_shadow_values);
   // Remove the internal spacing in `time_view_` and adjust spacing for shadows.
   time_view_->SetBorder(views::CreateEmptyBorder(
@@ -163,7 +164,8 @@
   // Inits the temp view.
   temperature_ = AddChildView(std::make_unique<views::Label>());
   temperature_->SetAutoColorReadabilityEnabled(false);
-  temperature_->SetEnabledColor(kTextColor);
+  temperature_->SetEnabledColor(ambient::util::GetContentLayerColor(
+      AshColorProvider::ContentLayerType::kTextColorPrimary));
   temperature_->SetFontList(GetWeatherTemperatureFontList());
   temperature_->SetShadows(text_shadow_values);
   temperature_->SetBorder(views::CreateEmptyBorder(
diff --git a/ash/ambient/ui/media_string_view.cc b/ash/ambient/ui/media_string_view.cc
index 3e35d390..f64e816 100644
--- a/ash/ambient/ui/media_string_view.cc
+++ b/ash/ambient/ui/media_string_view.cc
@@ -207,8 +207,10 @@
   icon_ = AddChildView(std::make_unique<views::ImageView>());
   icon_->SetPreferredSize(
       gfx::Size(kMusicNoteIconSizeDip, kMusicNoteIconSizeDip));
-  icon_->SetImage(gfx::CreateVectorIcon(kMusicNoteIcon, kMusicNoteIconSizeDip,
-                                        SK_ColorWHITE));
+  icon_->SetImage(gfx::CreateVectorIcon(
+      kMusicNoteIcon, kMusicNoteIconSizeDip,
+      ambient::util::GetContentLayerColor(
+          AshColorProvider::ContentLayerType::kIconColorPrimary)));
 
   media_text_container_ = AddChildView(std::make_unique<views::View>());
   media_text_container_->SetPaintToLayer();
@@ -227,13 +229,13 @@
   media_text_->layer()->SetFillsBoundsOpaquely(false);
 
   // Defines the appearance.
-  constexpr SkColor kTextColor = SK_ColorWHITE;
   constexpr int kDefaultFontSizeDip = 64;
   constexpr int kMediaStringFontSizeDip = 18;
   media_text_->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_TO_HEAD);
   media_text_->SetVerticalAlignment(gfx::VerticalAlignment::ALIGN_MIDDLE);
   media_text_->SetAutoColorReadabilityEnabled(false);
-  media_text_->SetEnabledColor(kTextColor);
+  media_text_->SetEnabledColor(ambient::util::GetContentLayerColor(
+      AshColorProvider::ContentLayerType::kTextColorPrimary));
   media_text_->SetFontList(
       ambient::util::GetDefaultFontlist()
           .DeriveWithSizeDelta(kMediaStringFontSizeDip - kDefaultFontSizeDip)
diff --git a/ash/ambient/util/ambient_util.cc b/ash/ambient/util/ambient_util.cc
index e623c46..5fd24552 100644
--- a/ash/ambient/util/ambient_util.cc
+++ b/ash/ambient/util/ambient_util.cc
@@ -7,6 +7,7 @@
 #include "ash/constants/ash_features.h"
 #include "ash/public/cpp/ambient/ambient_backend_controller.h"
 #include "ash/public/cpp/ambient/ambient_client.h"
+#include "ash/style/ash_color_provider.h"
 #include "base/no_destructor.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/gfx/color_palette.h"
@@ -24,6 +25,25 @@
   return LockScreen::HasInstance() && LockScreen::Get()->screen_type() == type;
 }
 
+SkColor GetContentLayerColor(
+    AshColorProvider::ContentLayerType content_layer_type) {
+  auto* ash_color_provider = AshColorProvider::Get();
+
+  switch (content_layer_type) {
+    case AshColorProvider::ContentLayerType::kTextColorPrimary:
+    case AshColorProvider::ContentLayerType::kTextColorSecondary:
+    case AshColorProvider::ContentLayerType::kIconColorPrimary:
+    case AshColorProvider::ContentLayerType::kIconColorSecondary:
+      return ash_color_provider->IsDarkModeEnabled()
+                 ? ash_color_provider->GetContentLayerColor(content_layer_type)
+                 : SK_ColorWHITE;
+    default:
+      NOTREACHED() << "Unsupported content layer type";
+      // Return a very bright color so it's obvious there is a mistake.
+      return gfx::kPlaceholderColor;
+  }
+}
+
 const gfx::FontList& GetDefaultFontlist() {
   static const base::NoDestructor<gfx::FontList> font_list("Google Sans, 64px");
   return *font_list;
diff --git a/ash/ambient/util/ambient_util.h b/ash/ambient/util/ambient_util.h
index fcb525bd..5f37332 100644
--- a/ash/ambient/util/ambient_util.h
+++ b/ash/ambient/util/ambient_util.h
@@ -8,6 +8,7 @@
 #include "ash/ash_export.h"
 #include "ash/login/ui/lock_screen.h"
 #include "ash/public/cpp/ambient/ambient_backend_controller.h"
+#include "ash/style/ash_color_provider.h"
 #include "ui/gfx/font_list.h"
 #include "ui/gfx/shadow_value.h"
 
@@ -19,6 +20,12 @@
 // Returns true if Ash is showing lock screen.
 ASH_EXPORT bool IsShowing(LockScreen::ScreenType type);
 
+// Ambient mode uses non-standard colors for some text and the media icon, so
+// provides a wrapper for |AshColorProvider::GetContentLayerColor|. This is
+// currently only supported for primary and secondary text and icons.
+ASH_EXPORT SkColor
+GetContentLayerColor(AshColorProvider::ContentLayerType content_layer_type);
+
 // Returns the default fontlist for Ambient Mode.
 ASH_EXPORT const gfx::FontList& GetDefaultFontlist();
 
diff --git a/ash/app_list/model/search/search_result.h b/ash/app_list/model/search/search_result.h
index f3a98ee0..7d3d754 100644
--- a/ash/app_list/model/search/search_result.h
+++ b/ash/app_list/model/search/search_result.h
@@ -39,6 +39,7 @@
   using Action = ash::SearchResultAction;
   using Actions = ash::SearchResultActions;
   using DisplayIndex = ash::SearchResultDisplayIndex;
+  using OmniboxType = ash::SearchResultOmniboxType;
 
   SearchResult();
   virtual ~SearchResult();
@@ -118,6 +119,11 @@
     metadata_->display_index = display_index;
   }
 
+  OmniboxType omnibox_type() const { return metadata_->omnibox_type; }
+  void set_omnibox_type(OmniboxType omnibox_type) {
+    metadata_->omnibox_type = omnibox_type;
+  }
+
   float position_priority() const { return metadata_->position_priority; }
   void set_position_priority(float position_priority) {
     metadata_->position_priority = position_priority;
diff --git a/ash/app_list/views/search_result_view.cc b/ash/app_list/views/search_result_view.cc
index b6d54bd0..37766ca 100644
--- a/ash/app_list/views/search_result_view.cc
+++ b/ash/app_list/views/search_result_view.cc
@@ -54,6 +54,27 @@
 // Delta applied to font size of all AppListSearchResult titles.
 constexpr int kSearchResultTitleTextSizeDelta = 2;
 
+// Corner radius for downloaded image icons.
+constexpr int kImageIconCornerRadius = 4;
+
+class RoundedCornerImageView : public views::ImageView {
+ public:
+  RoundedCornerImageView() = default;
+
+  RoundedCornerImageView(const RoundedCornerImageView&) = delete;
+  RoundedCornerImageView& operator=(const RoundedCornerImageView&) = delete;
+
+ protected:
+  // views::ImageView:
+  void OnPaint(gfx::Canvas* canvas) override {
+    SkPath mask;
+    mask.addRoundRect(gfx::RectToSkRect(GetImageBounds()),
+                      kImageIconCornerRadius, kImageIconCornerRadius);
+    canvas->ClipPath(mask, true);
+    ImageView::OnPaint(canvas);
+  }
+};
+
 }  // namespace
 
 // static
@@ -67,15 +88,14 @@
                                   base::Unretained(this)));
 
   icon_ = AddChildView(std::make_unique<views::ImageView>());
-  display_icon_ = AddChildView(std::make_unique<views::ImageView>());
+  image_icon_ = AddChildView(std::make_unique<RoundedCornerImageView>());
   badge_icon_ = AddChildView(std::make_unique<views::ImageView>());
   auto* actions_view =
       AddChildView(std::make_unique<SearchResultActionsView>(this));
   set_actions_view(actions_view);
 
   icon_->SetCanProcessEventsWithinSubtree(false);
-  display_icon_->SetCanProcessEventsWithinSubtree(false);
-  SetDisplayIcon(gfx::ImageSkia());
+  image_icon_->SetCanProcessEventsWithinSubtree(false);
   badge_icon_->SetCanProcessEventsWithinSubtree(false);
 
   set_context_menu_controller(this);
@@ -189,12 +209,18 @@
 
   gfx::Rect icon_bounds(rect);
 
-  const bool has_display_icon = !display_icon_->GetImage().isNull();
-  views::ImageView* icon = has_display_icon ? display_icon_ : icon_;
-  const int left_right_padding =
+  const bool use_image_icon =
+      IsRichImage() && !image_icon_->GetImage().isNull();
+  views::ImageView* icon = use_image_icon ? image_icon_ : icon_;
+  int left_right_padding =
       (kPreferredIconViewWidth - icon->GetImage().width()) / 2;
-  const int top_bottom_padding =
-      (rect.height() - icon->GetImage().height()) / 2;
+  int top_bottom_padding = (rect.height() - icon->GetImage().height()) / 2;
+  if (IsAnswer()) {
+    // Answer icon circles are slightly cropped if we use exact padding, so give
+    // them an extra unit of leeway.
+    left_right_padding -= 1;
+    top_bottom_padding -= 1;
+  }
   icon_bounds.set_width(kPreferredIconViewWidth);
   icon_bounds.Inset(left_right_padding, top_bottom_padding);
   icon_bounds.Intersect(rect);
@@ -376,10 +402,36 @@
   // looks nicer to keep the stale icon for a little while on screen instead of
   // clearing it out. It should work correctly as long as the SearchResult does
   // not forget to SetIcon when it's ready.
-  const gfx::ImageSkia icon(result() ? result()->icon() : gfx::ImageSkia());
-  if (!icon.isNull())
-    SetIconImage(icon, icon_,
-                 AppListConfig::instance().search_list_icon_dimension());
+  if (result() && !result()->icon().isNull()) {
+    if (IsRichImage()) {
+      gfx::ImageSkia image = result()->icon();
+
+      // Images could be rectangular, and we should preserve the aspect ratio.
+      const int dimension =
+          AppListConfig::instance().search_list_image_icon_dimension();
+      int width = image.width();
+      int height = image.height();
+      if (width != height) {
+        const int max = std::max(width, height);
+        width = dimension * width / max;
+        height = dimension * height / max;
+        SetIconImage(image, image_icon_, gfx::Size(width, height));
+      } else {
+        SetIconImage(image, image_icon_, gfx::Size(dimension, dimension));
+      }
+
+      icon_->SetVisible(false);
+      image_icon_->SetVisible(true);
+    } else {
+      const int dimension =
+          IsAnswer()
+              ? AppListConfig::instance().search_list_answer_icon_dimension()
+              : AppListConfig::instance().search_list_icon_dimension();
+      SetIconImage(result()->icon(), icon_, gfx::Size(dimension, dimension));
+      icon_->SetVisible(true);
+      image_icon_->SetVisible(false);
+    }
+  }
 
   // Updates |badge_icon_|.
   const gfx::ImageSkia badge_icon(result() ? result()->badge_icon()
@@ -387,8 +439,9 @@
   if (badge_icon.isNull()) {
     badge_icon_->SetVisible(false);
   } else {
-    SetIconImage(badge_icon, badge_icon_,
-                 AppListConfig::instance().search_list_badge_icon_dimension());
+    const int dimension =
+        AppListConfig::instance().search_list_badge_icon_dimension();
+    SetIconImage(badge_icon, badge_icon_, gfx::Size(dimension, dimension));
     badge_icon_->SetVisible(true);
   }
 
@@ -404,12 +457,12 @@
 
 void SearchResultView::SetIconImage(const gfx::ImageSkia& source,
                                     views::ImageView* const icon,
-                                    const int icon_dimension) {
+                                    const gfx::Size& size) {
   gfx::ImageSkia image(source);
   image = gfx::ImageSkiaOperations::CreateResizedImage(
-      source, skia::ImageOperations::RESIZE_BEST,
-      gfx::Size(icon_dimension, icon_dimension));
+      source, skia::ImageOperations::RESIZE_BEST, size);
   icon->SetImage(image);
+  icon->SetImageSize(size);
 }
 
 void SearchResultView::OnSearchResultActionActivated(size_t index) {
@@ -490,10 +543,14 @@
   source->RequestFocus();
 }
 
-void SearchResultView::SetDisplayIcon(const gfx::ImageSkia& source) {
-  display_icon_->SetImage(source);
-  display_icon_->SetVisible(!source.isNull());
-  icon_->SetVisible(source.isNull());
+bool SearchResultView::IsAnswer() const {
+  return app_list_features::IsOmniboxRichEntitiesEnabled() && result() &&
+         result()->omnibox_type() == SearchResultOmniboxType::kAnswer;
+}
+
+bool SearchResultView::IsRichImage() const {
+  return app_list_features::IsOmniboxRichEntitiesEnabled() && result() &&
+         result()->omnibox_type() == SearchResultOmniboxType::kRichImage;
 }
 
 }  // namespace ash
diff --git a/ash/app_list/views/search_result_view.h b/ash/app_list/views/search_result_view.h
index 84a01c7..686ba67 100644
--- a/ash/app_list/views/search_result_view.h
+++ b/ash/app_list/views/search_result_view.h
@@ -54,8 +54,6 @@
   // Sets/gets SearchResult displayed by this view.
   void OnResultChanged() override;
 
-  void SetDisplayIcon(const gfx::ImageSkia& source);
-
  private:
   friend class test::SearchResultListViewTest;
 
@@ -101,7 +99,7 @@
 
   void SetIconImage(const gfx::ImageSkia& source,
                     views::ImageView* const icon,
-                    const int icon_dimension);
+                    const gfx::Size& size);
 
   // SearchResultActionsViewDelegate overrides:
   void OnSearchResultActionActivated(size_t index) override;
@@ -110,15 +108,19 @@
   // Invoked when the context menu closes.
   void OnMenuClosed();
 
+  // Whether this result is one of the rich entity types.
+  bool IsAnswer() const;
+  bool IsRichImage() const;
+
   // Parent list view. Owned by views hierarchy.
   SearchResultListView* list_view_;
 
   AppListViewDelegate* view_delegate_;
 
   views::ImageView* icon_;  // Owned by views hierarchy.
-  // If a |display_icon_| is set, we will show |display_icon_|, not |icon_|.
-  views::ImageView* display_icon_;  // Owned by views hierarchy.
-  views::ImageView* badge_icon_;    // Owned by views hierarchy.
+  // Rich image results will show |image_icon_| instead of |icon_|.
+  views::ImageView* image_icon_;  // Owned by views hierarchy.
+  views::ImageView* badge_icon_;  // Owned by views hierarchy.
   std::unique_ptr<gfx::RenderText> title_text_;
   std::unique_ptr<gfx::RenderText> details_text_;
 
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 49ef314..c1eecdd 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -831,6 +831,9 @@
       <message name="IDS_ASH_SWITCH_ACCESS_INCREMENT" desc="The label for the Switch Access menu option to increment the selected input.">
         Increment
       </message>
+      <message name="IDS_ASH_SWITCH_ACCESS_ITEM_SCAN" desc="The label for the Switch Access menu option to begin scanning through items onscreen.">
+        Item Scan
+      </message>
       <message name="IDS_ASH_SWITCH_ACCESS_JUMP_TO_BEGINNING_OF_TEXT" desc="The label for the Switch Access menu option to move the cursor to the beginning of the text.">
         Beginning
       </message>
diff --git a/ash/ash_strings_grd/IDS_ASH_SWITCH_ACCESS_ITEM_SCAN.png.sha1 b/ash/ash_strings_grd/IDS_ASH_SWITCH_ACCESS_ITEM_SCAN.png.sha1
new file mode 100644
index 0000000..452a0331
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_SWITCH_ACCESS_ITEM_SCAN.png.sha1
@@ -0,0 +1 @@
+c244e5884bae426e9c19ef78aa17b0557bd1cfa0
\ No newline at end of file
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 6883d12e..4c4168381 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -421,10 +421,6 @@
 const base::Feature kImeMojoDecoder{"ImeMojoDecoder",
                                     base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Enable or disable the new improved keyboard shortcuts.
-const base::Feature kImprovedKeyboardShortcuts{
-    "ImprovedKeyboardShortcuts", base::FEATURE_DISABLED_BY_DEFAULT};
-
 // Enable or disable system emoji picker.
 const base::Feature kImeSystemEmojiPicker{"SystemEmojiPicker",
                                           base::FEATURE_DISABLED_BY_DEFAULT};
@@ -835,10 +831,6 @@
   return base::FeatureList::IsEnabled(kClipboardHistoryContextMenuNudge);
 }
 
-bool IsImprovedKeyboardShortcutsEnabled() {
-  return base::FeatureList::IsEnabled(kImprovedKeyboardShortcuts);
-}
-
 bool IsPciguardUiEnabled() {
   return base::FeatureList::IsEnabled(kEnablePciguardUi);
 }
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index f34f88a..c89aff97f 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -196,8 +196,6 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kImeSystemEmojiPicker;
 COMPONENT_EXPORT(ASH_CONSTANTS)
-extern const base::Feature kImprovedKeyboardShortcuts;
-COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kVirtualKeyboardFloatingDefault;
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kInstantTethering;
diff --git a/ash/display/cursor_window_controller.cc b/ash/display/cursor_window_controller.cc
index fb61bef4..fa3ea7c 100644
--- a/ash/display/cursor_window_controller.cc
+++ b/ash/display/cursor_window_controller.cc
@@ -19,11 +19,11 @@
 #include "ash/root_window_controller.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
-#include "ash/window_factory.h"
 #include "base/command_line.h"
 #include "base/metrics/histogram_macros.h"
 #include "components/prefs/pref_service.h"
 #include "ui/aura/env.h"
+#include "ui/aura/window.h"
 #include "ui/aura/window_delegate.h"
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/base/cursor/cursors_aura.h"
@@ -295,7 +295,7 @@
   } else {
     // Reusing the window does not work when the display is disconnected.
     // Just creates a new one instead. crbug.com/384218.
-    cursor_window_ = window_factory::NewWindow(delegate_.get());
+    cursor_window_ = std::make_unique<aura::Window>(delegate_.get());
     cursor_window_->SetTransparent(true);
     cursor_window_->Init(ui::LAYER_TEXTURED);
     cursor_window_->SetEventTargetingPolicy(aura::EventTargetingPolicy::kNone);
diff --git a/ash/display/mirror_window_controller.cc b/ash/display/mirror_window_controller.cc
index 52764de7..0fb6998 100644
--- a/ash/display/mirror_window_controller.cc
+++ b/ash/display/mirror_window_controller.cc
@@ -15,11 +15,11 @@
 #include "ash/host/root_window_transformer.h"
 #include "ash/root_window_settings.h"
 #include "ash/shell.h"
-#include "ash/window_factory.h"
 #include "base/strings/stringprintf.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/viz/common/surfaces/surface_id.h"
 #include "ui/aura/client/capture_client.h"
+#include "ui/aura/window.h"
 #include "ui/aura/window_delegate.h"
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/aura/window_tree_host.h"
@@ -225,7 +225,7 @@
       host->Show();
 
       aura::Window* mirror_window = host_info->mirror_window =
-          window_factory::NewWindow().release();
+          new aura::Window(nullptr);
       mirror_window->Init(ui::LAYER_SOLID_COLOR);
       host->window()->AddChild(mirror_window);
       host_info->ash_host->SetRootWindowTransformer(std::move(transformer));
diff --git a/ash/display/screen_orientation_controller_unittest.cc b/ash/display/screen_orientation_controller_unittest.cc
index 1b3e18d..c29e5f4 100644
--- a/ash/display/screen_orientation_controller_unittest.cc
+++ b/ash/display/screen_orientation_controller_unittest.cc
@@ -19,7 +19,6 @@
 #include "ash/test/ash_test_base.h"
 #include "ash/test/ash_test_helper.h"
 #include "ash/test_shell_delegate.h"
-#include "ash/window_factory.h"
 #include "ash/wm/splitview/split_view_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h"
@@ -108,7 +107,7 @@
 
 // Creates a window of type WINDOW_TYPE_CONTROL.
 std::unique_ptr<aura::Window> CreateControlWindow() {
-  std::unique_ptr<aura::Window> window = window_factory::NewWindow(
+  std::unique_ptr<aura::Window> window = std::make_unique<aura::Window>(
       nullptr, aura::client::WindowType::WINDOW_TYPE_CONTROL);
   window->Init(ui::LAYER_NOT_DRAWN);
   window->set_owned_by_parent(false);
diff --git a/ash/display/screen_position_controller_unittest.cc b/ash/display/screen_position_controller_unittest.cc
index 11bac47..4c045a8d 100644
--- a/ash/display/screen_position_controller_unittest.cc
+++ b/ash/display/screen_position_controller_unittest.cc
@@ -11,10 +11,10 @@
 #include "ash/screen_util.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
-#include "ash/window_factory.h"
 #include "base/run_loop.h"
 #include "ui/aura/env.h"
 #include "ui/aura/test/test_window_delegate.h"
+#include "ui/aura/window.h"
 #include "ui/aura/window_tracker.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/layout.h"
@@ -40,7 +40,7 @@
 
   void SetUp() override {
     AshTestBase::SetUp();
-    window_ = window_factory::NewWindow(&window_delegate_);
+    window_ = std::make_unique<aura::Window>(&window_delegate_);
     window_->SetType(aura::client::WINDOW_TYPE_NORMAL);
     window_->Init(ui::LAYER_NOT_DRAWN);
     ParentWindowInPrimaryRootWindow(window_.get());
diff --git a/ash/drag_drop/drag_drop_tracker.cc b/ash/drag_drop/drag_drop_tracker.cc
index d8f2e7e..9eb7e77 100644
--- a/ash/drag_drop/drag_drop_tracker.cc
+++ b/ash/drag_drop/drag_drop_tracker.cc
@@ -6,7 +6,6 @@
 
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/shell.h"
-#include "ash/window_factory.h"
 #include "ash/wm/window_util.h"
 #include "ui/aura/client/window_parenting_client.h"
 #include "ui/aura/window.h"
@@ -39,7 +38,8 @@
   static CaptureWindowActivationDelegate* activation_delegate_instance = NULL;
   if (!activation_delegate_instance)
     activation_delegate_instance = new CaptureWindowActivationDelegate;
-  std::unique_ptr<aura::Window> window = window_factory::NewWindow(delegate);
+  std::unique_ptr<aura::Window> window =
+      std::make_unique<aura::Window>(delegate);
   // Set type of window as popup to prevent different window manager codes
   // trying to manage this window.
   window->SetType(aura::client::WINDOW_TYPE_POPUP);
diff --git a/ash/extended_desktop_unittest.cc b/ash/extended_desktop_unittest.cc
index d0c0a96c..b9c6a42 100644
--- a/ash/extended_desktop_unittest.cc
+++ b/ash/extended_desktop_unittest.cc
@@ -11,7 +11,6 @@
 #include "ash/test/ash_test_base.h"
 #include "ash/test/test_widget_builder.h"
 #include "ash/test/test_window_builder.h"
-#include "ash/window_factory.h"
 #include "ash/wm/desks/desks_util.h"
 #include "ash/wm/window_properties.h"
 #include "ash/wm/window_util.h"
@@ -649,7 +648,7 @@
   wm::ActivateWindow(window);
   // Create a transient child window of |window| without parenting to |window|
   // yet.
-  std::unique_ptr<aura::Window> child = window_factory::NewWindow();
+  std::unique_ptr<aura::Window> child = std::make_unique<aura::Window>(nullptr);
   child->SetType(aura::client::WINDOW_TYPE_NORMAL);
   child->Init(ui::LAYER_TEXTURED);
   child->SetBounds(gfx::Rect(50, 50, 50, 50));
diff --git a/ash/host/DEPS b/ash/host/DEPS
index 9ad4d15..964f734 100644
--- a/ash/host/DEPS
+++ b/ash/host/DEPS
@@ -1,25 +1,11 @@
 include_rules = [
   "-ash",
   "+ash/host",
-  "+ash/ime",
   "+ash/ash_export.h",
   "+ash/public/cpp/ash_switches.h",
-  "+ash/window_factory.h",
-  "+ash/ws",
 ]
 
 specific_include_rules = {
-  "ash_window_tree_host_mus.cc": [
-    "+ash/shell.h",
-    "+ash/shell_delegate.h",
-  ],
-  "ash_window_tree_host_platform.cc": [
-    "+ash/shell.h",
-    "+ash/shell_delegate.h",
-    # AshWindowTreeHostPlatform is only used in classic ash, so it can use
-    # ozone.
-    "+ui/events/ozone/chromeos/cursor_controller.h",
-  ],
   "ash_window_tree_host_platform_unittest.cc" : [
     "+ash/test/ash_test_base.h",
   ]
diff --git a/ash/host/ash_window_tree_host_platform.cc b/ash/host/ash_window_tree_host_platform.cc
index 102f3d63..e899fa12 100644
--- a/ash/host/ash_window_tree_host_platform.cc
+++ b/ash/host/ash_window_tree_host_platform.cc
@@ -8,9 +8,6 @@
 
 #include "ash/host/root_window_transformer.h"
 #include "ash/host/transformer_helper.h"
-#include "ash/shell.h"
-#include "ash/shell_delegate.h"
-#include "ash/window_factory.h"
 #include "base/feature_list.h"
 #include "base/trace_event/trace_event.h"
 #include "ui/aura/null_window_targeter.h"
@@ -49,7 +46,7 @@
 AshWindowTreeHostPlatform::AshWindowTreeHostPlatform(
     ui::PlatformWindowInitProperties properties)
     : aura::WindowTreeHostPlatform(std::move(properties),
-                                   window_factory::NewWindow()),
+                                   std::make_unique<aura::Window>(nullptr)),
       transformer_helper_(this),
       input_controller_(
           ui::OzonePlatform::GetInstance()->GetInputController()) {
@@ -57,7 +54,7 @@
 }
 
 AshWindowTreeHostPlatform::AshWindowTreeHostPlatform()
-    : aura::WindowTreeHostPlatform(window_factory::NewWindow()),
+    : aura::WindowTreeHostPlatform(std::make_unique<aura::Window>(nullptr)),
       transformer_helper_(this) {
   CreateCompositor(viz::FrameSinkId(),
                    /* force_software_compositor */ false,
diff --git a/ash/keyboard/test_keyboard_ui.cc b/ash/keyboard/test_keyboard_ui.cc
index b6be41f..b72ce33 100644
--- a/ash/keyboard/test_keyboard_ui.cc
+++ b/ash/keyboard/test_keyboard_ui.cc
@@ -6,7 +6,6 @@
 
 #include "ash/keyboard/ui/test/keyboard_test_util.h"
 #include "ash/shell.h"
-#include "ash/window_factory.h"
 #include "ash/wm/window_util.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "ui/aura/window.h"
@@ -21,7 +20,7 @@
 
 aura::Window* TestKeyboardUI::LoadKeyboardWindow(LoadCallback callback) {
   DCHECK(!keyboard_window_);
-  keyboard_window_ = window_factory::NewWindow(&delegate_);
+  keyboard_window_ = std::make_unique<aura::Window>(&delegate_);
   keyboard_window_->Init(ui::LAYER_NOT_DRAWN);
 
   // Set a default size for the keyboard.
diff --git a/ash/metrics/desktop_task_switch_metric_recorder_unittest.cc b/ash/metrics/desktop_task_switch_metric_recorder_unittest.cc
index d5e7d7e..dc60d12 100644
--- a/ash/metrics/desktop_task_switch_metric_recorder_unittest.cc
+++ b/ash/metrics/desktop_task_switch_metric_recorder_unittest.cc
@@ -8,7 +8,6 @@
 
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
-#include "ash/window_factory.h"
 #include "base/test/metrics/user_action_tester.h"
 #include "ui/aura/client/window_types.h"
 #include "ui/aura/test/test_window_delegate.h"
@@ -102,7 +101,7 @@
 
 std::unique_ptr<aura::Window>
 DesktopTaskSwitchMetricRecorderTest::CreatePositionableWindow() const {
-  std::unique_ptr<aura::Window> window = window_factory::NewWindow(
+  std::unique_ptr<aura::Window> window = std::make_unique<aura::Window>(
       aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate());
   window->SetType(aura::client::WINDOW_TYPE_NORMAL);
   window->Init(ui::LAYER_NOT_DRAWN);
@@ -111,7 +110,7 @@
 
 std::unique_ptr<aura::Window>
 DesktopTaskSwitchMetricRecorderTest::CreateNonPositionableWindow() const {
-  std::unique_ptr<aura::Window> window = window_factory::NewWindow(
+  std::unique_ptr<aura::Window> window = std::make_unique<aura::Window>(
       aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate());
   window->SetType(aura::client::WINDOW_TYPE_UNKNOWN);
   window->Init(ui::LAYER_NOT_DRAWN);
diff --git a/ash/metrics/user_metrics_recorder.cc b/ash/metrics/user_metrics_recorder.cc
index 2fa09b5..fcff3d8a 100644
--- a/ash/metrics/user_metrics_recorder.cc
+++ b/ash/metrics/user_metrics_recorder.cc
@@ -12,6 +12,7 @@
 #include "ash/metrics/desktop_task_switch_metric_recorder.h"
 #include "ash/metrics/pointer_metrics_recorder.h"
 #include "ash/public/cpp/accessibility_controller_enums.h"
+#include "ash/public/cpp/ash_pref_names.h"
 #include "ash/public/cpp/shelf_item.h"
 #include "ash/public/cpp/shelf_model.h"
 #include "ash/public/cpp/shell_window_ids.h"
@@ -23,6 +24,7 @@
 #include "ash/wm/window_state.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
+#include "components/prefs/pref_service.h"
 #include "ui/aura/window.h"
 
 namespace ash {
@@ -479,6 +481,11 @@
     RecordShelfItemCounts();
     UMA_HISTOGRAM_COUNTS_100("Ash.NumberOfVisibleWindowsInPrimaryDisplay",
                              GetNumVisibleWindowsInPrimaryDisplay());
+
+    base::UmaHistogramBoolean(
+        "Ash.AppNotificationBadgingPref",
+        Shell::Get()->session_controller()->GetActivePrefService()->GetBoolean(
+            prefs::kAppNotificationBadgingEnabled));
   }
 
   // TODO(bruthig): Find out if this should only be logged when the user is
diff --git a/ash/metrics/user_metrics_recorder_unittest.cc b/ash/metrics/user_metrics_recorder_unittest.cc
index 12f4a2a4..284988a 100644
--- a/ash/metrics/user_metrics_recorder_unittest.cc
+++ b/ash/metrics/user_metrics_recorder_unittest.cc
@@ -33,6 +33,8 @@
 const char kAsh_Shelf_NumberOfUnpinnedItems[] =
     "Ash.Shelf.NumberOfUnpinnedItems";
 
+const char kAsh_NotificationBadgeShownPref[] = "Ash.AppNotificationBadgingPref";
+
 }  // namespace
 
 // Test fixture for the UserMetricsRecorder class. The tests manage their own
@@ -102,6 +104,7 @@
   histograms().ExpectTotalCount(kAsh_Shelf_NumberOfItems, 0);
   histograms().ExpectTotalCount(kAsh_Shelf_NumberOfPinnedItems, 0);
   histograms().ExpectTotalCount(kAsh_Shelf_NumberOfUnpinnedItems, 0);
+  histograms().ExpectTotalCount(kAsh_NotificationBadgeShownPref, 0);
 }
 
 // Verifies that the IsUserInActiveDesktopEnvironment() dependent stats are
@@ -116,6 +119,7 @@
   histograms().ExpectTotalCount(kAsh_Shelf_NumberOfItems, 1);
   histograms().ExpectTotalCount(kAsh_Shelf_NumberOfPinnedItems, 1);
   histograms().ExpectTotalCount(kAsh_Shelf_NumberOfUnpinnedItems, 1);
+  histograms().ExpectTotalCount(kAsh_NotificationBadgeShownPref, 1);
 }
 
 // Verifies recording of stats which are always recorded by
diff --git a/ash/public/cpp/app_list/app_list_config.cc b/ash/public/cpp/app_list/app_list_config.cc
index 34cfaad..25b8e675 100644
--- a/ash/public/cpp/app_list/app_list_config.cc
+++ b/ash/public/cpp/app_list/app_list_config.cc
@@ -272,6 +272,8 @@
       search_tile_badge_icon_dimension_(22),
       search_tile_badge_icon_offset_(5),
       search_list_icon_dimension_(20),
+      search_list_answer_icon_dimension_(24),
+      search_list_image_icon_dimension_(32),
       search_list_icon_vertical_bar_dimension_(48),
       search_list_badge_icon_dimension_(14),
       suggestion_chip_icon_dimension_(20),
@@ -389,6 +391,10 @@
       search_tile_badge_icon_offset_(
           base_config.search_tile_badge_icon_offset_),
       search_list_icon_dimension_(base_config.search_list_icon_dimension_),
+      search_list_answer_icon_dimension_(
+          base_config.search_list_answer_icon_dimension_),
+      search_list_image_icon_dimension_(
+          base_config.search_list_image_icon_dimension_),
       search_list_icon_vertical_bar_dimension_(
           base_config.search_list_icon_vertical_bar_dimension_),
       search_list_badge_icon_dimension_(
diff --git a/ash/public/cpp/app_list/app_list_config.h b/ash/public/cpp/app_list/app_list_config.h
index 666ff46..a8953d7 100644
--- a/ash/public/cpp/app_list/app_list_config.h
+++ b/ash/public/cpp/app_list/app_list_config.h
@@ -78,6 +78,12 @@
     return search_tile_badge_icon_offset_;
   }
   int search_list_icon_dimension() const { return search_list_icon_dimension_; }
+  int search_list_answer_icon_dimension() const {
+    return search_list_answer_icon_dimension_;
+  }
+  int search_list_image_icon_dimension() const {
+    return search_list_image_icon_dimension_;
+  }
   int search_list_icon_vertical_bar_dimension() const {
     return search_list_icon_vertical_bar_dimension_;
   }
@@ -213,6 +219,16 @@
     return gfx::Size(search_list_icon_dimension_, search_list_icon_dimension_);
   }
 
+  gfx::Size search_list_answer_icon_size() const {
+    return gfx::Size(search_list_answer_icon_dimension_,
+                     search_list_answer_icon_dimension_);
+  }
+
+  gfx::Size search_list_image_icon_size() const {
+    return gfx::Size(search_list_image_icon_dimension_,
+                     search_list_image_icon_dimension_);
+  }
+
   gfx::Size search_list_badge_icon_size() const {
     return gfx::Size(search_list_badge_icon_dimension_,
                      search_list_badge_icon_dimension_);
@@ -319,6 +335,12 @@
   // The icon dimension of list views in search result page view.
   const int search_list_icon_dimension_;
 
+  // The icon dimension of answer list views in search result page view.
+  const int search_list_answer_icon_dimension_;
+
+  // The dimension of image icons for list views in search result page view.
+  const int search_list_image_icon_dimension_;
+
   // The vertical bar icon dimension of list views in search result page view.
   const int search_list_icon_vertical_bar_dimension_;
 
diff --git a/ash/public/cpp/app_list/app_list_types.h b/ash/public/cpp/app_list/app_list_types.h
index f98dbb7..7d1eb6fb 100644
--- a/ash/public/cpp/app_list/app_list_types.h
+++ b/ash/public/cpp/app_list/app_list_types.h
@@ -68,7 +68,7 @@
 
   AppStatus app_status = AppStatus::kReady;  // App status.
 
-  std::string folder_id;   // Id of folder where the item resides.
+  std::string folder_id;           // Id of folder where the item resides.
   syncer::StringOrdinal position;  // Position of the item.
   bool is_folder = false;          // Whether this item is a folder.
   bool is_persistent = false;  // Whether this folder is allowed to contain only
@@ -184,6 +184,14 @@
   kUndefined,
 };
 
+// The rich entity subtype of Omnibox results.
+enum SearchResultOmniboxType {
+  kDefault,
+  kAnswer,
+  kRichImage,
+  kOmniboxTypeMax,  // Do not use.
+};
+
 // Actions for OmniBox zero state suggestion.
 enum OmniBoxZeroStateAction {
   // Removes the zero state suggestion.
@@ -293,6 +301,9 @@
   // Which index in the UI container should the result be placed in.
   SearchResultDisplayIndex display_index = SearchResultDisplayIndex::kUndefined;
 
+  // The rich entity subtype of Omnibox results.
+  SearchResultOmniboxType omnibox_type = SearchResultOmniboxType::kDefault;
+
   // A score to settle conflicts between two apps with the same requested
   // |display_index|.
   float position_priority = 0.0f;
@@ -309,10 +320,6 @@
   // Whether this result is a recommendation.
   bool is_recommendation = false;
 
-  // Whether this result is an answer. Answer results should originate from
-  // base::SuggestionAnswer.
-  bool is_answer = false;
-
   // A query URL associated with this result. The meaning and treatment of the
   // URL (e.g. displaying inline web contents) is dependent on the result type.
   base::Optional<GURL> query_url;
diff --git a/ash/resources/vector_icons/BUILD.gn b/ash/resources/vector_icons/BUILD.gn
index a4296e37..7c5aa75 100644
--- a/ash/resources/vector_icons/BUILD.gn
+++ b/ash/resources/vector_icons/BUILD.gn
@@ -213,6 +213,7 @@
     "switch_access_decrement.icon",
     "switch_access_end_text_selection.icon",
     "switch_access_increment.icon",
+    "switch_access_item_scan.icon",
     "switch_access_jump_to_beginning_of_text.icon",
     "switch_access_jump_to_end_of_text.icon",
     "switch_access_keyboard.icon",
diff --git a/ash/resources/vector_icons/switch_access_item_scan.icon b/ash/resources/vector_icons/switch_access_item_scan.icon
new file mode 100644
index 0000000..66b0836
--- /dev/null
+++ b/ash/resources/vector_icons/switch_access_item_scan.icon
@@ -0,0 +1,10 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 20,
+ROUND_RECT, 5, 5, 4, 4, 0,
+ROUND_RECT, 5, 12, 4, 4, 0,
+ROUND_RECT, 3.5, 3.5, 7, 7, 0,
+ROUND_RECT, 12, 5, 4, 4, 0,
+ROUND_RECT, 12, 12, 4, 4, 0
diff --git a/ash/root_window_controller.cc b/ash/root_window_controller.cc
index 93031bf..fc1f219 100644
--- a/ash/root_window_controller.cc
+++ b/ash/root_window_controller.cc
@@ -49,7 +49,6 @@
 #include "ash/touch/touch_hud_projection.h"
 #include "ash/touch/touch_observer_hud.h"
 #include "ash/wallpaper/wallpaper_widget_controller.h"
-#include "ash/window_factory.h"
 #include "ash/wm/always_on_top_controller.h"
 #include "ash/wm/container_finder.h"
 #include "ash/wm/desks/desks_controller.h"
@@ -1232,8 +1231,7 @@
                                                     const char* name,
                                                     aura::Window* parent) {
   aura::Window* window =
-      window_factory::NewWindow(nullptr, aura::client::WINDOW_TYPE_UNKNOWN)
-          .release();
+      new aura::Window(nullptr, aura::client::WINDOW_TYPE_UNKNOWN);
   window->Init(ui::LAYER_NOT_DRAWN);
   window->set_id(window_id);
   window->SetName(name);
diff --git a/ash/root_window_controller_unittest.cc b/ash/root_window_controller_unittest.cc
index 39c59e4d..8bda60b 100644
--- a/ash/root_window_controller_unittest.cc
+++ b/ash/root_window_controller_unittest.cc
@@ -17,7 +17,6 @@
 #include "ash/session/test_session_controller_client.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
-#include "ash/window_factory.h"
 #include "ash/wm/system_modal_container_layout_manager.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ash/wm/window_properties.h"
@@ -649,8 +648,8 @@
 TEST_F(RootWindowControllerTest, DontDeleteWindowsNotOwnedByParent) {
   DestroyedWindowObserver observer1;
   aura::test::TestWindowDelegate delegate1;
-  std::unique_ptr<aura::Window> window1 =
-      window_factory::NewWindow(&delegate1, aura::client::WINDOW_TYPE_CONTROL);
+  std::unique_ptr<aura::Window> window1 = std::make_unique<aura::Window>(
+      &delegate1, aura::client::WINDOW_TYPE_CONTROL);
   window1->set_owned_by_parent(false);
   observer1.SetWindow(window1.get());
   window1->Init(ui::LAYER_NOT_DRAWN);
@@ -658,7 +657,8 @@
       window1.get(), Shell::GetPrimaryRootWindow(), gfx::Rect());
 
   DestroyedWindowObserver observer2;
-  std::unique_ptr<aura::Window> window2 = window_factory::NewWindow();
+  std::unique_ptr<aura::Window> window2 =
+      std::make_unique<aura::Window>(nullptr);
   window2->set_owned_by_parent(false);
   observer2.SetWindow(window2.get());
   window2->Init(ui::LAYER_NOT_DRAWN);
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc
index 01ad3083..d1d189c 100644
--- a/ash/shelf/shelf_layout_manager_unittest.cc
+++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -58,7 +58,6 @@
 #include "ash/system/unified/unified_system_tray.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/wallpaper/wallpaper_controller_impl.h"
-#include "ash/window_factory.h"
 #include "ash/wm/lock_state_controller.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h"
diff --git a/ash/shelf/shelf_window_watcher_unittest.cc b/ash/shelf/shelf_window_watcher_unittest.cc
index 562c98f0..12d05176 100644
--- a/ash/shelf/shelf_window_watcher_unittest.cc
+++ b/ash/shelf/shelf_window_watcher_unittest.cc
@@ -14,7 +14,6 @@
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
-#include "ash/window_factory.h"
 #include "ash/wm/desks/desks_util.h"
 #include "ash/wm/window_resizer.h"
 #include "ash/wm/window_state.h"
@@ -246,7 +245,7 @@
 TEST_F(ShelfWindowWatcherTest, DontCreateShelfEntriesForChildWindows) {
   ShelfModel* model = ShelfModel::Get();
   std::unique_ptr<aura::Window> window =
-      window_factory::NewWindow(nullptr, aura::client::WINDOW_TYPE_NORMAL);
+      std::make_unique<aura::Window>(nullptr, aura::client::WINDOW_TYPE_NORMAL);
   window->Init(ui::LAYER_NOT_DRAWN);
   window->SetProperty(kShelfIDKey, ShelfID("a").Serialize());
   window->SetProperty(kShelfItemTypeKey, static_cast<int32_t>(TYPE_DIALOG));
@@ -257,7 +256,7 @@
   EXPECT_EQ(1, model->item_count());
 
   std::unique_ptr<aura::Window> child =
-      window_factory::NewWindow(nullptr, aura::client::WINDOW_TYPE_NORMAL);
+      std::make_unique<aura::Window>(nullptr, aura::client::WINDOW_TYPE_NORMAL);
   child->Init(ui::LAYER_NOT_DRAWN);
   child->SetProperty(kShelfIDKey, ShelfID("b").Serialize());
   child->SetProperty(kShelfItemTypeKey, static_cast<int32_t>(TYPE_DIALOG));
@@ -275,7 +274,7 @@
 TEST_F(ShelfWindowWatcherTest, CreateShelfEntriesForTransientWindows) {
   ShelfModel* model = ShelfModel::Get();
   std::unique_ptr<aura::Window> window =
-      window_factory::NewWindow(nullptr, aura::client::WINDOW_TYPE_NORMAL);
+      std::make_unique<aura::Window>(nullptr, aura::client::WINDOW_TYPE_NORMAL);
   window->Init(ui::LAYER_NOT_DRAWN);
   window->SetProperty(kShelfIDKey, ShelfID("a").Serialize());
   window->SetProperty(kShelfItemTypeKey, static_cast<int32_t>(TYPE_DIALOG));
@@ -286,7 +285,7 @@
   EXPECT_EQ(1, model->item_count());
 
   std::unique_ptr<aura::Window> transient =
-      window_factory::NewWindow(nullptr, aura::client::WINDOW_TYPE_NORMAL);
+      std::make_unique<aura::Window>(nullptr, aura::client::WINDOW_TYPE_NORMAL);
   transient->Init(ui::LAYER_NOT_DRAWN);
   transient->SetProperty(kShelfIDKey,
                          new std::string(ShelfID("b").Serialize()));
diff --git a/ash/shelf/test/shelf_layout_manager_test_base.cc b/ash/shelf/test/shelf_layout_manager_test_base.cc
index 87c0e117..f1964a1 100644
--- a/ash/shelf/test/shelf_layout_manager_test_base.cc
+++ b/ash/shelf/test/shelf_layout_manager_test_base.cc
@@ -9,7 +9,6 @@
 #include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shelf/shelf_view.h"
 #include "ash/shell.h"
-#include "ash/window_factory.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/workspace_controller.h"
@@ -178,7 +177,7 @@
 }
 
 aura::Window* ShelfLayoutManagerTestBase::CreateTestWindow() {
-  aura::Window* window = window_factory::NewWindow().release();
+  aura::Window* window = new aura::Window(nullptr);
   window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
   window->SetType(aura::client::WINDOW_TYPE_NORMAL);
   window->Init(ui::LAYER_TEXTURED);
@@ -188,7 +187,7 @@
 
 aura::Window* ShelfLayoutManagerTestBase::CreateTestWindowInParent(
     aura::Window* root_window) {
-  aura::Window* window = window_factory::NewWindow().release();
+  aura::Window* window = new aura::Window(nullptr);
   window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
   window->SetType(aura::client::WINDOW_TYPE_NORMAL);
   window->Init(ui::LAYER_TEXTURED);
diff --git a/ash/shell_unittest.cc b/ash/shell_unittest.cc
index d80a956..b896596 100644
--- a/ash/shell_unittest.cc
+++ b/ash/shell_unittest.cc
@@ -32,7 +32,6 @@
 #include "ash/test/test_widget_builder.h"
 #include "ash/test_shell_delegate.h"
 #include "ash/wallpaper/wallpaper_widget_controller.h"
-#include "ash/window_factory.h"
 #include "ash/wm/desks/desks_util.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/window_util.h"
@@ -463,7 +462,8 @@
 // Various assertions around auto-hide behavior.
 // TODO(jamescook): Move this to ShelfTest.
 TEST_F(ShellTest, ToggleAutoHide) {
-  std::unique_ptr<aura::Window> window = window_factory::NewWindow();
+  std::unique_ptr<aura::Window> window =
+      std::make_unique<aura::Window>(nullptr);
   window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
   window->SetType(aura::client::WINDOW_TYPE_NORMAL);
   window->Init(ui::LAYER_TEXTURED);
diff --git a/ash/system/accessibility/switch_access_menu_view.cc b/ash/system/accessibility/switch_access_menu_view.cc
index 8afc21bc..8207b2bd 100644
--- a/ash/system/accessibility/switch_access_menu_view.cc
+++ b/ash/system/accessibility/switch_access_menu_view.cc
@@ -47,6 +47,8 @@
           IDS_ASH_SWITCH_ACCESS_END_TEXT_SELECTION}},
         {"increment",
          {&kSwitchAccessIncrementIcon, IDS_ASH_SWITCH_ACCESS_INCREMENT}},
+        {"itemScan",
+         {&kSwitchAccessItemScanIcon, IDS_ASH_SWITCH_ACCESS_ITEM_SCAN}},
         {"jumpToBeginningOfText",
          {&kSwitchAccessJumpToBeginningOfTextIcon,
           IDS_ASH_SWITCH_ACCESS_JUMP_TO_BEGINNING_OF_TEXT}},
diff --git a/ash/system/overview/overview_button_tray_unittest.cc b/ash/system/overview/overview_button_tray_unittest.cc
index 5a57349..7f9069f 100644
--- a/ash/system/overview/overview_button_tray_unittest.cc
+++ b/ash/system/overview/overview_button_tray_unittest.cc
@@ -18,7 +18,6 @@
 #include "ash/system/status_area_widget_test_helper.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/ash_test_helper.h"
-#include "ash/window_factory.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/splitview/split_view_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
diff --git a/ash/system/phonehub/phone_hub_notification_controller.cc b/ash/system/phonehub/phone_hub_notification_controller.cc
index 96e6ab3..8240fc85 100644
--- a/ash/system/phonehub/phone_hub_notification_controller.cc
+++ b/ash/system/phonehub/phone_hub_notification_controller.cc
@@ -20,6 +20,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/timer/timer.h"
 #include "chromeos/components/phonehub/notification.h"
+#include "chromeos/components/phonehub/notification_interaction_handler.h"
 #include "chromeos/components/phonehub/phone_hub_manager.h"
 #include "chromeos/components/phonehub/phone_model.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -171,12 +172,14 @@
 
   void Click(const base::Optional<int>& button_index,
              const base::Optional<base::string16>& reply) override {
-    if (!controller_ || !button_index.has_value())
+    if (!controller_)
       return;
 
-    if (button_index.value() == kReplyButtonIndex && reply.has_value()) {
-      controller_->SendInlineReply(phone_hub_id_, reply.value());
-      return;
+    if (button_index.has_value()) {
+      if (button_index.value() == kReplyButtonIndex && reply.has_value())
+        controller_->SendInlineReply(phone_hub_id_, reply.value());
+    } else {
+      controller_->HandleNotificationBodyClick(phone_hub_id_);
     }
   }
 
@@ -255,6 +258,13 @@
     phone_model_ = phone_hub_manager->GetPhoneModel();
   else
     phone_model_ = nullptr;
+
+  if (phone_hub_manager) {
+    notification_interaction_handler_ =
+        phone_hub_manager->GetNotificationInteractionHandler();
+  } else {
+    notification_interaction_handler_ = nullptr;
+  }
 }
 
 const base::string16 PhoneHubNotificationController::GetPhoneName() const {
@@ -365,6 +375,22 @@
       NotificationInteraction::kDismiss);
 }
 
+void PhoneHubNotificationController::HandleNotificationBodyClick(
+    int64_t notification_id) {
+  CHECK(manager_);
+  if (!notification_interaction_handler_)
+    return;
+  const chromeos::phonehub::Notification* notification =
+      manager_->GetNotification(notification_id);
+  if (!notification)
+    return;
+  if (notification->interaction_behavior() ==
+      chromeos::phonehub::Notification::InteractionBehavior::kOpenable) {
+    notification_interaction_handler_->HandleNotificationClicked(
+        notification_id);
+  }
+}
+
 void PhoneHubNotificationController::SendInlineReply(
     int64_t notification_id,
     const base::string16& inline_reply_text) {
diff --git a/ash/system/phonehub/phone_hub_notification_controller.h b/ash/system/phonehub/phone_hub_notification_controller.h
index 6dd8c9e..63bbdd42 100644
--- a/ash/system/phonehub/phone_hub_notification_controller.h
+++ b/ash/system/phonehub/phone_hub_notification_controller.h
@@ -15,6 +15,7 @@
 namespace chromeos {
 namespace phonehub {
 class Notification;
+class NotificationInteractionHandler;
 class PhoneHubManager;
 class PhoneModel;
 }  // namespace phonehub
@@ -73,6 +74,7 @@
   // Callbacks for user interactions.
   void OpenSettings();
   void DismissNotification(int64_t notification_id);
+  void HandleNotificationBodyClick(int64_t notification_id);
   void SendInlineReply(int64_t notification_id,
                        const base::string16& inline_reply_text);
 
@@ -100,6 +102,8 @@
       base::WeakPtr<PhoneHubNotificationController> notification_controller,
       const message_center::Notification& notification);
 
+  chromeos::phonehub::NotificationInteractionHandler*
+      notification_interaction_handler_ = nullptr;
   chromeos::phonehub::NotificationManager* manager_ = nullptr;
   chromeos::phonehub::FeatureStatusProvider* feature_status_provider_ = nullptr;
   chromeos::phonehub::TetherController* tether_controller_ = nullptr;
diff --git a/ash/system/phonehub/phone_hub_notification_controller_unittest.cc b/ash/system/phonehub/phone_hub_notification_controller_unittest.cc
index 1be64f05..f4be821c 100644
--- a/ash/system/phonehub/phone_hub_notification_controller_unittest.cc
+++ b/ash/system/phonehub/phone_hub_notification_controller_unittest.cc
@@ -52,8 +52,9 @@
                                                     kPackageName,
                                                     /*icon=*/gfx::Image()),
       base::Time::Now(), chromeos::phonehub::Notification::Importance::kDefault,
-      /*inline_reply_id=*/0, base::UTF8ToUTF16(kTitle),
-      base::UTF8ToUTF16(kTextContent));
+      /*inline_reply_id=*/0,
+      chromeos::phonehub::Notification::InteractionBehavior::kOpenable,
+      base::UTF8ToUTF16(kTitle), base::UTF8ToUTF16(kTextContent));
 }
 
 class PhoneHubNotificationControllerTest : public AshTestBase {
@@ -64,7 +65,8 @@
 
   // AshTestBase:
   void SetUp() override {
-    feature_list_.InitAndEnableFeature(chromeos::features::kPhoneHub);
+    feature_list_.InitWithFeatures(
+        {chromeos::features::kPhoneHub, chromeos::features::kEcheSWA}, {});
     AshTestBase::SetUp();
 
     feature_status_provider_ =
@@ -130,8 +132,9 @@
                                                     kPackageName,
                                                     /*icon=*/gfx::Image()),
       base::Time::Now(), chromeos::phonehub::Notification::Importance::kDefault,
-      /*inline_reply_id=*/0, base::UTF8ToUTF16(kNewTitle),
-      base::UTF8ToUTF16(kNewTextContent));
+      /*inline_reply_id=*/0,
+      chromeos::phonehub::Notification::InteractionBehavior::kNone,
+      base::UTF8ToUTF16(kNewTitle), base::UTF8ToUTF16(kNewTextContent));
 
   notification_manager_->SetNotification(updated_notification);
 
@@ -190,6 +193,16 @@
   EXPECT_EQ(kInlineReply1, inline_replies[1].inline_reply_text);
 }
 
+TEST_F(PhoneHubNotificationControllerTest, HandleNotificationClick) {
+  chromeos::phonehub::FakeNotificationInteractionHandler* handler =
+      phone_hub_manager_.fake_notification_interaction_handler();
+  notification_manager_->SetNotificationsInternal(fake_notifications_);
+  message_center_->ClickOnNotification(kCrOSNotificationId0);
+  EXPECT_EQ(1u, handler->handled_notification_count());
+  message_center_->ClickOnNotification(kCrOSNotificationId1);
+  EXPECT_EQ(2u, handler->handled_notification_count());
+}
+
 TEST_F(PhoneHubNotificationControllerTest, ClickSettings) {
   notification_manager_->SetNotificationsInternal(fake_notifications_);
   EXPECT_TRUE(FindNotification(kCrOSNotificationId0));
@@ -221,8 +234,10 @@
       chromeos::phonehub::Notification::AppMetadata(base::UTF8ToUTF16(kAppName),
                                                     kPackageName, icon),
       timestamp, chromeos::phonehub::Notification::Importance::kHigh,
-      /*inline_reply_id=*/0, base::UTF8ToUTF16(kTitle),
-      base::UTF8ToUTF16(kTextContent), shared_image, contact_image);
+      /*inline_reply_id=*/0,
+      chromeos::phonehub::Notification::InteractionBehavior::kNone,
+      base::UTF8ToUTF16(kTitle), base::UTF8ToUTF16(kTextContent), shared_image,
+      contact_image);
 
   notification_manager_->SetNotification(fake_notification);
 
@@ -298,8 +313,9 @@
                                                     kPackageName,
                                                     /*icon=*/gfx::Image()),
       base::Time::Now(), chromeos::phonehub::Notification::Importance::kHigh,
-      /*inline_reply_id=*/0, base::UTF8ToUTF16(kTitle),
-      base::UTF8ToUTF16(kTextContent));
+      /*inline_reply_id=*/0,
+      chromeos::phonehub::Notification::InteractionBehavior::kNone,
+      base::UTF8ToUTF16(kTitle), base::UTF8ToUTF16(kTextContent));
 
   // Adding the notification for the first time shows a pop-up (MAX_PRIORITY).
   notification_manager_->SetNotification(fake_notification);
@@ -349,8 +365,9 @@
                                                     kPackageName,
                                                     /*icon=*/gfx::Image()),
       base::Time::Now(), chromeos::phonehub::Notification::Importance::kHigh,
-      /*inline_reply_id=*/0, base::UTF8ToUTF16(kTitle),
-      base::UTF8ToUTF16("New text"));
+      /*inline_reply_id=*/0,
+      chromeos::phonehub::Notification::InteractionBehavior::kNone,
+      base::UTF8ToUTF16(kTitle), base::UTF8ToUTF16("New text"));
 
   // Update the existingt notification; the priority should be MAX_PRIORITY, and
   // renotify should be true.
@@ -369,8 +386,9 @@
                                                     kPackageName,
                                                     /*icon=*/gfx::Image()),
       base::Time::Now(), chromeos::phonehub::Notification::Importance::kMin,
-      /*inline_reply_id=*/0, base::UTF8ToUTF16(kTitle),
-      base::UTF8ToUTF16(kTextContent));
+      /*inline_reply_id=*/0,
+      chromeos::phonehub::Notification::InteractionBehavior::kNone,
+      base::UTF8ToUTF16(kTitle), base::UTF8ToUTF16(kTextContent));
 
   // Adding the notification for the first time shows a pop-up (MAX_PRIORITY),
   // even though the notification itself is Importance::kMin.
diff --git a/ash/system/tray/tray_background_view.cc b/ash/system/tray/tray_background_view.cc
index 6f4657c..1122815 100644
--- a/ash/system/tray/tray_background_view.cc
+++ b/ash/system/tray/tray_background_view.cc
@@ -25,11 +25,11 @@
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/tray/tray_container.h"
 #include "ash/system/tray/tray_event_filter.h"
-#include "ash/window_factory.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/scoped_multi_source_observation.h"
 #include "base/time/time.h"
 #include "ui/accessibility/ax_node_data.h"
+#include "ui/aura/window.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_animation_element.h"
 #include "ui/compositor/layer_animation_sequence.h"
diff --git a/ash/test/test_widget_builder.cc b/ash/test/test_widget_builder.cc
index b53a03e..0c33cbcb 100644
--- a/ash/test/test_widget_builder.cc
+++ b/ash/test/test_widget_builder.cc
@@ -5,10 +5,10 @@
 #include "ash/test/test_widget_builder.h"
 
 #include "ash/shell.h"
-#include "ash/window_factory.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/window_parenting_client.h"
 #include "ui/aura/test/test_windows.h"
+#include "ui/aura/window.h"
 #include "ui/views/widget/widget_delegate.h"
 #include "ui/wm/core/coordinate_conversion.h"
 
diff --git a/ash/test/test_window_builder.cc b/ash/test/test_window_builder.cc
index 6af198b9..0fb416ae 100644
--- a/ash/test/test_window_builder.cc
+++ b/ash/test/test_window_builder.cc
@@ -5,10 +5,10 @@
 #include "ash/test/test_window_builder.h"
 
 #include "ash/shell.h"
-#include "ash/window_factory.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/window_parenting_client.h"
 #include "ui/aura/test/test_windows.h"
+#include "ui/aura/window.h"
 #include "ui/wm/core/coordinate_conversion.h"
 
 namespace ash {
@@ -97,7 +97,7 @@
   DCHECK(!built_);
   built_ = true;
   std::unique_ptr<aura::Window> window =
-      window_factory::NewWindow(delegate_, window_type_);
+      std::make_unique<aura::Window>(delegate_, window_type_);
   window->Init(layer_type_);
   window->AcquireAllPropertiesFrom(std::move(init_properties_));
   if (window_id_ != aura::Window::kInitialId)
diff --git a/ash/window_factory.cc b/ash/window_factory.cc
deleted file mode 100644
index b8526cf6..0000000
--- a/ash/window_factory.cc
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/window_factory.h"
-
-#include "ui/aura/window.h"
-
-namespace ash {
-namespace window_factory {
-
-std::unique_ptr<aura::Window> NewWindow(aura::WindowDelegate* delegate,
-                                        aura::client::WindowType type) {
-  return std::make_unique<aura::Window>(delegate, type);
-}
-
-}  // namespace window_factory
-}  // namespace ash
diff --git a/ash/window_factory.h b/ash/window_factory.h
deleted file mode 100644
index f0fc75b..0000000
--- a/ash/window_factory.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_WINDOW_FACTORY_H_
-#define ASH_WINDOW_FACTORY_H_
-
-#include <memory>
-
-#include "ash/ash_export.h"
-#include "ui/aura/client/window_types.h"
-
-namespace aura {
-class Window;
-class WindowDelegate;
-}  // namespace aura
-
-namespace ash {
-namespace window_factory {
-
-// All aura::Window creation in Ash goes through this function. It ensures the
-// aura::Window is created as appropriate for Ash.
-// Exporting solely for tests.
-ASH_EXPORT std::unique_ptr<aura::Window> NewWindow(
-    aura::WindowDelegate* delegate = nullptr,
-    aura::client::WindowType type = aura::client::WINDOW_TYPE_UNKNOWN);
-
-}  // namespace window_factory
-}  // namespace ash
-
-#endif  // ASH_WINDOW_FACTORY_H_
diff --git a/ash/window_user_data_unittest.cc b/ash/window_user_data_unittest.cc
index 53b57c1d..fbc5ea3 100644
--- a/ash/window_user_data_unittest.cc
+++ b/ash/window_user_data_unittest.cc
@@ -8,7 +8,6 @@
 
 #include "ash/public/cpp/autotest_private_api_utils.h"
 #include "ash/test/ash_test_base.h"
-#include "ash/window_factory.h"
 #include "ash/window_user_data.h"
 #include "ui/aura/window.h"
 #include "ui/compositor/layer_type.h"
@@ -36,7 +35,8 @@
 // Verifies clear() deletes the data associated with a window.
 TEST_F(WindowUserDataTest, ClearDestroys) {
   WindowUserData<Data> user_data;
-  std::unique_ptr<aura::Window> window = window_factory::NewWindow();
+  std::unique_ptr<aura::Window> window =
+      std::make_unique<aura::Window>(nullptr);
   window->Init(ui::LAYER_NOT_DRAWN);
   bool data_deleted = false;
   user_data.Set(window.get(), std::make_unique<Data>(&data_deleted));
@@ -48,7 +48,8 @@
 // Verifies Set() called with an existing window replaces the existing data.
 TEST_F(WindowUserDataTest, ReplaceDestroys) {
   WindowUserData<Data> user_data;
-  std::unique_ptr<aura::Window> window = window_factory::NewWindow();
+  std::unique_ptr<aura::Window> window =
+      std::make_unique<aura::Window>(nullptr);
   window->Init(ui::LAYER_NOT_DRAWN);
   bool data1_deleted = false;
   user_data.Set(window.get(), std::make_unique<Data>(&data1_deleted));
@@ -67,7 +68,8 @@
 // Verifies Set() with null deletes existing data.
 TEST_F(WindowUserDataTest, NullClears) {
   WindowUserData<Data> user_data;
-  std::unique_ptr<aura::Window> window = window_factory::NewWindow();
+  std::unique_ptr<aura::Window> window =
+      std::make_unique<aura::Window>(nullptr);
   window->Init(ui::LAYER_NOT_DRAWN);
   bool data1_deleted = false;
   user_data.Set(window.get(), std::make_unique<Data>(&data1_deleted));
diff --git a/ash/wm/ash_focus_rules_unittest.cc b/ash/wm/ash_focus_rules_unittest.cc
index bacc7d5..85edc151 100644
--- a/ash/wm/ash_focus_rules_unittest.cc
+++ b/ash/wm/ash_focus_rules_unittest.cc
@@ -11,13 +11,13 @@
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/ash_test_helper.h"
-#include "ash/window_factory.h"
 #include "ash/wm/desks/desks_util.h"
 #include "ash/wm/lock_state_controller.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/window_parenting_client.h"
+#include "ui/aura/window.h"
 #include "ui/base/ui_base_types.h"
 #include "ui/views/test/widget_test.h"
 #include "ui/views/view.h"
@@ -143,7 +143,7 @@
   aura::Window* CreateWindowInContainer(int container_id) {
     aura::Window* root_window = Shell::GetPrimaryRootWindow();
     aura::Window* container = Shell::GetContainer(root_window, container_id);
-    aura::Window* window = window_factory::NewWindow().release();
+    aura::Window* window = new aura::Window(nullptr);
     window->set_id(0);
     window->SetType(aura::client::WINDOW_TYPE_NORMAL);
     window->Init(ui::LAYER_TEXTURED);
diff --git a/ash/wm/default_window_resizer_unittest.cc b/ash/wm/default_window_resizer_unittest.cc
index 933101e..dc91e767 100644
--- a/ash/wm/default_window_resizer_unittest.cc
+++ b/ash/wm/default_window_resizer_unittest.cc
@@ -7,10 +7,10 @@
 #include "ash/root_window_controller.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
-#include "ash/window_factory.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/test/test_window_delegate.h"
+#include "ui/aura/window.h"
 #include "ui/base/hit_test.h"
 #include "ui/base/ui_base_types.h"
 #include "ui/compositor/test/test_utils.h"
@@ -29,8 +29,8 @@
 
     delegate_.set_minimum_size(gfx::Size(10, 10));
     delegate_.set_maximum_size(gfx::Size(500, 500));
-    aspect_ratio_window_ =
-        window_factory::NewWindow(&delegate_, aura::client::WINDOW_TYPE_NORMAL);
+    aspect_ratio_window_ = std::make_unique<aura::Window>(
+        &delegate_, aura::client::WINDOW_TYPE_NORMAL);
     aspect_ratio_window_->Init(ui::LAYER_NOT_DRAWN);
     ParentWindowInPrimaryRootWindow(aspect_ratio_window_.get());
   }
@@ -159,8 +159,8 @@
 }
 
 TEST_F(DefaultWindowResizerTest, NoResizeHistogramOnMove) {
-  std::unique_ptr<aura::Window> window =
-      window_factory::NewWindow(&delegate_, aura::client::WINDOW_TYPE_NORMAL);
+  std::unique_ptr<aura::Window> window = std::make_unique<aura::Window>(
+      &delegate_, aura::client::WINDOW_TYPE_NORMAL);
   window->Init(ui::LAYER_NOT_DRAWN);
   ParentWindowInPrimaryRootWindow(window.get());
   window->SetBounds(gfx::Rect(0, 0, 50, 50));
@@ -178,8 +178,8 @@
 }
 
 TEST_F(DefaultWindowResizerTest, ResizeHistogram) {
-  std::unique_ptr<aura::Window> window =
-      window_factory::NewWindow(&delegate_, aura::client::WINDOW_TYPE_NORMAL);
+  std::unique_ptr<aura::Window> window = std::make_unique<aura::Window>(
+      &delegate_, aura::client::WINDOW_TYPE_NORMAL);
   window->Init(ui::LAYER_NOT_DRAWN);
   ParentWindowInPrimaryRootWindow(window.get());
   window->SetBounds(gfx::Rect(0, 0, 50, 50));
diff --git a/ash/wm/desks/desks_unittests.cc b/ash/wm/desks/desks_unittests.cc
index cc684ce..0eac92f 100644
--- a/ash/wm/desks/desks_unittests.cc
+++ b/ash/wm/desks/desks_unittests.cc
@@ -26,7 +26,6 @@
 #include "ash/sticky_keys/sticky_keys_controller.h"
 #include "ash/style/ash_color_provider.h"
 #include "ash/test/ash_test_base.h"
-#include "ash/window_factory.h"
 #include "ash/wm/desks/close_desk_button.h"
 #include "ash/wm/desks/desk.h"
 #include "ash/wm/desks/desk_animation_base.h"
@@ -71,6 +70,7 @@
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/window_parenting_client.h"
 #include "ui/aura/test/test_window_delegate.h"
+#include "ui/aura/window.h"
 #include "ui/base/clipboard/clipboard_buffer.h"
 #include "ui/base/clipboard/scoped_clipboard_writer.h"
 #include "ui/base/ui_base_types.h"
@@ -106,7 +106,7 @@
     aura::Window* transient_parent,
     const gfx::Rect& bounds) {
   std::unique_ptr<aura::Window> window =
-      window_factory::NewWindow(nullptr, aura::client::WINDOW_TYPE_POPUP);
+      std::make_unique<aura::Window>(nullptr, aura::client::WINDOW_TYPE_POPUP);
   window->Init(ui::LAYER_NOT_DRAWN);
   window->SetBounds(bounds);
   ::wm::AddTransientChild(transient_parent, window.get());
diff --git a/ash/wm/drag_window_resizer_unittest.cc b/ash/wm/drag_window_resizer_unittest.cc
index 743e3af6..5d7d6ff 100644
--- a/ash/wm/drag_window_resizer_unittest.cc
+++ b/ash/wm/drag_window_resizer_unittest.cc
@@ -11,7 +11,6 @@
 #include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
-#include "ash/window_factory.h"
 #include "ash/wm/cursor_manager_test_api.h"
 #include "ash/wm/desks/desks_util.h"
 #include "ash/wm/drag_window_controller.h"
@@ -23,6 +22,7 @@
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/env.h"
 #include "ui/aura/test/test_window_delegate.h"
+#include "ui/aura/window.h"
 #include "ui/base/hit_test.h"
 #include "ui/base/ui_base_types.h"
 #include "ui/compositor/layer_delegate.h"
@@ -77,13 +77,13 @@
     gfx::Rect root_bounds(root->bounds());
     EXPECT_EQ(kRootHeight, root_bounds.height());
     EXPECT_EQ(800, root_bounds.width());
-    window_ = window_factory::NewWindow(&delegate_);
+    window_ = std::make_unique<aura::Window>(&delegate_);
     window_->SetType(aura::client::WINDOW_TYPE_NORMAL);
     window_->Init(ui::LAYER_NOT_DRAWN);
     ParentWindowInPrimaryRootWindow(window_.get());
     window_->set_id(1);
 
-    always_on_top_window_ = window_factory::NewWindow(&delegate2_);
+    always_on_top_window_ = std::make_unique<aura::Window>(&delegate2_);
     always_on_top_window_->SetType(aura::client::WINDOW_TYPE_NORMAL);
     always_on_top_window_->SetProperty(aura::client::kZOrderingKey,
                                        ui::ZOrderLevel::kFloatingWindow);
@@ -91,7 +91,7 @@
     ParentWindowInPrimaryRootWindow(always_on_top_window_.get());
     always_on_top_window_->set_id(2);
 
-    system_modal_window_ = window_factory::NewWindow(&delegate3_);
+    system_modal_window_ = std::make_unique<aura::Window>(&delegate3_);
     system_modal_window_->SetType(aura::client::WINDOW_TYPE_NORMAL);
     system_modal_window_->SetProperty(aura::client::kModalKey,
                                       ui::MODAL_TYPE_SYSTEM);
@@ -99,13 +99,13 @@
     ParentWindowInPrimaryRootWindow(system_modal_window_.get());
     system_modal_window_->set_id(3);
 
-    transient_child_ = window_factory::NewWindow(&delegate4_).release();
+    transient_child_ = new aura::Window(&delegate4_);
     transient_child_->SetType(aura::client::WINDOW_TYPE_NORMAL);
     transient_child_->Init(ui::LAYER_NOT_DRAWN);
     ParentWindowInPrimaryRootWindow(transient_child_);
     transient_child_->set_id(4);
 
-    transient_parent_ = window_factory::NewWindow(&delegate5_);
+    transient_parent_ = std::make_unique<aura::Window>(&delegate5_);
     transient_parent_->SetType(aura::client::WINDOW_TYPE_NORMAL);
     transient_parent_->Init(ui::LAYER_NOT_DRAWN);
     ParentWindowInPrimaryRootWindow(transient_parent_.get());
@@ -320,7 +320,8 @@
   ASSERT_EQ(2U, root_windows.size());
 
   aura::test::TestWindowDelegate delegate;
-  std::unique_ptr<aura::Window> window = window_factory::NewWindow(&delegate);
+  std::unique_ptr<aura::Window> window =
+      std::make_unique<aura::Window>(&delegate);
   window->SetType(aura::client::WINDOW_TYPE_NORMAL);
   window->Init(ui::LAYER_TEXTURED);
   ParentWindowInPrimaryRootWindow(window.get());
diff --git a/ash/wm/gestures/back_gesture/back_gesture_affordance.cc b/ash/wm/gestures/back_gesture/back_gesture_affordance.cc
index c66cbb2..458da85 100644
--- a/ash/wm/gestures/back_gesture/back_gesture_affordance.cc
+++ b/ash/wm/gestures/back_gesture/back_gesture_affordance.cc
@@ -10,7 +10,6 @@
 #include "ash/style/default_color_constants.h"
 #include "ash/style/default_colors.h"
 #include "ash/style/scoped_light_mode_as_default.h"
-#include "ash/window_factory.h"
 #include "ash/wm/splitview/split_view_controller.h"
 #include "ash/wm/splitview/split_view_divider.h"
 #include "ash/wm/window_util.h"
diff --git a/ash/wm/stacking_controller_unittest.cc b/ash/wm/stacking_controller_unittest.cc
index 192ae93..c35ab2d 100644
--- a/ash/wm/stacking_controller_unittest.cc
+++ b/ash/wm/stacking_controller_unittest.cc
@@ -5,7 +5,6 @@
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
-#include "ash/window_factory.h"
 #include "ash/wm/window_properties.h"
 #include "ash/wm/window_util.h"
 #include "ui/aura/client/aura_constants.h"
@@ -24,8 +23,7 @@
 
   aura::Window* CreateTestWindow() {
     aura::Window* window =
-        window_factory::NewWindow(nullptr, aura::client::WINDOW_TYPE_NORMAL)
-            .release();
+        new aura::Window(nullptr, aura::client::WINDOW_TYPE_NORMAL);
     window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
     window->Init(ui::LAYER_TEXTURED);
     return window;
diff --git a/ash/wm/system_gesture_event_filter_unittest.cc b/ash/wm/system_gesture_event_filter_unittest.cc
index d58babb..8e1702f4 100644
--- a/ash/wm/system_gesture_event_filter_unittest.cc
+++ b/ash/wm/system_gesture_event_filter_unittest.cc
@@ -9,7 +9,6 @@
 #include "ash/accelerators/accelerator_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
-#include "ash/window_factory.h"
 #include "ash/wm/window_positioning_utils.h"
 #include "ash/wm/window_state.h"
 #include "base/time/time.h"
@@ -17,6 +16,7 @@
 #include "ui/aura/env.h"
 #include "ui/aura/test/test_window_delegate.h"
 #include "ui/aura/test/test_windows.h"
+#include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/base/hit_test.h"
 #include "ui/display/manager/display_manager.h"
@@ -455,8 +455,8 @@
 
   aura::test::EventCountDelegate delegate;
   delegate.set_window_component(HTCLIENT);
-  std::unique_ptr<aura::Window> child =
-      window_factory::NewWindow(&delegate, aura::client::WINDOW_TYPE_CONTROL);
+  std::unique_ptr<aura::Window> child = std::make_unique<aura::Window>(
+      &delegate, aura::client::WINDOW_TYPE_CONTROL);
   child->Init(ui::LAYER_TEXTURED);
   parent->AddChild(child.get());
   child->SetBounds(gfx::Rect(100, 100));
diff --git a/ash/wm/system_modal_container_layout_manager_unittest.cc b/ash/wm/system_modal_container_layout_manager_unittest.cc
index f4277b5..1723e7e 100644
--- a/ash/wm/system_modal_container_layout_manager_unittest.cc
+++ b/ash/wm/system_modal_container_layout_manager_unittest.cc
@@ -16,7 +16,6 @@
 #include "ash/root_window_controller.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
-#include "ash/window_factory.h"
 #include "ash/wm/container_finder.h"
 #include "ash/wm/window_util.h"
 #include "base/command_line.h"
@@ -475,7 +474,7 @@
   aura::test::EventCountDelegate control_delegate;
   control_delegate.set_window_component(HTCLIENT);
   std::unique_ptr<aura::Window> child =
-      window_factory::NewWindow(&control_delegate);
+      std::make_unique<aura::Window>(&control_delegate);
   child->SetType(aura::client::WINDOW_TYPE_CONTROL);
   child->Init(ui::LAYER_TEXTURED);
   modal1_transient->AddChild(child.get());
@@ -869,7 +868,7 @@
 
   // Make sure that a child visibility change should not cause
   // inconsistent state.
-  std::unique_ptr<aura::Window> child = window_factory::NewWindow();
+  std::unique_ptr<aura::Window> child = std::make_unique<aura::Window>(nullptr);
   child->SetType(aura::client::WINDOW_TYPE_CONTROL);
   child->Init(ui::LAYER_TEXTURED);
   modal_window->AddChild(child.get());
diff --git a/ash/wm/toplevel_window_event_handler_unittest.cc b/ash/wm/toplevel_window_event_handler_unittest.cc
index 4c082e74..aa7867b5 100644
--- a/ash/wm/toplevel_window_event_handler_unittest.cc
+++ b/ash/wm/toplevel_window_event_handler_unittest.cc
@@ -15,7 +15,6 @@
 #include "ash/system/status_area_widget.h"
 #include "ash/system/status_area_widget_test_helper.h"
 #include "ash/test/ash_test_base.h"
-#include "ash/window_factory.h"
 #include "ash/wm/desks/desks_util.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/overview/overview_grid.h"
@@ -36,6 +35,7 @@
 #include "ui/aura/client/capture_client.h"
 #include "ui/aura/test/aura_test_base.h"
 #include "ui/aura/test/test_window_delegate.h"
+#include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/aura/window_observer.h"
 #include "ui/base/hit_test.h"
@@ -111,9 +111,7 @@
  protected:
   aura::Window* CreateWindow(int hittest_code) {
     TestWindowDelegate* d1 = new TestWindowDelegate(hittest_code);
-    aura::Window* w1 =
-        window_factory::NewWindow(d1, aura::client::WINDOW_TYPE_NORMAL)
-            .release();
+    aura::Window* w1 = new aura::Window(d1, aura::client::WINDOW_TYPE_NORMAL);
     w1->set_id(1);
     w1->Init(ui::LAYER_TEXTURED);
     aura::Window* parent = Shell::GetContainer(
diff --git a/ash/wm/window_dimmer.cc b/ash/wm/window_dimmer.cc
index 6be2d43..261602e5 100644
--- a/ash/wm/window_dimmer.cc
+++ b/ash/wm/window_dimmer.cc
@@ -6,7 +6,6 @@
 
 #include <memory>
 
-#include "ash/window_factory.h"
 #include "base/time/time.h"
 #include "ui/aura/window.h"
 #include "ui/compositor/layer.h"
@@ -26,9 +25,7 @@
                            bool animate,
                            Delegate* delegate)
     : parent_(parent),
-      window_(
-          window_factory::NewWindow(nullptr, aura::client::WINDOW_TYPE_NORMAL)
-              .release()),
+      window_(new aura::Window(nullptr, aura::client::WINDOW_TYPE_NORMAL)),
       delegate_(delegate) {
   window_->Init(ui::LAYER_SOLID_COLOR);
   window_->SetName("Dimming Window");
diff --git a/ash/wm/workspace/workspace_event_handler_unittest.cc b/ash/wm/workspace/workspace_event_handler_unittest.cc
index 1de3aa6..b933e39b 100644
--- a/ash/wm/workspace/workspace_event_handler_unittest.cc
+++ b/ash/wm/workspace/workspace_event_handler_unittest.cc
@@ -7,7 +7,6 @@
 #include "ash/screen_util.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
-#include "ash/window_factory.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
 #include "ash/wm/wm_event.h"
@@ -58,8 +57,7 @@
   aura::Window* CreateTestWindow(aura::WindowDelegate* delegate,
                                  const gfx::Rect& bounds) {
     aura::Window* window =
-        window_factory::NewWindow(delegate, aura::client::WINDOW_TYPE_NORMAL)
-            .release();
+        new aura::Window(delegate, aura::client::WINDOW_TYPE_NORMAL);
     window->Init(ui::LAYER_TEXTURED);
     ParentWindowInPrimaryRootWindow(window);
     window->SetBounds(bounds);
diff --git a/ash/wm/workspace/workspace_layout_manager_unittest.cc b/ash/wm/workspace/workspace_layout_manager_unittest.cc
index 95e74ede..2d0c2ae 100644
--- a/ash/wm/workspace/workspace_layout_manager_unittest.cc
+++ b/ash/wm/workspace/workspace_layout_manager_unittest.cc
@@ -35,7 +35,6 @@
 #include "ash/test/ash_test_base.h"
 #include "ash/test/test_window_builder.h"
 #include "ash/wallpaper/wallpaper_controller_test_api.h"
-#include "ash/window_factory.h"
 #include "ash/wm/always_on_top_controller.h"
 #include "ash/wm/desks/desks_util.h"
 #include "ash/wm/fullscreen_window_finder.h"
@@ -385,7 +384,7 @@
 TEST_F(WorkspaceLayoutManagerTest, DontClobberRestoreBounds) {
   DontClobberRestoreBoundsWindowObserver window_observer;
   std::unique_ptr<aura::Window> window =
-      window_factory::NewWindow(nullptr, aura::client::WINDOW_TYPE_NORMAL);
+      std::make_unique<aura::Window>(nullptr, aura::client::WINDOW_TYPE_NORMAL);
   window->Init(ui::LAYER_TEXTURED);
   window->SetBounds(gfx::Rect(10, 20, 30, 40));
   // NOTE: for this test to exercise the failure the observer needs to be added
@@ -425,7 +424,7 @@
 // bounds.
 TEST_F(WorkspaceLayoutManagerTest, MaximizeWithEmptySize) {
   std::unique_ptr<aura::Window> window =
-      window_factory::NewWindow(nullptr, aura::client::WINDOW_TYPE_NORMAL);
+      std::make_unique<aura::Window>(nullptr, aura::client::WINDOW_TYPE_NORMAL);
   window->Init(ui::LAYER_TEXTURED);
   window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
   aura::Window* active_desk_container =
@@ -647,7 +646,7 @@
        DoNotAdjustTransientWindowBoundsToEnsureMinimumVisibility) {
   UpdateDisplay("300x400");
   std::unique_ptr<aura::Window> window =
-      window_factory::NewWindow(nullptr, aura::client::WINDOW_TYPE_NORMAL);
+      std::make_unique<aura::Window>(nullptr, aura::client::WINDOW_TYPE_NORMAL);
   window->Init(ui::LAYER_TEXTURED);
   window->SetBounds(gfx::Rect(10, 0, 100, 200));
   ParentWindowInPrimaryRootWindow(window.get());
@@ -666,7 +665,7 @@
 
 TEST_F(WorkspaceLayoutManagerTest, EnsureWindowStateInOverlay) {
   std::unique_ptr<aura::Window> window =
-      window_factory::NewWindow(nullptr, aura::client::WINDOW_TYPE_NORMAL);
+      std::make_unique<aura::Window>(nullptr, aura::client::WINDOW_TYPE_NORMAL);
   window->Init(ui::LAYER_TEXTURED);
   auto* overlay_container =
       Shell::GetPrimaryRootWindowController()->GetContainer(
@@ -1136,7 +1135,7 @@
   }
 
   aura::Window* CreateTestWindowInParent(aura::Window* root_window) {
-    aura::Window* window = window_factory::NewWindow().release();
+    aura::Window* window = new aura::Window(nullptr);
     window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
     window->SetType(aura::client::WINDOW_TYPE_NORMAL);
     window->Init(ui::LAYER_TEXTURED);
diff --git a/ash/wm/workspace/workspace_window_resizer_unittest.cc b/ash/wm/workspace/workspace_window_resizer_unittest.cc
index b4398b3..61215824 100644
--- a/ash/wm/workspace/workspace_window_resizer_unittest.cc
+++ b/ash/wm/workspace/workspace_window_resizer_unittest.cc
@@ -10,7 +10,6 @@
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
-#include "ash/window_factory.h"
 #include "ash/wm/window_positioning_utils.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
@@ -23,6 +22,7 @@
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/test/test_window_delegate.h"
 #include "ui/aura/test/test_windows.h"
+#include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/base/hit_test.h"
 #include "ui/compositor/test/test_utils.h"
@@ -92,25 +92,25 @@
     gfx::Rect root_bounds(root->bounds());
     EXPECT_EQ(800, root_bounds.width());
     Shell::Get()->SetDisplayWorkAreaInsets(root, gfx::Insets());
-    window_ = window_factory::NewWindow(&delegate_);
+    window_ = std::make_unique<aura::Window>(&delegate_);
     window_->SetType(aura::client::WINDOW_TYPE_NORMAL);
     window_->Init(ui::LAYER_NOT_DRAWN);
     ParentWindowInPrimaryRootWindow(window_.get());
     window_->set_id(1);
 
-    window2_ = window_factory::NewWindow(&delegate2_);
+    window2_ = std::make_unique<aura::Window>(&delegate2_);
     window2_->SetType(aura::client::WINDOW_TYPE_NORMAL);
     window2_->Init(ui::LAYER_NOT_DRAWN);
     ParentWindowInPrimaryRootWindow(window2_.get());
     window2_->set_id(2);
 
-    window3_ = window_factory::NewWindow(&delegate3_);
+    window3_ = std::make_unique<aura::Window>(&delegate3_);
     window3_->SetType(aura::client::WINDOW_TYPE_NORMAL);
     window3_->Init(ui::LAYER_NOT_DRAWN);
     ParentWindowInPrimaryRootWindow(window3_.get());
     window3_->set_id(3);
 
-    window4_ = window_factory::NewWindow(&delegate4_);
+    window4_ = std::make_unique<aura::Window>(&delegate4_);
     window4_->SetType(aura::client::WINDOW_TYPE_NORMAL);
     window4_->Init(ui::LAYER_NOT_DRAWN);
     ParentWindowInPrimaryRootWindow(window4_.get());
diff --git a/ash/wm/workspace_controller_unittest.cc b/ash/wm/workspace_controller_unittest.cc
index 7d56c21b..4de056a 100644
--- a/ash/wm/workspace_controller_unittest.cc
+++ b/ash/wm/workspace_controller_unittest.cc
@@ -15,7 +15,6 @@
 #include "ash/shell.h"
 #include "ash/system/status_area_widget.h"
 #include "ash/test/ash_test_base.h"
-#include "ash/window_factory.h"
 #include "ash/wm/desks/desks_util.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
@@ -81,7 +80,7 @@
   ~WorkspaceControllerTest() override = default;
 
   aura::Window* CreateTestWindowUnparented() {
-    aura::Window* window = window_factory::NewWindow().release();
+    aura::Window* window = new aura::Window(nullptr);
     window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
     window->SetType(aura::client::WINDOW_TYPE_NORMAL);
     window->Init(ui::LAYER_TEXTURED);
@@ -89,7 +88,7 @@
   }
 
   aura::Window* CreateTestWindow() {
-    aura::Window* window = window_factory::NewWindow().release();
+    aura::Window* window = new aura::Window(nullptr);
     window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
     window->SetType(aura::client::WINDOW_TYPE_NORMAL);
     window->Init(ui::LAYER_TEXTURED);
diff --git a/base/fuchsia/filtered_service_directory.h b/base/fuchsia/filtered_service_directory.h
index d2fa303c..da13dcc9 100644
--- a/base/fuchsia/filtered_service_directory.h
+++ b/base/fuchsia/filtered_service_directory.h
@@ -46,14 +46,6 @@
   DISALLOW_COPY_AND_ASSIGN(FilteredServiceDirectory);
 };
 
-// TODO(crbug.com/1073821): Remove this block when out-of-tree callers have been
-// changed to use the non-fuchsia-sub-namespace version.
-namespace fuchsia {
-
-using FilteredServiceDirectory = ::base::FilteredServiceDirectory;
-
-}  // namespace fuchsia
-
 }  // namespace base
 
 #endif  // BASE_FUCHSIA_FILTERED_SERVICE_DIRECTORY_H_
diff --git a/base/fuchsia/scoped_service_binding.h b/base/fuchsia/scoped_service_binding.h
index 7196eb2..7ce44df 100644
--- a/base/fuchsia/scoped_service_binding.h
+++ b/base/fuchsia/scoped_service_binding.h
@@ -122,21 +122,6 @@
   DISALLOW_COPY_AND_ASSIGN(ScopedSingleClientServiceBinding);
 };
 
-// TODO(crbug.com/1073821): Remove this block when out-of-tree callers have been
-// changed to use the non-fuchsia-sub-namespace version.
-namespace fuchsia {
-
-template <typename Interface>
-using ScopedServiceBinding = ::base::ScopedServiceBinding<Interface>;
-
-template <typename Interface,
-          ScopedServiceBindingPolicy Policy =
-              ScopedServiceBindingPolicy::kPreferNew>
-using ScopedSingleClientServiceBinding =
-    ScopedSingleClientServiceBinding<Interface, Policy>;
-
-}  // namespace fuchsia
-
 }  // namespace base
 
 #endif  // BASE_FUCHSIA_SCOPED_SERVICE_BINDING_H_
diff --git a/base/fuchsia/scoped_service_publisher.h b/base/fuchsia/scoped_service_publisher.h
index bc230b2..9cdad04 100644
--- a/base/fuchsia/scoped_service_publisher.h
+++ b/base/fuchsia/scoped_service_publisher.h
@@ -47,15 +47,6 @@
   DISALLOW_COPY_AND_ASSIGN(ScopedServicePublisher);
 };
 
-// TODO(crbug.com/1073821): Remove this block when out-of-tree callers have been
-// changed to use the non-fuchsia-sub-namespace version.
-namespace fuchsia {
-
-template <typename Interface>
-using ScopedServicePublisher = ::base::ScopedServicePublisher<Interface>;
-
-}  // namespace fuchsia
-
 }  // namespace base
 
 #endif  // BASE_FUCHSIA_SCOPED_SERVICE_PUBLISHER_H_
diff --git a/base/metrics/field_trial.h b/base/metrics/field_trial.h
index 65c530df..040a4b73 100644
--- a/base/metrics/field_trial.h
+++ b/base/metrics/field_trial.h
@@ -774,8 +774,6 @@
   Lock lock_;
   RegistrationMap registered_;
 
-  std::map<std::string, std::string> seen_states_;
-
   // Entropy provider to be used for one-time randomized field trials. If NULL,
   // one-time randomization is not supported.
   std::unique_ptr<const FieldTrial::EntropyProvider> entropy_provider_;
diff --git a/base/metrics/histogram.cc b/base/metrics/histogram.cc
index 99524406..6e36e3f 100644
--- a/base/metrics/histogram.cc
+++ b/base/metrics/histogram.cc
@@ -650,20 +650,6 @@
   return true;
 }
 
-// Use the actual bucket widths (like a linear histogram) until the widths get
-// over some transition value, and then use that transition width.  Exponentials
-// get so big so fast (and we don't expect to see a lot of entries in the large
-// buckets), so we need this to make it possible to see what is going on and
-// not have 0-graphical-height buckets.
-double Histogram::GetBucketSize(Count current, uint32_t i) const {
-  DCHECK_GT(ranges(i + 1), ranges(i));
-  static const double kTransitionWidth = 5;
-  double denominator = ranges(i + 1) - ranges(i);
-  if (denominator > kTransitionWidth)
-    denominator = kTransitionWidth;  // Stop trying to normalize.
-  return current/denominator;
-}
-
 const std::string Histogram::GetAsciiBucketRange(uint32_t i) const {
   return GetSimpleAsciiBucketRange(ranges(i));
 }
@@ -719,8 +705,14 @@
 
   // Prepare to normalize graphical rendering of bucket contents.
   double max_size = 0;
+  double scaling_factor = 1;
   if (graph_it)
     max_size = GetPeakBucketSize(snapshot);
+  // Scale histogram bucket counts to take at most 72 characters.
+  // Note: Keep in sync w/ kLineLength sparse_histogram.cc
+  const double kLineLength = 72;
+  if (max_size > kLineLength)
+    scaling_factor = kLineLength / max_size;
 
   // Calculate space needed to print bucket range numbers.  Leave room to print
   // nearly the largest bucket range without sliding over the histogram.
@@ -762,9 +754,9 @@
       output->append(newline);
       continue;  // No reason to plot emptiness.
     }
-    double current_size = GetBucketSize(current, i);
+    Count current_size = round(current * scaling_factor);
     if (graph_it)
-      WriteAsciiBucketGraph(current_size, max_size, output);
+      WriteAsciiBucketGraph(current_size, kLineLength, output);
     WriteAsciiBucketContext(past, current, remaining, i, output);
     output->append(newline);
     past += current;
@@ -773,11 +765,11 @@
 }
 
 double Histogram::GetPeakBucketSize(const SampleVectorBase& samples) const {
-  double max = 0;
+  Count max = 0;
   for (uint32_t i = 0; i < bucket_count() ; ++i) {
-    double current_size = GetBucketSize(samples.GetCountAtIndex(i), i);
-    if (current_size > max)
-      max = current_size;
+    Count current = samples.GetCountAtIndex(i);
+    if (current > max)
+      max = current;
   }
   return max;
 }
@@ -980,14 +972,6 @@
                 meta,
                 logged_meta) {}
 
-double LinearHistogram::GetBucketSize(Count current, uint32_t i) const {
-  DCHECK_GT(ranges(i + 1), ranges(i));
-  // Adjacent buckets with different widths would have "surprisingly" many (few)
-  // samples in a histogram if we didn't normalize this way.
-  double denominator = ranges(i + 1) - ranges(i);
-  return current/denominator;
-}
-
 const std::string LinearHistogram::GetAsciiBucketRange(uint32_t i) const {
   int range = ranges(i);
   BucketDescriptionMap::const_iterator it = bucket_description_.find(range);
@@ -1343,12 +1327,6 @@
     pickle->WriteInt(bucket_ranges()->range(i));
 }
 
-double CustomHistogram::GetBucketSize(Count current, uint32_t i) const {
-  // If this is a histogram of enum values, normalizing the bucket count
-  // by the bucket range is not helpful, so just return the bucket count.
-  return current;
-}
-
 // static
 HistogramBase* CustomHistogram::DeserializeInfoImpl(PickleIterator* iter) {
   std::string histogram_name;
diff --git a/base/metrics/histogram.h b/base/metrics/histogram.h
index 98c83bb..c43a80c 100644
--- a/base/metrics/histogram.h
+++ b/base/metrics/histogram.h
@@ -261,9 +261,6 @@
   // Method to override to skip the display of the i'th bucket if it's empty.
   virtual bool PrintEmptyBucket(uint32_t index) const;
 
-  // Get normalized size, relative to the ranges(i).
-  virtual double GetBucketSize(Count current, uint32_t i) const;
-
   // Return a string description of what goes in a given bucket.
   // Most commonly this is the numeric value, but in derived classes it may
   // be a name (or string description) given to the bucket.
@@ -275,6 +272,7 @@
   FRIEND_TEST_ALL_PREFIXES(HistogramTest, BoundsTest);
   FRIEND_TEST_ALL_PREFIXES(HistogramTest, BucketPlacementTest);
   FRIEND_TEST_ALL_PREFIXES(HistogramTest, CorruptSampleCounts);
+  FRIEND_TEST_ALL_PREFIXES(HistogramTest, GetPeakBucketSize);
 
   friend class StatisticsRecorder;  // To allow it to delete duplicates.
   friend class StatisticsRecorderTest;
@@ -421,8 +419,6 @@
                   HistogramSamples::Metadata* meta,
                   HistogramSamples::Metadata* logged_meta);
 
-  double GetBucketSize(Count current, uint32_t i) const override;
-
   // If we have a description for a bucket, then return that.  Otherwise
   // let parent class provide a (numeric) description.
   const std::string GetAsciiBucketRange(uint32_t i) const override;
@@ -598,8 +594,6 @@
   // HistogramBase implementation:
   void SerializeInfoImpl(base::Pickle* pickle) const override;
 
-  double GetBucketSize(Count current, uint32_t i) const override;
-
  private:
   friend BASE_EXPORT HistogramBase* DeserializeHistogramInfo(
       base::PickleIterator* iter);
diff --git a/base/metrics/histogram_base.cc b/base/metrics/histogram_base.cc
index 2b006d02..42931fd 100644
--- a/base/metrics/histogram_base.cc
+++ b/base/metrics/histogram_base.cc
@@ -203,13 +203,10 @@
   }
 }
 
-void HistogramBase::WriteAsciiBucketGraph(double current_size,
-                                          double max_size,
+void HistogramBase::WriteAsciiBucketGraph(double x_count,
+                                          int line_length,
                                           std::string* output) const {
-  const int k_line_length = 72;  // Maximal horizontal width of graph.
-  int x_count = static_cast<int>(k_line_length * (current_size / max_size)
-                                 + 0.5);
-  int x_remainder = k_line_length - x_count;
+  int x_remainder = line_length - x_count;
 
   while (0 < x_count--)
     output->append("-");
diff --git a/base/metrics/histogram_base.h b/base/metrics/histogram_base.h
index e380859..5f15a39 100644
--- a/base/metrics/histogram_base.h
+++ b/base/metrics/histogram_base.h
@@ -281,9 +281,9 @@
                              int64_t* sum,
                              ListValue* buckets) const;
 
-  //// Produce actual graph (set of blank vs non blank char's) for a bucket.
-  void WriteAsciiBucketGraph(double current_size,
-                             double max_size,
+  // Produces an actual graph (set of blank vs non blank char's) for a bucket.
+  void WriteAsciiBucketGraph(double x_count,
+                             int line_length,
                              std::string* output) const;
 
   // Return a string description of what goes in a given bucket.
diff --git a/base/metrics/histogram_base_unittest.cc b/base/metrics/histogram_base_unittest.cc
index 5eded44..d89e99e 100644
--- a/base/metrics/histogram_base_unittest.cc
+++ b/base/metrics/histogram_base_unittest.cc
@@ -274,4 +274,28 @@
   EXPECT_EQ(add_count, samples->GetCount(0));
 }
 
+// Tests GetPeakBucketSize() returns accurate max bucket size.
+TEST(HistogramTest, GetPeakBucketSize) {
+  Histogram* histogram = static_cast<Histogram*>(
+      Histogram::FactoryGet("Histogram",
+                            /*minimum=*/1,
+                            /*maximum=*/64,
+                            /*bucket_count=*/8,
+                            /*flags=*/HistogramBase::kNoFlags));
+
+  // Add 1 sample to 0th bucket; 2 to 6th; 3 to 313th.
+  for (int i = 0; i < 1; i++) {
+    histogram->Add(0);
+  }
+  for (int i = 0; i < 2; i++) {
+    histogram->Add(6);
+  }
+  for (int i = 0; i < 3; i++) {
+    histogram->Add(313);
+  }
+
+  // The largest bucket size should be 3.
+  EXPECT_EQ(3, histogram->GetPeakBucketSize(*histogram->SnapshotAllSamples()));
+}
+
 }  // namespace base
diff --git a/base/metrics/histogram_unittest.cc b/base/metrics/histogram_unittest.cc
index 83a2c89..da050c40 100644
--- a/base/metrics/histogram_unittest.cc
+++ b/base/metrics/histogram_unittest.cc
@@ -967,7 +967,7 @@
   const char kOutputFormatRe[] =
       R"(Histogram: AsciiOut recorded 5 samples, mean = 4\.0.*\n)"
       R"(0  \.\.\. \n)"
-      R"(4  -+O \(5 = 100\.0%\) \{0\.0%\}\n)"
+      R"(4  -+O \s* \(5 = 100\.0%\) \{0\.0%\}\n)"
       R"(7  \.\.\. \n)";
 
   EXPECT_THAT(output, testing::MatchesRegex(kOutputFormatRe));
@@ -985,9 +985,41 @@
 
   const char kOutputHeaderFormatRe[] =
       R"(Histogram: HTMLOut recorded 5 samples, mean = 4\.0.*)";
-  const char kOutputBodyFormatRe[] = R"(0  \.\.\. \n)"
-                                     R"(4  -+O \(5 = 100\.0%\) \{0\.0%\}\n)"
-                                     R"(7  \.\.\. \n)";
+  const char kOutputBodyFormatRe[] =
+      R"(0  \.\.\. \n)"
+      R"(4  -+O \s*  \(5 = 100\.0%\) \{0\.0%\}\n)"
+      R"(7  \.\.\. \n)";
+
+  EXPECT_THAT(*header, testing::MatchesRegex(kOutputHeaderFormatRe));
+  EXPECT_THAT(*body, testing::MatchesRegex(kOutputBodyFormatRe));
+}
+
+// Tests ToGraphDict() returns deterministic length size and normalizes to
+// scale.
+TEST_P(HistogramTest, ToGraphDictNormalize) {
+  int count_bucket_1 = 80;
+  int value_bucket_1 = 4;
+  int count_bucket_2 = 40;
+  int value_bucket_2 = 5;
+  HistogramBase* histogram =
+      LinearHistogram::FactoryGet("AsciiOut", /*minimum=*/1, /*maximum=*/100,
+                                  /*bucket_count=*/80, HistogramBase::kNoFlags);
+  histogram->AddCount(/*value=*/value_bucket_1, /*count=*/count_bucket_1);
+  histogram->AddCount(/*value=*/value_bucket_2, /*count=*/count_bucket_2);
+
+  base::DictionaryValue output = histogram->ToGraphDict();
+  std::string* header = output.FindStringKey("header");
+  std::string* body = output.FindStringKey("body");
+
+  const char kOutputHeaderFormatRe[] =
+      R"(Histogram: AsciiOut recorded 120 samples, mean = 4\.3.*)";
+  const char kOutputBodyFormatRe[] =
+      R"(0  \.\.\. \n)"
+      R"(4  ---------------------------------------------------)"
+      R"(---------------------O \(80 = 66\.7%\) \{0\.0%\}\n)"
+      R"(5  ----------------)"
+      R"(--------------------O \s* \(40 = 33\.3%\) \{66\.7%\}\n)"
+      R"(6  \.\.\. \n)";
 
   EXPECT_THAT(*header, testing::MatchesRegex(kOutputHeaderFormatRe));
   EXPECT_THAT(*body, testing::MatchesRegex(kOutputBodyFormatRe));
diff --git a/base/metrics/sparse_histogram.cc b/base/metrics/sparse_histogram.cc
index 6d2720c..ad34fc2 100644
--- a/base/metrics/sparse_histogram.cc
+++ b/base/metrics/sparse_histogram.cc
@@ -268,6 +268,12 @@
       largest_count = count;
     it->Next();
   }
+  // Scale histogram bucket counts to take at most 72 characters.
+  // Note: Keep in sync w/ kLineLength histogram.cc
+  const double kLineLength = 72;
+  double scaling_factor = 1;
+  if (largest_count > kLineLength)
+    scaling_factor = kLineLength / largest_count;
   size_t print_width = GetSimpleAsciiBucketRange(largest_sample).size() + 1;
 
   // iterate over each item and display them
@@ -283,9 +289,9 @@
     output->append(range);
     for (size_t j = 0; range.size() + j < print_width + 1; ++j)
       output->push_back(' ');
-
+    Count current_size = round(count * scaling_factor);
     if (graph_it)
-      WriteAsciiBucketGraph(count, largest_count, output);
+      WriteAsciiBucketGraph(current_size, kLineLength, output);
     WriteAsciiBucketValue(count, scaled_total_count, output);
     output->append(newline);
     it->Next();
diff --git a/base/task/thread_pool/thread_pool_impl_unittest.cc b/base/task/thread_pool/thread_pool_impl_unittest.cc
index 83ef569c..b43d4fa 100644
--- a/base/task/thread_pool/thread_pool_impl_unittest.cc
+++ b/base/task/thread_pool/thread_pool_impl_unittest.cc
@@ -1502,9 +1502,17 @@
   TestUpdatePrioritySequenceScheduled(this, ThreadPolicy::MUST_USE_FOREGROUND);
 }
 
+// The test times out flakily on ASAN bots: https://crbug.com/1175764.
+#if defined(ADDRESS_SANITIZER)
+#define MAYBE_UpdatePriorityFromBestEffortNoThreadPolicy \
+  DISABLED_UpdatePriorityFromBestEffortNoThreadPolicy
+#else
+#define MAYBE_UpdatePriorityFromBestEffortNoThreadPolicy \
+  UpdatePriorityFromBestEffortNoThreadPolicy
+#endif
 // Verify that a ThreadPolicy has to be specified in TaskTraits to increase
 // TaskPriority from BEST_EFFORT.
-TEST_P(ThreadPoolImplTest, UpdatePriorityFromBestEffortNoThreadPolicy) {
+TEST_P(ThreadPoolImplTest, MAYBE_UpdatePriorityFromBestEffortNoThreadPolicy) {
   StartThreadPool();
   {
     auto task_runner = thread_pool_->CreateUpdateableSequencedTaskRunner(
diff --git a/base/types/strong_alias.h b/base/types/strong_alias.h
index 1d3b5539..394b01dc 100644
--- a/base/types/strong_alias.h
+++ b/base/types/strong_alias.h
@@ -95,7 +95,7 @@
   constexpr UnderlyingType&& value() && { return std::move(value_); }
   constexpr const UnderlyingType&& value() const&& { return std::move(value_); }
 
-  constexpr explicit operator UnderlyingType() const { return value_; }
+  constexpr explicit operator const UnderlyingType&() const& { return value_; }
 
   constexpr bool operator==(const StrongAlias& other) const {
     return value_ == other.value_;
diff --git a/base/util/type_safety/BUILD.gn b/base/util/type_safety/BUILD.gn
index a535a3f..a8050ea 100644
--- a/base/util/type_safety/BUILD.gn
+++ b/base/util/type_safety/BUILD.gn
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/nocompile.gni")
+
 # Change this target's type to component if it starts to contain more than
 # just headers. Header-only targets cannot be compiled to libraries, so it must
 # remain a source_set for now.
@@ -23,6 +25,18 @@
 
   deps = [
     ":type_safety",
+    "//base",
     "//testing/gtest",
   ]
 }
+
+if (enable_nocompile_tests) {
+  nocompile_test("base_util_typesafety_nocompile_tests") {
+    sources = [ "token_type_unittest.nc" ]
+    deps = [
+      ":type_safety",
+      "//base/test:run_all_unittests",
+      "//testing/gtest",
+    ]
+  }
+}
diff --git a/base/util/type_safety/token_type.h b/base/util/type_safety/token_type.h
index 2b0bf61..c9159f28 100644
--- a/base/util/type_safety/token_type.h
+++ b/base/util/type_safety/token_type.h
@@ -5,6 +5,8 @@
 #ifndef BASE_UTIL_TYPE_SAFETY_TOKEN_TYPE_H_
 #define BASE_UTIL_TYPE_SAFETY_TOKEN_TYPE_H_
 
+#include <type_traits>
+
 #include "base/types/strong_alias.h"
 #include "base/unguessable_token.h"
 
@@ -14,19 +16,34 @@
 // base::UnguessableToken, a TokenType<...> does not default to null and does
 // not expose the concept of null tokens. If you need to indicate a null token,
 // please use base::Optional<TokenType<...>>.
-template <typename TypeMarker>
+template <typename TypeMarker,
+          // Please do not use this unless absolutely necessary! It is only
+          // present in support of ongoing migrations. See crbug.com/1096617.
+          bool kAllowImplicitConversion = false>
 class TokenType : public base::StrongAlias<TypeMarker, base::UnguessableToken> {
  private:
   using Super = base::StrongAlias<TypeMarker, base::UnguessableToken>;
 
  public:
   TokenType() : Super(base::UnguessableToken::Create()) {}
-  explicit TokenType(const base::UnguessableToken& token) : Super(token) {}
+  // The parameter |unused| is here to prevent multiple definitions of a
+  // single argument constructor. This is only needed during the migration to
+  // strongly typed frame tokens.
+  // TODO(crbug.com/1096617): Remove this after the token type migration.
+  explicit TokenType(const base::UnguessableToken& token, int unused = 0)
+      : Super(token) {}
   TokenType(const TokenType& token) : Super(token.value()) {}
   TokenType(TokenType&& token) noexcept : Super(token.value()) {}
   TokenType& operator=(const TokenType& token) = default;
   TokenType& operator=(TokenType&& token) noexcept = default;
 
+  // Allow implicit promotion from base::UnguessableToken if specified.
+  // TODO(crbug.com/1096617): Remove this after the token type migration.
+  template <bool kAllowImplicitConversion2 = kAllowImplicitConversion,
+            typename = std::enable_if_t<kAllowImplicitConversion2>>
+  // NOLINTNEXTLINE(google-explicit-constructor)
+  TokenType(const base::UnguessableToken& token) : Super(token) {}
+
   // This object allows default assignment operators for compatibility with
   // STL containers.
 
@@ -41,6 +58,16 @@
 
   // Mimic the base::UnguessableToken API for ease and familiarity of use.
   std::string ToString() const { return this->value().ToString(); }
+
+  // Allow implicit conversion down to a base::UnguessableToken, if desired.
+  // This is only present during the strongly-typed token migration.
+  // TODO(crbug.com/1096617): Remove this after the token type migration.
+  template <bool kAllowImplicitConversion2 = kAllowImplicitConversion,
+            typename = std::enable_if_t<kAllowImplicitConversion2>>
+  // NOLINTNEXTLINE(google-explicit-constructor)
+  constexpr operator const base::UnguessableToken&() const& {
+    return this->value();
+  }
 };
 
 }  // namespace util
diff --git a/base/util/type_safety/token_type_unittest.cc b/base/util/type_safety/token_type_unittest.cc
index ebab66c1..2cccf50 100644
--- a/base/util/type_safety/token_type_unittest.cc
+++ b/base/util/type_safety/token_type_unittest.cc
@@ -4,11 +4,13 @@
 
 #include "base/util/type_safety/token_type.h"
 
+#include "base/unguessable_token.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace util {
 
 using FooToken = TokenType<class Foo>;
+using ConvertibleToken = TokenType<class Convertible, true>;
 
 TEST(TokenType, TokenApi) {
   // Test default initialization.
@@ -43,4 +45,17 @@
   EXPECT_EQ(token2.ToString(), token2.value().ToString());
 }
 
+TEST(TokenType, ImplicitConversion) {
+  ConvertibleToken token1;
+  base::UnguessableToken token2;
+  EXPECT_FALSE(token1.value().is_empty());
+  EXPECT_TRUE(token2.is_empty());
+
+  token2 = token1;
+  EXPECT_EQ(token1.value(), token2);
+
+  base::UnguessableToken token3(token1);
+  EXPECT_EQ(token1.value(), token3);
+}
+
 }  // namespace util
diff --git a/base/util/type_safety/token_type_unittest.nc b/base/util/type_safety/token_type_unittest.nc
new file mode 100644
index 0000000..d3d0043d
--- /dev/null
+++ b/base/util/type_safety/token_type_unittest.nc
@@ -0,0 +1,29 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This is a "No Compile Test" suite.
+// https://dev.chromium.org/developers/testing/no-compile-tests
+
+#include "base/util/type_safety/token_type.h"
+
+#include "base/unguessable_token.h"
+
+namespace util {
+
+using FooToken = TokenType<class Foo>;
+
+#if defined(TEST_IMPLICIT_CONVERSION_FAILS)
+
+void WontCompile() {
+  FooToken token1;
+
+  // FooToken should not be implicitly convertible to base::UnguessableToken.
+  base::UnguessableToken token2(token1);
+  base::UnguessableToken token3;
+  token3 = token1;
+}
+
+#endif
+
+}  // namespace util
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 9b50228..7360ec9e 100644
--- a/build/android/pylib/local/device/local_device_gtest_run.py
+++ b/build/android/pylib/local/device/local_device_gtest_run.py
@@ -65,6 +65,15 @@
   'net_unittests', 'services_unittests', 'unit_tests'
 ]
 
+# These are use for code coverage.
+_LLVM_PROFDATA_PATH = os.path.join(constants.DIR_SOURCE_ROOT, 'third_party',
+                                   'llvm-build', 'Release+Asserts', 'bin',
+                                   'llvm-profdata')
+# Name of the file extension for profraw data files.
+_PROFRAW_FILE_EXTENSION = 'profraw'
+# Name of the file where profraw data files are merged.
+_MERGE_PROFDATA_FILE_NAME = 'coverage_merged.' + _PROFRAW_FILE_EXTENSION
+
 # No-op context manager. If we used Python 3, we could change this to
 # contextlib.ExitStack()
 class _NullContextManager(object):
@@ -107,6 +116,59 @@
   return patterns
 
 
+def _MergeCoverageFiles(coverage_dir, profdata_dir):
+  """Merge coverage data files.
+
+  Each instrumentation activity generates a separate profraw data file. This
+  merges all profraw files in profdata_dir into a single file in
+  coverage_dir. This happens after each test, rather than waiting until after
+  all tests are ran to reduce the memory footprint used by all the profraw
+  files.
+
+  Args:
+    coverage_dir: The path to the coverage directory.
+    profdata_dir: The directory where the profraw data file(s) are located.
+
+  Return:
+    None
+  """
+  # profdata_dir may not exist if pulling coverage files failed.
+  if not os.path.exists(profdata_dir):
+    logging.debug('Profraw directory does not exist.')
+    return
+
+  merge_file = os.path.join(coverage_dir, _MERGE_PROFDATA_FILE_NAME)
+  profraw_files = [
+      os.path.join(profdata_dir, f) for f in os.listdir(profdata_dir)
+      if f.endswith(_PROFRAW_FILE_EXTENSION)
+  ]
+
+  try:
+    logging.debug('Merging target profraw files into merged profraw file.')
+    subprocess_cmd = [
+        _LLVM_PROFDATA_PATH,
+        'merge',
+        '-o',
+        merge_file,
+        '-sparse=true',
+    ]
+    # Grow the merge file by merging it with itself and the new files.
+    if os.path.exists(merge_file):
+      subprocess_cmd.append(merge_file)
+    subprocess_cmd.extend(profraw_files)
+    output = subprocess.check_output(subprocess_cmd)
+    logging.debug('Merge output: %s', output)
+  except subprocess.CalledProcessError:
+    # Don't raise error as that will kill the test run. When code coverage
+    # generates a report, that will raise the error in the report generation.
+    logging.error(
+        'Failed to merge target profdata files to create merged profraw file.')
+
+  # Free up memory space on bot as all data is in the merge file.
+  for f in profraw_files:
+    os.remove(f)
+
+
 def _PullCoverageFiles(device, device_coverage_dir, output_dir):
   """Pulls coverage files on device to host directory.
 
@@ -262,9 +324,11 @@
         raise
       finally:
         if self._coverage_dir and device_api >= version_codes.LOLLIPOP:
-          _PullCoverageFiles(
-              device, device_coverage_dir,
-              os.path.join(self._coverage_dir, str(self._coverage_index)))
+          with tempfile_ext.NamedTemporaryDirectory(
+              prefix=self._coverage_dir) as temp_d:
+            _PullCoverageFiles(device, device_coverage_dir, temp_d)
+            _MergeCoverageFiles(self._coverage_dir,
+                                os.path.join(temp_d, 'profraw'))
 
       return device.ReadFile(stdout_file.name).splitlines()
 
diff --git a/build/chromeos/test_runner.py b/build/chromeos/test_runner.py
index be0a389be..fc80a78f 100755
--- a/build/chromeos/test_runner.py
+++ b/build/chromeos/test_runner.py
@@ -585,12 +585,21 @@
       device_test_script_contents += ['export %s=%s' % (var_name, var_val)]
 
     if self._vpython_dir:
+      vpython_path = os.path.join(self._path_to_outdir, self._vpython_dir,
+                                  'vpython')
+      cpython_path = os.path.join(self._path_to_outdir, self._vpython_dir,
+                                  'bin', 'python')
+      if not os.path.exists(vpython_path) or not os.path.exists(cpython_path):
+        raise TestFormatError(
+            '--vpython-dir must point to a dir with both infra/python/cpython '
+            'and infra/tools/luci/vpython installed.')
       vpython_spec_path = os.path.relpath(
           os.path.join(CHROMIUM_SRC_PATH, '.vpython'), self._path_to_outdir)
       # Initialize the vpython cache. This can take 10-20s, and some tests
       # can't afford to wait that long on the first invocation.
       device_test_script_contents.extend([
-          'export PATH=$PATH:$PWD/%s' % (self._vpython_dir),
+          'export PATH=$PWD/%s:$PWD/%s/bin/:$PATH' %
+          (self._vpython_dir, self._vpython_dir),
           'vpython -vpython-spec %s -vpython-tool install' %
           (vpython_spec_path),
       ])
diff --git a/build/chromeos/test_runner_test.py b/build/chromeos/test_runner_test.py
index 067529b..b3166b47 100755
--- a/build/chromeos/test_runner_test.py
+++ b/build/chromeos/test_runner_test.py
@@ -261,6 +261,31 @@
           '--test-launcher-shard-index=0 --test-launcher-total-shards=1\n')
       mock_remove.assert_called_once_with('out_eve/Release/device_script.sh')
 
+  def test_gtest_with_vpython(self):
+    """Tests building a gtest with --vpython-dir."""
+    args = mock.MagicMock()
+    args.test_exe = 'base_unittests'
+    args.test_launcher_summary_output = None
+    args.trace_dir = None
+    args.runtime_deps_path = None
+    args.path_to_outdir = self._tmp_dir
+    args.vpython_dir = self._tmp_dir
+
+    # With vpython_dir initially empty, the test_runner should error out
+    # due to missing vpython binaries.
+    gtest = test_runner.GTestTest(args, None)
+    with self.assertRaises(test_runner.TestFormatError):
+      gtest.build_test_command()
+
+    # Create the two expected tools, and the test should be ready to run.
+    with open(os.path.join(args.vpython_dir, 'vpython'), 'w'):
+      pass  # Just touch the file.
+    os.mkdir(os.path.join(args.vpython_dir, 'bin'))
+    with open(os.path.join(args.vpython_dir, 'bin', 'python'), 'w'):
+      pass
+    gtest = test_runner.GTestTest(args, None)
+    gtest.build_test_command()
+
 
 if __name__ == '__main__':
   unittest.main()
diff --git a/cc/metrics/compositor_frame_reporter.cc b/cc/metrics/compositor_frame_reporter.cc
index 7a4c0f6c..f60a99b1 100644
--- a/cc/metrics/compositor_frame_reporter.cc
+++ b/cc/metrics/compositor_frame_reporter.cc
@@ -297,10 +297,8 @@
     static_cast<int>(EventMetrics::EventType::kMaxValue) + 1;
 constexpr int kEventLatencyScrollTypeCount =
     static_cast<int>(EventMetrics::ScrollType::kMaxValue) + 1;
-constexpr int kMaxEventLatencyHistogramBaseIndex =
-    kEventLatencyEventTypeCount * kEventLatencyScrollTypeCount;
 constexpr int kMaxEventLatencyHistogramIndex =
-    kMaxEventLatencyHistogramBaseIndex * (kStageTypeCount + kAllBreakdownCount);
+    kEventLatencyEventTypeCount * kEventLatencyScrollTypeCount;
 constexpr base::TimeDelta kEventLatencyHistogramMin =
     base::TimeDelta::FromMicroseconds(1);
 constexpr base::TimeDelta kEventLatencyHistogramMax =
@@ -953,6 +951,14 @@
 }
 
 void CompositorFrameReporter::ReportEventLatencyHistograms() const {
+  const StageData& total_latency_stage = stage_history_.back();
+  DCHECK_EQ(StageType::kTotalLatency, total_latency_stage.stage_type);
+
+  const std::string total_latency_stage_name =
+      GetStageName(static_cast<int>(StageType::kTotalLatency));
+  const std::string total_latency_histogram_name =
+      "EventLatency." + total_latency_stage_name;
+
   for (const auto& event_metrics : events_metrics_) {
     DCHECK(event_metrics);
     const std::string histogram_base_name =
@@ -962,12 +968,13 @@
         event_metrics->scroll_type()
             ? static_cast<int>(*event_metrics->scroll_type())
             : 0;
-    const int histogram_base_index =
+    const int event_histogram_index =
         event_type_index * kEventLatencyScrollTypeCount + scroll_type_index;
 
     const base::TimeTicks generated_timestamp =
         event_metrics->GetDispatchStageTimestamp(
             EventMetrics::DispatchStage::kGenerated);
+    DCHECK_LT(generated_timestamp, total_latency_stage.end_time);
 
     // For scroll events, report total latency up to gpu-swap-begin. This is
     // useful in comparing new EventLatency metrics with LatencyInfo-based
@@ -976,86 +983,43 @@
         !viz_breakdown_.swap_timings.is_null()) {
       const base::TimeDelta swap_begin_latency =
           viz_breakdown_.swap_timings.swap_start - generated_timestamp;
-      const std::string swap_begin_histogram_name =
+      const std::string event_swap_begin_histogram_name =
           histogram_base_name + ".TotalLatencyToSwapBegin";
-      // Note: There's a 1:1 mapping between `histogram_base_index` and
-      // `swap_begin_histogram_name` which allows the use of
+      // Note: There's a 1:1 mapping between `event_histogram_index` and
+      // `event_swap_begin_histogram_name` which allows the use of
       // `STATIC_HISTOGRAM_POINTER_GROUP()` to cache histogram objects.
       STATIC_HISTOGRAM_POINTER_GROUP(
-          swap_begin_histogram_name, histogram_base_index,
-          kMaxEventLatencyHistogramBaseIndex,
+          event_swap_begin_histogram_name, event_histogram_index,
+          kMaxEventLatencyHistogramIndex,
           AddTimeMicrosecondsGranularity(swap_begin_latency),
           base::Histogram::FactoryMicrosecondsTimeGet(
-              swap_begin_histogram_name, kEventLatencyHistogramMin,
+              event_swap_begin_histogram_name, kEventLatencyHistogramMin,
               kEventLatencyHistogramMax, kEventLatencyHistogramBucketCount,
               base::HistogramBase::kUmaTargetedHistogramFlag));
     }
 
-    // It is possible for an event to arrive in the compositor in the middle of
-    // a frame (e.g. the browser received the event *after* renderer received a
-    // begin-impl, and the event reached the compositor before that frame
-    // ended). To handle such cases, find the first stage that happens after the
-    // event's arrival in the browser.
-    auto stage_it =
-        std::find_if(stage_history_.begin(), stage_history_.end(),
-                     [generated_timestamp](const StageData& stage) {
-                       return stage.start_time > generated_timestamp;
-                     });
-    // TODO(crbug.com/1079116): Ideally, at least the start time of
-    // SubmitCompositorFrameToPresentationCompositorFrame stage should be
-    // greater than the event time stamp, but apparently, this is not always the
-    // case (see crbug.com/1093698). For now, skip to the next event in such
-    // cases. Hopefully, the work to reduce discrepancies between the new
-    // EventLatency and the old Event.Latency metrics would fix this issue. If
-    // not, we need to reconsider investigating this issue.
-    if (stage_it == stage_history_.end())
-      continue;
-
-    const base::TimeDelta b2r_latency =
-        stage_it->start_time - generated_timestamp;
-    const std::string b2r_histogram_name =
-        histogram_base_name + ".BrowserToRendererCompositor";
-    // Note: There's a 1:1 mapping between `histogram_base_index` and
-    // `b2r_histogram_name` which allows the use of
+    // Report total latency up to presentation for the event.
+    const base::TimeDelta total_latency =
+        total_latency_stage.end_time - generated_timestamp;
+    const std::string event_total_latency_histogram_name =
+        base::StrCat({histogram_base_name, ".", total_latency_stage_name});
+    // Note: There's a 1:1 mapping between `event_histogram_index` and
+    // `event_total_latency_histogram_name` which allows the use of
     // `STATIC_HISTOGRAM_POINTER_GROUP()` to cache histogram objects.
     STATIC_HISTOGRAM_POINTER_GROUP(
-        b2r_histogram_name, histogram_base_index,
-        kMaxEventLatencyHistogramBaseIndex,
-        AddTimeMicrosecondsGranularity(b2r_latency),
+        event_total_latency_histogram_name, event_histogram_index,
+        kMaxEventLatencyHistogramIndex,
+        AddTimeMicrosecondsGranularity(total_latency),
         base::Histogram::FactoryMicrosecondsTimeGet(
-            b2r_histogram_name, kEventLatencyHistogramMin,
+            event_total_latency_histogram_name, kEventLatencyHistogramMin,
             kEventLatencyHistogramMax, kEventLatencyHistogramBucketCount,
             base::HistogramBase::kUmaTargetedHistogramFlag));
 
-    for (; stage_it != stage_history_.end(); ++stage_it) {
-      // Total latency is calculated since the event timestamp.
-      const base::TimeTicks start_time =
-          stage_it->stage_type == StageType::kTotalLatency
-              ? generated_timestamp
-              : stage_it->start_time;
-      const base::TimeDelta latency = stage_it->end_time - start_time;
-      const int stage_type_index = static_cast<int>(stage_it->stage_type);
-      ReportEventLatencyHistogram(histogram_base_index, histogram_base_name,
-                                  stage_type_index, latency);
-
-      switch (stage_it->stage_type) {
-        case StageType::kSendBeginMainFrameToCommit:
-          ReportEventLatencyBlinkBreakdowns(histogram_base_index,
-                                            histogram_base_name);
-          break;
-        case StageType::kSubmitCompositorFrameToPresentationCompositorFrame:
-          ReportEventLatencyVizBreakdowns(histogram_base_index,
-                                          histogram_base_name);
-          break;
-        case StageType::kTotalLatency:
-          UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
-              "EventLatency.TotalLatency", latency, kEventLatencyHistogramMin,
-              kEventLatencyHistogramMax, kEventLatencyHistogramBucketCount);
-          break;
-        default:
-          break;
-      }
-    }
+    // Also, report total latency up to presentation for all event types in an
+    // aggregate histogram.
+    UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
+        total_latency_histogram_name, total_latency, kEventLatencyHistogramMin,
+        kEventLatencyHistogramMax, kEventLatencyHistogramBucketCount);
   }
 
   if (latency_ukm_reporter_) {
@@ -1065,54 +1029,6 @@
   }
 }
 
-void CompositorFrameReporter::ReportEventLatencyBlinkBreakdowns(
-    int histogram_base_index,
-    const std::string& histogram_base_name) const {
-  DCHECK(processed_blink_breakdown_);
-  for (auto it = processed_blink_breakdown_->CreateIterator(); it.IsValid();
-       it.Advance()) {
-    ReportEventLatencyHistogram(
-        histogram_base_index, histogram_base_name,
-        kBlinkBreakdownInitialIndex + static_cast<size_t>(it.GetBreakdown()),
-        it.GetLatency());
-  }
-}
-
-void CompositorFrameReporter::ReportEventLatencyVizBreakdowns(
-    int histogram_base_index,
-    const std::string& histogram_base_name) const {
-  DCHECK(processed_viz_breakdown_);
-  for (auto it = processed_viz_breakdown_->CreateIterator(false); it.IsValid();
-       it.Advance()) {
-    ReportEventLatencyHistogram(
-        histogram_base_index, histogram_base_name,
-        kVizBreakdownInitialIndex + static_cast<size_t>(it.GetBreakdown()),
-        it.GetDuration());
-  }
-}
-
-void CompositorFrameReporter::ReportEventLatencyHistogram(
-    int histogram_base_index,
-    const std::string& histogram_base_name,
-    int stage_type_index,
-    base::TimeDelta latency) const {
-  const std::string histogram_name =
-      base::StrCat({histogram_base_name, ".", GetStageName(stage_type_index)});
-  const int histogram_index =
-      histogram_base_index * (kStageTypeCount + kAllBreakdownCount) +
-      stage_type_index;
-  // Note: There's a 1:1 mapping between `histogram_index` and `histogram_name`
-  // which allows the use of `STATIC_HISTOGRAM_POINTER_GROUP()` to cache
-  // histogram objects.
-  STATIC_HISTOGRAM_POINTER_GROUP(
-      histogram_name, histogram_index, kMaxEventLatencyHistogramIndex,
-      AddTimeMicrosecondsGranularity(latency),
-      base::Histogram::FactoryMicrosecondsTimeGet(
-          histogram_name, kEventLatencyHistogramMin, kEventLatencyHistogramMax,
-          kEventLatencyHistogramBucketCount,
-          base::HistogramBase::kUmaTargetedHistogramFlag));
-}
-
 void CompositorFrameReporter::ReportCompositorLatencyTraceEvents() const {
   if (stage_history_.empty())
     return;
diff --git a/cc/metrics/compositor_frame_reporter.h b/cc/metrics/compositor_frame_reporter.h
index 01ef2fe..0e9f0001 100644
--- a/cc/metrics/compositor_frame_reporter.h
+++ b/cc/metrics/compositor_frame_reporter.h
@@ -333,16 +333,6 @@
       base::TimeDelta time_delta) const;
 
   void ReportEventLatencyHistograms() const;
-  void ReportEventLatencyBlinkBreakdowns(
-      int histogram_base_index,
-      const std::string& histogram_base_name) const;
-  void ReportEventLatencyVizBreakdowns(
-      int histogram_base_index,
-      const std::string& histogram_base_name) const;
-  void ReportEventLatencyHistogram(int histogram_base_index,
-                                   const std::string& histogram_base_name,
-                                   int stage_type_index,
-                                   base::TimeDelta latency) const;
 
   void ReportCompositorLatencyTraceEvents() const;
   void ReportEventLatencyTraceEvents() const;
diff --git a/cc/metrics/compositor_frame_reporter_unittest.cc b/cc/metrics/compositor_frame_reporter_unittest.cc
index d43ea31..e7a38ba 100644
--- a/cc/metrics/compositor_frame_reporter_unittest.cc
+++ b/cc/metrics/compositor_frame_reporter_unittest.cc
@@ -344,152 +344,6 @@
   }
 }
 
-// Tests that when a frame is presented to the user, event latency breakdown
-// metrics are reported properly.
-TEST_F(CompositorFrameReporterTest,
-       EventLatencyBreakdownsForPresentedFrameReported) {
-  base::HistogramTester histogram_tester;
-
-  std::unique_ptr<EventMetrics> event_metrics_ptrs[] = {
-      CreateEventMetrics(ui::ET_TOUCH_PRESSED, base::nullopt, base::nullopt),
-  };
-  EXPECT_THAT(event_metrics_ptrs, Each(NotNull()));
-  EventMetrics::List events_metrics(
-      std::make_move_iterator(std::begin(event_metrics_ptrs)),
-      std::make_move_iterator(std::end(event_metrics_ptrs)));
-  std::vector<base::TimeTicks> event_times = GetEventTimestamps(events_metrics);
-
-  auto begin_impl_time = AdvanceNowByMs(2);
-  pipeline_reporter_->StartStage(
-      CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame,
-      begin_impl_time);
-
-  auto begin_main_time = AdvanceNowByMs(3);
-  pipeline_reporter_->StartStage(
-      CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit,
-      begin_main_time);
-
-  auto begin_main_start_time = AdvanceNowByMs(4);
-  std::unique_ptr<BeginMainFrameMetrics> blink_breakdown =
-      BuildBlinkBreakdown();
-  // Make a copy of the breakdown to use in verifying expectations in the end.
-  BeginMainFrameMetrics blink_breakdown_copy = *blink_breakdown;
-  pipeline_reporter_->SetBlinkBreakdown(std::move(blink_breakdown),
-                                        begin_main_start_time);
-  auto begin_commit_time = AdvanceNowByMs(5);
-  pipeline_reporter_->StartStage(CompositorFrameReporter::StageType::kCommit,
-                                 begin_commit_time);
-
-  auto end_commit_time = AdvanceNowByMs(6);
-  pipeline_reporter_->StartStage(
-      CompositorFrameReporter::StageType::kEndCommitToActivation,
-      end_commit_time);
-
-  auto begin_activation_time = AdvanceNowByMs(7);
-  pipeline_reporter_->StartStage(
-      CompositorFrameReporter::StageType::kActivation, begin_activation_time);
-
-  auto end_activation_time = AdvanceNowByMs(8);
-  pipeline_reporter_->StartStage(
-      CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame,
-      end_activation_time);
-
-  auto submit_time = AdvanceNowByMs(9);
-  pipeline_reporter_->StartStage(
-      CompositorFrameReporter::StageType::
-          kSubmitCompositorFrameToPresentationCompositorFrame,
-      submit_time);
-  pipeline_reporter_->SetEventsMetrics(std::move(events_metrics));
-
-  AdvanceNowByMs(10);
-  viz::FrameTimingDetails viz_breakdown = BuildVizBreakdown();
-  pipeline_reporter_->SetVizBreakdown(viz_breakdown);
-  pipeline_reporter_->TerminateFrame(
-      CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame,
-      viz_breakdown.presentation_feedback.timestamp);
-
-  pipeline_reporter_ = nullptr;
-
-  struct {
-    const char* name;
-    const base::TimeDelta latency;
-  } expected_latencies[] = {
-      {"EventLatency.TouchPressed.BrowserToRendererCompositor",
-       begin_impl_time - event_times[0]},
-      {"EventLatency.TouchPressed.BeginImplFrameToSendBeginMainFrame",
-       begin_main_time - begin_impl_time},
-      {"EventLatency.TouchPressed.SendBeginMainFrameToCommit",
-       begin_commit_time - begin_main_time},
-      {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.HandleInputEvents",
-       blink_breakdown_copy.handle_input_events},
-      {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.Animate",
-       blink_breakdown_copy.animate},
-      {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.StyleUpdate",
-       blink_breakdown_copy.style_update},
-      {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.LayoutUpdate",
-       blink_breakdown_copy.layout_update},
-      {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.CompositingInputs",
-       blink_breakdown_copy.compositing_inputs},
-      {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.Prepaint",
-       blink_breakdown_copy.prepaint},
-      {"EventLatency.TouchPressed.SendBeginMainFrameToCommit"
-       ".CompositingAssignments",
-       blink_breakdown_copy.compositing_assignments},
-      {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.Paint",
-       blink_breakdown_copy.paint},
-      {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.CompositeCommit",
-       blink_breakdown_copy.composite_commit},
-      {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.UpdateLayers",
-       blink_breakdown_copy.update_layers},
-      {"EventLatency.TouchPressed.SendBeginMainFrameToCommit."
-       "BeginMainSentToStarted",
-       begin_main_start_time - begin_main_time},
-      {"EventLatency.TouchPressed.Commit", end_commit_time - begin_commit_time},
-      {"EventLatency.TouchPressed.EndCommitToActivation",
-       begin_activation_time - end_commit_time},
-      {"EventLatency.TouchPressed.Activation",
-       end_activation_time - begin_activation_time},
-      {"EventLatency.TouchPressed.EndActivateToSubmitCompositorFrame",
-       submit_time - end_activation_time},
-      {"EventLatency.TouchPressed."
-       "SubmitCompositorFrameToPresentationCompositorFrame",
-       viz_breakdown.presentation_feedback.timestamp - submit_time},
-      {"EventLatency.TouchPressed."
-       "SubmitCompositorFrameToPresentationCompositorFrame."
-       "SubmitToReceiveCompositorFrame",
-       viz_breakdown.received_compositor_frame_timestamp - submit_time},
-      {"EventLatency.TouchPressed."
-       "SubmitCompositorFrameToPresentationCompositorFrame."
-       "ReceivedCompositorFrameToStartDraw",
-       viz_breakdown.draw_start_timestamp -
-           viz_breakdown.received_compositor_frame_timestamp},
-      {"EventLatency.TouchPressed."
-       "SubmitCompositorFrameToPresentationCompositorFrame."
-       "StartDrawToSwapStart",
-       viz_breakdown.swap_timings.swap_start -
-           viz_breakdown.draw_start_timestamp},
-      {"EventLatency.TouchPressed."
-       "SubmitCompositorFrameToPresentationCompositorFrame.SwapStartToSwapEnd",
-       viz_breakdown.swap_timings.swap_end -
-           viz_breakdown.swap_timings.swap_start},
-      {"EventLatency.TouchPressed."
-       "SubmitCompositorFrameToPresentationCompositorFrame."
-       "SwapEndToPresentationCompositorFrame",
-       viz_breakdown.presentation_feedback.timestamp -
-           viz_breakdown.swap_timings.swap_end},
-      {"EventLatency.TouchPressed.TotalLatency",
-       viz_breakdown.presentation_feedback.timestamp - event_times[0]},
-      {"EventLatency.TotalLatency",
-       viz_breakdown.presentation_feedback.timestamp - event_times[0]},
-  };
-
-  for (const auto& expected_latency : expected_latencies) {
-    histogram_tester.ExpectTotalCount(expected_latency.name, 1);
-    histogram_tester.ExpectBucketCount(
-        expected_latency.name, expected_latency.latency.InMicroseconds(), 1);
-  }
-}
-
 // Tests that when a frame is presented to the user, total scroll event latency
 // metrics are reported properly.
 TEST_F(CompositorFrameReporterTest,
diff --git a/cc/metrics/compositor_frame_reporting_controller_unittest.cc b/cc/metrics/compositor_frame_reporting_controller_unittest.cc
index d0bffcb..8f13ac70 100644
--- a/cc/metrics/compositor_frame_reporting_controller_unittest.cc
+++ b/cc/metrics/compositor_frame_reporting_controller_unittest.cc
@@ -1159,120 +1159,6 @@
   }
 }
 
-// Tests that EventLatency breakdown histograms are reported properly when a
-// frame is presented to the user.
-TEST_F(CompositorFrameReportingControllerTest,
-       EventLatencyBreakdownsForPresentedFrameReported) {
-  base::HistogramTester histogram_tester;
-
-  std::unique_ptr<EventMetrics> event_metrics_ptrs[] = {
-      CreateEventMetrics(ui::ET_TOUCH_PRESSED, base::nullopt, base::nullopt),
-  };
-  EXPECT_THAT(event_metrics_ptrs, Each(NotNull()));
-  EventMetrics::List events_metrics(
-      std::make_move_iterator(std::begin(event_metrics_ptrs)),
-      std::make_move_iterator(std::end(event_metrics_ptrs)));
-  std::vector<base::TimeTicks> event_times = GetEventTimestamps(events_metrics);
-
-  // Do a commit with a breakdown of blink stages.
-  std::unique_ptr<BeginMainFrameMetrics> blink_breakdown =
-      BuildBlinkBreakdown();
-  // Make a copy of the breakdown to use in verifying expectations in the end.
-  BeginMainFrameMetrics blink_breakdown_copy = *blink_breakdown;
-  SimulateCommit(std::move(blink_breakdown));
-
-  // Submit a compositor frame and notify CompositorFrameReporter of the events
-  // affecting the frame.
-  ++next_token_;
-  SimulateSubmitCompositorFrame(*next_token_, {std::move(events_metrics), {}});
-
-  // Present the submitted compositor frame to the user.
-  AdvanceNowByMs(10);
-  viz::FrameTimingDetails viz_breakdown = BuildVizBreakdown();
-  reporting_controller_.DidPresentCompositorFrame(*next_token_, viz_breakdown);
-
-  // Verify that EventLatency histograms are recorded.
-  struct {
-    const char* name;
-    const base::TimeDelta latency;
-  } expected_latencies[] = {
-      {"EventLatency.TouchPressed.BrowserToRendererCompositor",
-       begin_impl_time_ - event_times[0]},
-      {"EventLatency.TouchPressed.BeginImplFrameToSendBeginMainFrame",
-       begin_main_time_ - begin_impl_time_},
-      {"EventLatency.TouchPressed.SendBeginMainFrameToCommit",
-       begin_commit_time_ - begin_main_time_},
-      {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.HandleInputEvents",
-       blink_breakdown_copy.handle_input_events},
-      {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.Animate",
-       blink_breakdown_copy.animate},
-      {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.StyleUpdate",
-       blink_breakdown_copy.style_update},
-      {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.LayoutUpdate",
-       blink_breakdown_copy.layout_update},
-      {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.CompositingInputs",
-       blink_breakdown_copy.compositing_inputs},
-      {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.Prepaint",
-       blink_breakdown_copy.prepaint},
-      {"EventLatency.TouchPressed.SendBeginMainFrameToCommit."
-       "CompositingAssignments",
-       blink_breakdown_copy.compositing_assignments},
-      {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.Paint",
-       blink_breakdown_copy.paint},
-      {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.CompositeCommit",
-       blink_breakdown_copy.composite_commit},
-      {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.UpdateLayers",
-       blink_breakdown_copy.update_layers},
-      {"EventLatency.TouchPressed.SendBeginMainFrameToCommit."
-       "BeginMainSentToStarted",
-       begin_main_start_time_ - begin_main_time_},
-      {"EventLatency.TouchPressed.Commit",
-       end_commit_time_ - begin_commit_time_},
-      {"EventLatency.TouchPressed.EndCommitToActivation",
-       begin_activation_time_ - end_commit_time_},
-      {"EventLatency.TouchPressed.Activation",
-       end_activation_time_ - begin_activation_time_},
-      {"EventLatency.TouchPressed.EndActivateToSubmitCompositorFrame",
-       submit_time_ - end_activation_time_},
-      {"EventLatency.TouchPressed."
-       "SubmitCompositorFrameToPresentationCompositorFrame",
-       viz_breakdown.presentation_feedback.timestamp - submit_time_},
-      {"EventLatency.TouchPressed."
-       "SubmitCompositorFrameToPresentationCompositorFrame."
-       "SubmitToReceiveCompositorFrame",
-       viz_breakdown.received_compositor_frame_timestamp - submit_time_},
-      {"EventLatency.TouchPressed."
-       "SubmitCompositorFrameToPresentationCompositorFrame."
-       "ReceivedCompositorFrameToStartDraw",
-       viz_breakdown.draw_start_timestamp -
-           viz_breakdown.received_compositor_frame_timestamp},
-      {"EventLatency.TouchPressed."
-       "SubmitCompositorFrameToPresentationCompositorFrame."
-       "StartDrawToSwapStart",
-       viz_breakdown.swap_timings.swap_start -
-           viz_breakdown.draw_start_timestamp},
-      {"EventLatency.TouchPressed."
-       "SubmitCompositorFrameToPresentationCompositorFrame.SwapStartToSwapEnd",
-       viz_breakdown.swap_timings.swap_end -
-           viz_breakdown.swap_timings.swap_start},
-      {"EventLatency.TouchPressed."
-       "SubmitCompositorFrameToPresentationCompositorFrame."
-       "SwapEndToPresentationCompositorFrame",
-       viz_breakdown.presentation_feedback.timestamp -
-           viz_breakdown.swap_timings.swap_end},
-      {"EventLatency.TouchPressed.TotalLatency",
-       viz_breakdown.presentation_feedback.timestamp - event_times[0]},
-      {"EventLatency.TotalLatency",
-       viz_breakdown.presentation_feedback.timestamp - event_times[0]},
-  };
-
-  for (const auto& expected_latency : expected_latencies) {
-    histogram_tester.ExpectTotalCount(expected_latency.name, 1);
-    histogram_tester.ExpectBucketCount(
-        expected_latency.name, expected_latency.latency.InMicroseconds(), 1);
-  }
-}
-
 // Tests that EventLatency total latency histograms are reported properly for
 // scroll events when a frame is presented to the user.
 TEST_F(CompositorFrameReportingControllerTest,
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 3488232..02b33a7 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -319,6 +319,8 @@
     "//chrome/browser/image_descriptions:java",
     "//chrome/browser/image_editor/public:java",
     "//chrome/browser/image_fetcher:java",
+    "//chrome/browser/language/android:base_module_java",
+    "//chrome/browser/language/android:java",
     "//chrome/browser/metrics_settings/android:java",
     "//chrome/browser/notifications/chime/android:java",
     "//chrome/browser/offline_pages/android:java",
@@ -1086,6 +1088,9 @@
     "//chrome/browser/image_fetcher:java",
     "//chrome/browser/incognito/interstitial/android:java",
     "//chrome/browser/incognito/interstitial/android:javatests",
+    "//chrome/browser/language/android:base_module_java",
+    "//chrome/browser/language/android:java",
+    "//chrome/browser/language/android:java_resources",
     "//chrome/browser/offline_pages/android:java",
     "//chrome/browser/omaha/android:java",
     "//chrome/browser/optimization_guide/android:java",
@@ -2202,8 +2207,6 @@
     "java/src/org/chromium/chrome/browser/download/DownloadBroadcastManager.java",
     "java/src/org/chromium/chrome/browser/download/DownloadForegroundService.java",
     "java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationService.java",
-    "java/src/org/chromium/chrome/browser/language/AppLocaleUtils.java",
-    "java/src/org/chromium/chrome/browser/language/GlobalAppLocaleController.java",
     "java/src/org/chromium/chrome/browser/media/MediaCaptureNotificationService.java",
     "java/src/org/chromium/chrome/browser/media/ui/ChromeMediaNotificationControllerServices.java",
     "java/src/org/chromium/chrome/browser/metrics/UmaUtils.java",
@@ -2226,6 +2229,7 @@
     "//base:jni_java",
     "//chrome/browser/download/android:file_provider_java",
     "//chrome/browser/flags:java",
+    "//chrome/browser/language/android:base_module_java",
     "//chrome/browser/preferences:java",
     "//chrome/browser/util:java",
     "//chrome/browser/version:java",
@@ -3472,7 +3476,6 @@
     "java/src/org/chromium/chrome/browser/tabmodel/TabModelJniBridge.java",
     "java/src/org/chromium/chrome/browser/tabmodel/TabModelObserverJniBridge.java",
     "java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java",
-    "java/src/org/chromium/chrome/browser/translate/TranslateBridge.java",
     "java/src/org/chromium/chrome/browser/usage_stats/NotificationSuspender.java",
     "java/src/org/chromium/chrome/browser/usage_stats/UsageStatsBridge.java",
     "java/src/org/chromium/chrome/browser/webapps/WebApkHandlerDelegate.java",
diff --git a/chrome/android/chrome_java_resources.gni b/chrome/android/chrome_java_resources.gni
index d02953e..c6663d0 100644
--- a/chrome/android/chrome_java_resources.gni
+++ b/chrome/android/chrome_java_resources.gni
@@ -61,7 +61,6 @@
   "java/res/drawable-hdpi/ic_dialer_not_found_red_40dp.png",
   "java/res/drawable-hdpi/ic_download_pause.png",
   "java/res/drawable-hdpi/ic_download_pending.png",
-  "java/res/drawable-hdpi/ic_drag_handle_grey600_24dp.png",
   "java/res/drawable-hdpi/ic_drive_site_white_24dp.png",
   "java/res/drawable-hdpi/ic_email_googblue_36dp.png",
   "java/res/drawable-hdpi/ic_error_grey800_24dp_filled.png",
@@ -190,7 +189,6 @@
   "java/res/drawable-mdpi/ic_dialer_not_found_red_40dp.png",
   "java/res/drawable-mdpi/ic_download_pause.png",
   "java/res/drawable-mdpi/ic_download_pending.png",
-  "java/res/drawable-mdpi/ic_drag_handle_grey600_24dp.png",
   "java/res/drawable-mdpi/ic_drive_site_white_24dp.png",
   "java/res/drawable-mdpi/ic_email_googblue_36dp.png",
   "java/res/drawable-mdpi/ic_error_grey800_24dp_filled.png",
@@ -307,7 +305,6 @@
   "java/res/drawable-xhdpi/ic_dialer_not_found_red_40dp.png",
   "java/res/drawable-xhdpi/ic_download_pause.png",
   "java/res/drawable-xhdpi/ic_download_pending.png",
-  "java/res/drawable-xhdpi/ic_drag_handle_grey600_24dp.png",
   "java/res/drawable-xhdpi/ic_drive_site_white_24dp.png",
   "java/res/drawable-xhdpi/ic_email_googblue_36dp.png",
   "java/res/drawable-xhdpi/ic_error_grey800_24dp_filled.png",
@@ -403,7 +400,6 @@
   "java/res/drawable-xxhdpi/ic_dialer_not_found_red_40dp.png",
   "java/res/drawable-xxhdpi/ic_download_pause.png",
   "java/res/drawable-xxhdpi/ic_download_pending.png",
-  "java/res/drawable-xxhdpi/ic_drag_handle_grey600_24dp.png",
   "java/res/drawable-xxhdpi/ic_drive_site_white_24dp.png",
   "java/res/drawable-xxhdpi/ic_email_googblue_36dp.png",
   "java/res/drawable-xxhdpi/ic_error_grey800_24dp_filled.png",
@@ -496,7 +492,6 @@
   "java/res/drawable-xxxhdpi/ic_devices_48dp.png",
   "java/res/drawable-xxxhdpi/ic_dialer_icon_blue_40dp.png",
   "java/res/drawable-xxxhdpi/ic_dialer_not_found_red_40dp.png",
-  "java/res/drawable-xxxhdpi/ic_drag_handle_grey600_24dp.png",
   "java/res/drawable-xxxhdpi/ic_drive_site_white_24dp.png",
   "java/res/drawable-xxxhdpi/ic_email_googblue_36dp.png",
   "java/res/drawable-xxxhdpi/ic_error_grey800_24dp_filled.png",
@@ -685,15 +680,12 @@
   "java/res/layout-sw360dp/preference_spinner_single_line.xml",
   "java/res/layout-sw600dp/find_toolbar.xml",
   "java/res/layout-sw600dp/location_bar.xml",
-  "java/res/layout/accept_languages_item.xml",
-  "java/res/layout/accept_languages_list.xml",
   "java/res/layout/accessibility_tab_switcher.xml",
   "java/res/layout/accessibility_tab_switcher_list_item.xml",
   "java/res/layout/account_chooser_dialog_item.xml",
   "java/res/layout/account_chooser_dialog_title.xml",
   "java/res/layout/account_divider_preference.xml",
   "java/res/layout/account_management_account_row.xml",
-  "java/res/layout/add_languages_main.xml",
   "java/res/layout/add_to_menu_dialog.xml",
   "java/res/layout/add_to_menu_dialog_item.xml",
   "java/res/layout/assistant_voice_search_consent_ui.xml",
@@ -795,7 +787,6 @@
   "java/res/layout/language_ask_prompt_content.xml",
   "java/res/layout/language_ask_prompt_row.xml",
   "java/res/layout/language_ask_prompt_row_separator.xml",
-  "java/res/layout/languages_preference.xml",
   "java/res/layout/lightweight_fre_tos.xml",
   "java/res/layout/list_menu_button.xml",
   "java/res/layout/location_bar.xml",
@@ -899,7 +890,6 @@
   "java/res/menu/bookmark_action_bar_menu.xml",
   "java/res/menu/custom_tabs_menu.xml",
   "java/res/menu/history_manager_menu.xml",
-  "java/res/menu/languages_action_bar_menu.xml",
   "java/res/menu/password_entry_viewer_action_bar_menu.xml",
   "java/res/menu/prefeditor_editor_menu.xml",
   "java/res/menu/save_password_preferences_action_bar_menu.xml",
@@ -954,6 +944,7 @@
   "java/res/xml/main_preferences.xml",
   "java/res/xml/manage_sync_preferences.xml",
   "java/res/xml/notifications_preferences.xml",
+  "java/res/xml/phone_as_a_security_key_accessory_filter.xml",
   "java/res/xml/privacy_preferences.xml",
   "java/res/xml/search_widget_info.xml",
   "java/res/xml/sync_and_services_preferences.xml",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index de23a94..e57c7c9 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -749,14 +749,7 @@
   "java/src/org/chromium/chrome/browser/invalidation/SessionsInvalidationManager.java",
   "java/src/org/chromium/chrome/browser/javascript/WebContextFetcher.java",
   "java/src/org/chromium/chrome/browser/language/LanguageAskPrompt.java",
-  "java/src/org/chromium/chrome/browser/language/settings/AddLanguageFragment.java",
-  "java/src/org/chromium/chrome/browser/language/settings/AvailableUiLanguages.java",
-  "java/src/org/chromium/chrome/browser/language/settings/LanguageItem.java",
-  "java/src/org/chromium/chrome/browser/language/settings/LanguageItemPickerPreference.java",
-  "java/src/org/chromium/chrome/browser/language/settings/LanguageListBaseAdapter.java",
-  "java/src/org/chromium/chrome/browser/language/settings/LanguageListPreference.java",
   "java/src/org/chromium/chrome/browser/language/settings/LanguageSettings.java",
-  "java/src/org/chromium/chrome/browser/language/settings/LanguagesManager.java",
   "java/src/org/chromium/chrome/browser/lens/LensController.java",
   "java/src/org/chromium/chrome/browser/lens/LensIntentParams.java",
   "java/src/org/chromium/chrome/browser/lens/LensQueryParams.java",
@@ -1396,7 +1389,6 @@
   "java/src/org/chromium/chrome/browser/tracing/settings/TracingCategoriesSettings.java",
   "java/src/org/chromium/chrome/browser/tracing/settings/TracingSettings.java",
   "java/src/org/chromium/chrome/browser/translate/TranslateAssistContent.java",
-  "java/src/org/chromium/chrome/browser/translate/TranslateBridge.java",
   "java/src/org/chromium/chrome/browser/translate/TranslateIntentHandler.java",
   "java/src/org/chromium/chrome/browser/translate/TranslateUtils.java",
   "java/src/org/chromium/chrome/browser/ui/BottomContainer.java",
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index 8f791aad..1acf856 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -562,7 +562,9 @@
   "javatests/src/org/chromium/chrome/browser/tab/state/LevelDBPersistedTabDataStorageTest.java",
   "javatests/src/org/chromium/chrome/browser/tab/state/LoadCallbackHelper.java",
   "javatests/src/org/chromium/chrome/browser/tab/state/PersistedTabDataTest.java",
+  "javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataLegacyTest.java",
   "javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataTest.java",
+  "javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataTestUtils.java",
   "javatests/src/org/chromium/chrome/browser/tabbed_mode/TabbedNavigationBarColorControllerTest.java",
   "javatests/src/org/chromium/chrome/browser/tabmodel/AsyncTabCreationParamsManagerTest.java",
   "javatests/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreatorTest.java",
diff --git a/chrome/android/expectations/lint-baseline.xml b/chrome/android/expectations/lint-baseline.xml
index eb8aa153..a9ba3d4 100644
--- a/chrome/android/expectations/lint-baseline.xml
+++ b/chrome/android/expectations/lint-baseline.xml
@@ -1690,7 +1690,7 @@
         errorLine1="        holder.mStartIcon.setOnTouchListener((v, event) -> {"
         errorLine2="        ^">
         <location
-            file="../../chrome/android/java/src/org/chromium/chrome/browser/language/settings/LanguageListBaseAdapter.java"
+            file="../../chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/LanguageListBaseAdapter.java"
             line="173"
             column="9"/>
     </issue>
@@ -1701,7 +1701,7 @@
         errorLine1="        holder.mStartIcon.setOnTouchListener((v, event) -> {"
         errorLine2="                                             ^">
         <location
-            file="../../chrome/android/java/src/org/chromium/chrome/browser/language/settings/LanguageListBaseAdapter.java"
+            file="../../chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/LanguageListBaseAdapter.java"
             line="173"
             column="46"/>
     </issue>
@@ -1910,7 +1910,7 @@
         errorLine1="        android:clickable=&quot;true&quot;"
         errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
-            file="../../chrome/android/java/res/layout/languages_preference.xml"
+            file="../../chrome/browser/language/android/java/res/layout/languages_preference.xml"
             line="32"
             column="9"/>
     </issue>
diff --git a/chrome/android/expectations/monochrome_public_bundle.AndroidManifest.expected b/chrome/android/expectations/monochrome_public_bundle.AndroidManifest.expected
index 7733ffc..aa219cf 100644
--- a/chrome/android/expectations/monochrome_public_bundle.AndroidManifest.expected
+++ b/chrome/android/expectations/monochrome_public_bundle.AndroidManifest.expected
@@ -611,6 +611,10 @@
         android:name="org.chromium.chrome.browser.webauth.authenticator.CableAuthenticatorActivity"
         android:permission="com.google.android.gms.auth.cryptauth.permission.CABLEV2_SERVER_LINK"
         android:theme="@style/Theme.Chromium.Activity.Fullscreen">
+      <intent-filter>  # DIFF-ANCHOR: 5873407a
+        <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"/>
+      </intent-filter>  # DIFF-ANCHOR: 5873407a
+      <meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" android:resource="@xml/phone_as_a_security_key_accessory_filter"/>
     </activity>  # DIFF-ANCHOR: a1fac31f
     <activity  # DIFF-ANCHOR: 1d9b4077
         android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
diff --git a/chrome/android/expectations/trichrome_chrome_bundle.AndroidManifest.expected b/chrome/android/expectations/trichrome_chrome_bundle.AndroidManifest.expected
index 24d31898..7365b5a0 100644
--- a/chrome/android/expectations/trichrome_chrome_bundle.AndroidManifest.expected
+++ b/chrome/android/expectations/trichrome_chrome_bundle.AndroidManifest.expected
@@ -584,6 +584,10 @@
         android:name="org.chromium.chrome.browser.webauth.authenticator.CableAuthenticatorActivity"
         android:permission="com.google.android.gms.auth.cryptauth.permission.CABLEV2_SERVER_LINK"
         android:theme="@style/Theme.Chromium.Activity.Fullscreen">
+      <intent-filter>  # DIFF-ANCHOR: 5873407a
+        <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"/>
+      </intent-filter>  # DIFF-ANCHOR: 5873407a
+      <meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" android:resource="@xml/phone_as_a_security_key_accessory_filter"/>
     </activity>  # DIFF-ANCHOR: a1fac31f
     <activity  # DIFF-ANCHOR: 1d9b4077
         android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
diff --git a/chrome/android/features/cablev2_authenticator/java/res/layout/cablev2_serverlink.xml b/chrome/android/features/cablev2_authenticator/java/res/layout/cablev2_serverlink.xml
index 99640c73..0c9420d 100644
--- a/chrome/android/features/cablev2_authenticator/java/res/layout/cablev2_serverlink.xml
+++ b/chrome/android/features/cablev2_authenticator/java/res/layout/cablev2_serverlink.xml
@@ -8,61 +8,50 @@
     a:layout_width="match_parent"
     a:layout_height="match_parent"
     a:orientation="vertical"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:ignore="HardcodedText">
-
-  <ImageView
-      a:id="@+id/logo"
-      a:layout_width="match_parent"
-      a:layout_height="24dp"
-      a:layout_marginTop="56dp"
-      a:contentDescription="@null"
-      a:gravity="center_horizontal"
-      a:src="@drawable/product_logo_name"/>
+    xmlns:tools="http://schemas.android.com/tools">
 
   <TextView
       a:layout_width="match_parent"
       a:layout_height="wrap_content"
-      a:layout_marginTop="24dp"
+      a:layout_marginTop="104dp"
       a:layout_marginLeft="24dp"
       a:layout_marginRight="24dp"
-      a:fontFamily="@font/chrome_google_sans"
       a:gravity="center_horizontal"
-      a:text="Connecting to your device"
-      a:textColor="@color/modern_grey_900"
-      a:textSize="24sp"/>
+      a:text="@string/cablev2_serverlink_connecting_to_your_device"
+      a:textSize="24sp" />
 
-    <RelativeLayout
+  <RelativeLayout
+      a:layout_width="wrap_content"
+      a:layout_height="wrap_content"
+      a:layoutDirection="ltr"
+      a:layout_marginTop="52dp"
+      a:layout_marginLeft="24dp"
+      a:layout_marginRight="24dp"
+      a:layout_gravity="center_horizontal">
+    <ImageView
+        a:id="@+id/spinner"
+        a:contentDescription="@null"
         a:layout_width="wrap_content"
         a:layout_height="wrap_content"
-        a:layoutDirection="ltr"
-        a:layout_marginTop="52dp"
-        a:layout_marginLeft="24dp"
-        a:layout_marginRight="24dp"
-        a:layout_gravity="center_horizontal">
-      <ImageView
-          a:id="@+id/spinner"
-          a:contentDescription="Spinning circle"
-          a:layout_width="wrap_content"
-          a:layout_height="wrap_content"
-          a:layout_gravity="center"
-          a:layout_centerInParent="true"
-          a:layout_centerVertical="true"/>
-      <ImageView
-          a:contentDescription="Lock icon"
-          a:layout_height="wrap_content"
-          a:layout_width="wrap_content"
-          a:src="@drawable/ic_lock_googblue_48dp"
-          a:layout_centerInParent="true"
-          a:layout_centerVertical="true"/>
-    </RelativeLayout>
-
-    <TextView
-        a:id="@+id/status_text"
-        a:layout_width="wrap_content"
+        a:layout_gravity="center"
+        a:layout_centerInParent="true"
+        a:layout_centerVertical="true" />
+    <ImageView
+        a:contentDescription="@null"
         a:layout_height="wrap_content"
-        a:layout_marginTop="4dp"
-        a:layout_gravity="center_horizontal"
-        a:padding="0px"
-        a:textSize="14sp"/>
+        a:layout_width="wrap_content"
+        a:src="@drawable/ic_lock_googblue_48dp"
+        a:layout_centerInParent="true"
+        a:layout_centerVertical="true" />
+  </RelativeLayout>
+
+  <TextView
+      a:id="@+id/status_text"
+      a:layout_width="wrap_content"
+      a:layout_height="wrap_content"
+      a:layout_marginTop="4dp"
+      a:layout_gravity="center_horizontal"
+      a:padding="0px"
+      a:textSize="14sp" />
+
 </LinearLayout>
diff --git a/chrome/android/features/cablev2_authenticator/java/res/layout/cablev2_usb_attached.xml b/chrome/android/features/cablev2_authenticator/java/res/layout/cablev2_usb_attached.xml
index cb6d53f..ba8f10f 100644
--- a/chrome/android/features/cablev2_authenticator/java/res/layout/cablev2_usb_attached.xml
+++ b/chrome/android/features/cablev2_authenticator/java/res/layout/cablev2_usb_attached.xml
@@ -12,13 +12,12 @@
   <TextView
       a:id="@+id/usb_label"
       xmlns:tools="http://schemas.android.com/tools"
-      tools:ignore="HardcodedText"
       style="@style/TextAppearance.Headline.Primary"
       a:layout_width="match_parent"
       a:layout_height="wrap_content"
       a:layout_marginBottom="40dp"
       a:gravity="center"
-      a:text="Connected with USB cable" />
+      a:text="@string/cablev2_usb_discon_title" />
 
   <ImageView
       a:id="@+id/usb_icon_disconnect"
@@ -34,11 +33,10 @@
   <TextView
       a:id="@+id/usb_title"
       xmlns:tools="http://schemas.android.com/tools"
-      tools:ignore="HardcodedText"
       style="@style/TextAppearance.TextLarge.Secondary"
       a:layout_width="match_parent"
       a:layout_height="wrap_content"
       a:gravity="center"
-      a:text="Disconnect when you're done." />
+      a:text="@string/cablev2_usb_discon_body" />
 
 </LinearLayout>
diff --git a/chrome/android/features/cablev2_authenticator/java/res/layout/cablev2_usb_prompt.xml b/chrome/android/features/cablev2_authenticator/java/res/layout/cablev2_usb_prompt.xml
index 306513c9..6f000d4 100644
--- a/chrome/android/features/cablev2_authenticator/java/res/layout/cablev2_usb_prompt.xml
+++ b/chrome/android/features/cablev2_authenticator/java/res/layout/cablev2_usb_prompt.xml
@@ -12,13 +12,12 @@
   <TextView
       a:id="@+id/usb_label"
       xmlns:tools="http://schemas.android.com/tools"
-      tools:ignore="HardcodedText"
       style="@style/TextAppearance.Headline.Primary"
       a:layout_width="match_parent"
       a:layout_height="wrap_content"
       a:layout_marginBottom="40dp"
       a:gravity="center"
-      a:text="Connection seems to be taking quite long…" />
+      a:text="@string/cablev2_usb_prompt_title" />
 
   <ImageView
       a:id="@+id/usb_icon"
@@ -34,11 +33,10 @@
   <TextView
       a:id="@+id/usb_title"
       xmlns:tools="http://schemas.android.com/tools"
-      tools:ignore="HardcodedText"
       style="@style/TextAppearance.TextLarge.Secondary"
       a:layout_width="match_parent"
       a:layout_height="wrap_content"
       a:gravity="center"
-      a:text="Try connecting your phone with a USB cable" />
+      a:text="@string/cablev2_usb_prompt_body" />
 
 </LinearLayout>
diff --git a/chrome/android/features/cablev2_authenticator/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticatorUI.java b/chrome/android/features/cablev2_authenticator/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticatorUI.java
index 4fd389a5..b6321ad 100644
--- a/chrome/android/features/cablev2_authenticator/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticatorUI.java
+++ b/chrome/android/features/cablev2_authenticator/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticatorUI.java
@@ -118,10 +118,9 @@
     }
 
     @Override
-    @SuppressLint("SetTextI18n")
     public View onCreateView(
             LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-        getActivity().setTitle("Security Key");
+        getActivity().setTitle(R.string.cablev2_activity_title);
         ViewGroup top = new LinearLayout(getContext());
         View v = null;
 
@@ -279,7 +278,6 @@
         }
     }
 
-    @SuppressLint("SetTextI18n")
     void onStatus(int code) {
         switch (mMode) {
             case QR:
@@ -295,14 +293,18 @@
 
             case SERVER_LINK:
                 // These values must match up with the Status enum in v2_authenticator.h
-                // TODO(agl): translate
+                int id = -1;
                 if (code == 1) {
-                    mStatusText.setText("Waiting for other computer");
+                    id = R.string.cablev2_serverlink_status_connecting;
                 } else if (code == 2) {
-                    mStatusText.setText("Connected to other computer");
+                    id = R.string.cablev2_serverlink_status_connected;
                 } else if (code == 3) {
-                    mStatusText.setText("Processing request");
+                    id = R.string.cablev2_serverlink_status_processing;
+                } else {
+                    break;
                 }
+
+                mStatusText.setText(getResources().getString(id));
                 break;
 
             case FCM:
@@ -343,30 +345,27 @@
         mAuthenticator.onActivityResult(requestCode, resultCode, data);
     }
 
-    @SuppressLint("SetTextI18n")
     void onAuthenticatorConnected() {}
 
     void onAuthenticatorResult(CableAuthenticator.Result result) {
         getActivity().runOnUiThread(() -> {
-            // TODO: Temporary UI, needs i18n.
-            String toast = "An error occured. Please try again.";
+            int id = -1;
             switch (result) {
                 case REGISTER_OK:
-                    toast = "Registration succeeded";
+                    id = R.string.cablev2_registration_succeeded;
                     break;
                 case REGISTER_ERROR:
-                    toast = "Registration failed";
+                    id = R.string.cablev2_registration_failed;
                     break;
                 case SIGN_OK:
-                    toast = "Sign-in succeeded";
+                    id = R.string.cablev2_sign_in_succeeded;
                     break;
                 case SIGN_ERROR:
-                    toast = "Sign-in failed";
-                    break;
                 case OTHER:
+                    id = R.string.cablev2_sign_in_failed;
                     break;
             }
-            Toast.makeText(getActivity(), toast, Toast.LENGTH_SHORT).show();
+            Toast.makeText(getActivity(), getResources().getString(id), Toast.LENGTH_SHORT).show();
         });
     }
 
diff --git a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/ExploreSurfaceCoordinator.java b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/ExploreSurfaceCoordinator.java
index a819712b..e3c184bb 100644
--- a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/ExploreSurfaceCoordinator.java
+++ b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/ExploreSurfaceCoordinator.java
@@ -123,9 +123,9 @@
 
         FeedSurfaceCoordinator feedSurfaceCoordinator = new FeedSurfaceCoordinator(mActivity,
                 mActivity.getSnackbarManager(), mActivity.getTabModelSelector(),
-                mActivity.getActivityTabProvider(), null, null, sectionHeaderView,
-                feedActionOptions, isInNightMode, this, mExploreSurfaceNavigationDelegate, profile,
-                isPlaceholderShown, bottomSheetController, mActivity.getShareDelegateSupplier(),
+                mActivity.getWindowAndroid(), null, null, sectionHeaderView, feedActionOptions,
+                isInNightMode, this, mExploreSurfaceNavigationDelegate, profile, isPlaceholderShown,
+                bottomSheetController, mActivity.getShareDelegateSupplier(),
                 scrollableContainerDelegate);
         feedSurfaceCoordinator.getView().setId(R.id.start_surface_explore_view);
         return feedSurfaceCoordinator;
diff --git a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTest.java b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTest.java
index b56b59e..b4a62b4 100644
--- a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTest.java
+++ b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTest.java
@@ -1089,6 +1089,7 @@
             "force-fieldtrials=Study/Group",
             IMMEDIATE_RETURN_PARAMS + "/start_surface_variation/single"})
     @ParameterAnnotations.UseMethodParameter(FeedParams.class)
+    @DisabledTest(message = "crbug.com/1177555")
     public void renderSingleAsHomepage_Landscape(boolean isFeedV2) throws IOException {
         // clang-format on
         setFeedVersion(isFeedV2);
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/ConditionalTabStripTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/ConditionalTabStripTest.java
index 8c1e5f0..0d5567f 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/ConditionalTabStripTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/ConditionalTabStripTest.java
@@ -4,8 +4,6 @@
 
 package org.chromium.chrome.browser.tasks.tab_management;
 
-import static android.os.Build.VERSION_CODES.M;
-
 import static androidx.test.espresso.Espresso.onView;
 import static androidx.test.espresso.action.ViewActions.click;
 import static androidx.test.espresso.action.ViewActions.longClick;
@@ -36,6 +34,7 @@
 
 import android.content.Context;
 import android.os.Build;
+import android.os.Build.VERSION_CODES;
 import android.support.test.InstrumentationRegistry;
 import android.view.View;
 import android.widget.ListView;
@@ -170,8 +169,10 @@
 
     @Test
     @MediumTest
-    @DisableIf.Build(sdk_is_less_than = M, message = "crbug.com/1081832")
-    public void testStrip_updateWithAddition() throws Exception {
+    @DisableIf.Build(sdk_is_less_than = VERSION_CODES.O,
+            message = "Failing or flaky on N & P, see crbug.com/1177383 & crbug.com/1081832")
+    public void
+    testStrip_updateWithAddition() throws Exception {
         ChromeTabbedActivity cta = sActivityTestRule.getActivity();
         verifyHidingStrip();
 
@@ -445,7 +446,10 @@
 
     @Test
     @MediumTest
-    public void testStrip_enabled_notExpired() throws Exception {
+    @DisableIf.Build(sdk_is_greater_than = VERSION_CODES.M, sdk_is_less_than = VERSION_CODES.O,
+            message = "Failing on N, see crbug.com/1177383")
+    public void
+    testStrip_enabled_notExpired() throws Exception {
         ChromeTabbedActivity cta = sActivityTestRule.getActivity();
         for (int i = 0; i < 3; i++) {
             createBlankPageWithLaunchType(cta, false, TabLaunchType.FROM_CHROME_UI);
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java
index 0018822e..9e35ee8d 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java
@@ -292,6 +292,7 @@
 
     @Test
     @MediumTest
+    @DisabledTest(message = "Flaky test - see: https://crbug.com/1177149")
     public void testTabGridDialogAnimation() {
         final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
         createTabs(cta, false, 2);
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java
index 06e782b5..728498d 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java
@@ -46,7 +46,6 @@
 import org.chromium.chrome.browser.share.ShareDelegate;
 import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
 import org.chromium.chrome.browser.signin.ui.PersonalizedSigninPromoView;
-import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
 import org.chromium.chrome.browser.user_education.UserEducationHelper;
@@ -57,6 +56,7 @@
 import org.chromium.components.feature_engagement.Tracker;
 import org.chromium.ui.UiUtils;
 import org.chromium.ui.base.ViewUtils;
+import org.chromium.ui.base.WindowAndroid;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -84,7 +84,7 @@
                 SnackbarManager snackbarManager,
                 NativePageNavigationDelegate pageNavigationDelegate, UiConfig uiConfig,
                 boolean placeholderShown, BottomSheetController bottomSheetController,
-                Supplier<Tab> tabSupplier, FeedV1ActionOptions v1ActionOptions,
+                WindowAndroid windowAndroid, FeedV1ActionOptions v1ActionOptions,
                 Supplier<ShareDelegate> shareDelegateSupplier);
 
         /**
@@ -109,7 +109,7 @@
     private final FeedSurfaceMediator mMediator;
     private final BottomSheetController mBottomSheetController;
     private final FeedV1ActionOptions mV1ActionOptions;
-    private final Supplier<Tab> mTabSupplier;
+    private final WindowAndroid mWindowAndroid;
     private final Supplier<ShareDelegate> mShareSupplier;
 
     private UiConfig mUiConfig;
@@ -251,10 +251,10 @@
 
     /**
      * Constructs a new FeedSurfaceCoordinator.
-     *  @param activity The containing {@link ChromeActivity}.
+     * @param activity The containing {@link ChromeActivity}.
      * @param snackbarManager The {@link SnackbarManager} displaying Snackbar UI.
      * @param tabModelSelector {@link TabModelSelector} object.
-     * @param tabProvider Provides the current active tab.
+     * @param windowAndroid The window of the page.
      * @param snapScrollHelper The {@link SnapScrollHelper} for the New Tab Page.
      * @param ntpHeader The extra header on top of the feeds for the New Tab Page.
      * @param sectionHeaderView The {@link SectionHeaderView} for the feed.
@@ -266,10 +266,10 @@
      * @param profile The current user profile.
      * @param isPlaceholderShownInitially Whether the placeholder is shown initially.
      * @param bottomSheetController The bottom sheet controller, used in v2.
-     * @param shareDelegateSupplier
+     * @param shareDelegateSupplier The supplier for the share delegate used to share articles.
      */
     public FeedSurfaceCoordinator(Activity activity, SnackbarManager snackbarManager,
-            TabModelSelector tabModelSelector, Supplier<Tab> tabProvider,
+            TabModelSelector tabModelSelector, WindowAndroid windowAndroid,
             @Nullable SnapScrollHelper snapScrollHelper, @Nullable View ntpHeader,
             @Nullable SectionHeaderView sectionHeaderView, FeedV1ActionOptions actionOptions,
             boolean showDarkBackground, FeedSurfaceDelegate delegate,
@@ -290,7 +290,7 @@
         mBottomSheetController = bottomSheetController;
         mProfile = profile;
         mV1ActionOptions = actionOptions;
-        mTabSupplier = tabProvider;
+        mWindowAndroid = windowAndroid;
         mShareSupplier = shareDelegateSupplier;
         mScrollableContainerDelegate = externalScrollableContainerDelegate;
 
@@ -394,7 +394,7 @@
         mStreamCreatedTimeMs = SystemClock.elapsedRealtime();
         mStream = mStreamWrapper.createStream(mProfile, mActivity, mShowDarkBackground,
                 mSnackbarManager, mPageNavigationDelegate, mUiConfig, mIsPlaceholderShownInitially,
-                mBottomSheetController, mTabSupplier, mV1ActionOptions, mShareSupplier);
+                mBottomSheetController, mWindowAndroid, mV1ActionOptions, mShareSupplier);
 
         mStreamLifecycleManager = mDelegate.createStreamLifecycleManager(mStream, mActivity);
 
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStream.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStream.java
index 12e7c7b8..1b07d96 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStream.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStream.java
@@ -26,9 +26,9 @@
 import org.chromium.chrome.browser.native_page.NativePageNavigationDelegate;
 import org.chromium.chrome.browser.ntp.ScrollListener;
 import org.chromium.chrome.browser.share.ShareDelegate;
-import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
+import org.chromium.ui.base.WindowAndroid;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -58,14 +58,14 @@
     public FeedStream(Activity activity, boolean isBackgroundDark, SnackbarManager snackbarManager,
             NativePageNavigationDelegate nativePageNavigationDelegate,
             BottomSheetController bottomSheetController, boolean isPlaceholderShown,
-            Supplier<Tab> tabSupplier, Supplier<ShareDelegate> shareDelegateSupplier) {
+            WindowAndroid windowAndroid, Supplier<ShareDelegate> shareDelegateSupplier) {
         // TODO(petewil): Use isBackgroundDark to turn on dark theme.
         this.mActivity = activity;
 
         this.mFeedStreamSurface = new FeedStreamSurface(activity, isBackgroundDark, snackbarManager,
                 nativePageNavigationDelegate, bottomSheetController,
                 HelpAndFeedbackLauncherImpl.getInstance(), isPlaceholderShown,
-                new FeedStreamSurface.ShareHelperWrapper(tabSupplier, shareDelegateSupplier));
+                new FeedStreamSurface.ShareHelperWrapper(windowAndroid, shareDelegateSupplier));
     }
 
     @Override
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStreamSurface.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStreamSurface.java
index ae48a968..fb58cdb 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStreamSurface.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStreamSurface.java
@@ -71,6 +71,7 @@
 import org.chromium.content_public.common.Referrer;
 import org.chromium.network.mojom.ReferrerPolicy;
 import org.chromium.ui.base.PageTransition;
+import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.mojom.WindowOpenDisposition;
 import org.chromium.url.GURL;
 
@@ -221,11 +222,11 @@
      * Makes it easier to test.
      */
     public static class ShareHelperWrapper {
-        private Supplier<Tab> mTabSupplier;
+        private WindowAndroid mWindowAndroid;
         private Supplier<ShareDelegate> mShareDelegateSupplier;
         public ShareHelperWrapper(
-                Supplier<Tab> tabSupplier, Supplier<ShareDelegate> shareDelegateSupplier) {
-            mTabSupplier = tabSupplier;
+                WindowAndroid windowAndroid, Supplier<ShareDelegate> shareDelegateSupplier) {
+            mWindowAndroid = windowAndroid;
             mShareDelegateSupplier = shareDelegateSupplier;
         }
 
@@ -234,9 +235,7 @@
          * Brings up the share sheet.
          */
         public void share(String url, String title) {
-            ShareParams params =
-                    new ShareParams.Builder(mTabSupplier.get().getWindowAndroid(), title, url)
-                            .build();
+            ShareParams params = new ShareParams.Builder(mWindowAndroid, title, url).build();
             mShareDelegateSupplier.get().share(
                     params, new ChromeShareExtras.Builder().build(), ShareOrigin.FEED);
         }
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStreamWrapper.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStreamWrapper.java
index 2e98e19..8a7d324 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStreamWrapper.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStreamWrapper.java
@@ -14,10 +14,10 @@
 import org.chromium.chrome.browser.native_page.NativePageNavigationDelegate;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.share.ShareDelegate;
-import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.widget.displaystyle.UiConfig;
+import org.chromium.ui.base.WindowAndroid;
 
 /**
  * Wraps management of the Feed V2 stream.
@@ -40,10 +40,10 @@
     public Stream createStream(Profile profile, Activity activity, boolean showDarkBackground,
             SnackbarManager snackbarManager, NativePageNavigationDelegate pageNavigationDelegate,
             UiConfig uiConfig, boolean placeholderShown,
-            BottomSheetController bottomSheetController, Supplier<Tab> tabSupplier,
+            BottomSheetController bottomSheetController, WindowAndroid windowAndroid,
             FeedV1ActionOptions v1ActionOptions, Supplier<ShareDelegate> shareDelegateSupplier) {
         mStream = new FeedStream(activity, showDarkBackground, snackbarManager,
-                pageNavigationDelegate, bottomSheetController, placeholderShown, tabSupplier,
+                pageNavigationDelegate, bottomSheetController, placeholderShown, windowAndroid,
                 shareDelegateSupplier);
         return mStream;
     }
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
index 49c743b9..764c0c6 100644
--- a/chrome/android/java/AndroidManifest.xml
+++ b/chrome/android/java/AndroidManifest.xml
@@ -851,6 +851,14 @@
             android:permission="com.google.android.gms.auth.cryptauth.permission.CABLEV2_SERVER_LINK"
             android:exported="true"
             android:launchMode="singleTop">
+            <!-- This activity can be started by GMSCore, and is thus exported
+            with a permission set, or can be started by the Android system in
+            the case that a USB device is attached. -->
+            <intent-filter>
+                 <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
+            </intent-filter>
+            <meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
+                android:resource="@xml/phone_as_a_security_key_accessory_filter" />
         </activity>
 
         <receiver
diff --git a/chrome/android/java/res/layout/main.xml b/chrome/android/java/res/layout/main.xml
index fa1214a5..ce2aba1 100644
--- a/chrome/android/java/res/layout/main.xml
+++ b/chrome/android/java/res/layout/main.xml
@@ -96,7 +96,8 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:visibility="gone"
-            android:layout_gravity="start|top" />
+            android:layout_gravity="start|top"
+            android:focusable="true" />
 
         <ViewStub
             android:id="@+id/status_indicator_stub"
diff --git a/chrome/android/java/res/xml/phone_as_a_security_key_accessory_filter.xml b/chrome/android/java/res/xml/phone_as_a_security_key_accessory_filter.xml
new file mode 100644
index 0000000..02d96be
--- /dev/null
+++ b/chrome/android/java/res/xml/phone_as_a_security_key_accessory_filter.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+This filter captures desktop Chrome (using AOA[1]) trying to use this phone
+as a security key over USB. The model string is a random, magic value
+that identifies such requests. On the desktop side this magic value is
+known as |kCableOverAOAModel| in services/device/public/mojom/usb_device.mojom.
+
+[1] https://source.android.com/devices/accessories/aoa
+-->
+<resources>
+    <usb-accessory model="12eba9f901039b36"/>
+    <!-- Never attempt to match a version here: https://crbug.com/1174217 -->
+</resources>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 706c64d..4eacc2e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -407,8 +407,9 @@
     public ChromeTabbedActivity() {
         mMainIntentMetrics = new MainIntentBehaviorMetrics();
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
-            mMultiInstanceManager = new MultiInstanceManager(this, getTabModelSelectorSupplier(),
-                    getMultiWindowModeStateDispatcher(), getLifecycleDispatcher(), this);
+            mMultiInstanceManager =
+                    new MultiInstanceManager(this, getTabModelOrchestratorSupplier(),
+                            getMultiWindowModeStateDispatcher(), getLifecycleDispatcher(), this);
         } else {
             mMultiInstanceManager = null;
         }
@@ -886,7 +887,7 @@
     public void onStopWithNative() {
         super.onStopWithNative();
 
-        mTabModelSelectorImpl.saveState();
+        mTabModelOrchestrator.saveState();
         mHasDeterminedOverviewStateForCurrentSession = false;
     }
 
@@ -1106,7 +1107,7 @@
                     CommandLine.getInstance().hasSwitch(ChromeSwitches.NO_RESTORE_STATE);
             if (noRestoreState) {
                 // Clear the state files because they are inconsistent and useless from now on.
-                mTabModelSelectorImpl.clearState();
+                mTabModelOrchestrator.clearState();
             } else {
                 // State should be clear when we start first run and hence we do not need to load
                 // a previous state. This may change the current Model, watch out for initialization
@@ -1114,7 +1115,7 @@
                 // Never attempt to restore incognito tabs when this activity was previously swiped
                 // away in Recents. http://crbug.com/626629
                 boolean ignoreIncognitoFiles = !hadCipherData;
-                mTabModelSelectorImpl.loadState(ignoreIncognitoFiles);
+                mTabModelOrchestrator.loadState(ignoreIncognitoFiles);
             }
 
             mInactivityTracker.register(this.getLifecycleDispatcher());
@@ -1142,7 +1143,7 @@
                     AsyncTabParamsManagerSingleton.getInstance().hasParamsWithTabToReparent()
                     && getSavedInstanceState() == null;
             mCreatedTabOnStartup = getCurrentTabModel().getCount() > 0
-                    || mTabModelSelectorImpl.getRestoredTabCount() > 0 || isIntentWithEffect
+                    || mTabModelOrchestrator.getRestoredTabCount() > 0 || isIntentWithEffect
                     || hasTabWaitingForReparenting;
 
             // We always need to try to restore tabs. The set of tabs might be empty, but at least
@@ -1152,7 +1153,7 @@
                     || (shouldShowTabSwitcherOnStart()
                             && !mTabModelSelectorImpl.isIncognitoSelected());
 
-            mTabModelSelectorImpl.restoreTabs(activeTabBeingRestored);
+            mTabModelOrchestrator.restoreTabs(activeTabBeingRestored);
 
             // Only create an initial tab if no tabs were restored and no intent was handled.
             // Also, check whether the active tab was supposed to be restored and that the total
@@ -1267,7 +1268,7 @@
             TabModel tabModel = getCurrentTabModel();
             switch (tabOpenType) {
                 case TabOpenType.REUSE_URL_MATCHING_TAB_ELSE_NEW_TAB:
-                    mTabModelSelectorImpl.tryToRestoreTabStateForUrl(url);
+                    mTabModelOrchestrator.tryToRestoreTabStateForUrl(url);
                     int tabToBeClobberedIndex = TabModelUtils.getTabIndexByUrl(tabModel, url);
                     Tab tabToBeClobbered = tabModel.getTabAt(tabToBeClobberedIndex);
                     if (tabToBeClobbered != null) {
@@ -1283,7 +1284,7 @@
                     LaunchMetrics.recordHomeScreenLaunchIntoTab(url, shortcutSource);
                     break;
                 case TabOpenType.BRING_TAB_TO_FRONT:
-                    mTabModelSelectorImpl.tryToRestoreTabStateForId(tabIdToBringToFront);
+                    mTabModelOrchestrator.tryToRestoreTabStateForId(tabIdToBringToFront);
 
                     int tabIndex = TabModelUtils.getTabIndexById(tabModel, tabIdToBringToFront);
                     if (tabIndex == TabModel.INVALID_TAB_INDEX) {
@@ -1328,7 +1329,7 @@
                     int tabId = IntentUtils.safeGetIntExtra(
                             intent, TabOpenType.REUSE_TAB_MATCHING_ID_STRING, Tab.INVALID_TAB_ID);
                     if (tabId != Tab.INVALID_TAB_ID) {
-                        mTabModelSelectorImpl.tryToRestoreTabStateForId(tabId);
+                        mTabModelOrchestrator.tryToRestoreTabStateForId(tabId);
                         int matchingTabIndex = TabModelUtils.getTabIndexById(tabModel, tabId);
                         boolean loaded = false;
                         if (matchingTabIndex != TabModel.INVALID_TAB_INDEX) {
@@ -2375,7 +2376,7 @@
      */
     @VisibleForTesting
     public void saveState() {
-        mTabModelSelectorImpl.saveState();
+        mTabModelOrchestrator.saveState();
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ShortcutHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/ShortcutHelper.java
index e920daa..3828d21 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ShortcutHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ShortcutHelper.java
@@ -4,24 +4,17 @@
 
 package org.chromium.chrome.browser;
 
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ShortcutInfo;
-import android.content.pm.ShortcutManager;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
-import android.graphics.drawable.Icon;
 import android.net.Uri;
-import android.os.Build;
 import android.text.TextUtils;
 import android.util.Base64;
 
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.ContextUtils;
-import org.chromium.base.Log;
-import org.chromium.base.ThreadUtils;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.task.AsyncTask;
 import org.chromium.chrome.R;
@@ -35,7 +28,6 @@
 import org.chromium.chrome.browser.webapps.WebappRegistry;
 import org.chromium.components.webapps.WebappsUtils;
 import org.chromium.content_public.common.ScreenOrientationConstants;
-import org.chromium.ui.widget.Toast;
 
 import java.io.ByteArrayOutputStream;
 import java.util.HashMap;
@@ -97,19 +89,15 @@
     public static class Delegate {
         /**
          * Request Android to add a shortcut to the home screen.
+         * @param id The generated GUID of the shortcut.
          * @param title Title of the shortcut.
          * @param icon Image that represents the shortcut.
          * @param isIconAdaptive Whether to create an Android Adaptive icon.
          * @param shortcutIntent Intent to fire when the shortcut is activated.
          */
-        public void addShortcutToHomescreen(
-                String title, Bitmap icon, boolean isIconAdaptive, Intent shortcutIntent) {
-            if (WebappsUtils.isRequestPinShortcutSupported()) {
-                addShortcutWithShortcutManager(title, icon, isIconAdaptive, shortcutIntent);
-                return;
-            }
-            Intent intent = WebappsUtils.createAddToHomeIntent(title, icon, shortcutIntent);
-            ContextUtils.getApplicationContext().sendBroadcast(intent);
+        public void addShortcutToHomescreen(String id, String title, Bitmap icon,
+                boolean isIconAdaptive, Intent shortcutIntent) {
+            WebappsUtils.addShortcutToHomescreen(id, title, icon, isIconAdaptive, shortcutIntent);
         }
 
         /**
@@ -159,7 +147,8 @@
             }
             @Override
             protected void onPostExecute(final Intent resultIntent) {
-                sDelegate.addShortcutToHomescreen(userTitle, icon, isIconAdaptive, resultIntent);
+                sDelegate.addShortcutToHomescreen(
+                        id, userTitle, icon, isIconAdaptive, resultIntent);
 
                 // Store the webapp data so that it is accessible without the intent.
                 WebappRegistry.getInstance().register(id, storage -> {
@@ -177,9 +166,6 @@
                         storeWebappSplashImage(id, splashImage);
                     }
                 });
-                if (shouldShowToastWhenAddingShortcut()) {
-                    showAddedToHomescreenToast(userTitle);
-                }
             }
         }
                 .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
@@ -195,49 +181,7 @@
         shortcutIntent.putExtra(EXTRA_ID, id);
         shortcutIntent.putExtra(EXTRA_SOURCE, source);
         shortcutIntent.setPackage(ContextUtils.getApplicationContext().getPackageName());
-        sDelegate.addShortcutToHomescreen(userTitle, icon, isIconAdaptive, shortcutIntent);
-        if (shouldShowToastWhenAddingShortcut()) {
-            showAddedToHomescreenToast(userTitle);
-        }
-    }
-
-    @TargetApi(Build.VERSION_CODES.O)
-    private static void addShortcutWithShortcutManager(
-            String title, Bitmap bitmap, boolean isMaskableIcon, Intent shortcutIntent) {
-        String id = shortcutIntent.getStringExtra(ShortcutHelper.EXTRA_ID);
-        Context context = ContextUtils.getApplicationContext();
-
-        if (bitmap == null) {
-            Log.e(TAG, "Failed to find an icon for " + title + ", not adding.");
-            return;
-        }
-        Icon icon = isMaskableIcon ? Icon.createWithAdaptiveBitmap(bitmap)
-                                   : Icon.createWithBitmap(bitmap);
-
-        ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(context, id)
-                                            .setShortLabel(title)
-                                            .setLongLabel(title)
-                                            .setIcon(icon)
-                                            .setIntent(shortcutIntent)
-                                            .build();
-        try {
-            ShortcutManager shortcutManager =
-                    ContextUtils.getApplicationContext().getSystemService(ShortcutManager.class);
-            shortcutManager.requestPinShortcut(shortcutInfo, null);
-        } catch (IllegalStateException e) {
-            Log.d(TAG,
-                    "Could not create pinned shortcut: device is locked, or "
-                            + "activity is backgrounded.");
-        }
-    }
-
-    /**
-     * Show toast to alert user that the shortcut was added to the home screen.
-     */
-    private static void showAddedToHomescreenToast(final String title) {
-        Context applicationContext = ContextUtils.getApplicationContext();
-        String toastText = applicationContext.getString(R.string.added_to_homescreen, title);
-        showToast(toastText);
+        sDelegate.addShortcutToHomescreen(id, userTitle, icon, isIconAdaptive, shortcutIntent);
     }
 
     /**
@@ -249,14 +193,7 @@
     private static void showWebApkInstallInProgressToast() {
         Context applicationContext = ContextUtils.getApplicationContext();
         String toastText = applicationContext.getString(R.string.webapk_install_in_progress);
-        showToast(toastText);
-    }
-
-    public static void showToast(String text) {
-        assert ThreadUtils.runningOnUiThread();
-        Toast toast =
-                Toast.makeText(ContextUtils.getApplicationContext(), text, Toast.LENGTH_SHORT);
-        toast.show();
+        WebappsUtils.showToast(toastText);
     }
 
     /**
@@ -448,10 +385,6 @@
         return builder.build().toString();
     }
 
-    private static boolean shouldShowToastWhenAddingShortcut() {
-        return !WebappsUtils.isRequestPinShortcutSupported();
-    }
-
     @CalledByNative
     public static void setForceWebApkUpdate(String id) {
         WebappDataStorage storage = WebappRegistry.getInstance().getWebappDataStorage(id);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
index fca8df9..83c4b42 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
@@ -245,6 +245,8 @@
     /** Used to access the {@link ShareDelegate} from {@link WindowAndroid}. */
     private final UnownedUserDataSupplier<ShareDelegate> mShareDelegateSupplier =
             new ShareDelegateSupplier();
+    private final ObservableSupplierImpl<TabModelOrchestrator> mTabModelOrchestratorSupplier =
+            new ObservableSupplierImpl<>();
     /** Used to access the {@link TabModelSelector} from {@link WindowAndroid}. */
     private final UnownedUserDataSupplier<TabModelSelector> mTabModelSelectorSupplier =
             new TabModelSelectorSupplier();
@@ -371,6 +373,7 @@
 
         // Create the orchestrator that manages Tab models and persistence
         mTabModelOrchestrator = createTabModelOrchestrator();
+        mTabModelOrchestratorSupplier.set(mTabModelOrchestrator);
 
         // There's no corresponding call to removeObserver() for this addObserver() because
         // mTabModelProfileSupplier has the same lifecycle as this activity.
@@ -1660,6 +1663,13 @@
     }
 
     /**
+     * Returns an {@link ObservableSupplier} for {@link TabModelOrchestrator}.
+     */
+    public final ObservableSupplier<TabModelOrchestrator> getTabModelOrchestratorSupplier() {
+        return mTabModelOrchestratorSupplier;
+    }
+
+    /**
      * Returns an {@link ObservableSupplier} for {@link TabModelSelector}. Prefer this method over
      * using {@link #getTabModelSelector()} directly.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/TabModelOrchestrator.java b/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/TabModelOrchestrator.java
index 1fe48e0..95eff06 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/TabModelOrchestrator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/TabModelOrchestrator.java
@@ -33,6 +33,13 @@
     }
 
     /**
+     * @return The {@link TabPersistentStore} managed by this orchestrator.
+     */
+    public TabPersistentStore getTabPersistentStore() {
+        return mTabPersistentStore;
+    }
+
+    /**
      * Destroy the {@link TabPersistentStore} and {@link TabModelSelectorImpl} members.
      */
     public void destroy() {
@@ -53,6 +60,72 @@
         }
     }
 
+    /**
+     * Save the current state of the tab model. Usage of this method is discouraged due to it
+     * writing to disk.
+     */
+    public void saveState() {
+        mTabModelSelector.commitAllTabClosures();
+        mTabPersistentStore.saveState();
+    }
+
+    /**
+     * Load the saved tab state. This should be called before any new tabs are created. The saved
+     * tabs shall not be restored until {@link #restoreTabs} is called.
+     * @param ignoreIncognitoFiles Whether to skip loading incognito tabs.
+     */
+    public void loadState(boolean ignoreIncognitoFiles) {
+        mTabPersistentStore.loadState(ignoreIncognitoFiles);
+    }
+
+    /**
+     * Restore the saved tabs which were loaded by {@link #loadState}.
+     *
+     * @param setActiveTab If true, synchronously load saved active tab and set it as the current
+     *                     active tab.
+     */
+    public void restoreTabs(boolean setActiveTab) {
+        mTabPersistentStore.restoreTabs(setActiveTab);
+    }
+
+    public void mergeState() {
+        mTabPersistentStore.mergeState();
+    }
+
+    public void clearState() {
+        mTabPersistentStore.clearState();
+    }
+
+    /**
+     * If there is an asynchronous session restore in-progress, try to synchronously restore
+     * the state of a tab with the given url as a frozen tab. This method has no effect if
+     * there isn't a tab being restored with this url, or the tab has already been restored.
+     */
+    public void tryToRestoreTabStateForUrl(String url) {
+        if (mTabModelSelector.isSessionRestoreInProgress()) {
+            mTabPersistentStore.restoreTabStateForUrl(url);
+        }
+    }
+
+    /**
+     * If there is an asynchronous session restore in-progress, try to synchronously restore
+     * the state of a tab with the given id as a frozen tab. This method has no effect if
+     * there isn't a tab being restored with this id, or the tab has already been restored.
+     */
+    public void tryToRestoreTabStateForId(int id) {
+        if (mTabModelSelector.isSessionRestoreInProgress()) {
+            mTabPersistentStore.restoreTabStateForId(id);
+        }
+    }
+
+    /**
+     * @return Number of restored tabs on cold startup.
+     */
+    public int getRestoredTabCount() {
+        if (mTabPersistentStore == null) return 0;
+        return mTabPersistentStore.getRestoredTabCount();
+    }
+
     protected void wireSelectorAndStore() {
         // Supply TabModelSelectorImpl with TabPersistentStore.
         //
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
index 3eb3ec5..b515bed 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
@@ -25,6 +25,7 @@
 import org.chromium.chrome.browser.WebContentsFactory;
 import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.app.tab_activity_glue.ReparentingDelegateFactory;
+import org.chromium.chrome.browser.app.tabmodel.TabModelOrchestrator;
 import org.chromium.chrome.browser.browserservices.BrowserServicesIntentDataProvider;
 import org.chromium.chrome.browser.compositor.CompositorViewHolder;
 import org.chromium.chrome.browser.customtabs.CustomTabDelegateFactory;
@@ -205,7 +206,7 @@
     }
 
     public void saveState() {
-        mTabFactory.getTabModelSelector().saveState();
+        mTabFactory.getTabModelOrchestrator().saveState();
     }
 
     public TabModelSelector getTabModelSelector() {
@@ -239,12 +240,13 @@
             mConnection.cancelSpeculation(mSession);
         }
 
-        TabModelSelectorImpl tabModelSelector = mTabFactory.getTabModelSelector();
+        TabModelOrchestrator tabModelOrchestrator = mTabFactory.getTabModelOrchestrator();
+        TabModelSelectorImpl tabModelSelector = tabModelOrchestrator.getTabModelSelector();
 
         TabModel tabModel = tabModelSelector.getModel(mIntentDataProvider.isIncognito());
         tabModel.addObserver(mTabObserverRegistrar);
 
-        finalizeCreatingTab(tabModelSelector, tabModel);
+        finalizeCreatingTab(tabModelOrchestrator, tabModel);
         Tab tab = mTabProvider.getTab();
         assert tab != null;
         assert mTabProvider.getInitialTabCreationMode() != TabCreationMode.NONE;
@@ -307,13 +309,13 @@
 
     // Creates the tab on native init, if it hasn't been created yet, and does all the additional
     // initialization steps necessary at this stage.
-    private void finalizeCreatingTab(TabModelSelectorImpl tabModelSelector, TabModel tabModel) {
+    private void finalizeCreatingTab(TabModelOrchestrator tabModelOrchestrator, TabModel tabModel) {
         Tab earlyCreatedTab = mTabProvider.getTab();
 
         Tab tab = earlyCreatedTab;
         @TabCreationMode int mode = mTabProvider.getInitialTabCreationMode();
 
-        Tab restoredTab = tryRestoringTab(tabModelSelector);
+        Tab restoredTab = tryRestoringTab(tabModelOrchestrator);
         if (restoredTab != null) {
             assert earlyCreatedTab == null :
                     "Shouldn't create a new tab when there's one to restore";
@@ -362,11 +364,11 @@
     }
 
     @Nullable
-    private Tab tryRestoringTab(TabModelSelectorImpl tabModelSelector) {
+    private Tab tryRestoringTab(TabModelOrchestrator tabModelOrchestrator) {
         if (mActivity.getSavedInstanceState() == null) return null;
-        tabModelSelector.loadState(true);
-        tabModelSelector.restoreTabs(true);
-        Tab tab = tabModelSelector.getCurrentTab();
+        tabModelOrchestrator.loadState(true);
+        tabModelOrchestrator.restoreTabs(true);
+        Tab tab = tabModelOrchestrator.getTabModelSelector().getCurrentTab();
         if (tab != null) {
             initializeTab(tab);
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabFactory.java
index d2427843..68487628 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabFactory.java
@@ -98,10 +98,7 @@
 
     /** Returns the previously created {@link TabModelSelector}. */
     public TabModelSelectorImpl getTabModelSelector() {
-        if (mTabModelOrchestrator == null) {
-            assert false;
-            createTabModelOrchestrator();
-        }
+        getTabModelOrchestrator();
         if (mTabModelOrchestrator.getTabModelSelector() == null) {
             assert false;
             createTabModels();
@@ -109,6 +106,15 @@
         return mTabModelOrchestrator.getTabModelSelector();
     }
 
+    /** Returns the previously created {@link CustomTabsTabModelOrchestrator}. */
+    public CustomTabsTabModelOrchestrator getTabModelOrchestrator() {
+        if (mTabModelOrchestrator == null) {
+            assert false;
+            createTabModelOrchestrator();
+        }
+        return mTabModelOrchestrator;
+    }
+
     /** Creates a {@link ChromeTabCreator}s for the custom tab. */
     public Pair<ChromeTabCreator, ChromeTabCreator> createTabCreators() {
         return Pair.create(createTabCreator(false), createTabCreator(true));
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManager.java b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManager.java
index d565acec..231ef124 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManager.java
@@ -27,6 +27,7 @@
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.app.tab_activity_glue.ReparentingTask;
+import org.chromium.chrome.browser.app.tabmodel.TabModelOrchestrator;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.ConfigurationChangedObserver;
@@ -74,7 +75,7 @@
     private ApplicationStatus.ActivityStateListener mOtherCTAStateObserver;
 
     private final Activity mActivity;
-    private final ObservableSupplier<TabModelSelector> mTabModelSelectorSupplier;
+    private final ObservableSupplier<TabModelOrchestrator> mTabModelOrchestratorSupplier;
     private final MultiWindowModeStateDispatcher mMultiWindowModeStateDispatcher;
     private final ActivityLifecycleDispatcher mActivityLifecycleDispatcher;
     private final MenuOrKeyboardActionController mMenuOrKeyboardActionController;
@@ -90,7 +91,7 @@
     /**
      * Create a new {@link MultiInstanceManager}.
      * @param activity The activity.
-     * @param tabModelSelectorSupplier A supplier for the {@link TabModelSelector} for the
+     * @param tabModelOrchestratorSupplier A supplier for the {@link TabModelOrchestrator} for the
      *         associated activity.
      * @param multiWindowModeStateDispatcher The {@link MultiWindowModeStateDispatcher} for the
      *         associated activity.
@@ -100,12 +101,12 @@
      *         associated activity.
      */
     public MultiInstanceManager(Activity activity,
-            ObservableSupplier<TabModelSelector> tabModelSelectorSupplier,
+            ObservableSupplier<TabModelOrchestrator> tabModelOrchestratorSupplier,
             MultiWindowModeStateDispatcher multiWindowModeStateDispatcher,
             ActivityLifecycleDispatcher activityLifecycleDispatcher,
             MenuOrKeyboardActionController menuOrKeyboardActionController) {
         mActivity = activity;
-        mTabModelSelectorSupplier = tabModelSelectorSupplier;
+        mTabModelOrchestratorSupplier = tabModelOrchestratorSupplier;
 
         mMultiWindowModeStateDispatcher = multiWindowModeStateDispatcher;
         mMultiWindowModeStateDispatcher.addObserver(this);
@@ -312,9 +313,12 @@
     @Override
     public boolean handleMenuOrKeyboardAction(int id, boolean fromMenu) {
         if (id == org.chromium.chrome.R.id.move_to_other_window_menu_id) {
-            Tab currentTab = mTabModelSelectorSupplier.get() == null
-                    ? null
-                    : mTabModelSelectorSupplier.get().getCurrentTab();
+            TabModelOrchestrator tabModelOrchestrator = mTabModelOrchestratorSupplier.get();
+            if (tabModelOrchestrator == null) return true;
+            TabModelSelector tabModelSelector = tabModelOrchestrator.getTabModelSelector();
+            if (tabModelSelector == null) return true;
+
+            Tab currentTab = tabModelSelector.getCurrentTab();
             if (currentTab != null) moveTabToOtherWindow(currentTab);
             return true;
         }
@@ -388,7 +392,7 @@
 
         killOtherTask();
         RecordUserAction.record("Android.MergeState.Live");
-        mTabModelSelectorSupplier.get().mergeState();
+        mTabModelOrchestratorSupplier.get().mergeState();
     }
 
     private static void setMergedInstanceTaskId(int mergedInstanceTaskId) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java
index 06a8038..3ca84ef 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java
@@ -91,7 +91,7 @@
                     mActivity.getLifecycleDispatcher(), mActivity.getTabModelSelector(),
                     mActivity.isTablet(), mUma.get(), ColorUtils.inNightMode(mActivity),
                     nativePageHost, tab, mBottomSheetController,
-                    mActivity.getShareDelegateSupplier());
+                    mActivity.getShareDelegateSupplier(), mActivity.getWindowAndroid());
         }
 
         protected NativePage buildBookmarksPage(Tab tab) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
index bcacbc3..3f8f5e2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
@@ -81,6 +81,7 @@
 import org.chromium.content_public.browser.NavigationController;
 import org.chromium.content_public.browser.NavigationEntry;
 import org.chromium.ui.base.DeviceFormFactor;
+import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.mojom.WindowOpenDisposition;
 
 import java.util.List;
@@ -278,13 +279,14 @@
      * @param tab The {@link Tab} that contains this new tab page.
      * @param bottomSheetController The controller for bottom sheets, used by the feed.
      * @param shareDelegateSupplier Supplies the Delegate used to open SharingHub.
+     * @param windowAndroid
      */
     public NewTabPage(Activity activity, BrowserControlsStateProvider browserControlsStateProvider,
             Supplier<Tab> activityTabProvider, SnackbarManager snackbarManager,
             ActivityLifecycleDispatcher lifecycleDispatcher, TabModelSelector tabModelSelector,
             boolean isTablet, NewTabPageUma uma, boolean isInNightMode,
             NativePageHost nativePageHost, Tab tab, BottomSheetController bottomSheetController,
-            ObservableSupplier<ShareDelegate> shareDelegateSupplier) {
+            ObservableSupplier<ShareDelegate> shareDelegateSupplier, WindowAndroid windowAndroid) {
         mConstructedTimeNs = System.nanoTime();
         TraceEvent.begin(TAG);
 
@@ -346,7 +348,7 @@
         mActivityLifecycleDispatcher.register(mLifecycleObserver);
 
         updateSearchProviderHasLogo();
-        initializeMainView(activity, activityTabProvider, snackbarManager, tabModelSelector, uma,
+        initializeMainView(activity, windowAndroid, snackbarManager, tabModelSelector, uma,
                 isInNightMode, bottomSheetController, shareDelegateSupplier);
 
         mBrowserControlsStateProvider = browserControlsStateProvider;
@@ -389,7 +391,7 @@
     /**
      * Create and initialize the main view contained in this NewTabPage.
      * @param activity The activity used to initialize the view.
-     * @param tabProvider Provides the current active tab.
+     * @param windowAndroid Provides the current active tab.
      * @param snackbarManager {@link SnackbarManager} object.
      * @param tabModelSelector {@link TabModelSelector} object.
      * @param uma {@link NewTabPageUma} object recording user metrics.
@@ -397,7 +399,7 @@
      * @param bottomSheetController The controller for bottom sheets.  Used by the feed.
      * @param shareDelegateSupplier Supplies a delegate used to open SharingHub.
      */
-    protected void initializeMainView(Activity activity, Supplier<Tab> tabProvider,
+    protected void initializeMainView(Activity activity, WindowAndroid windowAndroid,
             SnackbarManager snackbarManager, TabModelSelector tabModelSelector, NewTabPageUma uma,
             boolean isInNightMode, BottomSheetController bottomSheetController,
             ObservableSupplier<ShareDelegate> shareDelegateSupplier) {
@@ -421,8 +423,8 @@
         }
 
         mFeedSurfaceProvider =
-                new FeedSurfaceCoordinator(activity, snackbarManager, tabModelSelector, tabProvider,
-                        new SnapScrollHelper(mNewTabPageManager, mNewTabPageLayout),
+                new FeedSurfaceCoordinator(activity, snackbarManager, tabModelSelector,
+                        windowAndroid, new SnapScrollHelper(mNewTabPageManager, mNewTabPageLayout),
                         mNewTabPageLayout, sectionHeaderView, new FeedV1ActionOptions(),
                         isInNightMode, this, mNewTabPageManager.getNavigationDelegate(), profile,
                         /* isPlaceholderShownInitially= */ false, bottomSheetController,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusCoordinator.java
index 0828831..f39b0b2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusCoordinator.java
@@ -136,7 +136,7 @@
 
     /** Signals that native initialization has completed. */
     public void onNativeInitialized() {
-        mMediator.updateLocationBarIcon();
+        mMediator.updateLocationBarIcon(StatusView.IconTransitionType.CROSSFADE);
         mMediator.setStatusClickListener(this);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
index 72d32c8..75f7e45 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
@@ -26,7 +26,9 @@
 import org.chromium.chrome.browser.omnibox.LocationBarDataProvider;
 import org.chromium.chrome.browser.omnibox.SearchEngineLogoUtils;
 import org.chromium.chrome.browser.omnibox.UrlBarEditingTextStateProvider;
+import org.chromium.chrome.browser.omnibox.status.StatusProperties.PermissionIconResource;
 import org.chromium.chrome.browser.omnibox.status.StatusProperties.StatusIconResource;
+import org.chromium.chrome.browser.omnibox.status.StatusView.IconTransitionType;
 import org.chromium.chrome.browser.page_info.PageInfoIPHController;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.theme.ThemeUtils;
@@ -196,7 +198,7 @@
         if (mPageSecurityLevel == level) return;
         mPageSecurityLevel = level;
         updateStatusVisibility();
-        updateLocationBarIcon();
+        updateLocationBarIcon(IconTransitionType.CROSSFADE);
     }
 
     /**
@@ -204,7 +206,7 @@
      */
     void setSecurityIconResource(@DrawableRes int securityIcon) {
         mSecurityIconRes = securityIcon;
-        updateLocationBarIcon();
+        updateLocationBarIcon(IconTransitionType.CROSSFADE);
     }
 
     /**
@@ -212,7 +214,7 @@
      */
     void setSecurityIconTint(@ColorRes int tintList) {
         mSecurityIconTintRes = tintList;
-        updateLocationBarIcon();
+        updateLocationBarIcon(IconTransitionType.CROSSFADE);
     }
 
     /**
@@ -220,7 +222,7 @@
      */
     void setSecurityIconDescription(@StringRes int desc) {
         mSecurityIconDescriptionRes = desc;
-        updateLocationBarIcon();
+        updateLocationBarIcon(IconTransitionType.CROSSFADE);
     }
 
     /**
@@ -242,7 +244,7 @@
      */
     void setShowIconsWhenUrlFocused(boolean showIconWhenFocused) {
         mShowStatusIconWhenUrlFocused = showIconWhenFocused;
-        updateLocationBarIcon();
+        updateLocationBarIcon(IconTransitionType.CROSSFADE);
     }
 
     /**
@@ -284,7 +286,7 @@
 
         mUrlHasFocus = urlHasFocus;
         updateStatusVisibility();
-        updateLocationBarIcon();
+        updateLocationBarIcon(IconTransitionType.CROSSFADE);
 
         // Set the default match to be a search on an unfocus event to avoid the globe sticking
         // around for subsequent focus events.
@@ -345,7 +347,7 @@
             mModel.set(StatusProperties.STATUS_ICON_ALPHA, 1f);
         }
 
-        updateLocationBarIcon();
+        updateLocationBarIcon(IconTransitionType.CROSSFADE);
     }
 
     /**
@@ -425,7 +427,7 @@
         mNavigationIconTintRes = tintColor;
         if (textColor != 0) mModel.set(StatusProperties.VERBOSE_STATUS_TEXT_COLOR_RES, textColor);
 
-        updateLocationBarIcon();
+        updateLocationBarIcon(IconTransitionType.CROSSFADE);
     }
 
     /**
@@ -454,7 +456,7 @@
         mIsSearchEngineStateSetup = true;
         mIsSearchEngineGoogle = isSearchEngineGoogle;
         mSearchEngineLogoUrl = searchEngineUrl;
-        updateLocationBarIcon();
+        updateLocationBarIcon(IconTransitionType.CROSSFADE);
     }
 
     /**
@@ -468,7 +470,7 @@
      *     - shown only if specified,
      *     - not shown if URL is focused.
      */
-    void updateLocationBarIcon() {
+    void updateLocationBarIcon(@IconTransitionType int transitionType) {
         // Reset the last saved permission.
         mLastPermission = ContentSettingsType.DEFAULT;
         // Update the accessibility description before continuing since we need it either way.
@@ -503,8 +505,11 @@
                               : R.color.locationbar_status_preview_color_light;
         }
 
-        mModel.set(StatusProperties.STATUS_ICON_RESOURCE,
-                icon == 0 ? null : new StatusIconResource(icon, tint));
+        StatusIconResource statusIcon = icon == 0 ? null : new StatusIconResource(icon, tint);
+        if (statusIcon != null) {
+            statusIcon.setTransitionType(transitionType);
+        }
+        mModel.set(StatusProperties.STATUS_ICON_RESOURCE, statusIcon);
         mModel.set(StatusProperties.STATUS_ICON_ACCESSIBILITY_TOAST_RES, toast);
     }
 
@@ -623,7 +628,7 @@
     /* package */ void updateLocationBarIconForDefaultMatchCategory(boolean defaultMatchIsSearch) {
         if (defaultMatchIsSearch != mUrlBarTextIsSearch) {
             mUrlBarTextIsSearch = defaultMatchIsSearch;
-            updateLocationBarIcon();
+            updateLocationBarIcon(IconTransitionType.CROSSFADE);
         }
     }
 
@@ -692,15 +697,20 @@
         assert mLastPermission != ContentSettingsType.DEFAULT;
         Drawable permissionIcon =
                 ContentSettingsResources.getContentSettingsIcon(mContext, mLastPermission, result);
-        // TODO(crbug.com/1158288): Animate the icon change.
+        PermissionIconResource statusIcon = new PermissionIconResource(permissionIcon);
+        statusIcon.setTransitionType(IconTransitionType.ROTATE);
         // Set the timer to switch the icon back afterwards.
         mPermissionTaskHandler.removeCallbacksAndMessages(null);
-        mModel.set(StatusProperties.STATUS_ICON_RESOURCE, new StatusIconResource(permissionIcon));
+        mModel.set(StatusProperties.STATUS_ICON_RESOURCE, statusIcon);
         mPermissionTaskHandler.postDelayed(
-                mCallbackController.makeCancelable(this::updateLocationBarIcon),
+                mCallbackController.makeCancelable(this::resetPermissionIcon),
                 PERMISSION_ICON_DISPLAY_TIMEOUT_MS);
     }
 
+    private void resetPermissionIcon() {
+        updateLocationBarIcon(IconTransitionType.ROTATE);
+    }
+
     public int getLastPermission() {
         return mLastPermission;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusProperties.java
index 44d8824d..7490d03 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusProperties.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusProperties.java
@@ -7,18 +7,24 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.view.View;
 
 import androidx.annotation.ColorRes;
 import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.appcompat.content.res.AppCompatResources;
 import androidx.core.graphics.drawable.DrawableCompat;
 import androidx.core.util.ObjectsCompat;
 
+import org.chromium.base.ApiCompatibilityUtils;
+import org.chromium.chrome.R;
 import org.chromium.ui.UiUtils;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey;
@@ -36,6 +42,8 @@
         private String mIconIdentifier;
         private Bitmap mBitmap;
         private Drawable mDrawable;
+        private @StatusView.IconTransitionType int mIconTransitionType =
+                StatusView.IconTransitionType.CROSSFADE;
 
         /** Constructor for a custom drawable. */
         StatusIconResource(Drawable drawable) {
@@ -68,6 +76,17 @@
             return mIconRes;
         }
 
+        /** Set the animation transition type for this icon. */
+        void setTransitionType(@StatusView.IconTransitionType int type) {
+            mIconTransitionType = type;
+        }
+
+        /** @return The animation transition type for this icon. */
+        @StatusView.IconTransitionType
+        int getTransitionType() {
+            return mIconTransitionType;
+        }
+
         /** @return The {@link Drawable} for this StatusIconResource. */
         Drawable getDrawable(Context context, Resources resources) {
             if (mBitmap != null) {
@@ -109,6 +128,78 @@
         }
     }
 
+    /**
+     * Encapsulates a permission icon for StatusView. Adds a circle background and icon color
+     * highlight.
+     */
+    static class PermissionIconResource extends StatusIconResource {
+        private static Bitmap sCircleBackground;
+
+        PermissionIconResource(Drawable drawable) {
+            super(drawable);
+        }
+
+        PermissionIconResource(String iconIdentifier, Bitmap bitmap, @ColorRes int tint) {
+            super(iconIdentifier, bitmap, tint);
+        }
+
+        PermissionIconResource(@DrawableRes int iconRes, @ColorRes int tint) {
+            super(iconRes, tint);
+        }
+
+        /** Returns a {@link Drawable} for this StatusIconResource. */
+        @Override
+        Drawable getDrawable(Context context, Resources resources) {
+            Drawable icon = super.getDrawable(context, resources);
+            if (icon == null) {
+                return null;
+            }
+            icon.setColorFilter(
+                    ApiCompatibilityUtils.getColor(resources, R.color.default_icon_color_blue),
+                    PorterDuff.Mode.SRC_IN);
+            Bitmap circleCopy = getCircleBackground(resources);
+            Canvas canvas = new Canvas(circleCopy);
+            float radius = 0.5f * canvas.getWidth();
+            Bitmap iconBitmap = createScaledIcon(icon, 0.9f);
+            canvas.drawBitmap(iconBitmap, radius - iconBitmap.getWidth() / 2,
+                    radius - iconBitmap.getHeight() / 2, null);
+            return new BitmapDrawable(resources, circleCopy);
+        }
+
+        /** Returns a scaled bitmap of the icon passed in. */
+        private Bitmap createScaledIcon(@NonNull Drawable icon, float scaleFactor) {
+            // Create bitmap and canvas from icon.
+            Bitmap iconBitmap = Bitmap.createBitmap(
+                    icon.getIntrinsicWidth(), icon.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
+            Canvas canvas = new Canvas(iconBitmap);
+            int side = canvas.getWidth();
+            assert side == canvas.getHeight();
+            icon.setBounds(0, 0, side, side);
+            icon.draw(canvas);
+
+            // Scale bitmap
+            int scaledWidth = Math.round(icon.getIntrinsicWidth() * scaleFactor);
+            int scaledHeight = Math.round(icon.getIntrinsicHeight() * scaleFactor);
+            return Bitmap.createScaledBitmap(iconBitmap, scaledWidth, scaledHeight, false);
+        }
+
+        /** Returns a bitmap of the circle icon to be used for the Drawable. */
+        private Bitmap getCircleBackground(Resources resources) {
+            if (sCircleBackground == null) {
+                int width = resources.getDimensionPixelSize(R.dimen.location_bar_status_icon_width);
+                float radius = 0.5f * width;
+                sCircleBackground = Bitmap.createBitmap(width, width, Bitmap.Config.ARGB_8888);
+                Canvas canvas = new Canvas(sCircleBackground);
+                Paint paint = new Paint();
+                paint.setColor(ApiCompatibilityUtils.getColor(
+                        resources, R.color.toolbar_background_primary));
+                paint.setAntiAlias(true);
+                canvas.drawCircle(radius, radius, radius, paint);
+            }
+            return sCircleBackground.copy(sCircleBackground.getConfig(), true);
+        }
+    }
+
     /** Whether animations are turned on. */
     static final WritableBooleanPropertyKey ANIMATIONS_ENABLED = new WritableBooleanPropertyKey();
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusView.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusView.java
index c04b057..310fd772 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusView.java
@@ -7,6 +7,7 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.RotateDrawable;
 import android.graphics.drawable.TransitionDrawable;
 import android.util.AttributeSet;
 import android.view.TouchDelegate;
@@ -18,6 +19,8 @@
 import android.widget.TextView;
 
 import androidx.annotation.ColorRes;
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.StringRes;
 
@@ -26,13 +29,25 @@
 import org.chromium.chrome.browser.omnibox.LocationBarDataProvider;
 import org.chromium.chrome.browser.omnibox.SearchEngineLogoUtils;
 import org.chromium.components.browser_ui.widget.CompositeTouchDelegate;
+import org.chromium.components.browser_ui.widget.animation.Interpolators;
 import org.chromium.ui.widget.Toast;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * StatusView is a location bar's view displaying status (icons and/or text).
  */
 public class StatusView extends LinearLayout {
+    @IntDef({IconTransitionType.CROSSFADE, IconTransitionType.ROTATE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface IconTransitionType {
+        int CROSSFADE = 0;
+        int ROTATE = 1;
+    }
     public static final int ICON_ANIMATION_DURATION_MS = 225;
+    private static final int ICON_ROTATION_DURATION_MS = 250;
+    private static final int ICON_ROTATION_DEGREES = 180;
 
     private @Nullable View mIncognitoBadge;
     private int mIncognitoBadgeEndPaddingWithIcon;
@@ -192,7 +207,7 @@
     /**
      * Start animating transition of status icon.
      */
-    private void animateStatusIcon() {
+    private void animateStatusIcon(@IconTransitionType int transitionType) {
         Drawable targetIcon = mStatusIconDrawable;
         boolean wantIconHidden = mStatusIconDrawable == null;
 
@@ -229,14 +244,32 @@
                         && ((TransitionDrawable) existingDrawable).getNumberOfLayers() == 2) {
                     existingDrawable = ((TransitionDrawable) existingDrawable).getDrawable(1);
                 }
-                TransitionDrawable newImage =
-                        new TransitionDrawable(new Drawable[] {existingDrawable, targetIcon});
+
+                TransitionDrawable newImage = new TransitionDrawable(new Drawable[] {
+                        existingDrawable,
+                        transitionType == IconTransitionType.ROTATE ? getRotatedIcon(targetIcon)
+                                                                    : targetIcon});
 
                 mIconView.setImageDrawable(newImage);
 
                 // Note: crossfade controls blending, not animation.
                 newImage.setCrossFadeEnabled(true);
-                newImage.startTransition(mAnimationsEnabled ? ICON_ANIMATION_DURATION_MS : 0);
+
+                if (transitionType == IconTransitionType.CROSSFADE) {
+                    newImage.startTransition(mAnimationsEnabled ? ICON_ANIMATION_DURATION_MS : 0);
+                } else {
+                    mIconView.animate()
+                            .setDuration(ICON_ROTATION_DURATION_MS)
+                            .rotationBy(ICON_ROTATION_DEGREES)
+                            .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN_INTERPOLATOR)
+                            .withStartAction(
+                                    () -> { newImage.startTransition(ICON_ANIMATION_DURATION_MS); })
+                            .withEndAction(() -> {
+                                mIconView.setRotation(0);
+                                mIconView.setImageDrawable(targetIcon);
+                            })
+                            .start();
+                }
 
                 // Update the touch delegate only if the icons are swapped without animating the
                 // image view.
@@ -247,6 +280,16 @@
         }
     }
 
+    /** Returns a rotated version of the icon passed in. */
+    private Drawable getRotatedIcon(@NonNull Drawable icon) {
+        RotateDrawable rotated = new RotateDrawable();
+        rotated.setDrawable(icon);
+        rotated.setToDegrees(ICON_ROTATION_DEGREES);
+        // Jump drawable to its target state.
+        rotated.setLevel(10000);
+        return rotated;
+    }
+
     /**
      * Specify object to receive click events.
      *
@@ -280,9 +323,10 @@
         mAnimationsEnabled = enabled;
     }
 
-    void setStatusIconResources(Drawable statusIconDrawable) {
+    void setStatusIconResources(
+            @Nullable Drawable statusIconDrawable, @IconTransitionType int transitionType) {
         mStatusIconDrawable = statusIconDrawable;
-        animateStatusIcon();
+        animateStatusIcon(transitionType);
     }
 
     /** Specify the status icon alpha. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewBinder.java
index 1b50df5..53c1c9d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewBinder.java
@@ -37,10 +37,11 @@
         } else if (StatusProperties.STATUS_ICON_RESOURCE.equals(propertyKey)) {
             StatusIconResource res = model.get(StatusProperties.STATUS_ICON_RESOURCE);
             if (res == null) {
-                view.setStatusIconResources(null);
+                view.setStatusIconResources(null, StatusView.IconTransitionType.CROSSFADE);
                 return;
             }
-            view.setStatusIconResources(res.getDrawable(view.getContext(), view.getResources()));
+            view.setStatusIconResources(res.getDrawable(view.getContext(), view.getResources()),
+                    res.getTransitionType());
         } else if (StatusProperties.VERBOSE_STATUS_TEXT_COLOR_RES.equals(propertyKey)) {
             view.setVerboseStatusTextColor(
                     model.get(StatusProperties.VERBOSE_STATUS_TEXT_COLOR_RES));
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorBase.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorBase.java
index 5c7918a..7a55067 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorBase.java
@@ -265,9 +265,6 @@
     }
 
     @Override
-    public void mergeState() {}
-
-    @Override
     public void destroy() {
         removeObserver(mTabModelFilterProvider);
         mTabModelFilterProvider.destroy();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java
index b036ac1..fd5f1ce6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java
@@ -289,86 +289,6 @@
         return mCloseAllTabsDelegate.closeAllTabsRequest(incognito);
     }
 
-    /**
-     * Save the current state of the tab model. Usage of this method is discouraged due to it
-     * writing to disk.
-     */
-    public void saveState() {
-        commitAllTabClosures();
-        if (mTabSaver.get() != null) {
-            mTabSaver.get().saveState();
-        }
-    }
-
-    /**
-     * Load the saved tab state. This should be called before any new tabs are created. The saved
-     * tabs shall not be restored until {@link #restoreTabs} is called.
-     * @param ignoreIncognitoFiles Whether to skip loading incognito tabs.
-     */
-    public void loadState(boolean ignoreIncognitoFiles) {
-        if (mTabSaver.get() != null) {
-            mTabSaver.get().loadState(ignoreIncognitoFiles);
-        }
-    }
-
-    @Override
-    public void mergeState() {
-        if (mTabSaver.get() != null) {
-            mTabSaver.get().mergeState();
-        }
-    }
-
-    /**
-     * Restore the saved tabs which were loaded by {@link #loadState}.
-     *
-     * @param setActiveTab If true, synchronously load saved active tab and set it as the current
-     *                     active tab.
-     */
-    public void restoreTabs(boolean setActiveTab) {
-        if (mTabSaver.get() != null) {
-            mTabSaver.get().restoreTabs(setActiveTab);
-        }
-    }
-
-    /**
-     * If there is an asynchronous session restore in-progress, try to synchronously restore
-     * the state of a tab with the given url as a frozen tab. This method has no effect if
-     * there isn't a tab being restored with this url, or the tab has already been restored.
-     */
-    public void tryToRestoreTabStateForUrl(String url) {
-        if (mTabSaver.get() != null && isSessionRestoreInProgress()) {
-            mTabSaver.get().restoreTabStateForUrl(url);
-        }
-    }
-
-    /**
-     * If there is an asynchronous session restore in-progress, try to synchronously restore
-     * the state of a tab with the given id as a frozen tab. This method has no effect if
-     * there isn't a tab being restored with this id, or the tab has already been restored.
-     */
-    public void tryToRestoreTabStateForId(int id) {
-        if (isSessionRestoreInProgress()) {
-            mTabSaver.get().restoreTabStateForId(id);
-        }
-    }
-
-    public void clearState() {
-        if (mTabSaver.get() != null) {
-            mTabSaver.get().clearState();
-        }
-    }
-
-    /**
-     * @return Number of restored tabs on cold startup.
-     */
-    public int getRestoredTabCount() {
-        if (mTabSaver.get() != null) {
-            return mTabSaver.get().getRestoredTabCount();
-        } else {
-            return 0;
-        }
-    }
-
     @Override
     public void requestToShowTab(Tab tab, @TabSelectionType int type) {
         boolean isFromExternalApp =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInstallService.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInstallService.java
index be26143..4af5817 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInstallService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInstallService.java
@@ -15,13 +15,13 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ShortcutHelper;
 import org.chromium.chrome.browser.notifications.NotificationWrapperBuilderFactory;
 import org.chromium.chrome.browser.notifications.channels.ChromeChannelDefinitions;
 import org.chromium.components.browser_ui.notifications.NotificationWrapperBuilder;
 import org.chromium.components.url_formatter.SchemeDisplay;
 import org.chromium.components.url_formatter.UrlFormatter;
 import org.chromium.components.webapps.WebappsIconUtils;
+import org.chromium.components.webapps.WebappsUtils;
 import org.chromium.webapk.lib.client.WebApkNavigationClient;
 
 /** Java counterpart to webapk_install_service.h. */
@@ -62,7 +62,7 @@
             icon = WebappsIconUtils.generateAdaptiveIconBitmap(icon);
         }
         showNotification(manifestUrl, shortName, url, icon, message, null);
-        ShortcutHelper.showToast(message);
+        WebappsUtils.showToast(message);
     }
 
     private static void showNotification(String notificationId, String shortName, String url,
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 1a878b1d..6e4e5189 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/TabsOpenedFromExternalAppTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/TabsOpenedFromExternalAppTest.java
@@ -29,6 +29,7 @@
 import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.CriteriaNotSatisfiedException;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.FlakyTest;
 import org.chromium.base.test.util.UrlUtils;
@@ -478,6 +479,7 @@
     @Test
     @LargeTest
     @Feature({"Navigation"})
+    @DisabledTest(message = "Flaky test - see: https://crbug.com/1176476")
     public void testNewTabWithNewTabExtra() throws Exception {
         mActivityTestRule.startMainActivityOnBlankPage();
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsTest.java
index caef7487..8c6c27f2 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsTest.java
@@ -12,6 +12,7 @@
 import static org.chromium.chrome.browser.browserservices.digitalgoods.AcknowledgeConverter.RESPONSE_ACKNOWLEDGE;
 
 import android.net.Uri;
+import android.os.Build;
 import android.os.Bundle;
 import android.support.test.InstrumentationRegistry;
 
@@ -143,7 +144,11 @@
      */
     @Test
     @MediumTest
-    public void jsToTwaConnected() throws TimeoutException {
+    @DisableIf.Build(sdk_is_greater_than = Build.VERSION_CODES.LOLLIPOP_MR1,
+            sdk_is_less_than = Build.VERSION_CODES.N)
+    @DisableIf.Device(type = {UiDisableIf.TABLET})
+    public void
+    jsToTwaConnected() throws TimeoutException {
         DigitalGoodsFactoryImpl.setDigitalGoodsForTesting(createFixedDigitalGoods());
 
         // Note: The response code much be 0 for success otherwise it doesn't propagate through to
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/language/settings/LanguageSettingsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/language/settings/LanguageSettingsTest.java
index 7686c4bf7..3a534d92 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/language/settings/LanguageSettingsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/language/settings/LanguageSettingsTest.java
@@ -32,8 +32,8 @@
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Restriction;
-import org.chromium.chrome.R;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.browser.language.R;
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.settings.SettingsActivity;
 import org.chromium.chrome.browser.settings.SettingsActivityTestRule;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/status/StatusViewRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/status/StatusViewRenderTest.java
index 7cc52fa..0883247 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/status/StatusViewRenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/status/StatusViewRenderTest.java
@@ -29,6 +29,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.omnibox.NewTabPageDelegate;
 import org.chromium.chrome.browser.omnibox.SearchEngineLogoUtils;
+import org.chromium.chrome.browser.omnibox.status.StatusProperties.PermissionIconResource;
 import org.chromium.chrome.browser.omnibox.status.StatusProperties.StatusIconResource;
 import org.chromium.chrome.browser.toolbar.LocationBarModel;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
@@ -183,10 +184,11 @@
             Drawable locationIcon =
                     ContentSettingsResources.getContentSettingsIcon(mStatusView.getContext(),
                             ContentSettingsType.GEOLOCATION, ContentSettingValues.ALLOW);
+            PermissionIconResource statusIcon = new PermissionIconResource(locationIcon);
+            statusIcon.setTransitionType(StatusView.IconTransitionType.ROTATE);
             mStatusModel.set(StatusProperties.STATUS_ICON_ALPHA, 1f);
             mStatusModel.set(StatusProperties.SHOW_STATUS_ICON, true);
-            mStatusModel.set(
-                    StatusProperties.STATUS_ICON_RESOURCE, new StatusIconResource(locationIcon));
+            mStatusModel.set(StatusProperties.STATUS_ICON_RESOURCE, statusIcon);
         });
         mRenderTestRule.render(mStatusView, "status_view_with_location_permission_icon");
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchActivityTest.java
index fc65944..7219373 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchActivityTest.java
@@ -42,6 +42,7 @@
 import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.CriteriaNotSatisfiedException;
+import org.chromium.base.test.util.DisableIf;
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.FlakyTest;
 import org.chromium.chrome.R;
@@ -83,6 +84,7 @@
 import org.chromium.content_public.browser.test.util.TestTouchUtils;
 import org.chromium.content_public.common.ContentUrlConstants;
 import org.chromium.ui.base.Clipboard;
+import org.chromium.ui.test.util.UiDisableIf;
 import org.chromium.url.GURL;
 
 import java.io.ByteArrayOutputStream;
@@ -200,6 +202,7 @@
 
     @Test
     @SmallTest
+    @DisableIf.Device(type = {UiDisableIf.TABLET}) // see crbug.com/1177417
     public void testOmniboxSuggestionContainerAppears() throws Exception {
         SearchActivity searchActivity = startSearchActivity();
 
@@ -410,6 +413,7 @@
 
     @Test
     @SmallTest
+    @DisableIf.Device(type = {UiDisableIf.TABLET}) // see crbug.com/1177417
     public void testTypeBeforeDeferredInitialization() throws Exception {
         // Start the Activity.  It should pause and assume that a promo dialog has appeared.
         mTestDelegate.shouldDelayDeferredInitialization = true;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java
index c2ed9b7..f9e56a0f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java
@@ -224,6 +224,7 @@
     @EnableFeatures(
             {ChromeFeatureList.SAFETY_CHECK_ANDROID, ChromeFeatureList.SAFE_BROWSING_SECTION_UI})
     @DisableFeatures(ChromeFeatureList.MOBILE_IDENTITY_CONSISTENCY)
+    @DisabledTest(message = "Flaky test - see: https://crbug.com/1172608")
     public void
     testRenderDifferentSignedInStatesWithSafetyCheck() throws IOException {
         launchSettingsActivity();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/WebsitePermissionsFetcherTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/WebsitePermissionsFetcherTest.java
index c06afcc7..a35dfd7 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/WebsitePermissionsFetcherTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/WebsitePermissionsFetcherTest.java
@@ -493,7 +493,7 @@
         // If the ContentSettingsType.NUM_TYPES value changes *and* a new value has been exposed on
         // Android, then please update this code block to include a test for your new type.
         // Otherwise, just update count in the assert.
-        Assert.assertEquals(67, ContentSettingsType.NUM_TYPES);
+        Assert.assertEquals(66, ContentSettingsType.NUM_TYPES);
         websitePreferenceBridge.addContentSettingException(
                 new ContentSettingException(ContentSettingsType.COOKIES, googleOrigin,
                         ContentSettingValues.DEFAULT, preferenceSource));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataLegacyTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataLegacyTest.java
new file mode 100644
index 0000000..266939c5
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataLegacyTest.java
@@ -0,0 +1,528 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.tab.state;
+
+import static org.mockito.Mockito.doReturn;
+
+import android.support.test.filters.SmallTest;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.base.test.UiThreadTest;
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.JniMocker;
+import org.chromium.chrome.browser.endpoint_fetcher.EndpointFetcher;
+import org.chromium.chrome.browser.endpoint_fetcher.EndpointFetcherJni;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.browser.optimization_guide.OptimizationGuideBridge;
+import org.chromium.chrome.browser.optimization_guide.OptimizationGuideBridgeJni;
+import org.chromium.chrome.browser.page_annotations.PageAnnotationsService;
+import org.chromium.chrome.browser.page_annotations.PageAnnotationsServiceFactory;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.test.ChromeBrowserTestRule;
+import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
+import org.chromium.components.optimization_guide.OptimizationGuideDecision;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test relating to {@link ShoppingPersistedTabData} covering the client-side price tracking
+ * implementation.
+ */
+@RunWith(BaseJUnit4ClassRunner.class)
+@EnableFeatures({ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID + "<Study"})
+@CommandLineFlags.
+Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, "force-fieldtrials=Study/Group"})
+public class ShoppingPersistedTabDataLegacyTest {
+    @Rule
+    public final ChromeBrowserTestRule mBrowserTestRule = new ChromeBrowserTestRule();
+
+    @Rule
+    public JniMocker mMocker = new JniMocker();
+
+    @Rule
+    public TestRule mProcessor = new Features.InstrumentationProcessor();
+
+    @Mock
+    protected EndpointFetcher.Natives mEndpointFetcherJniMock;
+
+    @Mock
+    protected OptimizationGuideBridge.Natives mOptimizationGuideBridgeJniMock;
+
+    @Mock
+    protected Profile mProfileMock;
+
+    @Mock
+    protected PageAnnotationsServiceFactory mServiceFactoryMock;
+
+    @Mock
+    protected PageAnnotationsService mPageAnnotationsServiceMock;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mMocker.mock(EndpointFetcherJni.TEST_HOOKS, mEndpointFetcherJniMock);
+        mMocker.mock(OptimizationGuideBridgeJni.TEST_HOOKS, mOptimizationGuideBridgeJniMock);
+        // Ensure native pointer is initialized
+        doReturn(1L).when(mOptimizationGuideBridgeJniMock).init();
+        ShoppingPersistedTabDataTestUtils.mockOptimizationGuideResponse(
+                mOptimizationGuideBridgeJniMock, OptimizationGuideDecision.TRUE);
+        PersistedTabDataConfiguration.setUseTestConfig(true);
+
+        Profile.setLastUsedProfileForTesting(mProfileMock);
+        doReturn(mPageAnnotationsServiceMock).when(mServiceFactoryMock).getForLastUsedProfile();
+        ShoppingPersistedTabData.sPageAnnotationsServiceFactory = mServiceFactoryMock;
+    }
+
+    @SmallTest
+    @Test
+    @CommandLineFlags.
+    Add({"force-fieldtrial-params=Study.Group:price_tracking_time_to_live_ms/-1000"})
+    public void testShoppingPriceChange() {
+        shoppingPriceChange(ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
+                ShoppingPersistedTabDataTestUtils.TAB_ID,
+                ShoppingPersistedTabDataTestUtils.IS_INCOGNITO));
+    }
+
+    @SmallTest
+    @Test
+    @CommandLineFlags.
+    Add({"force-fieldtrial-params=Study.Group:price_tracking_time_to_live_ms/-1000"})
+    public void testShoppingPriceChangeExtraFetchAfterChange() {
+        Tab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
+                ShoppingPersistedTabDataTestUtils.TAB_ID,
+                ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
+        long mLastPriceChangeTimeMs = shoppingPriceChange(tab);
+        final Semaphore semaphore = new Semaphore(0);
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            ShoppingPersistedTabData.from(tab, (shoppingPersistedTabData) -> {
+                ShoppingPersistedTabDataTestUtils.verifyGetPageAnnotationsCalled(
+                        mPageAnnotationsServiceMock, 3);
+                Assert.assertEquals(ShoppingPersistedTabDataTestUtils.UPDATED_PRICE_MICROS,
+                        shoppingPersistedTabData.getPriceMicros());
+                Assert.assertEquals(ShoppingPersistedTabDataTestUtils.PRICE_MICROS,
+                        shoppingPersistedTabData.getPreviousPriceMicros());
+                Assert.assertEquals(mLastPriceChangeTimeMs,
+                        shoppingPersistedTabData.getLastPriceChangeTimeMs());
+                Assert.assertEquals(ShoppingPersistedTabDataTestUtils.UNITED_STATES_CURRENCY_CODE,
+                        shoppingPersistedTabData.getCurrencyCode());
+                semaphore.release();
+            });
+        });
+        ShoppingPersistedTabDataTestUtils.acquireSemaphore(semaphore);
+    }
+
+    private long shoppingPriceChange(Tab tab) {
+        final Semaphore initialSemaphore = new Semaphore(0);
+        final Semaphore updateSemaphore = new Semaphore(0);
+        ShoppingPersistedTabDataTestUtils.mockPageAnnotationsResponse(mPageAnnotationsServiceMock,
+                ShoppingPersistedTabDataTestUtils.MockPageAnnotationsResponse
+                        .BUYABLE_PRODUCT_INITIAL);
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            ShoppingPersistedTabData.from(tab, (shoppingPersistedTabData) -> {
+                ShoppingPersistedTabDataTestUtils.verifyGetPageAnnotationsCalled(
+                        mPageAnnotationsServiceMock, 1);
+                Assert.assertEquals(ShoppingPersistedTabDataTestUtils.PRICE_MICROS,
+                        shoppingPersistedTabData.getPriceMicros());
+                Assert.assertEquals(ShoppingPersistedTabDataTestUtils.UNITED_STATES_CURRENCY_CODE,
+                        shoppingPersistedTabData.getCurrencyCode());
+                Assert.assertEquals(ShoppingPersistedTabData.NO_PRICE_KNOWN,
+                        shoppingPersistedTabData.getPreviousPriceMicros());
+                Assert.assertEquals(ShoppingPersistedTabData.NO_TRANSITIONS_OCCURRED,
+                        shoppingPersistedTabData.getLastPriceChangeTimeMs());
+                initialSemaphore.release();
+            });
+        });
+        ShoppingPersistedTabDataTestUtils.acquireSemaphore(initialSemaphore);
+        long firstUpdateTime = ShoppingPersistedTabDataTestUtils.getTimeLastUpdatedOnUiThread(tab);
+        ShoppingPersistedTabDataTestUtils.mockPageAnnotationsResponse(mPageAnnotationsServiceMock,
+                ShoppingPersistedTabDataTestUtils.MockPageAnnotationsResponse
+                        .BUYABLE_PRODUCT_PRICE_UPDATED);
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            ShoppingPersistedTabData.from(tab, (updatedShoppingPersistedTabData) -> {
+                ShoppingPersistedTabDataTestUtils.verifyGetPageAnnotationsCalled(
+                        mPageAnnotationsServiceMock, 2);
+                Assert.assertEquals(ShoppingPersistedTabDataTestUtils.UPDATED_PRICE_MICROS,
+                        updatedShoppingPersistedTabData.getPriceMicros());
+                Assert.assertEquals(ShoppingPersistedTabDataTestUtils.PRICE_MICROS,
+                        updatedShoppingPersistedTabData.getPreviousPriceMicros());
+                Assert.assertTrue(firstUpdateTime
+                        < updatedShoppingPersistedTabData.getLastPriceChangeTimeMs());
+                updateSemaphore.release();
+            });
+        });
+        ShoppingPersistedTabDataTestUtils.acquireSemaphore(updateSemaphore);
+        return ShoppingPersistedTabDataTestUtils.getTimeLastUpdatedOnUiThread(tab);
+    }
+    @UiThreadTest
+    @SmallTest
+    @Test
+    public void testNoRefetch() {
+        final Semaphore initialSemaphore = new Semaphore(0);
+        final Semaphore updateSemaphore = new Semaphore(0);
+        Tab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
+                ShoppingPersistedTabDataTestUtils.TAB_ID,
+                ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
+        // Mock annotations response.
+        ShoppingPersistedTabDataTestUtils.mockPageAnnotationsResponse(mPageAnnotationsServiceMock,
+                ShoppingPersistedTabDataTestUtils.MockPageAnnotationsResponse
+                        .BUYABLE_PRODUCT_INITIAL);
+
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            ShoppingPersistedTabData.from(tab, (shoppingPersistedTabData) -> {
+                Assert.assertEquals(ShoppingPersistedTabDataTestUtils.PRICE_MICROS,
+                        shoppingPersistedTabData.getPriceMicros());
+                Assert.assertEquals(ShoppingPersistedTabData.NO_PRICE_KNOWN,
+                        shoppingPersistedTabData.getPreviousPriceMicros());
+                Assert.assertEquals(ShoppingPersistedTabDataTestUtils.UNITED_STATES_CURRENCY_CODE,
+                        shoppingPersistedTabData.getCurrencyCode());
+                // By setting time to live to be a negative number, an update
+                // will be forced in the subsequent call
+                initialSemaphore.release();
+            });
+        });
+        ShoppingPersistedTabDataTestUtils.acquireSemaphore(initialSemaphore);
+        ShoppingPersistedTabDataTestUtils.verifyGetPageAnnotationsCalled(
+                mPageAnnotationsServiceMock, 1);
+        ShoppingPersistedTabDataTestUtils.mockPageAnnotationsResponse(mPageAnnotationsServiceMock,
+                ShoppingPersistedTabDataTestUtils.MockPageAnnotationsResponse
+                        .BUYABLE_PRODUCT_PRICE_UPDATED);
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            ShoppingPersistedTabData.from(tab, (shoppingPersistedTabData) -> {
+                Assert.assertEquals(ShoppingPersistedTabDataTestUtils.PRICE_MICROS,
+                        shoppingPersistedTabData.getPriceMicros());
+                Assert.assertEquals(ShoppingPersistedTabData.NO_PRICE_KNOWN,
+                        shoppingPersistedTabData.getPreviousPriceMicros());
+
+                // By setting time to live to be a negative number, an update
+                // will be forced in the subsequent call
+                updateSemaphore.release();
+            });
+        });
+        ShoppingPersistedTabDataTestUtils.acquireSemaphore(updateSemaphore);
+        // PageAnnotationsService should not have been called a second time - because we haven't
+        // passed the time to live
+        ShoppingPersistedTabDataTestUtils.verifyGetPageAnnotationsCalled(
+                mPageAnnotationsServiceMock, 1);
+    }
+
+    @UiThreadTest
+    @SmallTest
+    @Test
+    public void testPriceDropLegacy() {
+        ShoppingPersistedTabData shoppingPersistedTabData =
+                ShoppingPersistedTabDataTestUtils.createShoppingPersistedTabDataWithDefaults();
+        shoppingPersistedTabData.mPriceDropMethod = ShoppingPersistedTabData.PriceDropMethod.LEGACY;
+        // Prices unknown is not a price drop
+        Assert.assertNull(shoppingPersistedTabData.getPriceDropLegacy());
+
+        // Same price is not a price drop
+        shoppingPersistedTabData.setPriceMicros(
+                ShoppingPersistedTabDataTestUtils.HIGH_PRICE_MICROS, null);
+        shoppingPersistedTabData.setPreviousPriceMicros(
+                ShoppingPersistedTabDataTestUtils.HIGH_PRICE_MICROS);
+        Assert.assertNull(shoppingPersistedTabData.getPriceDropLegacy());
+
+        // Lower -> Higher price is not a price drop
+        shoppingPersistedTabData.setPriceMicros(
+                ShoppingPersistedTabDataTestUtils.HIGH_PRICE_MICROS, null);
+        shoppingPersistedTabData.setPreviousPriceMicros(
+                ShoppingPersistedTabDataTestUtils.LOW_PRICE_MICROS);
+        Assert.assertNull(shoppingPersistedTabData.getPriceDropLegacy());
+
+        // Actual price drop (Higher -> Lower)
+        shoppingPersistedTabData.setPriceMicros(
+                ShoppingPersistedTabDataTestUtils.LOW_PRICE_MICROS, null);
+        shoppingPersistedTabData.setPreviousPriceMicros(
+                ShoppingPersistedTabDataTestUtils.HIGH_PRICE_MICROS);
+        ShoppingPersistedTabData.PriceDrop priceDrop =
+                shoppingPersistedTabData.getPriceDropLegacy();
+        Assert.assertEquals(ShoppingPersistedTabDataTestUtils.LOW_PRICE_FORMATTED, priceDrop.price);
+        Assert.assertEquals(
+                ShoppingPersistedTabDataTestUtils.HIGH_PRICE_FORMATTED, priceDrop.previousPrice);
+    }
+
+    @UiThreadTest
+    @SmallTest
+    @Test
+    public void testPriceDropLegacyFilterSamePrice() {
+        ShoppingPersistedTabData shoppingPersistedTabData =
+                ShoppingPersistedTabDataTestUtils.createShoppingPersistedTabDataWithDefaults();
+        shoppingPersistedTabData.mPriceDropMethod = ShoppingPersistedTabData.PriceDropMethod.LEGACY;
+
+        // $10 -> $10 is not a price drop (same price)
+        shoppingPersistedTabData.setPreviousPriceMicrosForTesting(10_000_000L);
+        shoppingPersistedTabData.setPriceMicrosForTesting(10_000_000L);
+        Assert.assertNull(shoppingPersistedTabData.getPriceDropLegacy());
+    }
+
+    @UiThreadTest
+    @SmallTest
+    @Test
+    public void testPriceDropLegacyFilterNoFormattedPriceDifference() {
+        ShoppingPersistedTabData shoppingPersistedTabData =
+                ShoppingPersistedTabDataTestUtils.createShoppingPersistedTabDataWithDefaults();
+        shoppingPersistedTabData.mPriceDropMethod = ShoppingPersistedTabData.PriceDropMethod.LEGACY;
+
+        // $10.40 -> $10 (which would be displayed $10 -> $10 is not a price drop)
+        shoppingPersistedTabData.setPreviousPriceMicrosForTesting(10_400_000L);
+        shoppingPersistedTabData.setPriceMicrosForTesting(10_000_000L);
+        Assert.assertNull(shoppingPersistedTabData.getPriceDropLegacy());
+    }
+
+    @UiThreadTest
+    @SmallTest
+    @Test
+    public void testPriceDropLegacyFilterAbsoluteDifferenceTooSmall() {
+        ShoppingPersistedTabData shoppingPersistedTabData =
+                ShoppingPersistedTabDataTestUtils.createShoppingPersistedTabDataWithDefaults();
+        shoppingPersistedTabData.mPriceDropMethod = ShoppingPersistedTabData.PriceDropMethod.LEGACY;
+
+        // $2 -> $1 ($1 price drop - less than $2. Not big enough)
+        shoppingPersistedTabData.setPreviousPriceMicrosForTesting(1_000_000L);
+        shoppingPersistedTabData.setPriceMicrosForTesting(2_000_000L);
+        Assert.assertNull(shoppingPersistedTabData.getPriceDropLegacy());
+    }
+
+    @UiThreadTest
+    @SmallTest
+    @Test
+    public void testPriceDropLegacyFilterPriceIncrease() {
+        ShoppingPersistedTabData shoppingPersistedTabData =
+                ShoppingPersistedTabDataTestUtils.createShoppingPersistedTabDataWithDefaults();
+        shoppingPersistedTabData.mPriceDropMethod = ShoppingPersistedTabData.PriceDropMethod.LEGACY;
+
+        // $9.33 -> $9.66 price increase is not a price drop
+        shoppingPersistedTabData.setPreviousPriceMicrosForTesting(9_330_000L);
+        shoppingPersistedTabData.setPriceMicrosForTesting(9_660_000L);
+        Assert.assertNull(shoppingPersistedTabData.getPriceDropLegacy());
+    }
+
+    @UiThreadTest
+    @SmallTest
+    @Test
+    public void testPriceDropLegacyFilterPercentageDropNotEnough() {
+        ShoppingPersistedTabData shoppingPersistedTabData =
+                ShoppingPersistedTabDataTestUtils.createShoppingPersistedTabDataWithDefaults();
+        shoppingPersistedTabData.mPriceDropMethod = ShoppingPersistedTabData.PriceDropMethod.LEGACY;
+
+        // $50 -> $46 (8% price drop (less than 10%) not big enough)
+        shoppingPersistedTabData.setPreviousPriceMicrosForTesting(50_000_000L);
+        shoppingPersistedTabData.setPriceMicrosForTesting(46_000_000L);
+        Assert.assertNull(shoppingPersistedTabData.getPriceDropLegacy());
+    }
+
+    @UiThreadTest
+    @SmallTest
+    @Test
+    public void testPriceDropLegacyFilterAllowedPriceDrop1() {
+        ShoppingPersistedTabData shoppingPersistedTabData =
+                ShoppingPersistedTabDataTestUtils.createShoppingPersistedTabDataWithDefaults();
+        shoppingPersistedTabData.mPriceDropMethod = ShoppingPersistedTabData.PriceDropMethod.LEGACY;
+
+        // $10 -> $7 (30% and $3 price drop is big enough)
+        shoppingPersistedTabData.setPreviousPriceMicrosForTesting(10_000_000L);
+        shoppingPersistedTabData.setPriceMicrosForTesting(7_000_000L);
+        Assert.assertNotNull(shoppingPersistedTabData.getPriceDropLegacy());
+        Assert.assertEquals("$7.00", shoppingPersistedTabData.getPriceDropLegacy().price);
+        Assert.assertEquals("$10", shoppingPersistedTabData.getPriceDropLegacy().previousPrice);
+    }
+
+    @UiThreadTest
+    @SmallTest
+    @Test
+    public void testPriceDropLegacyFilterAllowedPriceDrop2() {
+        ShoppingPersistedTabData shoppingPersistedTabData =
+                ShoppingPersistedTabDataTestUtils.createShoppingPersistedTabDataWithDefaults();
+        shoppingPersistedTabData.mPriceDropMethod = ShoppingPersistedTabData.PriceDropMethod.LEGACY;
+
+        // $15.72 -> $4.80 (70% and $10.92 price drop is big enough)
+        shoppingPersistedTabData.setPreviousPriceMicrosForTesting(15_720_000L);
+        shoppingPersistedTabData.setPriceMicrosForTesting(4_800_000L);
+        Assert.assertNotNull(shoppingPersistedTabData.getPriceDropLegacy());
+        Assert.assertEquals("$4.80", shoppingPersistedTabData.getPriceDropLegacy().price);
+        Assert.assertEquals("$16", shoppingPersistedTabData.getPriceDropLegacy().previousPrice);
+    }
+
+    @UiThreadTest
+    @SmallTest
+    @Test
+    public void testPriceDropLegacyFilterAllowedPriceDrop3() {
+        ShoppingPersistedTabData shoppingPersistedTabData =
+                ShoppingPersistedTabDataTestUtils.createShoppingPersistedTabDataWithDefaults();
+        shoppingPersistedTabData.mPriceDropMethod = ShoppingPersistedTabData.PriceDropMethod.LEGACY;
+
+        // $20 -> $10 (50% and $10 price drop is big enough)
+        shoppingPersistedTabData.setPreviousPriceMicrosForTesting(20_000_000L);
+        shoppingPersistedTabData.setPriceMicrosForTesting(10_000_000L);
+        Assert.assertNotNull(shoppingPersistedTabData.getPriceDropLegacy());
+        Assert.assertEquals("$10", shoppingPersistedTabData.getPriceDropLegacy().price);
+        Assert.assertEquals("$20", shoppingPersistedTabData.getPriceDropLegacy().previousPrice);
+    }
+
+    @UiThreadTest
+    @SmallTest
+    @Test
+    public void testPriceDropLegacyFilterAllowedPriceDrop4() {
+        ShoppingPersistedTabData shoppingPersistedTabData =
+                ShoppingPersistedTabDataTestUtils.createShoppingPersistedTabDataWithDefaults();
+        shoppingPersistedTabData.mPriceDropMethod = ShoppingPersistedTabData.PriceDropMethod.LEGACY;
+
+        // $30 -> $27 (10% and $3 price drop is big enough)
+        shoppingPersistedTabData.setPreviousPriceMicrosForTesting(30_000_000L);
+        shoppingPersistedTabData.setPriceMicrosForTesting(27_000_000L);
+        Assert.assertNotNull(shoppingPersistedTabData.getPriceDropLegacy());
+        Assert.assertEquals("$27", shoppingPersistedTabData.getPriceDropLegacy().price);
+        Assert.assertEquals("$30", shoppingPersistedTabData.getPriceDropLegacy().previousPrice);
+    }
+
+    @UiThreadTest
+    @SmallTest
+    @Test
+    public void testPriceDropLegacyFilterAllowedPriceDrop5() {
+        ShoppingPersistedTabData shoppingPersistedTabData =
+                ShoppingPersistedTabDataTestUtils.createShoppingPersistedTabDataWithDefaults();
+        shoppingPersistedTabData.mPriceDropMethod = ShoppingPersistedTabData.PriceDropMethod.LEGACY;
+
+        // $30.65 -> $25.50 (17% and $5.15 price drop is big enough)
+        shoppingPersistedTabData.setPreviousPriceMicrosForTesting(30_650_000L);
+        shoppingPersistedTabData.setPriceMicrosForTesting(25_500_000L);
+        Assert.assertNotNull(shoppingPersistedTabData.getPriceDropLegacy());
+        Assert.assertEquals("$26", shoppingPersistedTabData.getPriceDropLegacy().price);
+        Assert.assertEquals("$31", shoppingPersistedTabData.getPriceDropLegacy().previousPrice);
+    }
+
+    @UiThreadTest
+    @SmallTest
+    @Test
+    public void testPriceDropLegacyFilterAllowedPriceDrop6() {
+        ShoppingPersistedTabData shoppingPersistedTabData =
+                ShoppingPersistedTabDataTestUtils.createShoppingPersistedTabDataWithDefaults();
+        shoppingPersistedTabData.mPriceDropMethod = ShoppingPersistedTabData.PriceDropMethod.LEGACY;
+
+        // $9.65 -> $3.80 (40% and $5.85 price drop is big enough)
+        shoppingPersistedTabData.setPreviousPriceMicrosForTesting(9_650_000L);
+        shoppingPersistedTabData.setPriceMicrosForTesting(3_800_000L);
+        Assert.assertNotNull(shoppingPersistedTabData.getPriceDropLegacy());
+        Assert.assertEquals("$3.80", shoppingPersistedTabData.getPriceDropLegacy().price);
+        Assert.assertEquals("$9.65", shoppingPersistedTabData.getPriceDropLegacy().previousPrice);
+    }
+
+    @UiThreadTest
+    @SmallTest
+    @Test
+    public void testPriceDropLegacyFilterAllowedPriceDrop7() {
+        ShoppingPersistedTabData shoppingPersistedTabData =
+                ShoppingPersistedTabDataTestUtils.createShoppingPersistedTabDataWithDefaults();
+        shoppingPersistedTabData.mPriceDropMethod = ShoppingPersistedTabData.PriceDropMethod.LEGACY;
+        // $9.33 -> $0.90 (96% price drop and $8.43 price drop is big enough)
+        shoppingPersistedTabData.setPreviousPriceMicrosForTesting(9_330_000L);
+        shoppingPersistedTabData.setPriceMicrosForTesting(900_000L);
+        Assert.assertNotNull(shoppingPersistedTabData.getPriceDropLegacy());
+        Assert.assertEquals("$0.90", shoppingPersistedTabData.getPriceDropLegacy().price);
+        Assert.assertEquals("$9.33", shoppingPersistedTabData.getPriceDropLegacy().previousPrice);
+    }
+
+    @UiThreadTest
+    @SmallTest
+    @Test
+    public void testPriceDropLegacyFilterAllowedPriceDrop8() {
+        ShoppingPersistedTabData shoppingPersistedTabData =
+                ShoppingPersistedTabDataTestUtils.createShoppingPersistedTabDataWithDefaults();
+        shoppingPersistedTabData.mPriceDropMethod = ShoppingPersistedTabData.PriceDropMethod.LEGACY;
+        // $20 -> $18 (10% price drop and $2 price drop is big enough)
+        shoppingPersistedTabData.setPreviousPriceMicrosForTesting(20_000_000L);
+        shoppingPersistedTabData.setPriceMicrosForTesting(18_000_000L);
+        Assert.assertNotNull(shoppingPersistedTabData.getPriceDropLegacy());
+        Assert.assertEquals("$18", shoppingPersistedTabData.getPriceDropLegacy().price);
+        Assert.assertEquals("$20", shoppingPersistedTabData.getPriceDropLegacy().previousPrice);
+    }
+
+    @UiThreadTest
+    @SmallTest
+    @Test
+    public void testPriceDropLegacyFilterAllowedPriceDrop9() {
+        ShoppingPersistedTabData shoppingPersistedTabData =
+                ShoppingPersistedTabDataTestUtils.createShoppingPersistedTabDataWithDefaults();
+        shoppingPersistedTabData.mPriceDropMethod = ShoppingPersistedTabData.PriceDropMethod.LEGACY;
+        // $2 -> $0 ($2 price drop is big enough)
+        shoppingPersistedTabData.setPreviousPriceMicrosForTesting(2_000_000L);
+        shoppingPersistedTabData.setPriceMicrosForTesting(0L);
+        Assert.assertNotNull(shoppingPersistedTabData.getPriceDropLegacy());
+        Assert.assertEquals("$0.00", shoppingPersistedTabData.getPriceDropLegacy().price);
+        Assert.assertEquals("$2.00", shoppingPersistedTabData.getPriceDropLegacy().previousPrice);
+    }
+
+    @UiThreadTest
+    @SmallTest
+    @Test
+    public void testPriceDropLegacyGBP() {
+        ShoppingPersistedTabData shoppingPersistedTabData =
+                ShoppingPersistedTabDataTestUtils.createShoppingPersistedTabDataWithCurrencyCode(
+                        ShoppingPersistedTabDataTestUtils.TAB_ID,
+                        ShoppingPersistedTabDataTestUtils.IS_INCOGNITO,
+                        ShoppingPersistedTabDataTestUtils.GREAT_BRITAIN_CURRENCY_CODE);
+        shoppingPersistedTabData.mPriceDropMethod = ShoppingPersistedTabData.PriceDropMethod.LEGACY;
+        shoppingPersistedTabData.setPreviousPriceMicrosForTesting(15_000_000L);
+        shoppingPersistedTabData.setPriceMicrosForTesting(9_560_000L);
+        Assert.assertNotNull(shoppingPersistedTabData.getPriceDropLegacy());
+        Assert.assertEquals("£15", shoppingPersistedTabData.getPriceDropLegacy().previousPrice);
+        Assert.assertEquals("£9.56", shoppingPersistedTabData.getPriceDropLegacy().price);
+    }
+
+    @UiThreadTest
+    @SmallTest
+    @Test
+    public void testPriceDropLegacyJPY() {
+        ShoppingPersistedTabData shoppingPersistedTabData =
+                ShoppingPersistedTabDataTestUtils.createShoppingPersistedTabDataWithCurrencyCode(
+                        ShoppingPersistedTabDataTestUtils.TAB_ID,
+                        ShoppingPersistedTabDataTestUtils.IS_INCOGNITO,
+                        ShoppingPersistedTabDataTestUtils.JAPAN_CURRENCY_CODE);
+        shoppingPersistedTabData.mPriceDropMethod = ShoppingPersistedTabData.PriceDropMethod.LEGACY;
+        shoppingPersistedTabData.setPreviousPriceMicrosForTesting(3_140_000_000_000L);
+        shoppingPersistedTabData.setPriceMicrosForTesting(287_000_000_000L);
+        Assert.assertNotNull(shoppingPersistedTabData.getPriceDropLegacy());
+        Assert.assertEquals(
+                "¥3,140,000", shoppingPersistedTabData.getPriceDropLegacy().previousPrice);
+        Assert.assertEquals("¥287,000", shoppingPersistedTabData.getPriceDropLegacy().price);
+    }
+
+    @UiThreadTest
+    @SmallTest
+    @Test
+    public void testStalePriceDropUSD() {
+        ShoppingPersistedTabData shoppingPersistedTabData =
+                ShoppingPersistedTabDataTestUtils.createShoppingPersistedTabDataWithCurrencyCode(
+                        ShoppingPersistedTabDataTestUtils.TAB_ID,
+                        ShoppingPersistedTabDataTestUtils.IS_INCOGNITO,
+                        ShoppingPersistedTabDataTestUtils.UNITED_STATES_CURRENCY_CODE);
+        shoppingPersistedTabData.mPriceDropMethod = ShoppingPersistedTabData.PriceDropMethod.LEGACY;
+        // $10 -> $5 (50% and $5 price drop is big enough)
+        shoppingPersistedTabData.setPreviousPriceMicrosForTesting(10000000L);
+        shoppingPersistedTabData.setPriceMicrosForTesting(5000000L);
+        Assert.assertNotNull(shoppingPersistedTabData.getPriceDropLegacy());
+        Assert.assertEquals("$5.00", shoppingPersistedTabData.getPriceDropLegacy().price);
+        Assert.assertEquals("$10", shoppingPersistedTabData.getPriceDropLegacy().previousPrice);
+
+        // Price drops greater than a week old are removed
+        shoppingPersistedTabData.setLastPriceChangeTimeMsForTesting(
+                System.currentTimeMillis() - TimeUnit.DAYS.toMillis(8));
+        Assert.assertNull(shoppingPersistedTabData.getPriceDropLegacy());
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataTest.java
index c39ef30..2098161 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataTest.java
@@ -4,19 +4,10 @@
 
 package org.chromium.chrome.browser.tab.state;
 
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
 
 import android.support.test.filters.SmallTest;
 
-import androidx.annotation.IntDef;
-
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
@@ -25,10 +16,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
 
-import org.chromium.base.Callback;
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.UiThreadTest;
@@ -39,10 +27,7 @@
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.optimization_guide.OptimizationGuideBridge;
-import org.chromium.chrome.browser.optimization_guide.OptimizationGuideBridge.OptimizationGuideCallback;
 import org.chromium.chrome.browser.optimization_guide.OptimizationGuideBridgeJni;
-import org.chromium.chrome.browser.page_annotations.BuyableProductPageAnnotation;
-import org.chromium.chrome.browser.page_annotations.PageAnnotation;
 import org.chromium.chrome.browser.page_annotations.PageAnnotationsService;
 import org.chromium.chrome.browser.page_annotations.PageAnnotationsServiceFactory;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -53,14 +38,10 @@
 import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
 import org.chromium.components.optimization_guide.OptimizationGuideDecision;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.url.GURL;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.LinkedList;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
+
 /**
  * Test relating to {@link ShoppingPersistedTabData}
  */
@@ -69,16 +50,6 @@
 @CommandLineFlags.
 Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, "force-fieldtrials=Study/Group"})
 public class ShoppingPersistedTabDataTest {
-    @IntDef({MockPageAnnotationsResponse.BUYABLE_PRODUCT_INITIAL,
-            MockPageAnnotationsResponse.BUYABLE_PRODUCT_PRICE_UPDATED,
-            MockPageAnnotationsResponse.BUYABLE_PRODUCT_EMPTY})
-    @Retention(RetentionPolicy.SOURCE)
-    private @interface MockPageAnnotationsResponse {
-        int BUYABLE_PRODUCT_INITIAL = 0;
-        int BUYABLE_PRODUCT_PRICE_UPDATED = 1;
-        int BUYABLE_PRODUCT_EMPTY = 2;
-    }
-
     @Rule
     public final ChromeBrowserTestRule mBrowserTestRule = new ChromeBrowserTestRule();
 
@@ -88,33 +59,20 @@
     @Rule
     public TestRule mProcessor = new Features.InstrumentationProcessor();
 
-    private static final int TAB_ID = 1;
-    private static final boolean IS_INCOGNITO = false;
+    @Mock
+    protected EndpointFetcher.Natives mEndpointFetcherJniMock;
 
     @Mock
-    EndpointFetcher.Natives mEndpointFetcherJniMock;
+    protected OptimizationGuideBridge.Natives mOptimizationGuideBridgeJniMock;
 
     @Mock
-    OptimizationGuideBridge.Natives mOptimizationGuideBridgeJniMock;
+    protected Profile mProfileMock;
 
     @Mock
-    Profile mProfileMock;
+    protected PageAnnotationsServiceFactory mServiceFactoryMock;
 
     @Mock
-    PageAnnotationsServiceFactory mServiceFactoryMock;
-
-    @Mock
-    PageAnnotationsService mPageAnnotationsServiceMock;
-
-    private static final long PRICE_MICROS = 123456789012345L;
-    private static final long UPDATED_PRICE_MICROS = 287000000L;
-    private static final long HIGH_PRICE_MICROS = 141000000L;
-    private static final long LOW_PRICE_MICROS = 100000000L;
-    private static final String HIGH_PRICE_FORMATTED = "$141";
-    private static final String LOW_PRICE_FORMATTED = "$100";
-    private static final String UNITED_STATES_CURRENCY_CODE = "USD";
-    private static final String GREAT_BRITAIN_CURRENCY_CODE = "GBP";
-    private static final String JAPAN_CURRENCY_CODE = "JPY";
+    protected PageAnnotationsService mPageAnnotationsServiceMock;
 
     @Before
     public void setUp() {
@@ -123,7 +81,8 @@
         mMocker.mock(OptimizationGuideBridgeJni.TEST_HOOKS, mOptimizationGuideBridgeJniMock);
         // Ensure native pointer is initialized
         doReturn(1L).when(mOptimizationGuideBridgeJniMock).init();
-        mockOptimizationGuideResponse(OptimizationGuideDecision.TRUE);
+        ShoppingPersistedTabDataTestUtils.mockOptimizationGuideResponse(
+                mOptimizationGuideBridgeJniMock, OptimizationGuideDecision.TRUE);
         PersistedTabDataConfiguration.setUseTestConfig(true);
 
         Profile.setLastUsedProfileForTesting(mProfileMock);
@@ -135,66 +94,42 @@
     @SmallTest
     @Test
     public void testShoppingProto() {
-        Tab tab = new MockTab(TAB_ID, IS_INCOGNITO);
+        Tab tab = new MockTab(ShoppingPersistedTabDataTestUtils.TAB_ID,
+                ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
         ShoppingPersistedTabData shoppingPersistedTabData = new ShoppingPersistedTabData(tab);
         ObservableSupplierImpl<Boolean> supplier = new ObservableSupplierImpl<>();
         supplier.set(true);
         shoppingPersistedTabData.registerIsTabSaveEnabledSupplier(supplier);
-        shoppingPersistedTabData.setPriceMicros(PRICE_MICROS, null);
+        shoppingPersistedTabData.setPriceMicros(
+                ShoppingPersistedTabDataTestUtils.PRICE_MICROS, null);
         byte[] serialized = shoppingPersistedTabData.serialize();
         ShoppingPersistedTabData deserialized = new ShoppingPersistedTabData(tab);
         deserialized.deserialize(serialized);
-        Assert.assertEquals(PRICE_MICROS, deserialized.getPriceMicros());
+        Assert.assertEquals(
+                ShoppingPersistedTabDataTestUtils.PRICE_MICROS, deserialized.getPriceMicros());
         Assert.assertEquals(
                 ShoppingPersistedTabData.NO_PRICE_KNOWN, deserialized.getPreviousPriceMicros());
     }
 
     @SmallTest
     @Test
-    @CommandLineFlags.
-    Add({"force-fieldtrial-params=Study.Group:price_tracking_time_to_live_ms/-1000"})
-    public void testShoppingPriceChange() {
-        shoppingPriceChange(createTabOnUiThread(TAB_ID, IS_INCOGNITO));
-    }
-
-    @SmallTest
-    @Test
-    @CommandLineFlags.
-    Add({"force-fieldtrial-params=Study.Group:price_tracking_time_to_live_ms/-1000"})
-    public void testShoppingPriceChangeExtraFetchAfterChange() {
-        Tab tab = createTabOnUiThread(TAB_ID, IS_INCOGNITO);
-        long mLastPriceChangeTimeMs = shoppingPriceChange(tab);
-        final Semaphore semaphore = new Semaphore(0);
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            ShoppingPersistedTabData.from(tab, (shoppingPersistedTabData) -> {
-                verifyGetPageAnnotationsCalled(3);
-                Assert.assertEquals(
-                        UPDATED_PRICE_MICROS, shoppingPersistedTabData.getPriceMicros());
-                Assert.assertEquals(
-                        PRICE_MICROS, shoppingPersistedTabData.getPreviousPriceMicros());
-                Assert.assertEquals(mLastPriceChangeTimeMs,
-                        shoppingPersistedTabData.getLastPriceChangeTimeMs());
-                Assert.assertEquals(
-                        UNITED_STATES_CURRENCY_CODE, shoppingPersistedTabData.getCurrencyCode());
-                semaphore.release();
-            });
-        });
-        acquireSemaphore(semaphore);
-    }
-
-    @SmallTest
-    @Test
     public void testShoppingBloomFilterNotShoppingWebsite() {
-        mockPageAnnotationsResponse(MockPageAnnotationsResponse.BUYABLE_PRODUCT_INITIAL);
-        mockOptimizationGuideResponse(OptimizationGuideDecision.FALSE);
-        Tab tab = createTabOnUiThread(TAB_ID, IS_INCOGNITO);
+        ShoppingPersistedTabDataTestUtils.mockPageAnnotationsResponse(mPageAnnotationsServiceMock,
+                ShoppingPersistedTabDataTestUtils.MockPageAnnotationsResponse
+                        .BUYABLE_PRODUCT_INITIAL);
+        ShoppingPersistedTabDataTestUtils.mockOptimizationGuideResponse(
+                mOptimizationGuideBridgeJniMock, OptimizationGuideDecision.FALSE);
+        Tab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
+                ShoppingPersistedTabDataTestUtils.TAB_ID,
+                ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
         Semaphore semaphore = new Semaphore(0);
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             ShoppingPersistedTabData.from(
                     tab, (shoppingPersistedTabData) -> { semaphore.release(); });
         });
-        acquireSemaphore(semaphore);
-        verifyGetPageAnnotationsCalled(0);
+        ShoppingPersistedTabDataTestUtils.acquireSemaphore(semaphore);
+        ShoppingPersistedTabDataTestUtils.verifyGetPageAnnotationsCalled(
+                mPageAnnotationsServiceMock, 0);
     }
 
     @SmallTest
@@ -202,82 +137,63 @@
     public void testShoppingBloomFilterShoppingWebsite() {
         for (@OptimizationGuideDecision int decision :
                 new int[] {OptimizationGuideDecision.TRUE, OptimizationGuideDecision.UNKNOWN}) {
-            mockPageAnnotationsResponse(MockPageAnnotationsResponse.BUYABLE_PRODUCT_INITIAL);
-            mockOptimizationGuideResponse(decision);
-            Tab tab = createTabOnUiThread(TAB_ID, IS_INCOGNITO);
+            ShoppingPersistedTabDataTestUtils.mockPageAnnotationsResponse(
+                    mPageAnnotationsServiceMock,
+                    ShoppingPersistedTabDataTestUtils.MockPageAnnotationsResponse
+                            .BUYABLE_PRODUCT_INITIAL);
+            ShoppingPersistedTabDataTestUtils.mockOptimizationGuideResponse(
+                    mOptimizationGuideBridgeJniMock, decision);
+            Tab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
+                    ShoppingPersistedTabDataTestUtils.TAB_ID,
+                    ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
             Semaphore semaphore = new Semaphore(0);
             TestThreadUtils.runOnUiThreadBlocking(() -> {
                 ShoppingPersistedTabData.from(
                         tab, (shoppingPersistedTabData) -> { semaphore.release(); });
             });
-            acquireSemaphore(semaphore);
-            verifyGetPageAnnotationsCalled(1);
+            ShoppingPersistedTabDataTestUtils.acquireSemaphore(semaphore);
+            ShoppingPersistedTabDataTestUtils.verifyGetPageAnnotationsCalled(
+                    mPageAnnotationsServiceMock, 1);
         }
     }
 
-    private long shoppingPriceChange(Tab tab) {
-        final Semaphore initialSemaphore = new Semaphore(0);
-        final Semaphore updateSemaphore = new Semaphore(0);
-        mockPageAnnotationsResponse(MockPageAnnotationsResponse.BUYABLE_PRODUCT_INITIAL);
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            ShoppingPersistedTabData.from(tab, (shoppingPersistedTabData) -> {
-                verifyGetPageAnnotationsCalled(1);
-                Assert.assertEquals(PRICE_MICROS, shoppingPersistedTabData.getPriceMicros());
-                Assert.assertEquals(
-                        UNITED_STATES_CURRENCY_CODE, shoppingPersistedTabData.getCurrencyCode());
-                Assert.assertEquals(ShoppingPersistedTabData.NO_PRICE_KNOWN,
-                        shoppingPersistedTabData.getPreviousPriceMicros());
-                Assert.assertEquals(ShoppingPersistedTabData.NO_TRANSITIONS_OCCURRED,
-                        shoppingPersistedTabData.getLastPriceChangeTimeMs());
-                initialSemaphore.release();
-            });
-        });
-        acquireSemaphore(initialSemaphore);
-        long firstUpdateTime = getTimeLastUpdatedOnUiThread(tab);
-        mockPageAnnotationsResponse(MockPageAnnotationsResponse.BUYABLE_PRODUCT_PRICE_UPDATED);
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            ShoppingPersistedTabData.from(tab, (updatedShoppingPersistedTabData) -> {
-                verifyGetPageAnnotationsCalled(2);
-                Assert.assertEquals(
-                        UPDATED_PRICE_MICROS, updatedShoppingPersistedTabData.getPriceMicros());
-                Assert.assertEquals(
-                        PRICE_MICROS, updatedShoppingPersistedTabData.getPreviousPriceMicros());
-                Assert.assertTrue(firstUpdateTime
-                        < updatedShoppingPersistedTabData.getLastPriceChangeTimeMs());
-                updateSemaphore.release();
-            });
-        });
-        acquireSemaphore(updateSemaphore);
-        return getTimeLastUpdatedOnUiThread(tab);
-    }
     @UiThreadTest
     @SmallTest
     @Test
     public void testNoRefetch() {
         final Semaphore initialSemaphore = new Semaphore(0);
         final Semaphore updateSemaphore = new Semaphore(0);
-        Tab tab = createTabOnUiThread(TAB_ID, IS_INCOGNITO);
+        Tab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
+                ShoppingPersistedTabDataTestUtils.TAB_ID,
+                ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
         // Mock annotations response.
-        mockPageAnnotationsResponse(MockPageAnnotationsResponse.BUYABLE_PRODUCT_INITIAL);
+        ShoppingPersistedTabDataTestUtils.mockPageAnnotationsResponse(mPageAnnotationsServiceMock,
+                ShoppingPersistedTabDataTestUtils.MockPageAnnotationsResponse
+                        .BUYABLE_PRODUCT_INITIAL);
 
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             ShoppingPersistedTabData.from(tab, (shoppingPersistedTabData) -> {
-                Assert.assertEquals(PRICE_MICROS, shoppingPersistedTabData.getPriceMicros());
+                Assert.assertEquals(ShoppingPersistedTabDataTestUtils.PRICE_MICROS,
+                        shoppingPersistedTabData.getPriceMicros());
                 Assert.assertEquals(ShoppingPersistedTabData.NO_PRICE_KNOWN,
                         shoppingPersistedTabData.getPreviousPriceMicros());
-                Assert.assertEquals(
-                        UNITED_STATES_CURRENCY_CODE, shoppingPersistedTabData.getCurrencyCode());
+                Assert.assertEquals(ShoppingPersistedTabDataTestUtils.UNITED_STATES_CURRENCY_CODE,
+                        shoppingPersistedTabData.getCurrencyCode());
                 // By setting time to live to be a negative number, an update
                 // will be forced in the subsequent call
                 initialSemaphore.release();
             });
         });
-        acquireSemaphore(initialSemaphore);
-        verifyGetPageAnnotationsCalled(1);
-        mockPageAnnotationsResponse(MockPageAnnotationsResponse.BUYABLE_PRODUCT_PRICE_UPDATED);
+        ShoppingPersistedTabDataTestUtils.acquireSemaphore(initialSemaphore);
+        ShoppingPersistedTabDataTestUtils.verifyGetPageAnnotationsCalled(
+                mPageAnnotationsServiceMock, 1);
+        ShoppingPersistedTabDataTestUtils.mockPageAnnotationsResponse(mPageAnnotationsServiceMock,
+                ShoppingPersistedTabDataTestUtils.MockPageAnnotationsResponse
+                        .BUYABLE_PRODUCT_PRICE_UPDATED);
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             ShoppingPersistedTabData.from(tab, (shoppingPersistedTabData) -> {
-                Assert.assertEquals(PRICE_MICROS, shoppingPersistedTabData.getPriceMicros());
+                Assert.assertEquals(ShoppingPersistedTabDataTestUtils.PRICE_MICROS,
+                        shoppingPersistedTabData.getPriceMicros());
                 Assert.assertEquals(ShoppingPersistedTabData.NO_PRICE_KNOWN,
                         shoppingPersistedTabData.getPreviousPriceMicros());
 
@@ -286,39 +202,11 @@
                 updateSemaphore.release();
             });
         });
-        acquireSemaphore(updateSemaphore);
+        ShoppingPersistedTabDataTestUtils.acquireSemaphore(updateSemaphore);
         // EndpointFetcher should not have been called a second time - because we haven't passed the
         // time to live
-        verifyGetPageAnnotationsCalled(1);
-    }
-
-    private void mockPageAnnotationsResponse(@MockPageAnnotationsResponse int expectedResponse) {
-        doAnswer(new Answer<Void>() {
-            @Override
-            public Void answer(InvocationOnMock invocation) {
-                Callback callback = (Callback) invocation.getArguments()[1];
-                callback.onResult(new LinkedList<PageAnnotation>() {
-                    {
-                        switch (expectedResponse) {
-                            case MockPageAnnotationsResponse.BUYABLE_PRODUCT_INITIAL:
-                                add(new BuyableProductPageAnnotation(
-                                        PRICE_MICROS, UNITED_STATES_CURRENCY_CODE));
-                                break;
-                            case MockPageAnnotationsResponse.BUYABLE_PRODUCT_PRICE_UPDATED:
-                                add(new BuyableProductPageAnnotation(
-                                        UPDATED_PRICE_MICROS, UNITED_STATES_CURRENCY_CODE));
-                                break;
-                            case MockPageAnnotationsResponse.BUYABLE_PRODUCT_EMPTY:
-                            default:
-                                break;
-                        }
-                    }
-                });
-                return null;
-            }
-        })
-                .when(mPageAnnotationsServiceMock)
-                .getAnnotations(any(GURL.class), any(Callback.class));
+        ShoppingPersistedTabDataTestUtils.verifyGetPageAnnotationsCalled(
+                mPageAnnotationsServiceMock, 1);
     }
 
     @UiThreadTest
@@ -326,26 +214,33 @@
     @Test
     public void testPriceDrop() {
         ShoppingPersistedTabData shoppingPersistedTabData =
-                createShoppingPersistedTabDataWithDefaults();
+                ShoppingPersistedTabDataTestUtils.createShoppingPersistedTabDataWithDefaults();
         // Prices unknown is not a price drop
         Assert.assertNull(shoppingPersistedTabData.getPriceDrop());
 
         // Same price is not a price drop
-        shoppingPersistedTabData.setPriceMicros(HIGH_PRICE_MICROS, null);
-        shoppingPersistedTabData.setPreviousPriceMicros(HIGH_PRICE_MICROS);
+        shoppingPersistedTabData.setPriceMicros(
+                ShoppingPersistedTabDataTestUtils.HIGH_PRICE_MICROS, null);
+        shoppingPersistedTabData.setPreviousPriceMicros(
+                ShoppingPersistedTabDataTestUtils.HIGH_PRICE_MICROS);
         Assert.assertNull(shoppingPersistedTabData.getPriceDrop());
 
         // Lower -> Higher price is not a price drop
-        shoppingPersistedTabData.setPriceMicros(HIGH_PRICE_MICROS, null);
-        shoppingPersistedTabData.setPreviousPriceMicros(LOW_PRICE_MICROS);
+        shoppingPersistedTabData.setPriceMicros(
+                ShoppingPersistedTabDataTestUtils.HIGH_PRICE_MICROS, null);
+        shoppingPersistedTabData.setPreviousPriceMicros(
+                ShoppingPersistedTabDataTestUtils.LOW_PRICE_MICROS);
         Assert.assertNull(shoppingPersistedTabData.getPriceDrop());
 
         // Actual price drop (Higher -> Lower)
-        shoppingPersistedTabData.setPriceMicros(LOW_PRICE_MICROS, null);
-        shoppingPersistedTabData.setPreviousPriceMicros(HIGH_PRICE_MICROS);
+        shoppingPersistedTabData.setPriceMicros(
+                ShoppingPersistedTabDataTestUtils.LOW_PRICE_MICROS, null);
+        shoppingPersistedTabData.setPreviousPriceMicros(
+                ShoppingPersistedTabDataTestUtils.HIGH_PRICE_MICROS);
         ShoppingPersistedTabData.PriceDrop priceDrop = shoppingPersistedTabData.getPriceDrop();
-        Assert.assertEquals(LOW_PRICE_FORMATTED, priceDrop.price);
-        Assert.assertEquals(HIGH_PRICE_FORMATTED, priceDrop.previousPrice);
+        Assert.assertEquals(ShoppingPersistedTabDataTestUtils.LOW_PRICE_FORMATTED, priceDrop.price);
+        Assert.assertEquals(
+                ShoppingPersistedTabDataTestUtils.HIGH_PRICE_FORMATTED, priceDrop.previousPrice);
     }
 
     @UiThreadTest
@@ -353,7 +248,7 @@
     @Test
     public void testPriceDropFilterSamePrice() {
         ShoppingPersistedTabData shoppingPersistedTabData =
-                createShoppingPersistedTabDataWithDefaults();
+                ShoppingPersistedTabDataTestUtils.createShoppingPersistedTabDataWithDefaults();
 
         // $10 -> $10 is not a price drop (same price)
         shoppingPersistedTabData.setPreviousPriceMicrosForTesting(10_000_000L);
@@ -366,7 +261,7 @@
     @Test
     public void testPriceDropFilterNoFormattedPriceDifference() {
         ShoppingPersistedTabData shoppingPersistedTabData =
-                createShoppingPersistedTabDataWithDefaults();
+                ShoppingPersistedTabDataTestUtils.createShoppingPersistedTabDataWithDefaults();
 
         // $10.40 -> $10 (which would be displayed $10 -> $10 is not a price drop)
         shoppingPersistedTabData.setPreviousPriceMicrosForTesting(10_400_000L);
@@ -377,22 +272,9 @@
     @UiThreadTest
     @SmallTest
     @Test
-    public void testPriceDropFilterAbsoluteDifferenceTooSmall() {
-        ShoppingPersistedTabData shoppingPersistedTabData =
-                createShoppingPersistedTabDataWithDefaults();
-
-        // $2 -> $1 ($1 price drop - less than $2. Not big enough)
-        shoppingPersistedTabData.setPreviousPriceMicrosForTesting(1_000_000L);
-        shoppingPersistedTabData.setPriceMicrosForTesting(2_000_000L);
-        Assert.assertNull(shoppingPersistedTabData.getPriceDrop());
-    }
-
-    @UiThreadTest
-    @SmallTest
-    @Test
     public void testPriceDropFilterPriceIncrease() {
         ShoppingPersistedTabData shoppingPersistedTabData =
-                createShoppingPersistedTabDataWithDefaults();
+                ShoppingPersistedTabDataTestUtils.createShoppingPersistedTabDataWithDefaults();
 
         // $9.33 -> $9.66 price increase is not a price drop
         shoppingPersistedTabData.setPreviousPriceMicrosForTesting(9_330_000L);
@@ -403,174 +285,12 @@
     @UiThreadTest
     @SmallTest
     @Test
-    public void testPriceDropFilterPercentageDropNotEnough() {
-        ShoppingPersistedTabData shoppingPersistedTabData =
-                createShoppingPersistedTabDataWithDefaults();
-
-        // $50 -> $46 (8% price drop (less than 10%) not big enough)
-        shoppingPersistedTabData.setPreviousPriceMicrosForTesting(50_000_000L);
-        shoppingPersistedTabData.setPriceMicrosForTesting(46_000_000L);
-        Assert.assertNull(shoppingPersistedTabData.getPriceDrop());
-    }
-
-    private ShoppingPersistedTabData createShoppingPersistedTabDataWithDefaults() {
-        ShoppingPersistedTabData shoppingPersistedTabData =
-                new ShoppingPersistedTabData(createTabOnUiThread(TAB_ID, IS_INCOGNITO));
-        shoppingPersistedTabData.setCurrencyCode(UNITED_STATES_CURRENCY_CODE);
-        return shoppingPersistedTabData;
-    }
-
-    private ShoppingPersistedTabData createShoppingPersistedTabDataWithCurrencyCode(
-            int tabId, boolean isIncognito, String currencyCode) {
-        ShoppingPersistedTabData shoppingPersistedTabData =
-                new ShoppingPersistedTabData(createTabOnUiThread(tabId, isIncognito));
-        shoppingPersistedTabData.setCurrencyCode(currencyCode);
-        return shoppingPersistedTabData;
-    }
-
-    @UiThreadTest
-    @SmallTest
-    @Test
-    public void testPriceDropFilterAllowedPriceDrop1() {
-        ShoppingPersistedTabData shoppingPersistedTabData =
-                createShoppingPersistedTabDataWithDefaults();
-
-        // $10 -> $7 (30% and $3 price drop is big enough)
-        shoppingPersistedTabData.setPreviousPriceMicrosForTesting(10_000_000L);
-        shoppingPersistedTabData.setPriceMicrosForTesting(7_000_000L);
-        Assert.assertNotNull(shoppingPersistedTabData.getPriceDrop());
-        Assert.assertEquals("$7.00", shoppingPersistedTabData.getPriceDrop().price);
-        Assert.assertEquals("$10", shoppingPersistedTabData.getPriceDrop().previousPrice);
-    }
-
-    @UiThreadTest
-    @SmallTest
-    @Test
-    public void testPriceDropFilterAllowedPriceDrop2() {
-        ShoppingPersistedTabData shoppingPersistedTabData =
-                createShoppingPersistedTabDataWithDefaults();
-
-        // $15.72 -> $4.80 (70% and $10.92 price drop is big enough)
-        shoppingPersistedTabData.setPreviousPriceMicrosForTesting(15_720_000L);
-        shoppingPersistedTabData.setPriceMicrosForTesting(4_800_000L);
-        Assert.assertNotNull(shoppingPersistedTabData.getPriceDrop());
-        Assert.assertEquals("$4.80", shoppingPersistedTabData.getPriceDrop().price);
-        Assert.assertEquals("$16", shoppingPersistedTabData.getPriceDrop().previousPrice);
-    }
-
-    @UiThreadTest
-    @SmallTest
-    @Test
-    public void testPriceDropFilterAllowedPriceDrop3() {
-        ShoppingPersistedTabData shoppingPersistedTabData =
-                createShoppingPersistedTabDataWithDefaults();
-
-        // $20 -> $10 (50% and $10 price drop is big enough)
-        shoppingPersistedTabData.setPreviousPriceMicrosForTesting(20_000_000L);
-        shoppingPersistedTabData.setPriceMicrosForTesting(10_000_000L);
-        Assert.assertNotNull(shoppingPersistedTabData.getPriceDrop());
-        Assert.assertEquals("$10", shoppingPersistedTabData.getPriceDrop().price);
-        Assert.assertEquals("$20", shoppingPersistedTabData.getPriceDrop().previousPrice);
-    }
-
-    @UiThreadTest
-    @SmallTest
-    @Test
-    public void testPriceDropFilterAllowedPriceDrop4() {
-        ShoppingPersistedTabData shoppingPersistedTabData =
-                createShoppingPersistedTabDataWithDefaults();
-
-        // $30 -> $27 (10% and $3 price drop is big enough)
-        shoppingPersistedTabData.setPreviousPriceMicrosForTesting(30_000_000L);
-        shoppingPersistedTabData.setPriceMicrosForTesting(27_000_000L);
-        Assert.assertNotNull(shoppingPersistedTabData.getPriceDrop());
-        Assert.assertEquals("$27", shoppingPersistedTabData.getPriceDrop().price);
-        Assert.assertEquals("$30", shoppingPersistedTabData.getPriceDrop().previousPrice);
-    }
-
-    @UiThreadTest
-    @SmallTest
-    @Test
-    public void testPriceDropFilterAllowedPriceDrop5() {
-        ShoppingPersistedTabData shoppingPersistedTabData =
-                createShoppingPersistedTabDataWithDefaults();
-
-        // $30.65 -> $25.50 (17% and $5.15 price drop is big enough)
-        shoppingPersistedTabData.setPreviousPriceMicrosForTesting(30_650_000L);
-        shoppingPersistedTabData.setPriceMicrosForTesting(25_500_000L);
-        Assert.assertNotNull(shoppingPersistedTabData.getPriceDrop());
-        Assert.assertEquals("$26", shoppingPersistedTabData.getPriceDrop().price);
-        Assert.assertEquals("$31", shoppingPersistedTabData.getPriceDrop().previousPrice);
-    }
-
-    @UiThreadTest
-    @SmallTest
-    @Test
-    public void testPriceDropFilterAllowedPriceDrop6() {
-        ShoppingPersistedTabData shoppingPersistedTabData =
-                createShoppingPersistedTabDataWithDefaults();
-
-        // $9.65 -> $3.80 (40% and $5.85 price drop is big enough)
-        shoppingPersistedTabData.setPreviousPriceMicrosForTesting(9_650_000L);
-        shoppingPersistedTabData.setPriceMicrosForTesting(3_800_000L);
-        Assert.assertNotNull(shoppingPersistedTabData.getPriceDrop());
-        Assert.assertEquals("$3.80", shoppingPersistedTabData.getPriceDrop().price);
-        Assert.assertEquals("$9.65", shoppingPersistedTabData.getPriceDrop().previousPrice);
-    }
-
-    @UiThreadTest
-    @SmallTest
-    @Test
-    public void testPriceDropFilterAllowedPriceDrop7() {
-        ShoppingPersistedTabData shoppingPersistedTabData =
-                createShoppingPersistedTabDataWithDefaults();
-
-        // $9.33 -> $0.90 (96% price drop and $8.43 price drop is big enough)
-        shoppingPersistedTabData.setPreviousPriceMicrosForTesting(9_330_000L);
-        shoppingPersistedTabData.setPriceMicrosForTesting(900_000L);
-        Assert.assertNotNull(shoppingPersistedTabData.getPriceDrop());
-        Assert.assertEquals("$0.90", shoppingPersistedTabData.getPriceDrop().price);
-        Assert.assertEquals("$9.33", shoppingPersistedTabData.getPriceDrop().previousPrice);
-    }
-
-    @UiThreadTest
-    @SmallTest
-    @Test
-    public void testPriceDropFilterAllowedPriceDrop8() {
-        ShoppingPersistedTabData shoppingPersistedTabData =
-                createShoppingPersistedTabDataWithDefaults();
-
-        // $20 -> $18 (10% price drop and $2 price drop is big enough)
-        shoppingPersistedTabData.setPreviousPriceMicrosForTesting(20_000_000L);
-        shoppingPersistedTabData.setPriceMicrosForTesting(18_000_000L);
-        Assert.assertNotNull(shoppingPersistedTabData.getPriceDrop());
-        Assert.assertEquals("$18", shoppingPersistedTabData.getPriceDrop().price);
-        Assert.assertEquals("$20", shoppingPersistedTabData.getPriceDrop().previousPrice);
-    }
-
-    @UiThreadTest
-    @SmallTest
-    @Test
-    public void testPriceDropFilterAllowedPriceDrop9() {
-        ShoppingPersistedTabData shoppingPersistedTabData =
-                createShoppingPersistedTabDataWithDefaults();
-
-        // $2 -> $0 ($2 price drop is big enough)
-        shoppingPersistedTabData.setPreviousPriceMicrosForTesting(2_000_000L);
-        shoppingPersistedTabData.setPriceMicrosForTesting(0L);
-        Assert.assertNotNull(shoppingPersistedTabData.getPriceDrop());
-        Assert.assertEquals("$0.00", shoppingPersistedTabData.getPriceDrop().price);
-        Assert.assertEquals("$2.00", shoppingPersistedTabData.getPriceDrop().previousPrice);
-    }
-
-    @UiThreadTest
-    @SmallTest
-    @Test
     public void testPriceDropGBP() {
         ShoppingPersistedTabData shoppingPersistedTabData =
-                createShoppingPersistedTabDataWithCurrencyCode(
-                        TAB_ID, IS_INCOGNITO, GREAT_BRITAIN_CURRENCY_CODE);
-
+                ShoppingPersistedTabDataTestUtils.createShoppingPersistedTabDataWithCurrencyCode(
+                        ShoppingPersistedTabDataTestUtils.TAB_ID,
+                        ShoppingPersistedTabDataTestUtils.IS_INCOGNITO,
+                        ShoppingPersistedTabDataTestUtils.GREAT_BRITAIN_CURRENCY_CODE);
         shoppingPersistedTabData.setPreviousPriceMicrosForTesting(15_000_000L);
         shoppingPersistedTabData.setPriceMicrosForTesting(9_560_000L);
         Assert.assertNotNull(shoppingPersistedTabData.getPriceDrop());
@@ -583,9 +303,10 @@
     @Test
     public void testPriceDropJPY() {
         ShoppingPersistedTabData shoppingPersistedTabData =
-                createShoppingPersistedTabDataWithCurrencyCode(
-                        TAB_ID, IS_INCOGNITO, JAPAN_CURRENCY_CODE);
-
+                ShoppingPersistedTabDataTestUtils.createShoppingPersistedTabDataWithCurrencyCode(
+                        ShoppingPersistedTabDataTestUtils.TAB_ID,
+                        ShoppingPersistedTabDataTestUtils.IS_INCOGNITO,
+                        ShoppingPersistedTabDataTestUtils.JAPAN_CURRENCY_CODE);
         shoppingPersistedTabData.setPreviousPriceMicrosForTesting(3_140_000_000_000L);
         shoppingPersistedTabData.setPriceMicrosForTesting(287_000_000_000L);
         Assert.assertNotNull(shoppingPersistedTabData.getPriceDrop());
@@ -598,8 +319,10 @@
     @Test
     public void testStalePriceDropUSD() {
         ShoppingPersistedTabData shoppingPersistedTabData =
-                createShoppingPersistedTabDataWithCurrencyCode(
-                        TAB_ID, IS_INCOGNITO, UNITED_STATES_CURRENCY_CODE);
+                ShoppingPersistedTabDataTestUtils.createShoppingPersistedTabDataWithCurrencyCode(
+                        ShoppingPersistedTabDataTestUtils.TAB_ID,
+                        ShoppingPersistedTabDataTestUtils.IS_INCOGNITO,
+                        ShoppingPersistedTabDataTestUtils.UNITED_STATES_CURRENCY_CODE);
         // $10 -> $5 (50% and $5 price drop is big enough)
         shoppingPersistedTabData.setPreviousPriceMicrosForTesting(10000000L);
         shoppingPersistedTabData.setPriceMicrosForTesting(5000000L);
@@ -617,7 +340,9 @@
     @SmallTest
     @Test
     public void testNewUrl() {
-        Tab tab = createTabOnUiThread(TAB_ID, IS_INCOGNITO);
+        Tab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
+                ShoppingPersistedTabDataTestUtils.TAB_ID,
+                ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
         ShoppingPersistedTabData shoppingPersistedTabData = new ShoppingPersistedTabData(tab);
         Assert.assertFalse(shoppingPersistedTabData.mIsTabSaveEnabledSupplier.get());
         shoppingPersistedTabData.mIsTabSaveEnabledSupplier.set(true);
@@ -630,15 +355,39 @@
     @Test
     public void testSPTDSavingEnabledUponSuccessfulResponse() {
         final Semaphore semaphore = new Semaphore(0);
-        Tab tab = createTabOnUiThread(TAB_ID, IS_INCOGNITO);
-        mockPageAnnotationsResponse(MockPageAnnotationsResponse.BUYABLE_PRODUCT_INITIAL);
+        Tab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
+                ShoppingPersistedTabDataTestUtils.TAB_ID,
+                ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
+        ShoppingPersistedTabDataTestUtils.mockPageAnnotationsResponse(mPageAnnotationsServiceMock,
+                ShoppingPersistedTabDataTestUtils.MockPageAnnotationsResponse
+                        .BUYABLE_PRODUCT_INITIAL);
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             ShoppingPersistedTabData.from(tab, (shoppingPersistedTabData) -> {
                 Assert.assertTrue(shoppingPersistedTabData.mIsTabSaveEnabledSupplier.get());
                 semaphore.release();
             });
         });
-        acquireSemaphore(semaphore);
+        ShoppingPersistedTabDataTestUtils.acquireSemaphore(semaphore);
+    }
+
+    @UiThreadTest
+    @SmallTest
+    @Test
+    public void testSPTDSavingEnabledUponSuccessfulProductUpdateResponse() {
+        final Semaphore semaphore = new Semaphore(0);
+        Tab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
+                ShoppingPersistedTabDataTestUtils.TAB_ID,
+                ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
+        ShoppingPersistedTabDataTestUtils.mockPageAnnotationsResponse(mPageAnnotationsServiceMock,
+                ShoppingPersistedTabDataTestUtils.MockPageAnnotationsResponse
+                        .BUYABLE_PRODUCT_AND_PRODUCT_UPDATE);
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            ShoppingPersistedTabData.from(tab, (shoppingPersistedTabData) -> {
+                Assert.assertTrue(shoppingPersistedTabData.mIsTabSaveEnabledSupplier.get());
+                semaphore.release();
+            });
+        });
+        ShoppingPersistedTabDataTestUtils.acquireSemaphore(semaphore);
     }
 
     @UiThreadTest
@@ -646,22 +395,28 @@
     @Test
     public void testSPTDNullUponUnsuccessfulResponse() {
         final Semaphore semaphore = new Semaphore(0);
-        Tab tab = createTabOnUiThread(TAB_ID, IS_INCOGNITO);
-        mockPageAnnotationsResponse(MockPageAnnotationsResponse.BUYABLE_PRODUCT_EMPTY);
+        Tab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
+                ShoppingPersistedTabDataTestUtils.TAB_ID,
+                ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
+        ShoppingPersistedTabDataTestUtils.mockPageAnnotationsResponse(mPageAnnotationsServiceMock,
+                ShoppingPersistedTabDataTestUtils.MockPageAnnotationsResponse
+                        .BUYABLE_PRODUCT_EMPTY);
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             ShoppingPersistedTabData.from(tab, (shoppingPersistedTabData) -> {
                 Assert.assertNull(shoppingPersistedTabData);
                 semaphore.release();
             });
         });
-        acquireSemaphore(semaphore);
+        ShoppingPersistedTabDataTestUtils.acquireSemaphore(semaphore);
     }
 
     @UiThreadTest
     @SmallTest
     @Test
     public void testSerializationBug() {
-        Tab tab = createTabOnUiThread(TAB_ID, IS_INCOGNITO);
+        Tab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
+                ShoppingPersistedTabDataTestUtils.TAB_ID,
+                ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
         ShoppingPersistedTabData shoppingPersistedTabData = new ShoppingPersistedTabData(tab);
         shoppingPersistedTabData.setPriceMicros(42_000_000L, null);
         byte[] serialized = shoppingPersistedTabData.serialize();
@@ -671,55 +426,4 @@
                 new ShoppingPersistedTabData(tab, serialized, config.getStorage(), config.getId());
         Assert.assertEquals(42_000_000L, deserialized.getPriceMicros());
     }
-
-    private void verifyEndpointFetcherCalled(int numTimes) {
-        verify(mEndpointFetcherJniMock, times(numTimes))
-                .nativeFetchChromeAPIKey(any(Profile.class), anyString(), anyString(), anyString(),
-                        anyString(), anyLong(), any(String[].class), any(Callback.class));
-    }
-
-    private void verifyGetPageAnnotationsCalled(int numTimes) {
-        verify(mPageAnnotationsServiceMock, times(numTimes))
-                .getAnnotations(any(GURL.class), any(Callback.class));
-    }
-
-    private static Tab createTabOnUiThread(int tabId, boolean isIncognito) {
-        AtomicReference<Tab> res = new AtomicReference<>();
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> { res.set(MockTab.createAndInitialize(TAB_ID, IS_INCOGNITO)); });
-        return res.get();
-    }
-
-    private static long getTimeLastUpdatedOnUiThread(Tab tab) {
-        AtomicReference<Long> res = new AtomicReference<>();
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            res.set(PersistedTabData.from(tab, ShoppingPersistedTabData.class)
-                            .getLastPriceChangeTimeMs());
-        });
-        return res.get();
-    }
-
-    private static void acquireSemaphore(Semaphore semaphore) {
-        try {
-            semaphore.acquire();
-        } catch (InterruptedException e) {
-            // Throw Runtime exception to make catching InterruptedException unnecessary
-            throw new RuntimeException(e);
-        }
-    }
-
-    private void mockOptimizationGuideResponse(@OptimizationGuideDecision int decision) {
-        doAnswer(new Answer<Void>() {
-            @Override
-            public Void answer(InvocationOnMock invocation) {
-                OptimizationGuideCallback callback =
-                        (OptimizationGuideCallback) invocation.getArguments()[3];
-                callback.onOptimizationGuideDecision(decision, null);
-                return null;
-            }
-        })
-                .when(mOptimizationGuideBridgeJniMock)
-                .canApplyOptimization(
-                        anyLong(), any(GURL.class), anyInt(), any(OptimizationGuideCallback.class));
-    }
 }
\ No newline at end of file
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataTestUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataTestUtils.java
new file mode 100644
index 0000000..ed255b00
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataTestUtils.java
@@ -0,0 +1,178 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.tab.state;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import androidx.annotation.IntDef;
+
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import org.chromium.base.Callback;
+import org.chromium.chrome.browser.endpoint_fetcher.EndpointFetcher;
+import org.chromium.chrome.browser.optimization_guide.OptimizationGuideBridge;
+import org.chromium.chrome.browser.optimization_guide.OptimizationGuideBridge.OptimizationGuideCallback;
+import org.chromium.chrome.browser.page_annotations.BuyableProductPageAnnotation;
+import org.chromium.chrome.browser.page_annotations.PageAnnotation;
+import org.chromium.chrome.browser.page_annotations.PageAnnotationsService;
+import org.chromium.chrome.browser.page_annotations.ProductPriceUpdatePageAnnotation;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.tab.MockTab;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.components.optimization_guide.OptimizationGuideDecision;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.url.GURL;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.LinkedList;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.atomic.AtomicReference;
+/**
+ * Helper class for {@link ShoppingPersistedTabDataTest} & {@link
+ * ShoppingPersistedTabDataLegacyTest}.
+ */
+public abstract class ShoppingPersistedTabDataTestUtils {
+    @IntDef({MockPageAnnotationsResponse.BUYABLE_PRODUCT_INITIAL,
+            MockPageAnnotationsResponse.BUYABLE_PRODUCT_PRICE_UPDATED,
+            MockPageAnnotationsResponse.BUYABLE_PRODUCT_AND_PRODUCT_UPDATE,
+            MockPageAnnotationsResponse.PRODUCT_PRICE_UPDATE,
+            MockPageAnnotationsResponse.BUYABLE_PRODUCT_EMPTY})
+    @Retention(RetentionPolicy.SOURCE)
+    @interface MockPageAnnotationsResponse {
+        int BUYABLE_PRODUCT_INITIAL = 0;
+        int BUYABLE_PRODUCT_PRICE_UPDATED = 1;
+        int BUYABLE_PRODUCT_AND_PRODUCT_UPDATE = 2;
+        int PRODUCT_PRICE_UPDATE = 3;
+        int BUYABLE_PRODUCT_EMPTY = 4;
+    }
+
+    static final long PRICE_MICROS = 123456789012345L;
+    static final long UPDATED_PRICE_MICROS = 287000000L;
+    static final long HIGH_PRICE_MICROS = 141000000L;
+    static final long LOW_PRICE_MICROS = 100000000L;
+    static final String HIGH_PRICE_FORMATTED = "$141";
+    static final String LOW_PRICE_FORMATTED = "$100";
+    static final String UNITED_STATES_CURRENCY_CODE = "USD";
+    static final String GREAT_BRITAIN_CURRENCY_CODE = "GBP";
+    static final String JAPAN_CURRENCY_CODE = "JPY";
+    static final int TAB_ID = 1;
+    static final boolean IS_INCOGNITO = false;
+
+    static ShoppingPersistedTabData createShoppingPersistedTabDataWithDefaults() {
+        ShoppingPersistedTabData shoppingPersistedTabData =
+                new ShoppingPersistedTabData(createTabOnUiThread(TAB_ID, IS_INCOGNITO));
+        shoppingPersistedTabData.setCurrencyCode(UNITED_STATES_CURRENCY_CODE);
+        return shoppingPersistedTabData;
+    }
+
+    static ShoppingPersistedTabData createShoppingPersistedTabDataWithCurrencyCode(
+            int tabId, boolean isIncognito, String currencyCode) {
+        ShoppingPersistedTabData shoppingPersistedTabData =
+                new ShoppingPersistedTabData(createTabOnUiThread(tabId, isIncognito));
+        shoppingPersistedTabData.setCurrencyCode(currencyCode);
+        return shoppingPersistedTabData;
+    }
+
+    static Tab createTabOnUiThread(int tabId, boolean isIncognito) {
+        AtomicReference<Tab> res = new AtomicReference<>();
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> { res.set(MockTab.createAndInitialize(tabId, isIncognito)); });
+        return res.get();
+    }
+
+    static long getTimeLastUpdatedOnUiThread(Tab tab) {
+        AtomicReference<Long> res = new AtomicReference<>();
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            res.set(PersistedTabData.from(tab, ShoppingPersistedTabData.class)
+                            .getLastPriceChangeTimeMs());
+        });
+        return res.get();
+    }
+
+    static void acquireSemaphore(Semaphore semaphore) {
+        try {
+            semaphore.acquire();
+        } catch (InterruptedException e) {
+            // Throw Runtime exception to make catching InterruptedException unnecessary
+            throw new RuntimeException(e);
+        }
+    }
+
+    static void mockOptimizationGuideResponse(OptimizationGuideBridge.Natives optimizationGuideJni,
+            @OptimizationGuideDecision int decision) {
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) {
+                OptimizationGuideCallback callback =
+                        (OptimizationGuideCallback) invocation.getArguments()[3];
+                callback.onOptimizationGuideDecision(decision, null);
+                return null;
+            }
+        })
+                .when(optimizationGuideJni)
+                .canApplyOptimization(
+                        anyLong(), any(GURL.class), anyInt(), any(OptimizationGuideCallback.class));
+    }
+
+    static void mockPageAnnotationsResponse(PageAnnotationsService pageAnnotationsService,
+            @MockPageAnnotationsResponse int expectedResponse) {
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) {
+                Callback callback = (Callback) invocation.getArguments()[1];
+                callback.onResult(new LinkedList<PageAnnotation>() {
+                    {
+                        switch (expectedResponse) {
+                            case MockPageAnnotationsResponse.BUYABLE_PRODUCT_INITIAL:
+                                add(new BuyableProductPageAnnotation(
+                                        PRICE_MICROS, UNITED_STATES_CURRENCY_CODE));
+                                break;
+                            case MockPageAnnotationsResponse.BUYABLE_PRODUCT_PRICE_UPDATED:
+                                add(new BuyableProductPageAnnotation(
+                                        UPDATED_PRICE_MICROS, UNITED_STATES_CURRENCY_CODE));
+                                break;
+                            case MockPageAnnotationsResponse.BUYABLE_PRODUCT_AND_PRODUCT_UPDATE:
+                                add(new BuyableProductPageAnnotation(
+                                        PRICE_MICROS, UNITED_STATES_CURRENCY_CODE));
+                                add(new ProductPriceUpdatePageAnnotation(PRICE_MICROS,
+                                        UPDATED_PRICE_MICROS, UNITED_STATES_CURRENCY_CODE));
+                                break;
+                            case MockPageAnnotationsResponse.PRODUCT_PRICE_UPDATE:
+                                add(new ProductPriceUpdatePageAnnotation(PRICE_MICROS,
+                                        UPDATED_PRICE_MICROS, UNITED_STATES_CURRENCY_CODE));
+                                break;
+                            case MockPageAnnotationsResponse.BUYABLE_PRODUCT_EMPTY:
+                            default:
+                                break;
+                        }
+                    }
+                });
+                return null;
+            }
+        })
+                .when(pageAnnotationsService)
+                .getAnnotations(any(GURL.class), any(Callback.class));
+    }
+
+    static void verifyEndpointFetcherCalled(EndpointFetcher.Natives endpointFetcher, int numTimes) {
+        verify(endpointFetcher, times(numTimes))
+                .nativeFetchChromeAPIKey(any(Profile.class), anyString(), anyString(), anyString(),
+                        anyString(), anyLong(), any(String[].class), any(Callback.class));
+    }
+
+    static void verifyGetPageAnnotationsCalled(
+            PageAnnotationsService pageAnnotationsService, int numTimes) {
+        verify(pageAnnotationsService, times(numTimes))
+                .getAnnotations(any(GURL.class), any(Callback.class));
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/UndoTabModelTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/UndoTabModelTest.java
index 2a3b486..2aeea4c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/UndoTabModelTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/UndoTabModelTest.java
@@ -29,6 +29,7 @@
 import org.chromium.base.test.util.Restriction;
 import org.chromium.base.test.util.UrlUtils;
 import org.chromium.chrome.browser.ChromeTabbedActivity2;
+import org.chromium.chrome.browser.app.tabmodel.TabModelOrchestrator;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.multiwindow.MultiWindowTestHelper;
 import org.chromium.chrome.browser.tab.Tab;
@@ -42,6 +43,7 @@
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.ui.test.util.UiRestriction;
 
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
 
 /**
@@ -283,10 +285,10 @@
         }
     }
 
-    private void saveStateOnUiThread(final TabModelSelector selector) {
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> { ((TabModelSelectorImpl) selector).saveState(); });
+    private void saveStateOnUiThread(final TabModelOrchestrator orchestrator) {
+        TestThreadUtils.runOnUiThreadBlocking(() -> { orchestrator.saveState(); });
 
+        TabModelSelector selector = orchestrator.getTabModelSelector();
         for (int i = 0; i < selector.getModels().size(); i++) {
             TabList tabs = selector.getModels().get(i).getComprehensiveModel();
             for (int j = 0; j < tabs.getCount(); j++) {
@@ -1381,7 +1383,7 @@
     }
 
     /**
-     * Test calling {@link TabModelSelectorImpl#saveState()} commits all pending closures:
+     * Test calling {@link TabModelOrchestrator#saveState()} commits all pending closures:
      *     Action                     Model List         Close List        Comprehensive List
      * 1.  Initial State              [ 0 1s ]           -                 [ 0 1s ]
      * 2.  CloseTab(0, allow undo)    [ 1s ]             [ 0 ]             [ 0 1s ]
@@ -1390,8 +1392,10 @@
     @Test
     @MediumTest
     @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE) // See crbug.com/633607
-    public void testSaveStateCommitsUndos() throws TimeoutException {
-        TabModelSelector selector = sActivityTestRule.getActivity().getTabModelSelector();
+    public void testSaveStateCommitsUndos() throws TimeoutException, ExecutionException {
+        TabModelOrchestrator orchestrator = TestThreadUtils.runOnUiThreadBlocking(
+                () -> sActivityTestRule.getActivity().getTabModelOrchestratorSupplier().get());
+        TabModelSelector selector = orchestrator.getTabModelSelector();
         TabModel model = selector.getModel(false);
         ChromeTabCreator tabCreator = sActivityTestRule.getActivity().getTabCreator(false);
         createTabOnUiThread(tabCreator);
@@ -1409,7 +1413,7 @@
         checkState(model, new Tab[] { tab1 }, tab1, EMPTY, fullList, tab1);
 
         // 3.
-        saveStateOnUiThread(selector);
+        saveStateOnUiThread(orchestrator);
         fullList = new Tab[] { tab1 };
         checkState(model, new Tab[] { tab1 }, tab1, EMPTY, fullList, tab1);
         Assert.assertTrue(tab0.isClosing());
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/AddToHomescreenTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/AddToHomescreenTest.java
index b8e04ae..e46ebe6 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/AddToHomescreenTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/AddToHomescreenTest.java
@@ -103,8 +103,8 @@
         public boolean mRequestedShortcutAdaptable;
 
         @Override
-        public void addShortcutToHomescreen(
-                String title, Bitmap icon, boolean iconAdaptable, Intent shortcutIntent) {
+        public void addShortcutToHomescreen(String id, String title, Bitmap icon,
+                boolean iconAdaptable, Intent shortcutIntent) {
             mRequestedShortcutTitle = title;
             mRequestedShortcutIntent = shortcutIntent;
             mRequestedShortcutAdaptable = iconAdaptable;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityContentTestEnvironment.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityContentTestEnvironment.java
index 9675a57..13ea6e93 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityContentTestEnvironment.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityContentTestEnvironment.java
@@ -33,6 +33,7 @@
 import org.chromium.chrome.browser.WebContentsFactory;
 import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.app.tab_activity_glue.ReparentingTask;
+import org.chromium.chrome.browser.app.tabmodel.CustomTabsTabModelOrchestrator;
 import org.chromium.chrome.browser.compositor.CompositorViewHolder;
 import org.chromium.chrome.browser.customtabs.CloseButtonNavigator;
 import org.chromium.chrome.browser.customtabs.CustomTabDelegateFactory;
@@ -83,6 +84,7 @@
     @Mock public WarmupManager warmupManager;
     @Mock public CustomTabTabPersistencePolicy tabPersistencePolicy;
     @Mock public CustomTabActivityTabFactory tabFactory;
+    @Mock public CustomTabsTabModelOrchestrator tabModelOrchestrator;
     @Mock public CustomTabObserver customTabObserver;
     @Mock public WebContentsFactory webContentsFactory;
     @Mock public ActivityTabProvider activityTabProvider;
@@ -130,7 +132,9 @@
         when(intentDataProvider.getUrlToLoad()).thenReturn(INITIAL_URL);
         when(tabFactory.createTab(webContentsCaptor.capture(), any(), any()))
                 .thenReturn(tabFromFactory);
+        when(tabFactory.getTabModelOrchestrator()).thenReturn(tabModelOrchestrator);
         when(tabFactory.getTabModelSelector()).thenReturn(tabModelSelector);
+        when(tabModelOrchestrator.getTabModelSelector()).thenReturn(tabModelSelector);
         when(tabModelSelector.getModel(anyBoolean())).thenReturn(tabModel);
         when(connection.getSpeculatedUrl(any())).thenReturn(SPECULATED_URL);
         when(browserInitializer.isFullBrowserInitialized()).thenReturn(true);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/feed/v2/FeedStreamTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/feed/v2/FeedStreamTest.java
index 8255f63..34004aa 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/feed/v2/FeedStreamTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/feed/v2/FeedStreamTest.java
@@ -38,9 +38,9 @@
 import org.chromium.base.test.util.JniMocker;
 import org.chromium.chrome.browser.native_page.NativePageNavigationDelegate;
 import org.chromium.chrome.browser.share.ShareDelegate;
-import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
+import org.chromium.ui.base.WindowAndroid;
 
 /** Unit tests for {@link FeedStream}. */
 @RunWith(BaseRobolectricTestRunner.class)
@@ -63,7 +63,7 @@
     @Mock
     private FeedServiceBridge.Natives mFeedServiceBridgeJniMock;
     @Mock
-    private Supplier<Tab> mTabSupplier;
+    private WindowAndroid mWindowAndroid;
     @Mock
     private Supplier<ShareDelegate> mShareDelegateSupplier;
 
@@ -82,7 +82,7 @@
         // Surfaces won't open until after startup.
         FeedStreamSurface.startup();
         mFeedStream = new FeedStream(mActivity, false, mSnackbarManager, mPageNavigationDelegate,
-                mBottomSheetController, /* isPlaceholderShown= */ false, mTabSupplier,
+                mBottomSheetController, /* isPlaceholderShown= */ false, mWindowAndroid,
                 mShareDelegateSupplier);
         mFeedStream.onCreate(null);
         mRecyclerView = (RecyclerView) mFeedStream.getView();
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 96f96ab..513c0d4d 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -10769,6 +10769,9 @@
       <message name="IDS_WEBAUTHN_CABLEV2_AOA_DESCRIPTION" desc="Contents of the dialog shown when the user tries to sign-in using a phone as a security key.">
         Use a USB cable to connect your phone to your computer. If your phone is already connected, unplug it and plug it back in.
       </message>
+      <message name="IDS_WEBAUTHN_CABLEV2_AOA_REQUEST_DESCRIPTION" desc="Potentially displayed in a permissions dialog on Android to describe the type of request that a computer, that is connected over USB, is making. In this context the request from the computer is to use the Android device as a security key. A 'security key' is typically a physical USB token used for authentication (e.g. a gNubby) but in this case the phone is acting as one. The translation of 'security key' should match the phrase used in TC IDs such as 1685482178220389035 and 7775033610159191691">
+        Security key request
+      </message>
 
       <message name="IDS_WEBAUTHN_CABLE_ACTIVATE_DESCRIPTION_SHORT" desc="Second line of text in an item letting the user know that they can use their phone as a security key. The user needs to check their phone and respond to a notification that will ask them to press a button on the phone's screen to confirm that they're logging in.">
         Unlock your phone and confirm it's you
diff --git a/chrome/app/generated_resources_grd/IDS_WEBAUTHN_CABLEV2_AOA_REQUEST_DESCRIPTION.png.sha1 b/chrome/app/generated_resources_grd/IDS_WEBAUTHN_CABLEV2_AOA_REQUEST_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..c698a40
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_WEBAUTHN_CABLEV2_AOA_REQUEST_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+02d1c7bd472cd43e0f5ace1b9730db72d1d1002c
\ No newline at end of file
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index a6633f0..08b986f 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -1174,11 +1174,38 @@
   </message>
 
   <!-- Privacy Sandbox -->
-  <message name="IDS_SETTINGS_PRIVACY_SANDBOX_TITLE" desc="The title of the Privacy Sandbox page">
+  <message name="IDS_SETTINGS_PRIVACY_SANDBOX_TITLE" desc="The title of the Privacy Sandbox page. 'Privacy Sandbox' is a noun phrase (title case).">
     Privacy Sandbox
   </message>
-  <message name="IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_HEADING" desc="The heading displayed at the top of the Privacy Sandbox page">
-    The Privacy Sandbox
+  <message name="IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_HEADING" desc="The heading displayed at the top of the Privacy Sandbox page. 'Privacy Sandbox' is a noun phrase (title case).">
+    About Privacy Sandbox
+  </message>
+  <message name="IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_EXPLANATION1" desc="Part 1 of the explanation of the Privacy Sandbox, shown on the Privacy Sandbox Page.">
+    Privacy Sandbox is an ongoing initiative to preserve the open web that will help safeguard you from tracking mechanisms.
+  </message>
+  <message name="IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_EXPLANATION2" desc="Part 2 of the explanation of the Privacy Sandbox, shown on the Privacy Sandbox Page.">
+    Today, websites rely on many technologies, like third-party cookies, for important services like showing relevant ads and measuring a site’s performance.
+  </message>
+  <message name="IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_EXPLANATION3" desc="Part 3 of the explanation of the Privacy Sandbox, shown on the Privacy Sandbox Page.">
+    Privacy Sandbox preserves the vitality of the open web by creating better ways to perform these services—without breaking sites, and while preventing you from being surreptitiously tracked across the web.
+  </message>
+  <message name="IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_EXPLANATION4" desc="Part 4 of the explanation of the Privacy Sandbox, shown on the Privacy Sandbox Page. The 'Learn more' link navigates the user to a page with more details.">
+    Privacy Sandbox is still in active development and is available in selected regions. For now, sites may try out Privacy Sandbox while continuing to use current web technologies like third-party cookies. <ph name="BEGIN_LINK">&lt;a href="$1" target="_blank"&gt;</ph>Learn more<ph name="END_LINK">&lt;/a&gt;</ph>
+  </message>
+  <message name="IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_SETTING_TITLE" desc="Title of the description of the setting to enable or disable the Privacy Sandbox.">
+    Enable Privacy Sandbox trials
+  </message>
+  <message name="IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_SETTING_EXPLANATION1" desc="Part 1 of the explanation of the setting to enable or disable the Privacy Sandbox.">
+    When enabled, sites may use the privacy-preserving techniques shown here to provide their content and services. These include alternatives to cross-site tracking. More trials may be added over time.
+  </message>
+  <message name="IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_SETTING_EXPLANATION2" desc="Part 2 of the explanation of the setting to enable or disable the Privacy Sandbox.">
+    Advertisers can learn when thousands of users share a similar interest—like a crowd at a concert—and select ads for the crowd, rather than an individual person.
+  </message>
+  <message name="IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_SETTING_EXPLANATION3" desc="Part 3 of the explanation of the setting to enable or disable the Privacy Sandbox.">
+    Advertisers can study the effectiveness of ads in a way that does not track you across sites.
+  </message>
+  <message name="IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_DETAILS" desc="Label of a button in the Privacy Sandbox page that leads the user to more details about the Privacy Sandbox.">
+    Details
   </message>
 
   <!-- Safety check -->
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_DETAILS.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_DETAILS.png.sha1
new file mode 100644
index 0000000..0083563f
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_DETAILS.png.sha1
@@ -0,0 +1 @@
+f300a3eb3d9a385468688a11bfd60a0f5363c4ee
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_EXPLANATION1.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_EXPLANATION1.png.sha1
new file mode 100644
index 0000000..0083563f
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_EXPLANATION1.png.sha1
@@ -0,0 +1 @@
+f300a3eb3d9a385468688a11bfd60a0f5363c4ee
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_EXPLANATION2.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_EXPLANATION2.png.sha1
new file mode 100644
index 0000000..0083563f
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_EXPLANATION2.png.sha1
@@ -0,0 +1 @@
+f300a3eb3d9a385468688a11bfd60a0f5363c4ee
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_EXPLANATION3.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_EXPLANATION3.png.sha1
new file mode 100644
index 0000000..0083563f
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_EXPLANATION3.png.sha1
@@ -0,0 +1 @@
+f300a3eb3d9a385468688a11bfd60a0f5363c4ee
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_EXPLANATION4.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_EXPLANATION4.png.sha1
new file mode 100644
index 0000000..0083563f
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_EXPLANATION4.png.sha1
@@ -0,0 +1 @@
+f300a3eb3d9a385468688a11bfd60a0f5363c4ee
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_HEADING.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_HEADING.png.sha1
new file mode 100644
index 0000000..0083563f
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_HEADING.png.sha1
@@ -0,0 +1 @@
+f300a3eb3d9a385468688a11bfd60a0f5363c4ee
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_SETTING_EXPLANATION1.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_SETTING_EXPLANATION1.png.sha1
new file mode 100644
index 0000000..0083563f
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_SETTING_EXPLANATION1.png.sha1
@@ -0,0 +1 @@
+f300a3eb3d9a385468688a11bfd60a0f5363c4ee
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_SETTING_EXPLANATION2.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_SETTING_EXPLANATION2.png.sha1
new file mode 100644
index 0000000..0083563f
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_SETTING_EXPLANATION2.png.sha1
@@ -0,0 +1 @@
+f300a3eb3d9a385468688a11bfd60a0f5363c4ee
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_SETTING_EXPLANATION3.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_SETTING_EXPLANATION3.png.sha1
new file mode 100644
index 0000000..0083563f
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_SETTING_EXPLANATION3.png.sha1
@@ -0,0 +1 @@
+f300a3eb3d9a385468688a11bfd60a0f5363c4ee
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_SETTING_TITLE.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_SETTING_TITLE.png.sha1
new file mode 100644
index 0000000..0083563f
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_SETTING_TITLE.png.sha1
@@ -0,0 +1 @@
+f300a3eb3d9a385468688a11bfd60a0f5363c4ee
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRIVACY_SANDBOX_TITLE.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRIVACY_SANDBOX_TITLE.png.sha1
new file mode 100644
index 0000000..0083563f
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRIVACY_SANDBOX_TITLE.png.sha1
@@ -0,0 +1 @@
+f300a3eb3d9a385468688a11bfd60a0f5363c4ee
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 10df4e46..d1b8870 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -340,8 +340,6 @@
     "component_updater/sth_set_component_remover.h",
     "component_updater/subresource_filter_component_installer.cc",
     "component_updater/subresource_filter_component_installer.h",
-    "component_updater/tls_deprecation_config_component_installer.cc",
-    "component_updater/tls_deprecation_config_component_installer.h",
     "component_updater/trust_token_key_commitments_component_installer.cc",
     "component_updater/trust_token_key_commitments_component_installer.h",
     "component_updater/zxcvbn_data_component_installer.cc",
@@ -1757,8 +1755,6 @@
     "ssl/ssl_error_controller_client.h",
     "ssl/stateful_ssl_host_state_delegate_factory.cc",
     "ssl/stateful_ssl_host_state_delegate_factory.h",
-    "ssl/tls_deprecation_config.cc",
-    "ssl/tls_deprecation_config.h",
     "ssl/typed_navigation_upgrade_throttle.cc",
     "ssl/typed_navigation_upgrade_throttle.h",
     "startup_data.cc",
@@ -3248,6 +3244,7 @@
       "//chrome/browser/endpoint_fetcher:jni_headers",
       "//chrome/browser/feedback/android",
       "//chrome/browser/flags:flags_android",
+      "//chrome/browser/language/android:jni_headers",
       "//chrome/browser/long_screenshots:services",
       "//chrome/browser/notifications/chime/android",
       "//chrome/browser/notifications/scheduler/public",
@@ -4273,6 +4270,8 @@
     sources += [
       "apps/app_service/app_notifications.cc",
       "apps/app_service/app_notifications.h",
+      "apps/app_service/app_web_contents_data.cc",
+      "apps/app_service/app_web_contents_data.h",
       "apps/app_service/arc_activity_adaptive_icon_impl.cc",
       "apps/app_service/arc_activity_adaptive_icon_impl.h",
       "apps/app_service/arc_apps.cc",
@@ -6772,7 +6771,6 @@
     "//google_apis:test_support",
     "//net:test_support",
     "//services/data_decoder/public/cpp:test_support",
-    "//services/network/public/proto:tls_deprecation_config_proto",
     "//services/preferences/public/cpp/tracked:test_support",
     "//skia",
     "//testing/gmock",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 4210fd7..440d753 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2927,6 +2927,10 @@
         kOsCrOS,
         FEATURE_VALUE_TYPE(features::kNewShortcutMapping),
     },
+    {"improved-keyboard-shortcuts",
+     flag_descriptions::kImprovedKeyboardShortcutsName,
+     flag_descriptions::kImprovedKeyboardShortcutsDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(features::kImprovedKeyboardShortcuts)},
     {"shelf-hide-buttons-in-tablet",
      flag_descriptions::kHideShelfControlsInTabletModeName,
      flag_descriptions::kHideShelfControlsInTabletModeDescription, kOsCrOS,
@@ -3864,13 +3868,6 @@
      kOsCrOS,
      SINGLE_VALUE_TYPE(
          ::switches::kEnableExperimentalAccessibilitySwitchAccessSetupGuide)},
-    {"enable-experimental-accessibility-chromevox-annotations",
-     flag_descriptions::kExperimentalAccessibilityChromeVoxAnnotationsName,
-     flag_descriptions::
-         kExperimentalAccessibilityChromeVoxAnnotationsDescription,
-     kOsCrOS,
-     SINGLE_VALUE_TYPE(
-         ::switches::kEnableExperimentalAccessibilityChromeVoxAnnotations)},
     {"enable-experimental-kernel-vm-support",
      flag_descriptions::kKernelnextVMsName,
      flag_descriptions::kKernelnextVMsDescription, kOsCrOS,
@@ -4518,10 +4515,6 @@
      flag_descriptions::kTabGroupsAutoCreateDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(features::kTabGroupsAutoCreate)},
 
-    {"tab-groups-collapse", flag_descriptions::kTabGroupsCollapseName,
-     flag_descriptions::kTabGroupsCollapseDescription, kOsDesktop,
-     FEATURE_VALUE_TYPE(features::kTabGroupsCollapse)},
-
     {"tab-groups-collapse-freezing",
      flag_descriptions::kTabGroupsCollapseFreezingName,
      flag_descriptions::kTabGroupsCollapseFreezingDescription, kOsDesktop,
@@ -5604,11 +5597,6 @@
      kOsCrOS,
      FEATURE_VALUE_TYPE(
          chromeos::features::kDisablePeripheralDataAccessProtection)},
-
-    {"improved-keyboard-shortcuts",
-     flag_descriptions::kImprovedKeyboardShortcutsName,
-     flag_descriptions::kImprovedKeyboardShortcutsDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(chromeos::features::kImprovedKeyboardShortcuts)},
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
     {"enable-portals", flag_descriptions::kEnablePortalsName,
diff --git a/chrome/browser/apps/app_service/app_service_proxy.cc b/chrome/browser/apps/app_service/app_service_proxy.cc
index 192f84ab..dc19a74 100644
--- a/chrome/browser/apps/app_service/app_service_proxy.cc
+++ b/chrome/browser/apps/app_service/app_service_proxy.cc
@@ -220,6 +220,10 @@
   return app_registry_cache_;
 }
 
+apps::AppCapabilityAccessCache& AppServiceProxy::AppCapabilityAccessCache() {
+  return app_capability_access_cache_;
+}
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 apps::InstanceRegistry& AppServiceProxy::InstanceRegistry() {
   return instance_registry_;
diff --git a/chrome/browser/apps/app_service/app_service_proxy.h b/chrome/browser/apps/app_service/app_service_proxy.h
index 3e3f6ece..ba24d44 100644
--- a/chrome/browser/apps/app_service/app_service_proxy.h
+++ b/chrome/browser/apps/app_service/app_service_proxy.h
@@ -93,6 +93,7 @@
 
   mojo::Remote<apps::mojom::AppService>& AppService();
   apps::AppRegistryCache& AppRegistryCache();
+  apps::AppCapabilityAccessCache& AppCapabilityAccessCache();
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   apps::InstanceRegistry& InstanceRegistry();
diff --git a/chrome/browser/apps/app_service/app_web_contents_data.cc b/chrome/browser/apps/app_service/app_web_contents_data.cc
new file mode 100644
index 0000000..dde7b44
--- /dev/null
+++ b/chrome/browser/apps/app_service/app_web_contents_data.cc
@@ -0,0 +1,25 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/apps/app_service/app_web_contents_data.h"
+
+namespace apps {
+
+AppWebContentsData::AppWebContentsData(content::WebContents* web_contents,
+                                       Client* client)
+    : content::WebContentsObserver(web_contents), client_(client) {
+  DCHECK(web_contents);
+  DCHECK(client);
+}
+
+void AppWebContentsData::WebContentsDestroyed() {
+  if (client_) {
+    client_->OnWebContentsDestroyed(web_contents());
+  }
+  client_ = nullptr;
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(AppWebContentsData)
+
+}  // namespace apps
diff --git a/chrome/browser/apps/app_service/app_web_contents_data.h b/chrome/browser/apps/app_service/app_web_contents_data.h
new file mode 100644
index 0000000..d7805fb
--- /dev/null
+++ b/chrome/browser/apps/app_service/app_web_contents_data.h
@@ -0,0 +1,42 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_APPS_APP_SERVICE_APP_WEB_CONTENTS_DATA_H_
+#define CHROME_BROWSER_APPS_APP_SERVICE_APP_WEB_CONTENTS_DATA_H_
+
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+namespace apps {
+
+// AppWebContentsData is attached to the lifetime of a WebContents, and notifies
+// Observer when the WebContents is destroyed.
+class AppWebContentsData
+    : public content::WebContentsUserData<AppWebContentsData>,
+      public content::WebContentsObserver {
+ public:
+  class Client {
+   public:
+    // Invoked when the WebContents is being destroyed.
+    virtual void OnWebContentsDestroyed(content::WebContents* contents) = 0;
+  };
+
+  explicit AppWebContentsData(content::WebContents* contents, Client* client);
+  AppWebContentsData(const AppWebContentsData&) = delete;
+  AppWebContentsData& operator=(const AppWebContentsData&) = delete;
+  ~AppWebContentsData() override = default;
+
+ private:
+  friend class content::WebContentsUserData<AppWebContentsData>;
+  WEB_CONTENTS_USER_DATA_KEY_DECL();
+
+  // content::WebContentsObserver:
+  void WebContentsDestroyed() override;
+
+  Client* client_;
+};
+
+}  // namespace apps
+
+#endif  // CHROME_BROWSER_APPS_APP_SERVICE_APP_WEB_CONTENTS_DATA_H_
diff --git a/chrome/browser/apps/app_service/extension_apps_chromeos.cc b/chrome/browser/apps/app_service/extension_apps_chromeos.cc
index 786f139..63a7e642 100644
--- a/chrome/browser/apps/app_service/extension_apps_chromeos.cc
+++ b/chrome/browser/apps/app_service/extension_apps_chromeos.cc
@@ -426,12 +426,37 @@
     app_id = extension->id();
   }
 
+  if (media_requests_.IsNewRequest(app_id, web_contents, state)) {
+    content::WebContentsUserData<AppWebContentsData>::CreateForWebContents(
+        web_contents, this);
+  }
+
   auto result =
       media_requests_.UpdateRequests(app_id, web_contents, stream_type, state);
   ModifyCapabilityAccess(subscribers(), app_id, result.camera,
                          result.microphone);
 }
 
+void ExtensionAppsChromeOs::OnWebContentsDestroyed(
+    content::WebContents* web_contents) {
+  DCHECK(web_contents);
+
+  std::string app_id = extension_misc::kChromeAppId;
+  extensions::ExtensionRegistry* registry =
+      extensions::ExtensionRegistry::Get(profile());
+  DCHECK(registry);
+  const extensions::ExtensionSet& extensions = registry->enabled_extensions();
+  const extensions::Extension* extension =
+      extensions.GetAppByURL(web_contents->GetLastCommittedURL());
+  if (extension && Accepts(extension)) {
+    app_id = extension->id();
+  }
+
+  auto result = media_requests_.OnWebContentsDestroyed(app_id, web_contents);
+  ModifyCapabilityAccess(subscribers(), app_id, result.camera,
+                         result.microphone);
+}
+
 void ExtensionAppsChromeOs::OnNotificationDisplayed(
     const message_center::Notification& notification,
     const NotificationCommon::Metadata* const metadata) {
diff --git a/chrome/browser/apps/app_service/extension_apps_chromeos.h b/chrome/browser/apps/app_service/extension_apps_chromeos.h
index c8e63e9..f4d4fdc 100644
--- a/chrome/browser/apps/app_service/extension_apps_chromeos.h
+++ b/chrome/browser/apps/app_service/extension_apps_chromeos.h
@@ -13,6 +13,7 @@
 #include "base/scoped_observation.h"
 #include "base/scoped_observer.h"
 #include "chrome/browser/apps/app_service/app_notifications.h"
+#include "chrome/browser/apps/app_service/app_web_contents_data.h"
 #include "chrome/browser/apps/app_service/extension_apps_base.h"
 #include "chrome/browser/apps/app_service/icon_key_util.h"
 #include "chrome/browser/apps/app_service/media_requests.h"
@@ -53,7 +54,8 @@
                               public extensions::AppWindowRegistry::Observer,
                               public ArcAppListPrefs::Observer,
                               public NotificationDisplayService::Observer,
-                              public MediaCaptureDevicesDispatcher::Observer {
+                              public MediaCaptureDevicesDispatcher::Observer,
+                              public AppWebContentsData::Client {
  public:
   ExtensionAppsChromeOs(
       const mojo::Remote<apps::mojom::AppService>& app_service,
@@ -114,6 +116,9 @@
                        blink::mojom::MediaStreamType stream_type,
                        const content::MediaRequestState state) override;
 
+  // AppWebContentsData::Observer:
+  void OnWebContentsDestroyed(content::WebContents* contents) override;
+
   // NotificationDisplayService::Observer overrides.
   void OnNotificationDisplayed(
       const message_center::Notification& notification,
diff --git a/chrome/browser/apps/app_service/media_access_browsertest.cc b/chrome/browser/apps/app_service/media_access_browsertest.cc
new file mode 100644
index 0000000..af6aeab
--- /dev/null
+++ b/chrome/browser/apps/app_service/media_access_browsertest.cc
@@ -0,0 +1,671 @@
+// Copyright (c) 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+
+#include "base/run_loop.h"
+#include "base/scoped_observation.h"
+#include "base/test/thread_test_helper.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/platform_apps/app_browsertest_util.h"
+#include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
+#include "chrome/browser/ui/web_applications/web_app_controller_browsertest.h"
+#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/test/browser_test.h"
+#include "extensions/common/extension.h"
+
+using extensions::Extension;
+
+namespace {
+
+bool AccessingCamera(Profile* profile, const std::string& app_id) {
+  auto accessing_camera = apps::mojom::OptionalBool::kUnknown;
+  apps::AppServiceProxy* proxy =
+      apps::AppServiceProxyFactory::GetForProfile(profile);
+  proxy->FlushMojoCallsForTesting();
+  proxy->AppCapabilityAccessCache().ForOneApp(
+      app_id, [&accessing_camera](const apps::CapabilityAccessUpdate& update) {
+        accessing_camera = update.Camera();
+      });
+  return accessing_camera == apps::mojom::OptionalBool::kTrue;
+}
+
+bool AccessingMicrophone(Profile* profile, const std::string& app_id) {
+  auto accessing_microphone = apps::mojom::OptionalBool::kUnknown;
+  apps::AppServiceProxy* proxy =
+      apps::AppServiceProxyFactory::GetForProfile(profile);
+  proxy->FlushMojoCallsForTesting();
+  proxy->AppCapabilityAccessCache().ForOneApp(
+      app_id,
+      [&accessing_microphone](const apps::CapabilityAccessUpdate& update) {
+        accessing_microphone = update.Microphone();
+      });
+  return accessing_microphone == apps::mojom::OptionalBool::kTrue;
+}
+
+class FakeMediaObserver : public MediaCaptureDevicesDispatcher::Observer {
+ public:
+  explicit FakeMediaObserver(base::OnceClosure done_closure)
+      : done_closure_(std::move(done_closure)) {
+    media_dispatcher_.Observe(MediaCaptureDevicesDispatcher::GetInstance());
+  }
+  ~FakeMediaObserver() override = default;
+
+  // MediaCaptureDevicesDispatcher::Observer:
+  void OnRequestUpdate(int render_process_id,
+                       int render_frame_id,
+                       blink::mojom::MediaStreamType stream_type,
+                       const content::MediaRequestState state) override {
+    if (!done_closure_.is_null())
+      std::move(done_closure_).Run();
+    content::RunAllTasksUntilIdle();
+  }
+
+ private:
+  base::OnceClosure done_closure_;
+
+  base::ScopedObservation<MediaCaptureDevicesDispatcher,
+                          MediaCaptureDevicesDispatcher::Observer>
+      media_dispatcher_{this};
+};
+
+void MediaRequestChange(int render_process_id,
+                        int render_frame_id,
+                        const GURL& url,
+                        blink::mojom::MediaStreamType stream_type,
+                        content::MediaRequestState state) {
+  if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)) {
+    base::RunLoop run_loop;
+    FakeMediaObserver fake_observer(run_loop.QuitClosure());
+
+    content::GetIOThreadTaskRunner({})->PostTask(
+        FROM_HERE, base::BindOnce(&MediaRequestChange, render_process_id,
+                                  render_frame_id, url, stream_type, state));
+    run_loop.Run();
+    content::RunAllTasksUntilIdle();
+    return;
+  }
+
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  MediaCaptureDevicesDispatcher::GetInstance()->OnMediaRequestStateChanged(
+      render_process_id, render_frame_id, 0, url, stream_type, state);
+}
+
+void MediaRequestChangeForWebContent(content::WebContents* web_content,
+                                     const GURL& url,
+                                     blink::mojom::MediaStreamType stream_type,
+                                     content::MediaRequestState state) {
+  ASSERT_TRUE(web_content);
+  MediaRequestChange(web_content->GetMainFrame()->GetProcess()->GetID(),
+                     web_content->GetMainFrame()->GetRoutingID(), url,
+                     stream_type, state);
+}
+
+}  // namespace
+
+class MediaAccessExtensionAppsTest : public extensions::PlatformAppBrowserTest {
+ public:
+  void SetUpOnMainThread() override {
+    extensions::PlatformAppBrowserTest::SetUpOnMainThread();
+
+    ASSERT_TRUE(embedded_test_server()->Start());
+  }
+
+  void UninstallApp(const std::string& app_id) {
+    apps::AppServiceProxy* proxy =
+        apps::AppServiceProxyFactory::GetForProfile(profile());
+    proxy->UninstallSilently(app_id, apps::mojom::UninstallSource::kUser);
+    proxy->FlushMojoCallsForTesting();
+  }
+
+  GURL GetUrl1() {
+    return embedded_test_server()->GetURL("app.com", "/ssl/google.html");
+  }
+
+  GURL GetUrl2() {
+    return embedded_test_server()->GetURL("app.com", "/google/google.html");
+  }
+
+  content::WebContents* GetWebContents() {
+    return browser()->tab_strip_model()->GetActiveWebContents();
+  }
+
+  base::WeakPtr<MediaAccessExtensionAppsTest> GetWeakPtr() {
+    return weak_ptr_factory_.GetWeakPtr();
+  }
+
+ private:
+  base::WeakPtrFactory<MediaAccessExtensionAppsTest> weak_ptr_factory_{this};
+};
+
+IN_PROC_BROWSER_TEST_F(MediaAccessExtensionAppsTest,
+                       RequestAccessingForChromeInTabs) {
+  ui_test_utils::NavigateToURL(browser(), GetUrl1());
+
+  content::WebContents* web_content1 = GetWebContents();
+  // Request accessing the camera for |web_content1|.
+  MediaRequestChangeForWebContent(
+      web_content1, GetUrl1(),
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_DONE);
+  EXPECT_TRUE(
+      AccessingCamera(browser()->profile(), extension_misc::kChromeAppId));
+  EXPECT_FALSE(
+      AccessingMicrophone(browser()->profile(), extension_misc::kChromeAppId));
+
+  AddBlankTabAndShow(browser());
+  ui_test_utils::NavigateToURL(browser(), GetUrl2());
+  content::WebContents* web_content2 = GetWebContents();
+  // Request accessing the microphone for |web_content2|.
+  MediaRequestChangeForWebContent(
+      web_content2, GetUrl1(),
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_DONE);
+  EXPECT_TRUE(
+      AccessingCamera(browser()->profile(), extension_misc::kChromeAppId));
+  EXPECT_TRUE(
+      AccessingMicrophone(browser()->profile(), extension_misc::kChromeAppId));
+
+  // Stop accessing the camera for |web_content1|.
+  MediaRequestChangeForWebContent(
+      web_content1, GetUrl1(),
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_CLOSING);
+  EXPECT_FALSE(
+      AccessingCamera(browser()->profile(), extension_misc::kChromeAppId));
+  EXPECT_TRUE(
+      AccessingMicrophone(browser()->profile(), extension_misc::kChromeAppId));
+
+  // Stop accessing the microphone for |web_content2|.
+  MediaRequestChangeForWebContent(
+      web_content2, GetUrl1(),
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_CLOSING);
+  EXPECT_FALSE(
+      AccessingCamera(browser()->profile(), extension_misc::kChromeAppId));
+  EXPECT_FALSE(
+      AccessingMicrophone(browser()->profile(), extension_misc::kChromeAppId));
+}
+
+IN_PROC_BROWSER_TEST_F(MediaAccessExtensionAppsTest,
+                       RequestAccessingForChromeInNewBrowsers) {
+  Browser* browser1 =
+      Browser::Create(Browser::CreateParams(browser()->profile(), true));
+  ASSERT_TRUE(browser1);
+  ASSERT_NE(browser(), browser1);
+
+  AddBlankTabAndShow(browser1);
+  ui_test_utils::NavigateToURL(browser1, GetUrl1());
+  content::WebContents* web_content1 =
+      browser1->tab_strip_model()->GetActiveWebContents();
+  int render_process_id1 = web_content1->GetMainFrame()->GetProcess()->GetID();
+  int render_frame_id1 = web_content1->GetMainFrame()->GetRoutingID();
+  // Request accessing the camera and the microphone for |web_content1|.
+  MediaRequestChangeForWebContent(
+      web_content1, GetUrl1(),
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_DONE);
+  MediaRequestChangeForWebContent(
+      web_content1, GetUrl1(),
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_DONE);
+  EXPECT_TRUE(
+      AccessingCamera(browser()->profile(), extension_misc::kChromeAppId));
+  EXPECT_TRUE(
+      AccessingMicrophone(browser()->profile(), extension_misc::kChromeAppId));
+
+  AddBlankTabAndShow(browser1);
+  ui_test_utils::NavigateToURL(browser1, GetUrl2());
+  content::WebContents* web_content2 =
+      browser1->tab_strip_model()->GetActiveWebContents();
+  int render_process_id2 = web_content2->GetMainFrame()->GetProcess()->GetID();
+  int render_frame_id2 = web_content2->GetMainFrame()->GetRoutingID();
+  // Request accessing the camera and the microphone for |web_content2|.
+  MediaRequestChangeForWebContent(
+      web_content2, GetUrl2(),
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_DONE);
+  MediaRequestChangeForWebContent(
+      web_content2, GetUrl2(),
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_DONE);
+  EXPECT_TRUE(
+      AccessingCamera(browser()->profile(), extension_misc::kChromeAppId));
+  EXPECT_TRUE(
+      AccessingMicrophone(browser()->profile(), extension_misc::kChromeAppId));
+
+  // Close the tab for |web_content2|.
+  browser1->tab_strip_model()->CloseSelectedTabs();
+  EXPECT_TRUE(
+      AccessingCamera(browser()->profile(), extension_misc::kChromeAppId));
+  EXPECT_TRUE(
+      AccessingMicrophone(browser()->profile(), extension_misc::kChromeAppId));
+
+  MediaRequestChange(render_process_id2, render_frame_id2, GetUrl2(),
+                     blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+                     content::MEDIA_REQUEST_STATE_CLOSING);
+  MediaRequestChange(render_process_id2, render_frame_id2, GetUrl2(),
+                     blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+                     content::MEDIA_REQUEST_STATE_CLOSING);
+  EXPECT_TRUE(
+      AccessingCamera(browser()->profile(), extension_misc::kChromeAppId));
+  EXPECT_TRUE(
+      AccessingMicrophone(browser()->profile(), extension_misc::kChromeAppId));
+
+  // Close the tab for |web_content1|.
+  browser1->tab_strip_model()->CloseSelectedTabs();
+  EXPECT_FALSE(
+      AccessingCamera(browser()->profile(), extension_misc::kChromeAppId));
+  EXPECT_FALSE(
+      AccessingMicrophone(browser()->profile(), extension_misc::kChromeAppId));
+
+  MediaRequestChange(render_process_id1, render_frame_id1, GetUrl1(),
+                     blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+                     content::MEDIA_REQUEST_STATE_CLOSING);
+  MediaRequestChange(render_process_id1, render_frame_id1, GetUrl1(),
+                     blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+                     content::MEDIA_REQUEST_STATE_CLOSING);
+  EXPECT_FALSE(
+      AccessingCamera(browser()->profile(), extension_misc::kChromeAppId));
+  EXPECT_FALSE(
+      AccessingMicrophone(browser()->profile(), extension_misc::kChromeAppId));
+}
+
+IN_PROC_BROWSER_TEST_F(MediaAccessExtensionAppsTest,
+                       RequestAccessingForPlatformApp) {
+  const Extension* extension =
+      LoadAndLaunchPlatformApp("context_menu", "Launched");
+  ASSERT_TRUE(extension);
+
+  content::WebContents* web_contents = GetFirstAppWindowWebContents();
+  ASSERT_TRUE(web_contents);
+
+  // Request accessing the camera for |web_contents|.
+  MediaRequestChangeForWebContent(
+      web_contents, web_contents->GetURL(),
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_DONE);
+
+  EXPECT_TRUE(AccessingCamera(browser()->profile(), extension->id()));
+  EXPECT_FALSE(AccessingMicrophone(browser()->profile(), extension->id()));
+
+  // Request accessing the microphone for |web_contents|.
+  MediaRequestChangeForWebContent(
+      web_contents, web_contents->GetURL(),
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_DONE);
+
+  EXPECT_TRUE(AccessingCamera(browser()->profile(), extension->id()));
+  EXPECT_TRUE(AccessingMicrophone(browser()->profile(), extension->id()));
+
+  // Stop accessing the microphone for |web_contents|.
+  MediaRequestChangeForWebContent(
+      web_contents, web_contents->GetURL(),
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_CLOSING);
+
+  EXPECT_TRUE(AccessingCamera(browser()->profile(), extension->id()));
+  EXPECT_FALSE(AccessingMicrophone(browser()->profile(), extension->id()));
+
+  // Stop accessing the camera for |web_contents|.
+  MediaRequestChangeForWebContent(
+      web_contents, web_contents->GetURL(),
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_CLOSING);
+
+  EXPECT_FALSE(AccessingCamera(browser()->profile(), extension->id()));
+  EXPECT_FALSE(AccessingMicrophone(browser()->profile(), extension->id()));
+}
+
+IN_PROC_BROWSER_TEST_F(MediaAccessExtensionAppsTest,
+                       RequestAccessingForHostApp) {
+  const Extension* extension =
+      LoadExtension(test_data_dir_.AppendASCII("app1"));
+  ASSERT_TRUE(extension);
+
+  // Navigate to the app's launch URL.
+  auto url = extensions::AppLaunchInfo::GetLaunchWebURL(extension);
+  ui_test_utils::NavigateToURL(browser(), url);
+
+  content::WebContents* web_content1 =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ASSERT_TRUE(web_content1);
+
+  // Request accessing the camera and microphone for |web_contents|.
+  MediaRequestChangeForWebContent(
+      web_content1, url, blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_DONE);
+  MediaRequestChangeForWebContent(
+      web_content1, url, blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_DONE);
+
+  EXPECT_TRUE(AccessingCamera(browser()->profile(), extension->id()));
+  EXPECT_TRUE(AccessingMicrophone(browser()->profile(), extension->id()));
+
+  AddBlankTabAndShow(browser());
+  ui_test_utils::NavigateToURL(browser(), GetUrl1());
+  content::WebContents* web_content2 = GetWebContents();
+
+  // Request accessing the camera for |web_content2|.
+  MediaRequestChangeForWebContent(
+      web_content2, GetUrl1(),
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_DONE);
+
+  EXPECT_TRUE(AccessingCamera(browser()->profile(), extension->id()));
+  EXPECT_TRUE(AccessingMicrophone(browser()->profile(), extension->id()));
+  EXPECT_TRUE(
+      AccessingCamera(browser()->profile(), extension_misc::kChromeAppId));
+  EXPECT_FALSE(
+      AccessingMicrophone(browser()->profile(), extension_misc::kChromeAppId));
+
+  // Uninstall the app.
+  std::string app_id = extension->id();
+  UninstallApp(app_id);
+
+  EXPECT_FALSE(AccessingCamera(browser()->profile(), app_id));
+  EXPECT_FALSE(AccessingMicrophone(browser()->profile(), app_id));
+  EXPECT_TRUE(
+      AccessingCamera(browser()->profile(), extension_misc::kChromeAppId));
+  EXPECT_FALSE(
+      AccessingMicrophone(browser()->profile(), extension_misc::kChromeAppId));
+
+  // Request accessing the camera for |web_content2|.
+  MediaRequestChangeForWebContent(
+      web_content2, GetUrl1(),
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_CLOSING);
+
+  EXPECT_FALSE(AccessingCamera(browser()->profile(), app_id));
+  EXPECT_FALSE(AccessingMicrophone(browser()->profile(), app_id));
+  EXPECT_FALSE(
+      AccessingCamera(browser()->profile(), extension_misc::kChromeAppId));
+  EXPECT_FALSE(
+      AccessingMicrophone(browser()->profile(), extension_misc::kChromeAppId));
+}
+
+class MediaAccessWebAppsTest : public web_app::WebAppControllerBrowserTest {
+ public:
+  MediaAccessWebAppsTest() = default;
+  ~MediaAccessWebAppsTest() override = default;
+
+  std::string CreateWebApp(const GURL& url) const {
+    auto web_app_info = std::make_unique<WebApplicationInfo>();
+    web_app_info->start_url = url;
+    web_app_info->scope = url;
+    return web_app::InstallWebApp(browser()->profile(),
+                                  std::move(web_app_info));
+  }
+
+  void UninstallWebApp(const std::string& app_id) const {
+    web_app::UninstallWebApp(browser()->profile(), app_id);
+    apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
+        ->FlushMojoCallsForTesting();
+  }
+
+  GURL GetUrl1() {
+    return https_server()->GetURL("app.com", "/ssl/google.html");
+  }
+
+  GURL GetUrl2() {
+    return https_server()->GetURL("app.com", "/google/google.html");
+  }
+
+  content::WebContents* GetWebContents() {
+    return browser()->tab_strip_model()->GetActiveWebContents();
+  }
+
+  base::WeakPtr<MediaAccessWebAppsTest> GetWeakPtr() {
+    return weak_ptr_factory_.GetWeakPtr();
+  }
+
+ private:
+  base::WeakPtrFactory<MediaAccessWebAppsTest> weak_ptr_factory_{this};
+};
+
+IN_PROC_BROWSER_TEST_F(MediaAccessWebAppsTest, RequestAccessingCamera) {
+  std::string app_id = CreateWebApp(GetUrl1());
+
+  // Launch |app_id| in a new tab.
+  web_app::LaunchWebAppBrowser(browser()->profile(), app_id);
+  web_app::NavigateToURLAndWait(browser(), GetUrl1());
+
+  // Request accessing the camera for |app_id| in the new tab.
+  content::WebContents* web_content1 = GetWebContents();
+  MediaRequestChangeForWebContent(
+      web_content1, GetUrl1(),
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_DONE);
+  EXPECT_TRUE(AccessingCamera(browser()->profile(), app_id));
+
+  // Launch |app_id| in a new window.
+  content::WebContents* web_content2 = OpenApplication(app_id);
+  Browser* app_browser = BrowserList::GetInstance()->GetLastActive();
+  ASSERT_TRUE(app_browser);
+  ASSERT_NE(browser(), app_browser);
+
+  // Request accessing the camera for |app_id| in the new window.
+  MediaRequestChangeForWebContent(
+      web_content2, GetUrl1(),
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_DONE);
+  EXPECT_TRUE(AccessingCamera(browser()->profile(), app_id));
+
+  // Stop accessing the camera for |app_id| in the tab.
+  MediaRequestChangeForWebContent(
+      web_content1, GetUrl1(),
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_CLOSING);
+  EXPECT_TRUE(AccessingCamera(browser()->profile(), app_id));
+
+  // Stop accessing the camera for |app_id| in the window.
+  MediaRequestChangeForWebContent(
+      web_content2, GetUrl1(),
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_CLOSING);
+  EXPECT_FALSE(AccessingCamera(browser()->profile(), app_id));
+
+  web_app::CloseAndWait(app_browser);
+  EXPECT_FALSE(AccessingCamera(browser()->profile(), app_id));
+  web_app::CloseAndWait(browser());
+}
+
+IN_PROC_BROWSER_TEST_F(MediaAccessWebAppsTest, RequestAccessingMicrophone) {
+  std::string app_id = CreateWebApp(GetUrl1());
+
+  // Launch |app_id| in a new tab.
+  web_app::LaunchWebAppBrowser(browser()->profile(), app_id);
+  web_app::NavigateToURLAndWait(browser(), GetUrl1());
+
+  // Request accessing the camera for |app_id| in the new tab.
+  content::WebContents* web_content1 = GetWebContents();
+  int render_process_id1 = web_content1->GetMainFrame()->GetProcess()->GetID();
+  int render_frame_id1 = web_content1->GetMainFrame()->GetRoutingID();
+  MediaRequestChangeForWebContent(
+      web_content1, GetUrl1(),
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_DONE);
+  EXPECT_TRUE(AccessingMicrophone(browser()->profile(), app_id));
+
+  // Launch |app_id| in a new window.
+  content::WebContents* web_content2 = OpenApplication(app_id);
+  int render_process_id2 = web_content2->GetMainFrame()->GetProcess()->GetID();
+  int render_frame_id2 = web_content2->GetMainFrame()->GetRoutingID();
+  Browser* app_browser = BrowserList::GetInstance()->GetLastActive();
+  ASSERT_TRUE(app_browser);
+  ASSERT_NE(browser(), app_browser);
+
+  // Request accessing the camera for |app_id| in the new window.
+  MediaRequestChangeForWebContent(
+      web_content2, GetUrl1(),
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_DONE);
+  EXPECT_TRUE(AccessingMicrophone(browser()->profile(), app_id));
+
+  // Close browsers.
+  web_app::CloseAndWait(app_browser);
+  EXPECT_TRUE(AccessingMicrophone(browser()->profile(), app_id));
+
+  // Stop accessing the camera for |app_id| in the tab.
+  MediaRequestChange(render_process_id1, render_frame_id1, GetUrl1(),
+                     blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+                     content::MEDIA_REQUEST_STATE_CLOSING);
+  EXPECT_FALSE(AccessingMicrophone(browser()->profile(), app_id));
+
+  // Stop accessing the camera for |app_id| in the window.
+  MediaRequestChange(render_process_id2, render_frame_id2, GetUrl1(),
+                     blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+                     content::MEDIA_REQUEST_STATE_CLOSING);
+  EXPECT_FALSE(AccessingMicrophone(browser()->profile(), app_id));
+}
+
+IN_PROC_BROWSER_TEST_F(MediaAccessWebAppsTest, RemoveApp) {
+  std::string app_id = CreateWebApp(GetUrl1());
+
+  // Launch |app_id| in a new tab.
+  web_app::LaunchWebAppBrowser(browser()->profile(), app_id);
+  web_app::NavigateToURLAndWait(browser(), GetUrl1());
+
+  // Request accessing the camera and the microphone for |app_id| in the new
+  // tab.
+  content::WebContents* web_content1 = GetWebContents();
+  MediaRequestChangeForWebContent(
+      web_content1, GetUrl1(),
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_DONE);
+  MediaRequestChangeForWebContent(
+      web_content1, GetUrl1(),
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_DONE);
+  EXPECT_TRUE(AccessingMicrophone(browser()->profile(), app_id));
+  EXPECT_TRUE(AccessingCamera(browser()->profile(), app_id));
+
+  // Launch |app_id| in a new window.
+  content::WebContents* web_content2 = OpenApplication(app_id);
+  Browser* app_browser = BrowserList::GetInstance()->GetLastActive();
+  ASSERT_TRUE(app_browser);
+  ASSERT_NE(browser(), app_browser);
+
+  // Request accessing the camera and the microphone for |app_id| in the new
+  // window.
+  MediaRequestChangeForWebContent(
+      web_content2, GetUrl1(),
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_DONE);
+  MediaRequestChangeForWebContent(
+      web_content2, GetUrl1(),
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_DONE);
+  EXPECT_TRUE(AccessingMicrophone(browser()->profile(), app_id));
+  EXPECT_TRUE(AccessingCamera(browser()->profile(), app_id));
+
+  UninstallWebApp(app_id);
+  EXPECT_FALSE(AccessingCamera(browser()->profile(), app_id));
+  EXPECT_FALSE(AccessingMicrophone(browser()->profile(), app_id));
+
+  CreateWebApp(GetUrl1());
+
+  EXPECT_FALSE(AccessingCamera(browser()->profile(), app_id));
+  EXPECT_FALSE(AccessingMicrophone(browser()->profile(), app_id));
+}
+
+IN_PROC_BROWSER_TEST_F(MediaAccessWebAppsTest, TwoApps) {
+  std::string app_id1 = CreateWebApp(GetUrl1());
+
+  // Launch |app_id1| in a new tab.
+  web_app::LaunchWebAppBrowser(browser()->profile(), app_id1);
+  web_app::NavigateToURLAndWait(browser(), GetUrl1());
+
+  // Request accessing the camera and the microphone for |app_id1| in the new
+  // tab.
+  content::WebContents* web_content1 = GetWebContents();
+  MediaRequestChangeForWebContent(
+      web_content1, GetUrl1(),
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_DONE);
+  MediaRequestChangeForWebContent(
+      web_content1, GetUrl1(),
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_DONE);
+  EXPECT_TRUE(AccessingMicrophone(browser()->profile(), app_id1));
+  EXPECT_TRUE(AccessingCamera(browser()->profile(), app_id1));
+
+  std::string app_id2 = CreateWebApp(GetUrl2());
+
+  // Launch |app_id2| in a new window.
+  content::WebContents* web_content2 = OpenApplication(app_id2);
+  Browser* app_browser = BrowserList::GetInstance()->GetLastActive();
+  ASSERT_TRUE(app_browser);
+  ASSERT_NE(browser(), app_browser);
+
+  // Request accessing the camera and the microphone for |app_id2| in the new
+  // window.
+  MediaRequestChangeForWebContent(
+      web_content2, GetUrl1(),
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_DONE);
+  MediaRequestChangeForWebContent(
+      web_content2, GetUrl1(),
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_DONE);
+  EXPECT_TRUE(AccessingMicrophone(browser()->profile(), app_id2));
+  EXPECT_TRUE(AccessingCamera(browser()->profile(), app_id2));
+
+  UninstallWebApp(app_id1);
+  EXPECT_FALSE(AccessingCamera(browser()->profile(), app_id1));
+  EXPECT_FALSE(AccessingMicrophone(browser()->profile(), app_id1));
+  EXPECT_TRUE(AccessingMicrophone(browser()->profile(), app_id2));
+  EXPECT_TRUE(AccessingCamera(browser()->profile(), app_id2));
+
+  // Stop accessing the camera and the microphone for |app_id2|.
+  MediaRequestChangeForWebContent(
+      web_content2, GetUrl1(),
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_CLOSING);
+  MediaRequestChangeForWebContent(
+      web_content2, GetUrl1(),
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_CLOSING);
+  EXPECT_FALSE(AccessingCamera(browser()->profile(), app_id2));
+  EXPECT_FALSE(AccessingCamera(browser()->profile(), app_id2));
+
+  // Navigate to Url1, and check |app_id1| is not accessing the camera or the
+  // microphone, because it has been removed.
+  ui_test_utils::NavigateToURL(browser(), GetUrl1());
+  auto* web_content3 = GetWebContents();
+  MediaRequestChangeForWebContent(
+      web_content3, GetUrl1(),
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_DONE);
+  MediaRequestChangeForWebContent(
+      web_content3, GetUrl1(),
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_DONE);
+  EXPECT_FALSE(AccessingCamera(browser()->profile(), app_id1));
+  EXPECT_FALSE(AccessingMicrophone(browser()->profile(), app_id1));
+
+  // Navigate to Url2, and check |app_id2| is accessing the camera and the
+  // microphone.
+  ui_test_utils::NavigateToURL(browser(), GetUrl2());
+  auto* web_content4 = GetWebContents();
+  MediaRequestChangeForWebContent(
+      web_content4, GetUrl2(),
+      blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_DONE);
+  MediaRequestChangeForWebContent(
+      web_content4, GetUrl2(),
+      blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
+      content::MEDIA_REQUEST_STATE_DONE);
+  EXPECT_TRUE(AccessingMicrophone(browser()->profile(), app_id2));
+  EXPECT_TRUE(AccessingCamera(browser()->profile(), app_id2));
+  EXPECT_FALSE(AccessingCamera(browser()->profile(), app_id1));
+  EXPECT_FALSE(AccessingMicrophone(browser()->profile(), app_id1));
+}
diff --git a/chrome/browser/apps/app_service/media_requests.cc b/chrome/browser/apps/app_service/media_requests.cc
index e424deb..bd30e50 100644
--- a/chrome/browser/apps/app_service/media_requests.cc
+++ b/chrome/browser/apps/app_service/media_requests.cc
@@ -24,6 +24,21 @@
 
 MediaRequests::~MediaRequests() = default;
 
+bool MediaRequests::IsNewRequest(const std::string& app_id,
+                                 const content::WebContents* web_contents,
+                                 const content::MediaRequestState state) {
+  if (state != content::MEDIA_REQUEST_STATE_DONE) {
+    return false;
+  }
+
+  DCHECK(web_contents);
+
+  return !HasRequest(app_id, web_contents,
+                     app_id_to_web_contents_for_camera_) &&
+         !HasRequest(app_id, web_contents,
+                     app_id_to_web_contents_for_microphone_);
+}
+
 AccessingRequest MediaRequests::UpdateRequests(
     const std::string& app_id,
     const content::WebContents* web_contents,
@@ -64,6 +79,29 @@
       MaybeRemoveRequest(app_id, app_id_to_web_contents_for_microphone_));
 }
 
+AccessingRequest MediaRequests::OnWebContentsDestroyed(
+    const std::string& app_id,
+    const content::WebContents* web_contents) {
+  return AccessingRequest(
+      MaybeRemoveRequest(app_id, web_contents,
+                         app_id_to_web_contents_for_camera_),
+      MaybeRemoveRequest(app_id, web_contents,
+                         app_id_to_web_contents_for_microphone_));
+}
+
+bool MediaRequests::HasRequest(
+    const std::string& app_id,
+    const content::WebContents* web_contents,
+    const std::map<std::string, std::set<const content::WebContents*>>&
+        app_id_to_web_contents) {
+  auto it = app_id_to_web_contents.find(app_id);
+  if (it != app_id_to_web_contents.end() &&
+      it->second.find(web_contents) != it->second.end()) {
+    return true;
+  }
+  return false;
+}
+
 base::Optional<bool> MediaRequests::MaybeAddRequest(
     const std::string& app_id,
     const content::WebContents* web_contents,
diff --git a/chrome/browser/apps/app_service/media_requests.h b/chrome/browser/apps/app_service/media_requests.h
index 9df1967..7ab2b788 100644
--- a/chrome/browser/apps/app_service/media_requests.h
+++ b/chrome/browser/apps/app_service/media_requests.h
@@ -39,6 +39,13 @@
   MediaRequests(const MediaRequests&) = delete;
   MediaRequests& operator=(const MediaRequests&) = delete;
 
+  // Returns true if there is no existing access request of both camera and
+  // microphone for |app_id| and |web_contents|, and |state| is a new request.
+  // Otherwise, return false.
+  bool IsNewRequest(const std::string& app_id,
+                    const content::WebContents* web_contents,
+                    const content::MediaRequestState state);
+
   // Updates |app_id_to_web_contents_for_camera_| and
   // |app_id_to_web_contents_for_microphone_| to record the media accessing
   // requests for |app_id|. Returns the update result AccessingRequest.
@@ -53,7 +60,22 @@
   // AccessingRequest.camera or AccessingRequest.microphone.
   AccessingRequest RemoveRequests(const std::string& app_id);
 
+  // Invoked when |web_contents| is being destroyed. Removes requests in
+  // |app_id_to_web_contents_for_camera_| and
+  // |app_id_to_web_contents_for_microphone_| for the given |app_id| and
+  // |web_contents|. If there are media accessing requests for |app_id|, returns
+  // false for AccessingRequest.camera or AccessingRequest.microphone.
+  AccessingRequest OnWebContentsDestroyed(
+      const std::string& app_id,
+      const content::WebContents* web_contents);
+
  private:
+  bool HasRequest(
+      const std::string& app_id,
+      const content::WebContents* web_contents,
+      const std::map<std::string, std::set<const content::WebContents*>>&
+          app_id_to_web_contents);
+
   base::Optional<bool> MaybeAddRequest(
       const std::string& app_id,
       const content::WebContents* web_contents,
diff --git a/chrome/browser/apps/app_service/web_apps_chromeos.cc b/chrome/browser/apps/app_service/web_apps_chromeos.cc
index c37740a..87b1341 100644
--- a/chrome/browser/apps/app_service/web_apps_chromeos.cc
+++ b/chrome/browser/apps/app_service/web_apps_chromeos.cc
@@ -471,12 +471,40 @@
     return;
   }
 
+  if (media_requests_.IsNewRequest(app_id.value(), web_contents, state)) {
+    content::WebContentsUserData<AppWebContentsData>::CreateForWebContents(
+        web_contents, this);
+  }
+
   auto result = media_requests_.UpdateRequests(app_id.value(), web_contents,
                                                stream_type, state);
   ModifyCapabilityAccess(subscribers(), app_id.value(), result.camera,
                          result.microphone);
 }
 
+void WebAppsChromeOs::OnWebContentsDestroyed(
+    content::WebContents* web_contents) {
+  DCHECK(web_contents);
+
+  base::Optional<web_app::AppId> app_id =
+      web_app::FindInstalledAppWithUrlInScope(
+          profile(), web_contents->GetLastCommittedURL(),
+          /*window_only=*/false);
+  if (!app_id.has_value()) {
+    return;
+  }
+
+  const web_app::WebApp* web_app = GetWebApp(app_id.value());
+  if (!web_app || !Accepts(app_id.value())) {
+    return;
+  }
+
+  auto result =
+      media_requests_.OnWebContentsDestroyed(app_id.value(), web_contents);
+  ModifyCapabilityAccess(subscribers(), app_id.value(), result.camera,
+                         result.microphone);
+}
+
 void WebAppsChromeOs::OnNotificationDisplayed(
     const message_center::Notification& notification,
     const NotificationCommon::Metadata* const metadata) {
diff --git a/chrome/browser/apps/app_service/web_apps_chromeos.h b/chrome/browser/apps/app_service/web_apps_chromeos.h
index 8644a9d..485418e 100644
--- a/chrome/browser/apps/app_service/web_apps_chromeos.h
+++ b/chrome/browser/apps/app_service/web_apps_chromeos.h
@@ -11,6 +11,7 @@
 #include "base/scoped_observation.h"
 #include "base/scoped_observer.h"
 #include "chrome/browser/apps/app_service/app_notifications.h"
+#include "chrome/browser/apps/app_service/app_web_contents_data.h"
 #include "chrome/browser/apps/app_service/icon_key_util.h"
 #include "chrome/browser/apps/app_service/media_requests.h"
 #include "chrome/browser/apps/app_service/paused_apps.h"
@@ -40,7 +41,8 @@
 class WebAppsChromeOs : public WebAppsBase,
                         public ArcAppListPrefs::Observer,
                         public NotificationDisplayService::Observer,
-                        public MediaCaptureDevicesDispatcher::Observer {
+                        public MediaCaptureDevicesDispatcher::Observer,
+                        public AppWebContentsData::Client {
  public:
   WebAppsChromeOs(const mojo::Remote<apps::mojom::AppService>& app_service,
                   Profile* profile,
@@ -114,6 +116,9 @@
                        blink::mojom::MediaStreamType stream_type,
                        const content::MediaRequestState state) override;
 
+  // AppWebContentsData::Observer:
+  void OnWebContentsDestroyed(content::WebContents* contents) override;
+
   // NotificationDisplayService::Observer overrides.
   void OnNotificationDisplayed(
       const message_center::Notification& notification,
diff --git a/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc b/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc
index c0c5fc4..1906ce3 100644
--- a/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc
+++ b/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc
@@ -1204,6 +1204,11 @@
   });
   sm_.ExpectSpeech("copy Foo.");
 
+  // Do it again with the command Search+Ctrl+C, which should do the same thing
+  // but triggered through ChromeVox via synthesized keys.
+  sm_.Call([this]() { SendKeyPressWithSearchAndControl(ui::VKEY_C); });
+  sm_.ExpectSpeech("copy Foo.");
+
   sm_.Replay();
 }
 
diff --git a/chrome/browser/banners/android/java/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java b/chrome/browser/banners/android/java/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
index 2ef5348c..d8f1170 100644
--- a/chrome/browser/banners/android/java/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
+++ b/chrome/browser/banners/android/java/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
@@ -232,8 +232,8 @@
         AppBannerManager.setIsSupported(true);
         ShortcutHelper.setDelegateForTests(new ShortcutHelper.Delegate() {
             @Override
-            public void addShortcutToHomescreen(
-                    String title, Bitmap icon, boolean iconAdaptive, Intent shortcutIntent) {
+            public void addShortcutToHomescreen(String id, String title, Bitmap icon,
+                    boolean iconAdaptive, Intent shortcutIntent) {
                 // Ignore to prevent adding homescreen shortcuts.
             }
         });
diff --git a/chrome/browser/chrome_browser_main_win.cc b/chrome/browser/chrome_browser_main_win.cc
index af8b7ca..2de8d03 100644
--- a/chrome/browser/chrome_browser_main_win.cc
+++ b/chrome/browser/chrome_browser_main_win.cc
@@ -98,7 +98,7 @@
 #include "content/public/browser/render_process_host.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/main_function_params.h"
-#include "ui/base/cursor/win/win_cursor_factory.h"
+#include "ui/base/cursor/cursor_loader_win.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/l10n/l10n_util_win.h"
 #include "ui/base/ui_base_switches.h"
@@ -582,7 +582,7 @@
   ChromeBrowserMainParts::ToolkitInitialized();
   gfx::win::SetAdjustFontCallback(&AdjustUIFont);
   gfx::win::SetGetMinimumFontSizeCallback(&GetMinimumFontSize);
-  ui::SetCursorResourceModule(chrome::kBrowserResourcesDll);
+  ui::CursorLoaderWin::SetCursorResourceModule(chrome::kBrowserResourcesDll);
 }
 
 void ChromeBrowserMainPartsWin::PreMainMessageLoopStart() {
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 17a6363..0c3f51b7 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -833,6 +833,8 @@
     "arc/session/adb_sideloading_availability_delegate_impl.h",
     "arc/session/arc_app_id_provider_impl.cc",
     "arc/session/arc_app_id_provider_impl.h",
+    "arc/session/arc_demo_mode_preference_handler.cc",
+    "arc/session/arc_demo_mode_preference_handler.h",
     "arc/session/arc_play_store_enabled_preference_handler.cc",
     "arc/session/arc_play_store_enabled_preference_handler.h",
     "arc/session/arc_provisioning_result.cc",
@@ -3519,6 +3521,7 @@
     "arc/pip/arc_pip_bridge_unittest.cc",
     "arc/policy/arc_policy_bridge_unittest.cc",
     "arc/process/arc_process_unittest.cc",
+    "arc/session/arc_demo_mode_preference_handler_unittest.cc",
     "arc/session/arc_play_store_enabled_preference_handler_unittest.cc",
     "arc/session/arc_provisioning_result_unittest.cc",
     "arc/session/arc_session_manager_unittest.cc",
diff --git a/chrome/browser/chromeos/arc/session/arc_demo_mode_preference_handler.cc b/chrome/browser/chromeos/arc/session/arc_demo_mode_preference_handler.cc
new file mode 100644
index 0000000..dd796188
--- /dev/null
+++ b/chrome/browser/chromeos/arc/session/arc_demo_mode_preference_handler.cc
@@ -0,0 +1,69 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/arc/session/arc_demo_mode_preference_handler.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/check.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/arc/session/arc_session_manager.h"
+#include "chrome/browser/chromeos/login/demo_mode/demo_session.h"
+#include "chrome/common/pref_names.h"
+#include "components/arc/arc_util.h"
+#include "components/prefs/pref_service.h"
+
+namespace arc {
+
+// static
+std::unique_ptr<ArcDemoModePreferenceHandler>
+ArcDemoModePreferenceHandler::Create(ArcSessionManager* arc_session_manager) {
+  return base::WrapUnique(new ArcDemoModePreferenceHandler(
+      base::BindOnce(&ArcSessionManager::StopMiniArcIfNecessary,
+                     base::Unretained(arc_session_manager)),
+      g_browser_process->local_state()));
+}
+
+ArcDemoModePreferenceHandler::ArcDemoModePreferenceHandler(
+    base::OnceClosure preference_changed_callback,
+    PrefService* pref_service)
+    : preference_changed_callback_(std::move(preference_changed_callback)),
+      pref_service_(pref_service) {
+  pref_change_registrar_.Init(pref_service_);
+  pref_change_registrar_.Add(
+      prefs::kDemoModeConfig,
+      base::BindRepeating(&ArcDemoModePreferenceHandler::OnPreferenceChanged,
+                          base::Unretained(this)));
+}
+
+ArcDemoModePreferenceHandler::~ArcDemoModePreferenceHandler() = default;
+
+void ArcDemoModePreferenceHandler::OnPreferenceChanged() {
+  // On ARC++, the demo session apps image is directly mounted into the
+  // container namespace at upgrade time, so this isn't needed.
+  if (!IsArcVmEnabled())
+    return;
+
+  chromeos::DemoSession::DemoModeConfig config =
+      static_cast<chromeos::DemoSession::DemoModeConfig>(
+          pref_service_->GetInteger(prefs::kDemoModeConfig));
+  switch (config) {
+    case chromeos::DemoSession::DemoModeConfig::kNone:
+      return;
+    case chromeos::DemoSession::DemoModeConfig::kOnline:
+    case chromeos::DemoSession::DemoModeConfig::kOffline:
+      break;
+  }
+
+  VLOG(1) << "Demo Mode enabled; requesting ARCVM stop";
+  DCHECK(preference_changed_callback_);
+  std::move(preference_changed_callback_).Run();
+
+  pref_change_registrar_.RemoveAll();
+}
+
+}  // namespace arc
diff --git a/chrome/browser/chromeos/arc/session/arc_demo_mode_preference_handler.h b/chrome/browser/chromeos/arc/session/arc_demo_mode_preference_handler.h
new file mode 100644
index 0000000..e9b3c515
--- /dev/null
+++ b/chrome/browser/chromeos/arc/session/arc_demo_mode_preference_handler.h
@@ -0,0 +1,58 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_ARC_SESSION_ARC_DEMO_MODE_PREFERENCE_HANDLER_H_
+#define CHROME_BROWSER_CHROMEOS_ARC_SESSION_ARC_DEMO_MODE_PREFERENCE_HANDLER_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "components/prefs/pref_change_registrar.h"
+
+class PrefService;
+
+namespace arc {
+
+class ArcSessionManager;
+class ArcDemoModePreferenceHandlerTest;
+
+// Observes Demo Mode preference and stops mini-ARCVM once, if running, via
+// ArcSessionManager when the preference is enabled, which only happens after
+// Demo Mode enrollment is complete (in
+// chromeos::DemoSetupController::OnDeviceRegistered). ARCVM will automatically
+// start again once the Demo session starts. This is necessary because (1) the
+// demo apps image must be present at ARCVM boot and (2) mini-ARCVM is started
+// once the OOBE is visible, but before Demo Mode setup is completed. As a
+// result, that initial instance of mini-ARCVM does not pick up the demo apps
+// image, and the first demo session (which otherwise will upgrade and use that
+// instance of mini-ARCVM) cannot use the demo apps. This handler stops that
+// initial instance of mini-ARCVM if running. After the preference flip, ARCVM
+// instances (including mini instances) will load demo apps, which is
+// guaranteed by ArcVmClientAdapter.
+class ArcDemoModePreferenceHandler {
+ public:
+  static std::unique_ptr<ArcDemoModePreferenceHandler> Create(
+      ArcSessionManager* arc_session_manager);
+  ~ArcDemoModePreferenceHandler();
+  ArcDemoModePreferenceHandler(const ArcDemoModePreferenceHandler&) = delete;
+  ArcDemoModePreferenceHandler& operator=(const ArcDemoModePreferenceHandler&) =
+      delete;
+
+ private:
+  friend class ArcDemoModePreferenceHandlerTest;
+  ArcDemoModePreferenceHandler(base::OnceClosure preference_changed_callback,
+                               PrefService* pref_service);
+
+  // Called when Demo Mode preference is set after demo enrollment completes.
+  void OnPreferenceChanged();
+
+  base::OnceClosure preference_changed_callback_;
+  // Owned by global BrowserProcess instance (g_browser_process);
+  PrefService* pref_service_;
+  PrefChangeRegistrar pref_change_registrar_;
+};
+
+}  // namespace arc
+
+#endif  // CHROME_BROWSER_CHROMEOS_ARC_SESSION_ARC_DEMO_MODE_PREFERENCE_HANDLER_H_
diff --git a/chrome/browser/chromeos/arc/session/arc_demo_mode_preference_handler_unittest.cc b/chrome/browser/chromeos/arc/session/arc_demo_mode_preference_handler_unittest.cc
new file mode 100644
index 0000000..8286228
--- /dev/null
+++ b/chrome/browser/chromeos/arc/session/arc_demo_mode_preference_handler_unittest.cc
@@ -0,0 +1,102 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/arc/session/arc_demo_mode_preference_handler.h"
+
+#include "base/command_line.h"
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/login/demo_mode/demo_session.h"
+#include "chrome/common/pref_names.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/testing_pref_service.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace arc {
+
+class ArcDemoModePreferenceHandlerTest : public testing::Test {
+ public:
+  ArcDemoModePreferenceHandlerTest() = default;
+
+  void SetUp() override {
+    pref_service_.registry()->RegisterIntegerPref(
+        prefs::kDemoModeConfig,
+        static_cast<int>(chromeos::DemoSession::DemoModeConfig::kNone));
+    handler_ = base::WrapUnique<ArcDemoModePreferenceHandler>(
+        new ArcDemoModePreferenceHandler(
+            base::BindOnce(
+                &ArcDemoModePreferenceHandlerTest::OnDemoPreferenceChanged,
+                base::Unretained(this)),
+            &pref_service_));
+  }
+
+  void TearDown() override { handler_.reset(); }
+
+ protected:
+  void SetArcVmEnabled() {
+    auto* command_line = base::CommandLine::ForCurrentProcess();
+    command_line->InitFromArgv({"", "--enable-arcvm"});
+  }
+
+  bool handler_was_called() { return handler_was_called_; }
+
+  TestingPrefServiceSimple* pref_service() { return &pref_service_; }
+
+ private:
+  void OnDemoPreferenceChanged() { handler_was_called_ = true; }
+
+  content::BrowserTaskEnvironment task_environment_;
+  TestingPrefServiceSimple pref_service_;
+  bool handler_was_called_ = false;
+  std::unique_ptr<ArcDemoModePreferenceHandler> handler_;
+};
+
+TEST_F(ArcDemoModePreferenceHandlerTest, ConstructDestruct) {}
+
+TEST_F(ArcDemoModePreferenceHandlerTest, DemoPrefOnline) {
+  SetArcVmEnabled();
+  EXPECT_FALSE(handler_was_called());
+
+  pref_service()->SetInteger(
+      prefs::kDemoModeConfig,
+      static_cast<int>(chromeos::DemoSession::DemoModeConfig::kOnline));
+
+  EXPECT_TRUE(handler_was_called());
+}
+
+TEST_F(ArcDemoModePreferenceHandlerTest, DemoPrefOnline_NoArcVm) {
+  EXPECT_FALSE(handler_was_called());
+
+  pref_service()->SetInteger(
+      prefs::kDemoModeConfig,
+      static_cast<int>(chromeos::DemoSession::DemoModeConfig::kOnline));
+
+  EXPECT_FALSE(handler_was_called());
+}
+
+TEST_F(ArcDemoModePreferenceHandlerTest, DemoPrefOffline) {
+  SetArcVmEnabled();
+  EXPECT_FALSE(handler_was_called());
+
+  pref_service()->SetInteger(
+      prefs::kDemoModeConfig,
+      static_cast<int>(chromeos::DemoSession::DemoModeConfig::kOffline));
+
+  EXPECT_TRUE(handler_was_called());
+}
+
+TEST_F(ArcDemoModePreferenceHandlerTest, DemoPrefNone) {
+  SetArcVmEnabled();
+  EXPECT_FALSE(handler_was_called());
+
+  pref_service()->SetInteger(
+      prefs::kDemoModeConfig,
+      static_cast<int>(chromeos::DemoSession::DemoModeConfig::kNone));
+
+  EXPECT_FALSE(handler_was_called());
+}
+
+}  // namespace arc
diff --git a/chrome/browser/chromeos/arc/session/arc_service_launcher.cc b/chrome/browser/chromeos/arc/session/arc_service_launcher.cc
index 6ffe7162..9b7c1f8 100644
--- a/chrome/browser/chromeos/arc/session/arc_service_launcher.cc
+++ b/chrome/browser/chromeos/arc/session/arc_service_launcher.cc
@@ -41,6 +41,7 @@
 #include "chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_bridge.h"
 #include "chrome/browser/chromeos/arc/process/arc_process_service.h"
 #include "chrome/browser/chromeos/arc/screen_capture/arc_screen_capture_bridge.h"
+#include "chrome/browser/chromeos/arc/session/arc_demo_mode_preference_handler.h"
 #include "chrome/browser/chromeos/arc/session/arc_play_store_enabled_preference_handler.h"
 #include "chrome/browser/chromeos/arc/session/arc_session_manager.h"
 #include "chrome/browser/chromeos/arc/sharesheet/arc_sharesheet_bridge.h"
@@ -51,6 +52,7 @@
 #include "chrome/browser/chromeos/arc/user_session/arc_user_session_service.h"
 #include "chrome/browser/chromeos/arc/video/gpu_arc_video_service_host.h"
 #include "chrome/browser/chromeos/arc/wallpaper/arc_wallpaper_service.h"
+#include "chrome/browser/chromeos/login/startup_utils.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/arc/arc_usb_host_permission_manager.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
@@ -123,6 +125,11 @@
       scheduler_configuration_manager_(scheduler_configuration_manager) {
   DCHECK(g_arc_service_launcher == nullptr);
   g_arc_service_launcher = this;
+
+  if (!chromeos::StartupUtils::IsOobeCompleted()) {
+    arc_demo_mode_preference_handler_ =
+        ArcDemoModePreferenceHandler::Create(arc_session_manager_.get());
+  }
 }
 
 ArcServiceLauncher::~ArcServiceLauncher() {
diff --git a/chrome/browser/chromeos/arc/session/arc_service_launcher.h b/chrome/browser/chromeos/arc/session/arc_service_launcher.h
index a9619dd85..ff611b4 100644
--- a/chrome/browser/chromeos/arc/session/arc_service_launcher.h
+++ b/chrome/browser/chromeos/arc/session/arc_service_launcher.h
@@ -24,6 +24,7 @@
 
 namespace arc {
 
+class ArcDemoModePreferenceHandler;
 class ArcPlayStoreEnabledPreferenceHandler;
 class ArcServiceManager;
 class ArcSessionManager;
@@ -67,6 +68,8 @@
   std::unique_ptr<ArcSessionManager> arc_session_manager_;
   std::unique_ptr<ArcPlayStoreEnabledPreferenceHandler>
       arc_play_store_enabled_preference_handler_;
+  std::unique_ptr<ArcDemoModePreferenceHandler>
+      arc_demo_mode_preference_handler_;
   // |scheduler_configuration_manager_| outlives |this|.
   chromeos::SchedulerConfigurationManagerBase* const
       scheduler_configuration_manager_;
diff --git a/chrome/browser/chromeos/arc/session/arc_session_manager.cc b/chrome/browser/chromeos/arc/session/arc_session_manager.cc
index 2aed8990..bb57664 100644
--- a/chrome/browser/chromeos/arc/session/arc_session_manager.cc
+++ b/chrome/browser/chromeos/arc/session/arc_session_manager.cc
@@ -1675,6 +1675,13 @@
     observer.OnPropertyFilesExpanded(*property_files_expansion_result_);
 }
 
+void ArcSessionManager::StopMiniArcIfNecessary() {
+  DCHECK(!profile_);
+  pre_start_time_ = base::TimeTicks();
+  VLOG(1) << "Stopping mini-ARC instance (if any)";
+  arc_session_runner_->RequestStop();
+}
+
 std::ostream& operator<<(std::ostream& os,
                          const ArcSessionManager::State& state) {
 #define MAP_STATE(name)                \
diff --git a/chrome/browser/chromeos/arc/session/arc_session_manager.h b/chrome/browser/chromeos/arc/session/arc_session_manager.h
index 3386471..184499f2 100644
--- a/chrome/browser/chromeos/arc/session/arc_session_manager.h
+++ b/chrome/browser/chromeos/arc/session/arc_session_manager.h
@@ -298,6 +298,9 @@
   // Getter for |serialno|.
   std::string GetSerialNumber() const;
 
+  // Stops mini-ARC instance. This should only be called before login.
+  void StopMiniArcIfNecessary();
+
  private:
   // Reports statuses of OptIn flow to UMA.
   class ScopedOptInFlowTracker;
diff --git a/chrome/browser/chromeos/arc/session/arc_session_manager_unittest.cc b/chrome/browser/chromeos/arc/session/arc_session_manager_unittest.cc
index d8a1400..412920ce 100644
--- a/chrome/browser/chromeos/arc/session/arc_session_manager_unittest.cc
+++ b/chrome/browser/chromeos/arc/session/arc_session_manager_unittest.cc
@@ -220,6 +220,19 @@
             arc_session_manager()->state());
 }
 
+// We expect that StopMiniArcIfNecessary stops mini-ARC when it is running.
+TEST_F(ArcSessionManagerInLoginScreenTest, StopMiniArcIfNecessary) {
+  EXPECT_FALSE(arc_session());
+
+  SetArcAvailableCommandLineForTesting(base::CommandLine::ForCurrentProcess());
+
+  chromeos::SessionManagerClient::Get()->EmitLoginPromptVisible();
+  EXPECT_TRUE(arc_session());
+
+  arc_session_manager()->StopMiniArcIfNecessary();
+  EXPECT_FALSE(arc_session());
+}
+
 class ArcSessionManagerTestBase : public testing::Test {
  public:
   ArcSessionManagerTestBase()
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index c7f79a4..0e99baa4 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -231,8 +231,7 @@
   DISALLOW_COPY_AND_ASSIGN(FilesAppBrowserTest);
 };
 
-// TODO(crbug.com/1177117) Re-enable test
-IN_PROC_BROWSER_TEST_P(FilesAppBrowserTest, DISABLED_Test) {
+IN_PROC_BROWSER_TEST_P(FilesAppBrowserTest, Test) {
   StartTest();
 }
 
diff --git a/chrome/browser/chromeos/full_restore/app_launch_handler.cc b/chrome/browser/chromeos/full_restore/app_launch_handler.cc
index c6bf267..493808bd 100644
--- a/chrome/browser/chromeos/full_restore/app_launch_handler.cc
+++ b/chrome/browser/chromeos/full_restore/app_launch_handler.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/chromeos/full_restore/app_launch_handler.h"
 
 #include <set>
+#include <utility>
 #include <vector>
 
 #include "base/bind.h"
@@ -16,6 +17,7 @@
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/chromeos/login/session/user_session_manager.h"
 #include "chrome/browser/profiles/profile.h"
+#include "components/full_restore/app_launch_info.h"
 #include "components/full_restore/full_restore_read_handler.h"
 #include "components/full_restore/full_restore_save_handler.h"
 #include "components/full_restore/restore_data.h"
@@ -84,6 +86,10 @@
   MaybePostRestore();
 }
 
+void AppLaunchHandler::SetForceLaunchBrowserForTesting() {
+  force_launch_browser_ = true;
+}
+
 void AppLaunchHandler::OnGetRestoreData(
     std::unique_ptr<::full_restore::RestoreData> restore_data) {
   restore_data_ = std::move(restore_data);
@@ -149,6 +155,15 @@
 }
 
 void AppLaunchHandler::LaunchBrowser() {
+  // If the browser is not launched before reboot, don't launch browser during
+  // the startup phase.
+  const auto& launch_list = restore_data_->app_id_to_launch_list();
+  if (launch_list.find(extension_misc::kChromeAppId) == launch_list.end() &&
+      !force_launch_browser_) {
+    return;
+  }
+
+  restore_data_->RemoveApp(extension_misc::kChromeAppId);
   UserSessionManager::GetInstance()->LaunchBrowser(profile_);
   UserSessionManager::GetInstance()->MaybeLaunchSettings(profile_);
 }
@@ -160,7 +175,6 @@
   // For the Chrome browser, the browser session restore is used to restore the
   // web pages, so we don't need to launch the app.
   if (app_id == extension_misc::kChromeAppId) {
-    restore_data_->RemoveApp(app_id);
     return;
   }
 
diff --git a/chrome/browser/chromeos/full_restore/app_launch_handler.h b/chrome/browser/chromeos/full_restore/app_launch_handler.h
index 2e44506a..d5ac1aff 100644
--- a/chrome/browser/chromeos/full_restore/app_launch_handler.h
+++ b/chrome/browser/chromeos/full_restore/app_launch_handler.h
@@ -51,6 +51,9 @@
   // true to allow the restoration.
   void SetShouldRestore();
 
+  // Set force_launch_browser_ to launch browser for testing.
+  void SetForceLaunchBrowserForTesting();
+
  private:
   void OnGetRestoreData(
       std::unique_ptr<::full_restore::RestoreData> restore_data);
@@ -75,6 +78,8 @@
 
   bool should_launch_browser_ = false;
 
+  bool force_launch_browser_ = false;
+
   std::unique_ptr<::full_restore::RestoreData> restore_data_;
 
   base::WeakPtrFactory<AppLaunchHandler> weak_ptr_factory_{this};
diff --git a/chrome/browser/chromeos/full_restore/app_launch_handler_browsertest.cc b/chrome/browser/chromeos/full_restore/app_launch_handler_browsertest.cc
index a8d9efb..2d103bf4 100644
--- a/chrome/browser/chromeos/full_restore/app_launch_handler_browsertest.cc
+++ b/chrome/browser/chromeos/full_restore/app_launch_handler_browsertest.cc
@@ -173,7 +173,7 @@
 
   CreateWebApp();
 
-  // Set should restore
+  // Set should restore.
   app_launch_handler->SetShouldRestore();
 
   content::RunAllTasksUntilIdle();
@@ -223,7 +223,7 @@
   // Create AppLaunchHandler.
   auto app_launch_handler = std::make_unique<AppLaunchHandler>(profile());
 
-  // Set should restore
+  // Set should restore.
   app_launch_handler->SetShouldRestore();
   content::RunAllTasksUntilIdle();
 
@@ -234,6 +234,38 @@
   EXPECT_EQ(count + 1, BrowserList::GetInstance()->size());
 }
 
+IN_PROC_BROWSER_TEST_F(AppLaunchHandlerBrowserTest,
+                       RestoreAndNoBrowserLaunchInfo) {
+  size_t count = BrowserList::GetInstance()->size();
+
+  // Add app launch info, but no browser launch info.
+  ::full_restore::SaveAppLaunchInfo(
+      profile()->GetPath(),
+      std::make_unique<::full_restore::AppLaunchInfo>(
+          kAppId, kId, apps::mojom::LaunchContainer::kLaunchContainerWindow,
+          WindowOpenDisposition::NEW_WINDOW, display::kDefaultDisplayId,
+          std::vector<base::FilePath>{}, nullptr));
+
+  // Remove the browser app to mock no browser launch info.
+  ::full_restore::FullRestoreSaveHandler::GetInstance()->RemoveApp(
+      profile()->GetPath(), extension_misc::kChromeAppId);
+
+  WaitForAppLaunchInfoSaved();
+
+  // Create AppLaunchHandler.
+  auto app_launch_handler = std::make_unique<AppLaunchHandler>(profile());
+
+  // Set should restore.
+  app_launch_handler->SetShouldRestore();
+  content::RunAllTasksUntilIdle();
+
+  app_launch_handler->LaunchBrowserWhenReady();
+  content::RunAllTasksUntilIdle();
+
+  // Verify there is new browser launched.
+  EXPECT_EQ(count, BrowserList::GetInstance()->size());
+}
+
 IN_PROC_BROWSER_TEST_F(AppLaunchHandlerBrowserTest, LaunchBrowserAndRestore) {
   size_t count = BrowserList::GetInstance()->size();
 
@@ -253,7 +285,7 @@
   // Verify there is no new browser launched.
   EXPECT_EQ(count, BrowserList::GetInstance()->size());
 
-  // Set should restore
+  // Set should restore.
   app_launch_handler->SetShouldRestore();
   content::RunAllTasksUntilIdle();
 
@@ -320,7 +352,7 @@
   CreateWebApp();
   content::RunAllTasksUntilIdle();
 
-  // Set should restore
+  // Set should restore.
   app_launch_handler->SetShouldRestore();
   content::RunAllTasksUntilIdle();
 
diff --git a/chrome/browser/chromeos/full_restore/full_restore_service.cc b/chrome/browser/chromeos/full_restore/full_restore_service.cc
index 81070c341..cdad256 100644
--- a/chrome/browser/chromeos/full_restore/full_restore_service.cc
+++ b/chrome/browser/chromeos/full_restore/full_restore_service.cc
@@ -62,6 +62,10 @@
 }
 
 void FullRestoreService::RestoreForTesting() {
+  // If there is no browser launch info, the browser won't be launched. So call
+  // SetForceLaunchBrowserForTesting to launch the browser for testing.
+  app_launch_handler_->SetForceLaunchBrowserForTesting();
+
   Restore();
 }
 
diff --git a/chrome/browser/chromeos/printing/print_management/print_job_info_mojom_conversions.cc b/chrome/browser/chromeos/printing/print_management/print_job_info_mojom_conversions.cc
index 3f5bafa..e0d745a 100644
--- a/chrome/browser/chromeos/printing/print_management/print_job_info_mojom_conversions.cc
+++ b/chrome/browser/chromeos/printing/print_management/print_job_info_mojom_conversions.cc
@@ -155,7 +155,6 @@
   mojom::PrintJobInfoPtr print_job_mojom = mojom::PrintJobInfo::New();
   print_job_mojom->active_print_job_info = std::move(active_job_info_mojom);
   print_job_mojom->id = job.GetUniqueId();
-  // TODO(crbug/1091953): Use 16-bit strings.
   print_job_mojom->title = base::UTF8ToUTF16(job.document_title());
   print_job_mojom->creation_time = job.creation_time();
   print_job_mojom->number_of_pages = job.total_page_number();
diff --git a/chrome/browser/chromeos/scanning/scan_service.cc b/chrome/browser/chromeos/scanning/scan_service.cc
index c496342..e8a4f8b1 100644
--- a/chrome/browser/chromeos/scanning/scan_service.cc
+++ b/chrome/browser/chromeos/scanning/scan_service.cc
@@ -40,9 +40,6 @@
 
 namespace mojo_ipc = scanning::mojom;
 
-// Path to the active user's "My files" folder.
-constexpr char kActiveUserMyFilesPath[] = "/home/chronos/user/MyFiles";
-
 // The conversion quality when converting from PNG to JPG.
 constexpr int kJpgQuality = 100;
 
@@ -485,8 +482,7 @@
 }
 
 bool ScanService::FilePathSupported(const base::FilePath& file_path) {
-  if (file_path == base::FilePath(kActiveUserMyFilesPath) ||
-      file_path == my_files_path_ ||
+  if (file_path == my_files_path_ ||
       (!file_path.ReferencesParent() &&
        (my_files_path_.IsParent(file_path) ||
         google_drive_path_.IsParent(file_path)))) {
diff --git a/chrome/browser/chromeos/scanning/scanning_paths_provider_impl.cc b/chrome/browser/chromeos/scanning/scanning_paths_provider_impl.cc
index 262d1c92..cb491531 100644
--- a/chrome/browser/chromeos/scanning/scanning_paths_provider_impl.cc
+++ b/chrome/browser/chromeos/scanning/scanning_paths_provider_impl.cc
@@ -42,4 +42,10 @@
   return path.BaseName().value();
 }
 
+base::FilePath ScanningPathsProviderImpl::GetMyFilesPath(
+    content::WebUI* web_ui) {
+  return file_manager::util::GetMyFilesFolderForProfile(
+      Profile::FromWebUI(web_ui));
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/scanning/scanning_paths_provider_impl.h b/chrome/browser/chromeos/scanning/scanning_paths_provider_impl.h
index 7a969f9..570cf28 100644
--- a/chrome/browser/chromeos/scanning/scanning_paths_provider_impl.h
+++ b/chrome/browser/chromeos/scanning/scanning_paths_provider_impl.h
@@ -28,6 +28,7 @@
   // ScanningPathsProvider:
   std::string GetBaseNameFromPath(content::WebUI* web_ui,
                                   const base::FilePath& path) override;
+  base::FilePath GetMyFilesPath(content::WebUI* web_ui) override;
 };
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/scanning/scanning_paths_provider_impl_unittest.cc b/chrome/browser/chromeos/scanning/scanning_paths_provider_impl_unittest.cc
index fe26d54..63a60241 100644
--- a/chrome/browser/chromeos/scanning/scanning_paths_provider_impl_unittest.cc
+++ b/chrome/browser/chromeos/scanning/scanning_paths_provider_impl_unittest.cc
@@ -130,4 +130,13 @@
                             web_ui_.get(), my_files_path));
 }
 
+// Validates that the correct MyFiles path is returned.
+TEST_F(ScanningPathsProviderImplTest, GetMyFilesPath) {
+  const base::FilePath my_files_path =
+      file_manager::util::GetMyFilesFolderForProfile(profile_);
+
+  EXPECT_EQ(my_files_path,
+            scanning_paths_provider_.GetMyFilesPath(web_ui_.get()));
+}
+
 }  // namespace chromeos.
diff --git a/chrome/browser/chromeos/scanning/scanning_util.cc b/chrome/browser/chromeos/scanning/scanning_util.cc
index 40db144..e009b88 100644
--- a/chrome/browser/chromeos/scanning/scanning_util.cc
+++ b/chrome/browser/chromeos/scanning/scanning_util.cc
@@ -13,9 +13,6 @@
 
 namespace {
 
-// Path to the active user's "My files" folder.
-constexpr char kActiveUserMyFilesPath[] = "/home/chronos/user/MyFiles";
-
 // Determines if |path_to_file| is a supported file path for the Files app. Only
 // files under the |drive_path| and |my_files_path| paths are allowed to be
 // opened to from the Scan app.
@@ -29,8 +26,7 @@
   if (drive_path.IsParent(path_to_file))
     return true;
 
-  if (path_to_file.DirName() == base::FilePath(kActiveUserMyFilesPath) ||
-      my_files_path.IsParent(path_to_file))
+  if (my_files_path.IsParent(path_to_file))
     return true;
 
   return false;
@@ -48,13 +44,7 @@
   if (!base::PathExists(path_to_file))
     return false;
 
-  // Replace the MyFiles alias with the full MyFiles path.
-  base::FilePath path_to_use(path_to_file);
-  if (path_to_use.DirName().value() == kActiveUserMyFilesPath) {
-    path_to_use = my_files_path.Append(path_to_file.BaseName());
-  }
-
-  platform_util::ShowItemInFolder(Profile::FromWebUI(web_ui), path_to_use);
+  platform_util::ShowItemInFolder(Profile::FromWebUI(web_ui), path_to_file);
   return true;
 }
 
diff --git a/chrome/browser/chromeos/web_applications/camera_system_web_app_info.cc b/chrome/browser/chromeos/web_applications/camera_system_web_app_info.cc
index cc95815..a744551 100644
--- a/chrome/browser/chromeos/web_applications/camera_system_web_app_info.cc
+++ b/chrome/browser/chromeos/web_applications/camera_system_web_app_info.cc
@@ -21,12 +21,11 @@
   web_app::CreateIconInfoForSystemWebApp(
       info->start_url,
       {
-          {"camera_app_icons_48.png", 48,
-           IDR_CHROMEOS_CAMERA_APP_IMAGES_CAMERA_APP_ICONS_48_PNG},
+          {"camera_app_icons_48.png", 48, IDR_CAMERA_CAMERA_APP_ICONS_48_PNG},
           {"camera_app_icons_128.png", 128,
-           IDR_CHROMEOS_CAMERA_APP_IMAGES_CAMERA_APP_ICONS_128_PNG},
+           IDR_CAMERA_CAMERA_APP_ICONS_128_PNG},
           {"camera_app_icons_192.png", 192,
-           IDR_CHROMEOS_CAMERA_APP_IMAGES_CAMERA_APP_ICONS_192_PNG},
+           IDR_CAMERA_CAMERA_APP_ICONS_192_PNG},
       },
       *info);
   info->theme_color = 0xff000000;
diff --git a/chrome/browser/component_updater/registration.cc b/chrome/browser/component_updater/registration.cc
index 15dda54..1a87ee5 100644
--- a/chrome/browser/component_updater/registration.cc
+++ b/chrome/browser/component_updater/registration.cc
@@ -27,7 +27,6 @@
 #include "chrome/browser/component_updater/ssl_error_assistant_component_installer.h"
 #include "chrome/browser/component_updater/sth_set_component_remover.h"
 #include "chrome/browser/component_updater/subresource_filter_component_installer.h"
-#include "chrome/browser/component_updater/tls_deprecation_config_component_installer.h"
 #include "chrome/browser/component_updater/trust_token_key_commitments_component_installer.h"
 #include "chrome/browser/component_updater/zxcvbn_data_component_installer.h"
 #include "chrome/common/buildflags.h"
@@ -169,7 +168,6 @@
 
   RegisterSafetyTipsComponent(cus);
   RegisterCrowdDenyComponent(cus);
-  RegisterTLSDeprecationConfigComponent(cus);
 
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING) && defined(OS_ANDROID)
   component_updater::RegisterGamesComponent(cus, profile_prefs);
diff --git a/chrome/browser/component_updater/tls_deprecation_config_component_installer.cc b/chrome/browser/component_updater/tls_deprecation_config_component_installer.cc
deleted file mode 100644
index e334a8b..0000000
--- a/chrome/browser/component_updater/tls_deprecation_config_component_installer.cc
+++ /dev/null
@@ -1,178 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/component_updater/tls_deprecation_config_component_installer.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/callback_helpers.h"
-#include "base/files/file_util.h"
-#include "base/logging.h"
-#include "base/memory/ref_counted.h"
-#include "base/no_destructor.h"
-#include "base/stl_util.h"
-#include "base/task/post_task.h"
-#include "base/task/task_traits.h"
-#include "base/task/thread_pool.h"
-#include "base/threading/scoped_blocking_call.h"
-#include "chrome/browser/ssl/tls_deprecation_config.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/network_service_instance.h"
-#include "services/network/public/mojom/network_service.mojom.h"
-
-using component_updater::ComponentUpdateService;
-
-namespace component_updater {
-
-namespace {
-
-const base::FilePath::CharType kTLSDeprecationConfigBinaryPbFileName[] =
-    FILE_PATH_LITERAL("tls_deprecation_config.pb");
-
-// The SHA256 of the SubjectPublicKeyInfo used to sign the extension.
-// The extension id is: bklopemakmnopmghhmccadeonafabnal
-const uint8_t kTLSDeprecationConfigPublicKeySHA256[32] = {
-    0x1a, 0xbe, 0xf4, 0xc0, 0xac, 0xde, 0xfc, 0x67, 0x7c, 0x22, 0x03,
-    0x4e, 0xd0, 0x50, 0x1d, 0x0b, 0xed, 0x45, 0x0f, 0xcb, 0x0b, 0x7f,
-    0xad, 0x4f, 0xb6, 0x7b, 0x7c, 0x8f, 0xbf, 0xda, 0xa8, 0xe3};
-
-// Singleton object used to configure Network Services and memoize the TLS
-// deprecation configuration, so that it can be reloaded when the Network
-// Service restarts.
-base::FilePath& GetConfigPathInstance() {
-  static base::NoDestructor<base::FilePath> instance;
-  return *instance;
-}
-
-base::FilePath GetInstalledPath(const base::FilePath& base) {
-  return base.Append(kTLSDeprecationConfigBinaryPbFileName);
-}
-
-// Returns the contents of the file at |config_path|.
-std::string LoadConfig(const base::FilePath& config_path) {
-  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
-                                                base::BlockingType::WILL_BLOCK);
-  std::string config_bytes;
-  base::ReadFileToString(config_path, &config_bytes);
-  return config_bytes;
-}
-
-// Updates the browser and network service with a new binary config.
-void UpdateLegacyTLSConfigOnUI(const std::string& binary_config) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
-  SetRemoteTLSDeprecationConfig(binary_config);
-
-  network::mojom::NetworkService* network_service =
-      content::GetNetworkService();
-  network_service->UpdateLegacyTLSConfig(
-      base::as_bytes(base::make_span(binary_config)), base::DoNothing());
-}
-
-void SetLegacyTLSConfig() {
-  if (GetConfigPathInstance().empty())
-    return;
-
-  base::ThreadPool::PostTaskAndReplyWithResult(
-      FROM_HERE, {base::TaskPriority::USER_VISIBLE, base::MayBlock()},
-      base::BindOnce(&LoadConfig, GetConfigPathInstance()),
-      base::BindOnce(&UpdateLegacyTLSConfigOnUI));
-}
-
-}  // namespace
-
-TLSDeprecationConfigComponentInstallerPolicy::
-    TLSDeprecationConfigComponentInstallerPolicy() = default;
-
-TLSDeprecationConfigComponentInstallerPolicy::
-    ~TLSDeprecationConfigComponentInstallerPolicy() = default;
-
-// static
-void TLSDeprecationConfigComponentInstallerPolicy::
-    ReconfigureAfterNetworkRestart() {
-  SetLegacyTLSConfig();
-}
-
-bool TLSDeprecationConfigComponentInstallerPolicy::
-    SupportsGroupPolicyEnabledComponentUpdates() const {
-  return false;
-}
-
-bool TLSDeprecationConfigComponentInstallerPolicy::RequiresNetworkEncryption()
-    const {
-  return false;
-}
-
-update_client::CrxInstaller::Result
-TLSDeprecationConfigComponentInstallerPolicy::OnCustomInstall(
-    const base::DictionaryValue& /* manifest */,
-    const base::FilePath& /* install_dir */) {
-  return update_client::CrxInstaller::Result(0);  // Nothing custom here.
-}
-
-void TLSDeprecationConfigComponentInstallerPolicy::OnCustomUninstall() {}
-
-void TLSDeprecationConfigComponentInstallerPolicy::ComponentReady(
-    const base::Version& version,
-    const base::FilePath& install_dir,
-    std::unique_ptr<base::DictionaryValue> manifest) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  DVLOG(1) << "Component ready, version " << version.GetString() << " in "
-           << install_dir.value();
-
-  const base::FilePath pb_path = GetInstalledPath(install_dir);
-  if (pb_path.empty())
-    return;
-
-  // Save the installed component path for future reloads.
-  GetConfigPathInstance() = pb_path;
-
-  // The default proto will always be a placeholder since the updated versions
-  // are not checked into the repo. Simply load whatever the component updater
-  // gave us without checking the default proto from the resource bundle.
-  SetLegacyTLSConfig();
-}
-
-// Called during startup and installation before ComponentReady().
-bool TLSDeprecationConfigComponentInstallerPolicy::VerifyInstallation(
-    const base::DictionaryValue& /* manifest */,
-    const base::FilePath& install_dir) const {
-  // No need to actually validate the proto here, since we'll do the checking
-  // in PopulateFromDynamicUpdate().
-  return base::PathExists(GetInstalledPath(install_dir));
-}
-
-base::FilePath
-TLSDeprecationConfigComponentInstallerPolicy::GetRelativeInstallDir() const {
-  return base::FilePath(FILE_PATH_LITERAL("TLSDeprecationConfig"));
-}
-
-void TLSDeprecationConfigComponentInstallerPolicy::GetHash(
-    std::vector<uint8_t>* hash) const {
-  hash->assign(kTLSDeprecationConfigPublicKeySHA256,
-               kTLSDeprecationConfigPublicKeySHA256 +
-                   base::size(kTLSDeprecationConfigPublicKeySHA256));
-}
-
-std::string TLSDeprecationConfigComponentInstallerPolicy::GetName() const {
-  return "Legacy TLS Deprecation Configuration";
-}
-
-update_client::InstallerAttributes
-TLSDeprecationConfigComponentInstallerPolicy::GetInstallerAttributes() const {
-  return update_client::InstallerAttributes();
-}
-
-void RegisterTLSDeprecationConfigComponent(ComponentUpdateService* cus) {
-  DVLOG(1) << "Registering TLS Deprecation Config component.";
-
-  auto installer = base::MakeRefCounted<ComponentInstaller>(
-      std::make_unique<TLSDeprecationConfigComponentInstallerPolicy>());
-  installer->Register(cus, base::OnceClosure());
-}
-
-}  // namespace component_updater
diff --git a/chrome/browser/component_updater/tls_deprecation_config_component_installer.h b/chrome/browser/component_updater/tls_deprecation_config_component_installer.h
deleted file mode 100644
index c77f983..0000000
--- a/chrome/browser/component_updater/tls_deprecation_config_component_installer.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_COMPONENT_UPDATER_TLS_DEPRECATION_CONFIG_COMPONENT_INSTALLER_H_
-#define CHROME_BROWSER_COMPONENT_UPDATER_TLS_DEPRECATION_CONFIG_COMPONENT_INSTALLER_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/values.h"
-#include "components/component_updater/component_installer.h"
-
-namespace base {
-class FilePath;
-}  // namespace base
-
-namespace component_updater {
-
-class TLSDeprecationConfigComponentInstallerPolicy
-    : public ComponentInstallerPolicy {
- public:
-  TLSDeprecationConfigComponentInstallerPolicy();
-  ~TLSDeprecationConfigComponentInstallerPolicy() override;
-
-  // Queues a task to reconfigure the network service after the Network Service
-  // instance has changed (i.e., as signaled by
-  // content::ContentBrowserClient::OnNetworkServiceCreated).
-  static void ReconfigureAfterNetworkRestart();
-
- private:
-  // ComponentInstallerPolicy methods:
-  bool SupportsGroupPolicyEnabledComponentUpdates() const override;
-  bool RequiresNetworkEncryption() const override;
-  update_client::CrxInstaller::Result OnCustomInstall(
-      const base::DictionaryValue& manifest,
-      const base::FilePath& install_dir) override;
-  void OnCustomUninstall() override;
-  bool VerifyInstallation(const base::DictionaryValue& manifest,
-                          const base::FilePath& install_dir) const override;
-  void ComponentReady(const base::Version& version,
-                      const base::FilePath& install_dir,
-                      std::unique_ptr<base::DictionaryValue> manifest) override;
-  base::FilePath GetRelativeInstallDir() const override;
-  void GetHash(std::vector<uint8_t>* hash) const override;
-  std::string GetName() const override;
-  update_client::InstallerAttributes GetInstallerAttributes() const override;
-
-  DISALLOW_COPY_AND_ASSIGN(TLSDeprecationConfigComponentInstallerPolicy);
-};
-
-void RegisterTLSDeprecationConfigComponent(ComponentUpdateService* cus);
-
-}  // namespace component_updater
-
-#endif  // CHROME_BROWSER_COMPONENT_UPDATER_TLS_DEPRECATION_CONFIG_COMPONENT_INSTALLER_H_
diff --git a/chrome/browser/download/download_ui_controller_unittest.cc b/chrome/browser/download/download_ui_controller_unittest.cc
index d824730..ce37d70 100644
--- a/chrome/browser/download/download_ui_controller_unittest.cc
+++ b/chrome/browser/download/download_ui_controller_unittest.cc
@@ -393,7 +393,6 @@
 TEST_F(DownloadUIControllerTest, LegacyTLSMetrics) {
   base::HistogramTester histograms;
   SecurityStateTabHelper::CreateForWebContents(web_contents());
-  InitializeEmptyLegacyTLSConfig();
 
   auto navigation =
       CreateLegacyTLSNavigation(GURL(kLegacyTLSURL), web_contents());
@@ -416,36 +415,9 @@
   histograms.ExpectUniqueSample("Security.LegacyTLS.DownloadStarted", true, 1);
 }
 
-TEST_F(DownloadUIControllerTest, LegacyTLSControlSiteMetrics) {
-  base::HistogramTester histograms;
-  SecurityStateTabHelper::CreateForWebContents(web_contents());
-  InitializeLegacyTLSConfigWithControl();
-
-  auto navigation =
-      CreateLegacyTLSNavigation(GURL(kLegacyTLSURL), web_contents());
-  navigation->Commit();
-
-  // Start a download from the same page, setting up the mock item to correctly
-  // associate with the WebContents of the previous navigation.
-  std::unique_ptr<MockDownloadItem> item(CreateMockInProgressDownload());
-  GURL download_url("https://download.test/file.bin");
-  EXPECT_CALL(*item, GetURL()).WillRepeatedly(ReturnRef(download_url));
-  EXPECT_CALL(*item, GetOriginalUrl()).WillRepeatedly(ReturnRef(download_url));
-  content::DownloadItemUtils::AttachInfo(item.get(), browser_context(),
-                                         web_contents());
-
-  DownloadUIController controller(manager(), GetTestDelegate());
-
-  ASSERT_TRUE(manager_observer());
-  manager_observer()->OnDownloadCreated(manager(), item.get());
-
-  histograms.ExpectUniqueSample("Security.LegacyTLS.DownloadStarted", false, 1);
-}
-
 TEST_F(DownloadUIControllerTest, LegacyTLSGoodSiteMetrics) {
   base::HistogramTester histograms;
   SecurityStateTabHelper::CreateForWebContents(web_contents());
-  InitializeEmptyLegacyTLSConfig();
 
   auto navigation =
       CreateNonlegacyTLSNavigation(GURL("https://good.test"), web_contents());
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index bc22d4c3..34cc7f1 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -1137,6 +1137,7 @@
     ]
     deps += [
       "//components/enterprise",
+      "//components/enterprise/common/proto:connectors_proto",
       "//components/keep_alive_registry",
     ]
     if (is_linux || is_chromeos) {
diff --git a/chrome/browser/extensions/api/certificate_provider/certificate_provider_apitest.cc b/chrome/browser/extensions/api/certificate_provider/certificate_provider_apitest.cc
index 198de57..e899fd4 100644
--- a/chrome/browser/extensions/api/certificate_provider/certificate_provider_apitest.cc
+++ b/chrome/browser/extensions/api/certificate_provider/certificate_provider_apitest.cc
@@ -25,6 +25,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind.h"
 #include "base/values.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/certificate_provider/certificate_provider.h"
 #include "chrome/browser/chromeos/certificate_provider/certificate_provider_service.h"
 #include "chrome/browser/chromeos/certificate_provider/certificate_provider_service_factory.h"
@@ -33,6 +34,7 @@
 #include "chrome/browser/extensions/api/certificate_provider/certificate_provider_api.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/net/system_network_context_manager.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/test/base/ui_test_utils.h"
@@ -192,12 +194,11 @@
     base::Value autoselect_policy(base::Value::Type::LIST);
     autoselect_policy.Append(autoselect_pattern);
 
-    policy::PolicyMap policy;
-    policy.Set(policy::key::kAutoSelectCertificateForUrls,
-               policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
-               policy::POLICY_SOURCE_CLOUD, std::move(autoselect_policy),
-               nullptr);
-    provider_.UpdateChromePolicy(policy);
+    policy_map_.Set(policy::key::kAutoSelectCertificateForUrls,
+                    policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
+                    policy::POLICY_SOURCE_CLOUD, std::move(autoselect_policy),
+                    nullptr);
+    provider_.UpdateChromePolicy(policy_map_);
 
     content::RunAllPendingInMessageLoop();
 
@@ -264,6 +265,7 @@
  protected:
   testing::NiceMock<policy::MockConfigurationPolicyProvider> provider_;
   chromeos::CertificateProviderService* cert_provider_service_ = nullptr;
+  policy::PolicyMap policy_map_;
 
  private:
   const char* const kClientCertUrl = "/client-cert";
@@ -684,7 +686,21 @@
 
 // Tests the RSA MD5/SHA-1 signature algorithm. Note that TLS 1.1 is used in
 // order to make this algorithm employed.
+// TODO(cthomp): The SSLVersionMin policy will be removed in M-91, making these
+// algorithms unsupported.
 IN_PROC_BROWSER_TEST_F(CertificateProviderApiMockedExtensionTest, RsaMd5Sha1) {
+  // This test requires the SSLVersionMin policy is set to allow TLS 1.0.
+  base::Value ssl_policy("tls1");  // TLS 1.0
+  policy_map_.Set(policy::key::kSSLVersionMin, policy::POLICY_LEVEL_MANDATORY,
+                  policy::POLICY_SCOPE_MACHINE, policy::POLICY_SOURCE_CLOUD,
+                  std::move(ssl_policy), nullptr);
+  EXPECT_NO_FATAL_FAILURE(provider_.UpdateChromePolicy(policy_map_));
+
+  // Wait for the updated SSL configuration to be sent to the network service,
+  // to avoid a race.
+  g_browser_process->system_network_context_manager()
+      ->FlushSSLConfigManagerForTesting();
+
   ASSERT_TRUE(StartHttpsServer(net::SSL_PROTOCOL_VERSION_TLS1_1));
   ExecuteJavascript("supportedAlgorithms = ['RSASSA_PKCS1_v1_5_MD5_SHA1'];");
   ExecuteJavascript("registerForSignatureRequests();");
@@ -696,8 +712,22 @@
 
 // Tests the RSA MD5/SHA-1 signature algorithm using the legacy version of the
 // API. Note that TLS 1.1 is used in order to make this algorithm employed.
+// TODO(cthomp): The SSLVersionMin policy will be removed in M-91, making these
+// algorithms unsupported.
 IN_PROC_BROWSER_TEST_F(CertificateProviderApiMockedExtensionTest,
                        LegacyRsaMd5Sha1) {
+  // This test requires the SSLVersionMin policy is set to allow TLS 1.0.
+  base::Value ssl_policy("tls1");  // TLS 1.0
+  policy_map_.Set(policy::key::kSSLVersionMin, policy::POLICY_LEVEL_MANDATORY,
+                  policy::POLICY_SCOPE_MACHINE, policy::POLICY_SOURCE_CLOUD,
+                  std::move(ssl_policy), nullptr);
+  EXPECT_NO_FATAL_FAILURE(provider_.UpdateChromePolicy(policy_map_));
+
+  // Wait for the updated SSL configuration to be sent to the network service,
+  // to avoid a race.
+  g_browser_process->system_network_context_manager()
+      ->FlushSSLConfigManagerForTesting();
+
   ASSERT_TRUE(StartHttpsServer(net::SSL_PROTOCOL_VERSION_TLS1_1));
   ExecuteJavascript("supportedLegacyHashes = ['MD5_SHA1'];");
   ExecuteJavascript("registerAsLegacyCertificateProvider();");
@@ -819,8 +849,22 @@
 // Tests that the RSA MD5/SHA-1 signature algorithm is used in case of TLS 1.1,
 // even when there are other algorithms specified (which are stronger but aren't
 // supported on TLS 1.1).
+// TODO(cthomp): The SSLVersionMin policy will be removed in M-91, making these
+// algorithms unsupported.
 IN_PROC_BROWSER_TEST_F(CertificateProviderApiMockedExtensionTest,
                        RsaMd5Sha1AndOthers) {
+  // This test requires the SSLVersionMin policy is set to allow TLS 1.0.
+  base::Value ssl_policy("tls1");  // TLS 1.0
+  policy_map_.Set(policy::key::kSSLVersionMin, policy::POLICY_LEVEL_MANDATORY,
+                  policy::POLICY_SCOPE_MACHINE, policy::POLICY_SOURCE_CLOUD,
+                  std::move(ssl_policy), nullptr);
+  EXPECT_NO_FATAL_FAILURE(provider_.UpdateChromePolicy(policy_map_));
+
+  // Wait for the updated SSL configuration to be sent to the network service,
+  // to avoid a race.
+  g_browser_process->system_network_context_manager()
+      ->FlushSSLConfigManagerForTesting();
+
   ASSERT_TRUE(StartHttpsServer(net::SSL_PROTOCOL_VERSION_TLS1_1));
   ExecuteJavascript(
       "supportedAlgorithms = ['RSASSA_PKCS1_v1_5_SHA512', "
diff --git a/chrome/browser/extensions/api/content_settings/content_settings_api.cc b/chrome/browser/extensions/api/content_settings/content_settings_api.cc
index 143d6f7..c080055b 100644
--- a/chrome/browser/extensions/api/content_settings/content_settings_api.cc
+++ b/chrome/browser/extensions/api/content_settings/content_settings_api.cc
@@ -63,11 +63,6 @@
   // We remove the ContentSettingsType parameter since this is added by the
   // renderer, and is not part of the JSON schema.
   args->Remove(0, nullptr);
-  // PLUGINS have been deprecated, so ignore requests for removing them.
-  if (content_type_str == "plugins") {
-    *content_type = ContentSettingsType::DEPRECATED_PLUGINS;
-    return true;
-  }
   *content_type =
       extensions::content_settings_helpers::StringToContentSettingsType(
           content_type_str);
@@ -86,12 +81,6 @@
   std::unique_ptr<Clear::Params> params(Clear::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params.get());
 
-  if (content_type == ContentSettingsType::DEPRECATED_PLUGINS) {
-    return RespondNow(
-        Error(content_settings_api_constants::
-                  kSettingPluginContentSettingsClearIsDisallowed));
-  }
-
   ExtensionPrefsScope scope = kExtensionPrefsScopeRegular;
   bool incognito = false;
   if (params->details.scope ==
@@ -126,10 +115,6 @@
   std::unique_ptr<Get::Params> params(Get::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params.get());
 
-  if (content_type == ContentSettingsType::DEPRECATED_PLUGINS) {
-    return RespondNow(Error(content_settings_api_constants::
-                                kSettingPluginContentSettingsGetIsDisallowed));
-  }
 
   GURL primary_url(params->details.primary_url);
   if (!primary_url.is_valid()) {
@@ -199,12 +184,6 @@
   std::unique_ptr<Set::Params> params(Set::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params.get());
 
-  // PLUGINS have been deprecated.
-  if (content_type == ContentSettingsType::DEPRECATED_PLUGINS) {
-    return RespondNow(Error(content_settings_api_constants::
-                                kSettingPluginContentSettingsIsDisallowed));
-  }
-
   std::string primary_error;
   ContentSettingsPattern primary_pattern =
       content_settings_helpers::ParseExtensionPattern(
@@ -329,10 +308,6 @@
   ContentSettingsType content_type;
   EXTENSION_FUNCTION_VALIDATE(RemoveContentType(args_.get(), &content_type));
 
-  if (content_type != ContentSettingsType::DEPRECATED_PLUGINS) {
-    return RespondNow(NoArguments());
-  }
-
 #if BUILDFLAG(ENABLE_PLUGINS)
   return RespondNow(
       Error(content_settings_api_constants::
diff --git a/chrome/browser/extensions/api/content_settings/content_settings_api_constants.cc b/chrome/browser/extensions/api/content_settings/content_settings_api_constants.cc
index 6176c1f6..b942cab 100644
--- a/chrome/browser/extensions/api/content_settings/content_settings_api_constants.cc
+++ b/chrome/browser/extensions/api/content_settings/content_settings_api_constants.cc
@@ -23,15 +23,8 @@
     "You cannot read incognito content settings when no incognito window "
     "is open.";
 const char kInvalidUrlError[] = "The URL \"*\" is invalid.";
-const char kSettingPluginContentSettingsIsDisallowed[] =
-    "`chrome.contentSettings.plugins.set()` API is no longer supported.";
 const char kSettingPluginContentSettingsResourceIdentifierIsDisallowed[] =
     "`chrome.contentSettings.plugins.getResourceIdentifiers()` API is no "
     "longer supported.";
-const char kSettingPluginContentSettingsGetIsDisallowed[] =
-    "`chrome.contentSettings.plugins.get()` API is no longer supported.";
-const char kSettingPluginContentSettingsClearIsDisallowed[] =
-    "`chrome.contentSettings.plugins.clear()` API is no longer supported.";
-
 }  // namespace content_settings_api_constants
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/content_settings/content_settings_api_constants.h b/chrome/browser/extensions/api/content_settings/content_settings_api_constants.h
index adb68a18..20ac8055 100644
--- a/chrome/browser/extensions/api/content_settings/content_settings_api_constants.h
+++ b/chrome/browser/extensions/api/content_settings/content_settings_api_constants.h
@@ -23,10 +23,7 @@
 extern const char kIncognitoContextError[];
 extern const char kIncognitoSessionOnlyError[];
 extern const char kInvalidUrlError[];
-extern const char kSettingPluginContentSettingsIsDisallowed[];
 extern const char kSettingPluginContentSettingsResourceIdentifierIsDisallowed[];
-extern const char kSettingPluginContentSettingsGetIsDisallowed[];
-extern const char kSettingPluginContentSettingsClearIsDisallowed[];
 }  // namespace content_settings_api_constants
 }  // namespace extensions
 
diff --git a/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc b/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc
index c643175..53ec8c2e5 100644
--- a/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc
+++ b/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc
@@ -348,26 +348,4 @@
   histogram_tester.ExpectTotalCount(
       "ContentSettings.ExtensionNonEmbeddedSettingSet", 2);
 }
-
-IN_PROC_BROWSER_TEST_P(ExtensionContentSettingsApiLazyTest, PluginsApiTest) {
-  constexpr char kExtensionPath[] = "content_settings/disablepluginsapi";
-  EXPECT_TRUE(RunLazyTest(kExtensionPath)) << message_;
-}
-
-IN_PROC_BROWSER_TEST_P(ExtensionContentSettingsApiLazyTest, ConsoleErrorTest) {
-  constexpr char kExtensionPath[] = "content_settings/disablepluginsapi";
-  const extensions::Extension* extension =
-      LoadExtension(test_data_dir_.AppendASCII(kExtensionPath));
-  ASSERT_TRUE(extension);
-  auto* web_contents = extensions::ProcessManager::Get(profile())
-                           ->GetBackgroundHostForExtension(extension->id())
-                           ->host_contents();
-  content::WebContentsConsoleObserver console_observer(web_contents);
-  console_observer.SetPattern("*API is no longer supported*");
-  browsertest_util::ExecuteScriptInBackgroundPageNoWait(
-      profile(), extension->id(), "setPluginsSetting()");
-  console_observer.Wait();
-  EXPECT_EQ(1u, console_observer.messages().size());
-}
-
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/enterprise_reporting_private/context_info_fetcher.cc b/chrome/browser/extensions/api/enterprise_reporting_private/context_info_fetcher.cc
index 1c12f6f..8018117 100644
--- a/chrome/browser/extensions/api/enterprise_reporting_private/context_info_fetcher.cc
+++ b/chrome/browser/extensions/api/enterprise_reporting_private/context_info_fetcher.cc
@@ -8,6 +8,7 @@
 
 #include "base/callback_forward.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "chrome/browser/enterprise/connectors/connectors_service.h"
 #include "chrome/browser/enterprise/util/affiliation.h"
 #include "chrome/browser/profiles/profile.h"
 #include "device_management_backend.pb.h"
diff --git a/chrome/browser/extensions/api/enterprise_reporting_private/context_info_fetcher.h b/chrome/browser/extensions/api/enterprise_reporting_private/context_info_fetcher.h
index d68a8f8..10d26035 100644
--- a/chrome/browser/extensions/api/enterprise_reporting_private/context_info_fetcher.h
+++ b/chrome/browser/extensions/api/enterprise_reporting_private/context_info_fetcher.h
@@ -6,13 +6,17 @@
 #define CHROME_BROWSER_EXTENSIONS_API_ENTERPRISE_REPORTING_PRIVATE_CONTEXT_INFO_FETCHER_H_
 
 #include "base/callback_forward.h"
-#include "chrome/browser/enterprise/connectors/connectors_service.h"
 #include "chrome/common/extensions/api/enterprise_reporting_private.h"
 
 namespace content {
 class BrowserContext;
 }  // namespace content
 
+namespace enterprise_connectors {
+enum AnalysisConnector : int;
+class ConnectorsService;
+}  // namespace enterprise_connectors
+
 namespace extensions {
 namespace enterprise_reporting {
 
diff --git a/chrome/browser/extensions/chrome_component_extension_resource_manager.cc b/chrome/browser/extensions/chrome_component_extension_resource_manager.cc
index 7f7767a..5e7c8e2 100644
--- a/chrome/browser/extensions/chrome_component_extension_resource_manager.cc
+++ b/chrome/browser/extensions/chrome_component_extension_resource_manager.cc
@@ -165,11 +165,11 @@
     size_t size) {
   for (size_t i = 0; i < size; ++i) {
     base::FilePath resource_path =
-        base::FilePath().AppendASCII(entries[i].name);
+        base::FilePath().AppendASCII(entries[i].path);
     resource_path = resource_path.NormalizePathSeparators();
 
     DCHECK(!base::Contains(path_to_resource_id_, resource_path));
-    path_to_resource_id_[resource_path] = entries[i].value;
+    path_to_resource_id_[resource_path] = entries[i].id;
   }
 }
 
diff --git a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc
index 258b726a..4d75735c 100644
--- a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc
+++ b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/file_system_access/chrome_file_system_access_permission_context.h"
 
+#include <memory>
 #include <string>
 #include <utility>
 
@@ -12,8 +13,10 @@
 #include "base/files/file_path.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/path_service.h"
+#include "base/strings/strcat.h"
 #include "base/task/thread_pool.h"
 #include "base/util/values/values_util.h"
+#include "base/values.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
@@ -37,8 +40,32 @@
 using HandleType = content::FileSystemAccessPermissionContext::HandleType;
 
 // Dictionary keys for the FILE_SYSTEM_LAST_PICKED_DIRECTORY website setting.
-const char kLastPickedDirectoryKey[] = "default-path";
-const char kLastPickedDirectoryTypeKey[] = "default-path-type";
+// Schema (per origin):
+// {
+//  ...
+//   {
+//     "default-id" : { "path" : <path> , "path-type" : <type>}
+//     "custom-id-fruit" : { "path" : <path> , "path-type" : <type> }
+//     "custom-id-flower" : { "path" : <path> , "path-type" : <type> }
+//     ...
+//   }
+//  ...
+// }
+const char kDefaultLastPickedDirectoryKey[] = "default-id";
+const char kCustomLastPickedDirectoryKey[] = "custom-id";
+const char kPathKey[] = "path";
+const char kPathTypeKey[] = "path-type";
+
+// TODO(https://crbug.com/1177334): Remove migration logic.
+// Deprecated 2/2021. Former schema (per origin):
+// {
+//  ...
+//   "default-path" : <path>,
+//   "default-path-type" : <type>,
+//  ...
+// }
+const char kDeprecatedLastPickedDirectoryKey[] = "default-path";
+const char kDeprecatedLastPickedDirectoryTypeKey[] = "default-path-type";
 
 void ShowFileSystemAccessRestrictedDirectoryDialogOnUIThread(
     content::GlobalFrameRoutingId frame_id,
@@ -305,6 +332,11 @@
   return ChromeFileSystemAccessPermissionContext::AfterWriteCheckResult::kBlock;
 }
 
+std::string GenerateLastPickedDirectoryKey(const std::string& id) {
+  return id.empty() ? kDefaultLastPickedDirectoryKey
+                    : base::StrCat({kCustomLastPickedDirectoryKey, "-", id});
+}
+
 }  // namespace
 
 ChromeFileSystemAccessPermissionContext::Grants::Grants() = default;
@@ -438,23 +470,79 @@
   return false;
 }
 
-void ChromeFileSystemAccessPermissionContext::SetLastPickedDirectory(
-    const url::Origin& origin,
-    const base::FilePath& path,
-    const PathType type) {
-  base::Value dict(base::Value::Type::DICTIONARY);
-  dict.SetKey(kLastPickedDirectoryKey, util::FilePathToValue(path));
-  dict.SetIntKey(kLastPickedDirectoryTypeKey, static_cast<int>(type));
+// TODO(https://crbug.com/1177334): Remove migration logic.
+void ChromeFileSystemAccessPermissionContext::MaybeMigrateOriginToNewSchema(
+    const url::Origin& origin) {
+  std::unique_ptr<base::Value> value = content_settings()->GetWebsiteSetting(
+      origin.GetURL(), origin.GetURL(),
+      ContentSettingsType::FILE_SYSTEM_LAST_PICKED_DIRECTORY, /*info=*/nullptr);
+
+  if (!value)
+    return;
+
+  auto* default_path_value = value->FindKey(kDeprecatedLastPickedDirectoryKey);
+  if (!default_path_value)
+    return;
+
+  auto default_path =
+      util::ValueToFilePath(default_path_value).value_or(base::FilePath());
+  auto default_type =
+      value->FindIntKey(kDeprecatedLastPickedDirectoryTypeKey) ==
+              static_cast<int>(PathType::kExternal)
+          ? PathType::kExternal
+          : PathType::kLocal;
+
+  // Remove old keys.
+  value->RemoveKey(kDeprecatedLastPickedDirectoryKey);
+  value->RemoveKey(kDeprecatedLastPickedDirectoryTypeKey);
+
+  // Set this information as the default.
+  base::Value entry(base::Value::Type::DICTIONARY);
+  entry.SetKey(kPathKey, util::FilePathToValue(default_path));
+  entry.SetIntKey(kPathTypeKey, static_cast<int>(default_type));
+
+  value->SetKey(GenerateLastPickedDirectoryKey(std::string()),
+                std::move(entry));
 
   content_settings_->SetWebsiteSettingDefaultScope(
       origin.GetURL(), origin.GetURL(),
-      ContentSettingsType::FILE_SYSTEM_LAST_PICKED_DIRECTORY,
-      base::Value::ToUniquePtrValue(std::move(dict)));
+      ContentSettingsType::FILE_SYSTEM_LAST_PICKED_DIRECTORY, std::move(value));
+}
+
+void ChromeFileSystemAccessPermissionContext::SetLastPickedDirectory(
+    const url::Origin& origin,
+    const std::string& id,
+    const base::FilePath& path,
+    const PathType type) {
+  MaybeMigrateOriginToNewSchema(origin);
+
+  std::unique_ptr<base::Value> value = content_settings()->GetWebsiteSetting(
+      origin.GetURL(), origin.GetURL(),
+      ContentSettingsType::FILE_SYSTEM_LAST_PICKED_DIRECTORY, /*info=*/nullptr);
+  if (!value) {
+    value = std::make_unique<base::Value>(base::Value::Type::DICTIONARY);
+  }
+
+  // Create an entry into the nested dictionary.
+  base::Value entry(base::Value::Type::DICTIONARY);
+  entry.SetKey(kPathKey, util::FilePathToValue(path));
+  entry.SetIntKey(kPathTypeKey, static_cast<int>(type));
+
+  // TODO(https://crbug.com/1171354): Limit the number of `id`s that can be set
+  // per origin.
+  value->SetKey(GenerateLastPickedDirectoryKey(id), std::move(entry));
+
+  content_settings_->SetWebsiteSettingDefaultScope(
+      origin.GetURL(), origin.GetURL(),
+      ContentSettingsType::FILE_SYSTEM_LAST_PICKED_DIRECTORY, std::move(value));
 }
 
 ChromeFileSystemAccessPermissionContext::PathInfo
 ChromeFileSystemAccessPermissionContext::GetLastPickedDirectory(
-    const url::Origin& origin) {
+    const url::Origin& origin,
+    const std::string& id) {
+  MaybeMigrateOriginToNewSchema(origin);
+
   std::unique_ptr<base::Value> value = content_settings()->GetWebsiteSetting(
       origin.GetURL(), origin.GetURL(),
       ContentSettingsType::FILE_SYSTEM_LAST_PICKED_DIRECTORY, /*info=*/nullptr);
@@ -463,15 +551,17 @@
   if (!value)
     return path_info;
 
-  auto type_int = value->FindIntKey(kLastPickedDirectoryTypeKey)
+  auto* entry = value->FindDictKey(GenerateLastPickedDirectoryKey(id));
+  if (!entry)
+    return path_info;
+
+  auto type_int = entry->FindIntKey(kPathTypeKey)
                       .value_or(static_cast<int>(PathType::kLocal));
   path_info.type = type_int == static_cast<int>(PathType::kExternal)
                        ? PathType::kExternal
                        : PathType::kLocal;
-  path_info.path =
-      util::ValueToFilePath(value->FindKey(kLastPickedDirectoryKey))
-          .value_or(base::FilePath());
-
+  path_info.path = util::ValueToFilePath(entry->FindKey(kPathKey))
+                       .value_or(base::FilePath());
   return path_info;
 }
 
diff --git a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.h b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.h
index e7210fd0..a63bb32f 100644
--- a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.h
+++ b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.h
@@ -61,9 +61,11 @@
   bool CanObtainWritePermission(const url::Origin& origin) override;
 
   void SetLastPickedDirectory(const url::Origin& origin,
+                              const std::string& id,
                               const base::FilePath& path,
                               const PathType type) override;
-  PathInfo GetLastPickedDirectory(const url::Origin& origin) override;
+  PathInfo GetLastPickedDirectory(const url::Origin& origin,
+                                  const std::string& id) override;
   base::FilePath GetWellKnownDirectoryPath(
       blink::mojom::WellKnownDirectory directory) override;
 
@@ -110,6 +112,8 @@
       base::OnceCallback<void(SensitiveDirectoryResult)> callback,
       bool should_block);
 
+  void MaybeMigrateOriginToNewSchema(const url::Origin& origin);
+
   virtual base::WeakPtr<ChromeFileSystemAccessPermissionContext>
   GetWeakPtr() = 0;
 
diff --git a/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc b/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
index aef430c4..19ff7a2 100644
--- a/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
+++ b/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/run_loop.h"
 #include "base/test/bind.h"
 #include "base/test/scoped_path_override.h"
+#include "base/util/values/values_util.h"
 #include "build/build_config.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/profiles/profile.h"
@@ -25,7 +26,6 @@
 #include "content/public/browser/render_process_host.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/test_renderer_host.h"
-#include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
@@ -138,6 +138,7 @@
       url::Origin::Create(GURL("https://example.com"));
   const url::Origin kTestOrigin2 =
       url::Origin::Create(GURL("https://test.com"));
+  const std::string kTestStartingDirectoryId = "test_id";
   const base::FilePath kTestPath =
       base::FilePath(FILE_PATH_LITERAL("/foo/bar"));
   const url::Origin kChromeOrigin = url::Origin::Create(GURL("chrome://test"));
@@ -415,54 +416,141 @@
 }
 
 TEST_F(ChromeFileSystemAccessPermissionContextTest, GetLastPickedDirectory) {
-  auto file_info = permission_context()->GetLastPickedDirectory(kTestOrigin);
+  auto file_info = permission_context()->GetLastPickedDirectory(
+      kTestOrigin, kTestStartingDirectoryId);
   EXPECT_EQ(file_info.path, base::FilePath());
   EXPECT_EQ(file_info.type, PathType::kLocal);
 }
 
 TEST_F(ChromeFileSystemAccessPermissionContextTest, SetLastPickedDirectory) {
-  EXPECT_EQ(permission_context()->GetLastPickedDirectory(kTestOrigin).path,
+  EXPECT_EQ(permission_context()
+                ->GetLastPickedDirectory(kTestOrigin, kTestStartingDirectoryId)
+                .path,
             base::FilePath());
 
   auto type = PathType::kLocal;
-  permission_context()->SetLastPickedDirectory(kTestOrigin, kTestPath, type);
-  auto path_info = permission_context()->GetLastPickedDirectory(kTestOrigin);
+  permission_context()->SetLastPickedDirectory(
+      kTestOrigin, kTestStartingDirectoryId, kTestPath, type);
+  auto path_info = permission_context()->GetLastPickedDirectory(
+      kTestOrigin, kTestStartingDirectoryId);
   EXPECT_EQ(path_info.path, kTestPath);
   EXPECT_EQ(path_info.type, type);
 
   auto new_path = path_info.path.AppendASCII("baz");
   auto new_type = PathType::kExternal;
-  permission_context()->SetLastPickedDirectory(kTestOrigin, new_path, new_type);
-  auto new_path_info =
-      permission_context()->GetLastPickedDirectory(kTestOrigin);
+  permission_context()->SetLastPickedDirectory(
+      kTestOrigin, kTestStartingDirectoryId, new_path, new_type);
+  auto new_path_info = permission_context()->GetLastPickedDirectory(
+      kTestOrigin, kTestStartingDirectoryId);
   EXPECT_EQ(new_path_info.path, new_path);
   EXPECT_EQ(new_path_info.type, new_type);
 }
 
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
+       SetLastPickedDirectory_DefaultId) {
+  EXPECT_EQ(permission_context()
+                ->GetLastPickedDirectory(kTestOrigin, kTestStartingDirectoryId)
+                .path,
+            base::FilePath());
+
+  // SetLastPickedDirectory with `kTestStartingDirectoryId`.
+  auto type = PathType::kLocal;
+  permission_context()->SetLastPickedDirectory(
+      kTestOrigin, kTestStartingDirectoryId, kTestPath, type);
+  auto path_info = permission_context()->GetLastPickedDirectory(
+      kTestOrigin, kTestStartingDirectoryId);
+  EXPECT_EQ(path_info.path, kTestPath);
+  EXPECT_EQ(path_info.type, type);
+
+  // SetLastPickedDirectory with an empty (default) ID.
+  auto new_id = std::string();
+  auto new_path = path_info.path.AppendASCII("baz");
+  auto new_type = PathType::kExternal;
+  permission_context()->SetLastPickedDirectory(kTestOrigin, new_id, new_path,
+                                               new_type);
+  auto new_path_info =
+      permission_context()->GetLastPickedDirectory(kTestOrigin, new_id);
+  EXPECT_EQ(new_path_info.path, new_path);
+  EXPECT_EQ(new_path_info.type, new_type);
+
+  // Confirm that the original ID can still be retrieved as before.
+  auto old_path_info = permission_context()->GetLastPickedDirectory(
+      kTestOrigin, kTestStartingDirectoryId);
+  EXPECT_EQ(old_path_info.path, kTestPath);
+  EXPECT_EQ(old_path_info.type, type);
+}
+
+TEST_F(ChromeFileSystemAccessPermissionContextTest,
        SetLastPickedDirectory_NewPermissionContext) {
-  EXPECT_EQ(permission_context()->GetLastPickedDirectory(kTestOrigin).path,
+  EXPECT_EQ(permission_context()
+                ->GetLastPickedDirectory(kTestOrigin, kTestStartingDirectoryId)
+                .path,
             base::FilePath());
 
   const base::FilePath path = base::FilePath(FILE_PATH_LITERAL("/baz/bar"));
 
-  permission_context()->SetLastPickedDirectory(kTestOrigin, path,
-                                               PathType::kLocal);
-  ASSERT_EQ(permission_context()->GetLastPickedDirectory(kTestOrigin).path,
+  permission_context()->SetLastPickedDirectory(
+      kTestOrigin, kTestStartingDirectoryId, path, PathType::kLocal);
+  ASSERT_EQ(permission_context()
+                ->GetLastPickedDirectory(kTestOrigin, kTestStartingDirectoryId)
+                .path,
             path);
 
   TestFileSystemAccessPermissionContext new_permission_context(
       browser_context());
-  EXPECT_EQ(new_permission_context.GetLastPickedDirectory(kTestOrigin).path,
+  EXPECT_EQ(new_permission_context
+                .GetLastPickedDirectory(kTestOrigin, kTestStartingDirectoryId)
+                .path,
             path);
 
   auto new_path = path.AppendASCII("foo");
-  new_permission_context.SetLastPickedDirectory(kTestOrigin, new_path,
-                                                PathType::kLocal);
-  EXPECT_EQ(permission_context()->GetLastPickedDirectory(kTestOrigin).path,
+  new_permission_context.SetLastPickedDirectory(
+      kTestOrigin, kTestStartingDirectoryId, new_path, PathType::kLocal);
+  EXPECT_EQ(permission_context()
+                ->GetLastPickedDirectory(kTestOrigin, kTestStartingDirectoryId)
+                .path,
             new_path);
 }
 
+// TODO(https://crbug.com/1177334): Remove test when removing migration logic.
+TEST_F(ChromeFileSystemAccessPermissionContextTest,
+       Migrate_LastPickedDirectory) {
+  EXPECT_EQ(permission_context()
+                ->GetLastPickedDirectory(kTestOrigin, kTestStartingDirectoryId)
+                .path,
+            base::FilePath());
+
+  // Set keys using the old method.
+  const char kDeprecatedLastPickedDirectoryKey[] = "default-path";
+  const char kDeprecatedLastPickedDirectoryTypeKey[] = "default-path-type";
+  const base::FilePath path = base::FilePath(FILE_PATH_LITERAL("/baz/bar"));
+  const auto type = PathType::kExternal;
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetKey(kDeprecatedLastPickedDirectoryKey, util::FilePathToValue(path));
+  dict.SetIntKey(kDeprecatedLastPickedDirectoryTypeKey, static_cast<int>(type));
+  HostContentSettingsMapFactory::GetForProfile(&profile_)
+      ->SetWebsiteSettingDefaultScope(
+          kTestOrigin.GetURL(), kTestOrigin.GetURL(),
+          ContentSettingsType::FILE_SYSTEM_LAST_PICKED_DIRECTORY,
+          base::Value::ToUniquePtrValue(std::move(dict)));
+
+  // Retrieve key using the new method. Information should have been migrated.
+  auto result = permission_context()->GetLastPickedDirectory(
+      kTestOrigin, /*id=*/std::string());
+  EXPECT_EQ(result.path, path);
+  EXPECT_EQ(result.type, type);
+
+  // Confirm that the old keys have been removed.
+  std::unique_ptr<base::Value> value =
+      HostContentSettingsMapFactory::GetForProfile(&profile_)
+          ->GetWebsiteSetting(
+              kTestOrigin.GetURL(), kTestOrigin.GetURL(),
+              ContentSettingsType::FILE_SYSTEM_LAST_PICKED_DIRECTORY,
+              /*info=*/nullptr);
+  EXPECT_FALSE(value->FindKey(kDeprecatedLastPickedDirectoryKey));
+  EXPECT_FALSE(value->FindIntKey(kDeprecatedLastPickedDirectoryKey));
+}
+
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
        GetWellKnownDirectoryPath_Base_OK) {
   base::ScopedPathOverride user_desktop_override(
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index a185eeff..fec799b 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1602,11 +1602,6 @@
     "expiry_milestone": 92
   },
   {
-    "name": "enable-experimental-accessibility-chromevox-annotations",
-    "owners": [ "akihiroota" ],
-    "expiry_milestone": 89
-  },
-  {
     "name": "enable-experimental-accessibility-dictation-extension",
     "owners": [ "akihiroota" ],
     "expiry_milestone": 92
@@ -1914,12 +1909,12 @@
   {
     "name": "enable-magnifier-new-focus-following",
     "owners": [ "josiahk", "//ui/accessibility/OWNERS" ],
-    "expiry_milestone": 88
+    "expiry_milestone": 92
   },
   {
     "name": "enable-magnifier-panning-improvements",
     "owners": [ "josiahk", "//ui/accessibility/OWNERS" ],
-    "expiry_milestone": 91
+    "expiry_milestone": 92
   },
   {
     "name": "enable-mark-http-as",
@@ -2425,7 +2420,9 @@
   {
     "name": "enable-use-aaudio-driver",
     "owners": [ "tguilbert" ],
-    "expiry_milestone": 88
+    // This feature has launched and is on by default. We are still debugging a
+    // few occasional issues, for which it's useful to turns this flag off.
+    "expiry_milestone": 92
   },
   {
     "name": "enable-use-zoom-for-dsf",
@@ -4776,19 +4773,9 @@
     "expiry_milestone": 95
   },
   {
-    "name": "tab-groups",
-    "owners": [ "chrome-desktop-ui-sea@google.com", "connily" ],
-    "expiry_milestone": 88
-  },
-  {
     "name": "tab-groups-auto-create",
     "owners": [ "chrome-desktop-ui-sea@google.com", "cyan" ],
-    "expiry_milestone": 89
-  },
-  {
-    "name": "tab-groups-collapse",
-    "owners": [ "chrome-desktop-ui-sea@google.com", "cyan" ],
-    "expiry_milestone": 89
+    "expiry_milestone": 92
   },
   {
     "name": "tab-groups-collapse-freezing",
@@ -4798,7 +4785,7 @@
   {
     "name": "tab-groups-feedback",
     "owners": [ "chrome-desktop-ui-sea@google.com", "cyan" ],
-    "expiry_milestone": 86
+    "expiry_milestone": 92
   },
   {
     "name": "tab-groups-new-badge-promo",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 960a4da4..e8f5ea8 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -4306,11 +4306,6 @@
     "Show a notification bubble once an application has switched to "
     "non-immersive fullscreen mode or obtained pointer lock.";
 
-const char kExperimentalAccessibilityChromeVoxAnnotationsName[] =
-    "Enable experimental ChromeVox annotations feature.";
-const char kExperimentalAccessibilityChromeVoxAnnotationsDescription[] =
-    "Allows users to create custom annotations for elements using ChromeVox.";
-
 const char kExperimentalAccessibilityDictationExtensionName[] =
     "Experimental accessibility dictation extension.";
 const char kExperimentalAccessibilityDictationExtensionDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 9b79841bc..ae63d4da 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -2517,9 +2517,6 @@
 extern const char kExoLockNotificationName[];
 extern const char kExoLockNotificationDescription[];
 
-extern const char kExperimentalAccessibilityChromeVoxAnnotationsName[];
-extern const char kExperimentalAccessibilityChromeVoxAnnotationsDescription[];
-
 extern const char kExperimentalAccessibilityChromeVoxTutorialName[];
 extern const char kExperimentalAccessibilityChromeVoxTutorialDescription[];
 
diff --git a/chrome/browser/lacros/browser_interactive_uitest.cc b/chrome/browser/lacros/browser_interactive_uitest.cc
new file mode 100644
index 0000000..21f8e2c6
--- /dev/null
+++ b/chrome/browser/lacros/browser_interactive_uitest.cc
@@ -0,0 +1,40 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/interactive_test_utils.h"
+#include "content/public/test/browser_test.h"
+
+namespace {
+
+using BrowserInteractiveUiTest = InProcessBrowserTest;
+
+// Verifies that the Lacros browser can activate one of its own windows.
+// Regression test for https://crbug.com/1172448
+IN_PROC_BROWSER_TEST_F(BrowserInteractiveUiTest, LacrosWindowActivation) {
+  Browser* first_browser = browser();
+
+  // Ensure the initial window is active.
+  ui_test_utils::BrowserActivationWaiter waiter1(first_browser);
+  waiter1.WaitForActivation();
+  EXPECT_TRUE(first_browser->window()->IsActive());
+
+  // Create a second browser.
+  Browser* second_browser = CreateBrowser(first_browser->profile());
+  ui_test_utils::BrowserActivationWaiter waiter2(second_browser);
+  waiter2.WaitForActivation();
+  EXPECT_TRUE(second_browser->window()->IsActive());
+
+  // Activate the first browser.
+  ui_test_utils::BrowserActivationWaiter waiter3(first_browser);
+  first_browser->window()->Activate();
+  waiter3.WaitForActivation();
+
+  // First browser is now active again.
+  EXPECT_TRUE(first_browser->window()->IsActive());
+}
+
+}  // namespace
diff --git a/chrome/browser/language/android/BUILD.gn b/chrome/browser/language/android/BUILD.gn
new file mode 100644
index 0000000..69f8e0d5
--- /dev/null
+++ b/chrome/browser/language/android/BUILD.gn
@@ -0,0 +1,80 @@
+# Copyright 2021 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/rules.gni")
+
+# Language files used in //chrome/android:base_module_java
+android_library("base_module_java") {
+  sources = [
+    "java/src/org/chromium/chrome/browser/language/AppLocaleUtils.java",
+    "java/src/org/chromium/chrome/browser/language/GlobalAppLocaleController.java",
+  ]
+  deps = [
+    "//base:base_java",
+    "//chrome/browser/preferences:java",
+    "//third_party/google_android_play_core:com_google_android_play_core_java",
+  ]
+}
+
+android_library("java") {
+  sources = [
+    "java/src/org/chromium/chrome/browser/language/settings/AddLanguageFragment.java",
+    "java/src/org/chromium/chrome/browser/language/settings/AvailableUiLanguages.java",
+    "java/src/org/chromium/chrome/browser/language/settings/LanguageItem.java",
+    "java/src/org/chromium/chrome/browser/language/settings/LanguageItemPickerPreference.java",
+    "java/src/org/chromium/chrome/browser/language/settings/LanguageListBaseAdapter.java",
+    "java/src/org/chromium/chrome/browser/language/settings/LanguageListPreference.java",
+    "java/src/org/chromium/chrome/browser/language/settings/LanguagesManager.java",
+
+    # Todo(https://crbug.com/1176981): Remove cyclical dependancy with Translate bridge.
+    "java/src/org/chromium/chrome/browser/translate/TranslateBridge.java",
+  ]
+  deps = [
+    ":base_module_java",
+    ":java_resources",
+    "//base:base_java",
+    "//base:jni_java",
+    "//chrome/browser/flags:java",
+    "//chrome/browser/preferences:java",
+    "//chrome/browser/profiles/android:java",
+    "//chrome/browser/tab:java",
+    "//components/browser_ui/settings/android:java",
+    "//components/browser_ui/widget/android:java",
+    "//components/embedder_support/android:browser_context_java",
+    "//components/prefs/android:java",
+    "//components/user_prefs/android:java",
+    "//content/public/android:content_java",
+    "//third_party/android_deps:androidx_annotation_annotation_java",
+    "//third_party/android_deps:androidx_appcompat_appcompat_java",
+    "//third_party/android_deps:androidx_core_core_java",
+    "//third_party/android_deps:androidx_fragment_fragment_java",
+    "//third_party/android_deps:androidx_preference_preference_java",
+    "//third_party/android_deps:androidx_recyclerview_recyclerview_java",
+    "//ui/android:ui_full_java",
+    "//ui/android:ui_no_recycler_view_java",
+  ]
+  resources_package = "org.chromium.chrome.browser.language"
+  annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
+}
+
+generate_jni("jni_headers") {
+  sources =
+      [ "java/src/org/chromium/chrome/browser/translate/TranslateBridge.java" ]
+}
+android_resources("java_resources") {
+  sources = [
+    "java/res/layout/accept_languages_item.xml",
+    "java/res/layout/accept_languages_list.xml",
+    "java/res/layout/add_languages_main.xml",
+    "java/res/layout/languages_preference.xml",
+    "java/res/menu/languages_action_bar_menu.xml",
+  ]
+  deps = [
+    "//chrome/browser/ui/android/strings:ui_strings_grd",
+    "//components/browser_ui/settings/android:java_resources",
+    "//components/browser_ui/strings/android:browser_ui_strings_grd",
+    "//components/browser_ui/widget/android:java_resources",
+    "//ui/android:ui_java_resources",
+  ]
+}
diff --git a/chrome/android/java/res/layout/accept_languages_item.xml b/chrome/browser/language/android/java/res/layout/accept_languages_item.xml
similarity index 100%
rename from chrome/android/java/res/layout/accept_languages_item.xml
rename to chrome/browser/language/android/java/res/layout/accept_languages_item.xml
diff --git a/chrome/android/java/res/layout/accept_languages_list.xml b/chrome/browser/language/android/java/res/layout/accept_languages_list.xml
similarity index 100%
rename from chrome/android/java/res/layout/accept_languages_list.xml
rename to chrome/browser/language/android/java/res/layout/accept_languages_list.xml
diff --git a/chrome/android/java/res/layout/add_languages_main.xml b/chrome/browser/language/android/java/res/layout/add_languages_main.xml
similarity index 100%
rename from chrome/android/java/res/layout/add_languages_main.xml
rename to chrome/browser/language/android/java/res/layout/add_languages_main.xml
diff --git a/chrome/android/java/res/layout/languages_preference.xml b/chrome/browser/language/android/java/res/layout/languages_preference.xml
similarity index 100%
rename from chrome/android/java/res/layout/languages_preference.xml
rename to chrome/browser/language/android/java/res/layout/languages_preference.xml
diff --git a/chrome/android/java/res/menu/languages_action_bar_menu.xml b/chrome/browser/language/android/java/res/menu/languages_action_bar_menu.xml
similarity index 100%
rename from chrome/android/java/res/menu/languages_action_bar_menu.xml
rename to chrome/browser/language/android/java/res/menu/languages_action_bar_menu.xml
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/language/AppLocaleUtils.java b/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/AppLocaleUtils.java
similarity index 100%
rename from chrome/android/java/src/org/chromium/chrome/browser/language/AppLocaleUtils.java
rename to chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/AppLocaleUtils.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/language/GlobalAppLocaleController.java b/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/GlobalAppLocaleController.java
similarity index 100%
rename from chrome/android/java/src/org/chromium/chrome/browser/language/GlobalAppLocaleController.java
rename to chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/GlobalAppLocaleController.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/language/settings/AddLanguageFragment.java b/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/AddLanguageFragment.java
similarity index 99%
rename from chrome/android/java/src/org/chromium/chrome/browser/language/settings/AddLanguageFragment.java
rename to chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/AddLanguageFragment.java
index 380883f..6b17cbb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/language/settings/AddLanguageFragment.java
+++ b/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/AddLanguageFragment.java
@@ -23,8 +23,8 @@
 import androidx.recyclerview.widget.RecyclerView;
 import androidx.recyclerview.widget.RecyclerView.ViewHolder;
 
-import org.chromium.chrome.R;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.language.R;
 import org.chromium.components.browser_ui.settings.SettingsUtils;
 
 import java.util.ArrayList;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/language/settings/AvailableUiLanguages.java b/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/AvailableUiLanguages.java
similarity index 100%
rename from chrome/android/java/src/org/chromium/chrome/browser/language/settings/AvailableUiLanguages.java
rename to chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/AvailableUiLanguages.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/language/settings/LanguageItem.java b/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/LanguageItem.java
similarity index 98%
rename from chrome/android/java/src/org/chromium/chrome/browser/language/settings/LanguageItem.java
rename to chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/LanguageItem.java
index 890e8a01..12137cc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/language/settings/LanguageItem.java
+++ b/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/LanguageItem.java
@@ -7,9 +7,9 @@
 import android.text.TextUtils;
 
 import org.chromium.base.ContextUtils;
-import org.chromium.chrome.R;
 import org.chromium.chrome.browser.language.AppLocaleUtils;
 import org.chromium.chrome.browser.language.GlobalAppLocaleController;
+import org.chromium.chrome.browser.language.R;
 
 import java.util.Locale;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/language/settings/LanguageItemPickerPreference.java b/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/LanguageItemPickerPreference.java
similarity index 100%
rename from chrome/android/java/src/org/chromium/chrome/browser/language/settings/LanguageItemPickerPreference.java
rename to chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/LanguageItemPickerPreference.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/language/settings/LanguageListBaseAdapter.java b/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/LanguageListBaseAdapter.java
similarity index 99%
rename from chrome/android/java/src/org/chromium/chrome/browser/language/settings/LanguageListBaseAdapter.java
rename to chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/LanguageListBaseAdapter.java
index 4513fa0..77c3847 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/language/settings/LanguageListBaseAdapter.java
+++ b/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/LanguageListBaseAdapter.java
@@ -22,7 +22,7 @@
 import androidx.recyclerview.widget.RecyclerView;
 import androidx.recyclerview.widget.RecyclerView.ViewHolder;
 
-import org.chromium.chrome.R;
+import org.chromium.chrome.browser.language.R;
 import org.chromium.components.browser_ui.widget.dragreorder.DragReorderableListAdapter;
 import org.chromium.components.browser_ui.widget.dragreorder.DragStateDelegate;
 import org.chromium.components.browser_ui.widget.listmenu.ListMenuButton;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/language/settings/LanguageListPreference.java b/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/LanguageListPreference.java
similarity index 98%
rename from chrome/android/java/src/org/chromium/chrome/browser/language/settings/LanguageListPreference.java
rename to chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/LanguageListPreference.java
index b6694508..5f10c355 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/language/settings/LanguageListPreference.java
+++ b/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/LanguageListPreference.java
@@ -18,7 +18,7 @@
 import androidx.recyclerview.widget.RecyclerView;
 import androidx.recyclerview.widget.RecyclerView.ViewHolder;
 
-import org.chromium.chrome.R;
+import org.chromium.chrome.browser.language.R;
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.translate.TranslateBridge;
@@ -151,9 +151,7 @@
                 TintedDrawable.constructTintedDrawable(
                         getContext(), R.drawable.plus, R.color.default_control_color_active),
                 null, null, null);
-        mAddLanguageButton.setOnClickListener(view -> {
-            mLauncher.launchAddLanguage();
-        });
+        mAddLanguageButton.setOnClickListener(view -> { mLauncher.launchAddLanguage(); });
 
         mRecyclerView = (RecyclerView) holder.findViewById(R.id.language_list);
         LinearLayoutManager layoutMangager = new LinearLayoutManager(getContext());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/language/settings/LanguagesManager.java b/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/LanguagesManager.java
similarity index 100%
rename from chrome/android/java/src/org/chromium/chrome/browser/language/settings/LanguagesManager.java
rename to chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/LanguagesManager.java
diff --git a/chrome/browser/language/android/java/src/org/chromium/chrome/browser/translate/DEPS b/chrome/browser/language/android/java/src/org/chromium/chrome/browser/translate/DEPS
new file mode 100644
index 0000000..838459a9
--- /dev/null
+++ b/chrome/browser/language/android/java/src/org/chromium/chrome/browser/translate/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+content/public/android/java/src/org/chromium/content_public",
+]
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/translate/TranslateBridge.java b/chrome/browser/language/android/java/src/org/chromium/chrome/browser/translate/TranslateBridge.java
similarity index 100%
rename from chrome/android/java/src/org/chromium/chrome/browser/translate/TranslateBridge.java
rename to chrome/browser/language/android/java/src/org/chromium/chrome/browser/translate/TranslateBridge.java
diff --git a/chrome/browser/net/network_context_configuration_browsertest.cc b/chrome/browser/net/network_context_configuration_browsertest.cc
index bd1b26f..c0148de 100644
--- a/chrome/browser/net/network_context_configuration_browsertest.cc
+++ b/chrome/browser/net/network_context_configuration_browsertest.cc
@@ -684,70 +684,6 @@
     provider_.UpdateChromePolicy(policy_map);
   }
 
-  enum class WayToEnableSSLConfig { kViaPrefs, kViaPolicy };
-
-  // This helper function enables the kSSLVersionMin pref and tests that this
-  // pref is respected. kSSLVersionMin can be set in two ways: over prefs
-  // directly or over a policy. |way_to_enable| is used to determine the way to
-  // set the pref.
-  void TestEnablingSSLVersionMin(WayToEnableSSLConfig way_to_enable) {
-    // Start a TLS 1.0 server.
-    net::EmbeddedTestServer ssl_server(net::EmbeddedTestServer::TYPE_HTTPS);
-    net::SSLServerConfig ssl_config;
-    ssl_config.version_min = net::SSL_PROTOCOL_VERSION_TLS1;
-    ssl_config.version_max = net::SSL_PROTOCOL_VERSION_TLS1;
-    ssl_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK, ssl_config);
-    ssl_server.AddDefaultHandlers(GetChromeTestDataDir());
-    ASSERT_TRUE(ssl_server.Start());
-
-    std::unique_ptr<network::ResourceRequest> request =
-        std::make_unique<network::ResourceRequest>();
-    request->url = ssl_server.GetURL("/echo");
-    content::SimpleURLLoaderTestHelper simple_loader_helper;
-    std::unique_ptr<network::SimpleURLLoader> simple_loader =
-        network::SimpleURLLoader::Create(std::move(request),
-                                         TRAFFIC_ANNOTATION_FOR_TESTS);
-    simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
-        loader_factory(), simple_loader_helper.GetCallback());
-    simple_loader_helper.WaitForCallback();
-    ASSERT_TRUE(simple_loader_helper.response_body());
-    EXPECT_EQ(*simple_loader_helper.response_body(), "Echo");
-
-    if (way_to_enable == WayToEnableSSLConfig::kViaPrefs) {
-      // Disallow TLS 1.0 via prefs.
-      g_browser_process->local_state()->SetString(prefs::kSSLVersionMin,
-                                                  switches::kSSLVersionTLSv11);
-    } else {
-      // Disallow TLS 1.0 via policy.
-      policy::PolicyMap values;
-      values.Set(policy::key::kSSLVersionMin, policy::POLICY_LEVEL_MANDATORY,
-                 policy::POLICY_SCOPE_MACHINE, policy::POLICY_SOURCE_CLOUD,
-                 base::Value(switches::kSSLVersionTLSv11), nullptr);
-      base::RunLoop run_loop;
-      PrefChangeRegistrar pref_change_registrar;
-      pref_change_registrar.Init(g_browser_process->local_state());
-      pref_change_registrar.Add(prefs::kSSLVersionMin, run_loop.QuitClosure());
-      UpdateChromePolicy(values);
-      run_loop.Run();
-    }
-
-    g_browser_process->system_network_context_manager()
-        ->FlushSSLConfigManagerForTesting();
-
-    // With the new prefs, requests to the server should be blocked.
-    request = std::make_unique<network::ResourceRequest>();
-    request->url = ssl_server.GetURL("/echo");
-    content::SimpleURLLoaderTestHelper simple_loader_helper2;
-    simple_loader = network::SimpleURLLoader::Create(
-        std::move(request), TRAFFIC_ANNOTATION_FOR_TESTS);
-    simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
-        loader_factory(), simple_loader_helper2.GetCallback());
-    simple_loader_helper2.WaitForCallback();
-    EXPECT_FALSE(simple_loader_helper2.response_body());
-    EXPECT_EQ(net::ERR_SSL_VERSION_OR_CIPHER_MISMATCH,
-              simple_loader->NetError());
-  }
-
  private:
   void SimulateNetworkServiceCrashIfNecessary() {
     if (GetParam().network_service_state != NetworkServiceState::kRestarted ||
@@ -1312,52 +1248,6 @@
   }
 }
 
-// Check that the SSLConfig is hooked up. PRE_SSLConfig checks that changing
-// local_state() after start modifies the SSLConfig, SSLConfig makes sure the
-// (now modified) initial value of local_state() is respected.
-IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest, PRE_SSLConfig) {
-  if (IsRestartStateWithInProcessNetworkService())
-    return;
-  TestEnablingSSLVersionMin(WayToEnableSSLConfig::kViaPrefs);
-}
-
-IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest, SSLConfig) {
-  if (IsRestartStateWithInProcessNetworkService())
-    return;
-  // Start a TLS 1.0 server.
-  net::EmbeddedTestServer ssl_server(net::EmbeddedTestServer::TYPE_HTTPS);
-  net::SSLServerConfig ssl_config;
-  ssl_config.version_min = net::SSL_PROTOCOL_VERSION_TLS1;
-  ssl_config.version_max = net::SSL_PROTOCOL_VERSION_TLS1;
-  ssl_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK, ssl_config);
-  ASSERT_TRUE(ssl_server.Start());
-
-  // Making a request should fail, since PRE_SSLConfig saved a pref to disallow
-  // TLS 1.0.
-  std::unique_ptr<network::ResourceRequest> request =
-      std::make_unique<network::ResourceRequest>();
-  request->url = ssl_server.GetURL("/echo");
-  content::SimpleURLLoaderTestHelper simple_loader_helper;
-  std::unique_ptr<network::SimpleURLLoader> simple_loader =
-      network::SimpleURLLoader::Create(std::move(request),
-                                       TRAFFIC_ANNOTATION_FOR_TESTS);
-  simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
-      loader_factory(), simple_loader_helper.GetCallback());
-  simple_loader_helper.WaitForCallback();
-  EXPECT_FALSE(simple_loader_helper.response_body());
-  EXPECT_EQ(net::ERR_SSL_VERSION_OR_CIPHER_MISMATCH, simple_loader->NetError());
-}
-
-// This test does the same as
-// 'NetworkContextConfigurationBrowserTest.PRE_SSLConfig' but with the
-// difference that the SSLVersionMin is set via a policy (not via the prefs).
-IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest,
-                       SSLVersionMinSetViaPolicy) {
-  if (IsRestartStateWithInProcessNetworkService())
-    return;
-  TestEnablingSSLVersionMin(WayToEnableSSLConfig::kViaPolicy);
-}
-
 IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest, ProxyConfig) {
   if (IsRestartStateWithInProcessNetworkService())
     return;
diff --git a/chrome/browser/net/system_network_context_manager.cc b/chrome/browser/net/system_network_context_manager.cc
index b3fad2b..14a4c159 100644
--- a/chrome/browser/net/system_network_context_manager.cc
+++ b/chrome/browser/net/system_network_context_manager.cc
@@ -23,7 +23,6 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/component_updater/crl_set_component_installer.h"
 #include "chrome/browser/component_updater/first_party_sets_component_installer.h"
-#include "chrome/browser/component_updater/tls_deprecation_config_component_installer.h"
 #include "chrome/browser/net/chrome_mojo_proxy_resolver_factory.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/browser/ssl/sct_reporting_service.h"
@@ -539,10 +538,6 @@
   // Asynchronously reapply the most recently received CRLSet (if any).
   component_updater::CRLSetPolicy::ReconfigureAfterNetworkRestart();
 
-  // Asynchronously reapply the most recently received TLS deprecation config.
-  component_updater::TLSDeprecationConfigComponentInstallerPolicy::
-      ReconfigureAfterNetworkRestart();
-
   // Configure SCT Auditing in the NetworkService.
   SCTReportingService::ReconfigureAfterNetworkRestart();
 
diff --git a/chrome/browser/net/websocket_browsertest.cc b/chrome/browser/net/websocket_browsertest.cc
index 741d9fe..7d53713 100644
--- a/chrome/browser/net/websocket_browsertest.cc
+++ b/chrome/browser/net/websocket_browsertest.cc
@@ -18,6 +18,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind.h"
+#include "build/build_config.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/login/login_handler.h"
@@ -52,6 +53,10 @@
 #include "url/gurl.h"
 #include "url/origin.h"
 
+#if defined(OS_MAC)
+#include "base/mac/mac_util.h"
+#endif
+
 namespace {
 
 class WebSocketBrowserTest : public InProcessBrowserTest {
@@ -218,6 +223,13 @@
 }
 
 IN_PROC_BROWSER_TEST_F(WebSocketBrowserTest, SecureWebSocketSplitRecords) {
+  // TODO(crbug.com/1176880): Return early on macOS 10.11, because the
+  // WSS SpawnedTestServer does not support modern TLS on the 10.11 bot.
+#if defined(OS_MAC)
+  if (base::mac::IsAtMostOS10_11())
+    return;
+#endif
+
   // Launch a secure WebSocket server.
   ASSERT_TRUE(wss_server_.Start());
 
@@ -274,6 +286,13 @@
 }
 
 IN_PROC_BROWSER_TEST_F(WebSocketBrowserTest, WebSocketBasicAuthInHTTPSURL) {
+  // TODO(crbug.com/1176880): Return early on macOS 10.11, because the
+  // WSS SpawnedTestServer does not support modern TLS on the 10.11 bot.
+#if defined(OS_MAC)
+  if (base::mac::IsAtMostOS10_11())
+    return;
+#endif
+
   // Launch a basic-auth-protected secure WebSocket server.
   wss_server_.set_websocket_basic_auth(true);
   ASSERT_TRUE(wss_server_.Start());
@@ -350,6 +369,13 @@
 // for secure connections here because the unencrypted case is tested in the
 // Blink layout tests, and browser tests are expensive to run.
 IN_PROC_BROWSER_TEST_F(WebSocketBrowserTest, SSLConnectionLimit) {
+  // TODO(crbug.com/1176880): Return early on macOS 10.11, because the
+  // WSS SpawnedTestServer does not support modern TLS on the 10.11 bot.
+#if defined(OS_MAC)
+  if (base::mac::IsAtMostOS10_11())
+    return;
+#endif
+
   ASSERT_TRUE(wss_server_.Start());
 
   NavigateToHTTPS("multiple-connections.html");
@@ -359,6 +385,13 @@
 
 // Regression test for crbug.com/903553005
 IN_PROC_BROWSER_TEST_F(WebSocketBrowserTest, WebSocketAppliesHSTS) {
+  // TODO(crbug.com/1176880): Return early on macOS 10.11, because the
+  // WSS SpawnedTestServer does not support modern TLS on the 10.11 bot.
+#if defined(OS_MAC)
+  if (base::mac::IsAtMostOS10_11())
+    return;
+#endif
+
   net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
   https_server.SetSSLConfig(
       net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN);
diff --git a/chrome/browser/notifications/notification_interactive_uitest.cc b/chrome/browser/notifications/notification_interactive_uitest.cc
index babb0e4..0da719e2 100644
--- a/chrome/browser/notifications/notification_interactive_uitest.cc
+++ b/chrome/browser/notifications/notification_interactive_uitest.cc
@@ -433,8 +433,10 @@
   EXPECT_EQ(
       *ukm_recorder.GetEntryMetric(entry, "Source"),
       static_cast<int64_t>(permissions::PermissionSourceUI::INLINE_SETTINGS));
+  size_t num_values = 0;
   EXPECT_EQ(*ukm_recorder.GetEntryMetric(entry, "PermissionType"),
-            static_cast<int64_t>(ContentSettingsType::NOTIFICATIONS));
+            ContentSettingTypeToHistogramValue(
+                ContentSettingsType::NOTIFICATIONS, &num_values));
   EXPECT_EQ(*ukm_recorder.GetEntryMetric(entry, "Action"),
             static_cast<int64_t>(permissions::PermissionAction::REVOKED));
 }
diff --git a/chrome/browser/optimization_guide/page_text_observer_browsertest.cc b/chrome/browser/optimization_guide/page_text_observer_browsertest.cc
new file mode 100644
index 0000000..e4495fc
--- /dev/null
+++ b/chrome/browser/optimization_guide/page_text_observer_browsertest.cc
@@ -0,0 +1,164 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/optimization_guide/content/browser/page_text_observer.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/optional.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/optimization_guide/content/mojom/page_text_service.mojom.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_base.h"
+#include "content/public/test/browser_test_utils.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+
+namespace optimization_guide {
+
+class TestConsumer : public PageTextObserver::Consumer {
+ public:
+  TestConsumer() = default;
+  ~TestConsumer() = default;
+
+  void Reset() { was_called_ = false; }
+
+  void PopulateRequest(uint64_t max_size,
+                       const std::set<mojom::TextDumpEvent>& events) {
+    request_ = std::make_unique<PageTextObserver::ConsumerTextDumpRequest>();
+    request_->max_size = max_size;
+    request_->events = events;
+    request_->callback = base::BindRepeating(&TestConsumer::OnGotTextDump,
+                                             base::Unretained(this));
+  }
+
+  void WaitForPageText() {
+    if (text_) {
+      return;
+    }
+
+    base::RunLoop run_loop;
+    on_page_text_closure_ = run_loop.QuitClosure();
+    run_loop.Run();
+  }
+
+  bool was_called() const { return was_called_; }
+
+  const base::Optional<base::string16>& text() const { return text_; }
+
+  // PageTextObserver::Consumer:
+  std::unique_ptr<PageTextObserver::ConsumerTextDumpRequest>
+  MaybeRequestFrameTextDump(content::NavigationHandle* handle) override {
+    was_called_ = true;
+    return std::move(request_);
+  }
+
+ private:
+  void OnGotTextDump(const base::string16& text) {
+    text_ = text;
+    if (on_page_text_closure_) {
+      std::move(on_page_text_closure_).Run();
+    }
+  }
+
+  bool was_called_ = false;
+
+  std::unique_ptr<PageTextObserver::ConsumerTextDumpRequest> request_;
+
+  base::OnceClosure on_page_text_closure_;
+
+  base::Optional<base::string16> text_;
+};
+
+// This tests code in
+// //components/optimization_guide/content/browser/page_text_observer.h, but
+// this test is in //chrome because the components browsertests do not fully
+// standup a renderer process.
+
+class PageTextObserverBrowserTest : public InProcessBrowserTest {
+ public:
+  PageTextObserverBrowserTest() = default;
+  ~PageTextObserverBrowserTest() override = default;
+
+  void SetUpOnMainThread() override {
+    host_resolver()->AddRule("*", "127.0.0.1");
+    InProcessBrowserTest::SetUpOnMainThread();
+  }
+
+  content::WebContents* web_contents() {
+    return browser()->tab_strip_model()->GetActiveWebContents();
+  }
+
+  PageTextObserver* observer() {
+    return PageTextObserver::FromWebContents(web_contents());
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(PageTextObserverBrowserTest, SimpleCase) {
+  embedded_test_server()->ServeFilesFromSourceDirectory(
+      "chrome/test/data/optimization_guide");
+  ASSERT_TRUE(embedded_test_server()->Start());
+  PageTextObserver::CreateForWebContents(web_contents());
+  ASSERT_TRUE(observer());
+
+  TestConsumer consumer;
+  observer()->AddConsumer(&consumer);
+  consumer.PopulateRequest(/*max_size=*/1024,
+                           /*events=*/{mojom::TextDumpEvent::kFirstLayout});
+
+  GURL url(embedded_test_server()->GetURL("a.com", "/hello.html"));
+  ui_test_utils::NavigateToURL(browser(), url);
+  ASSERT_TRUE(consumer.was_called());
+
+  consumer.WaitForPageText();
+  ASSERT_TRUE(consumer.text());
+  EXPECT_EQ(base::ASCIIToUTF16("hello"), *consumer.text());
+}
+
+IN_PROC_BROWSER_TEST_F(PageTextObserverBrowserTest, FirstLayoutAndOnLoad) {
+  embedded_test_server()->ServeFilesFromSourceDirectory(
+      "chrome/test/data/optimization_guide");
+  ASSERT_TRUE(embedded_test_server()->Start());
+  PageTextObserver::CreateForWebContents(web_contents());
+  ASSERT_TRUE(observer());
+
+  TestConsumer first_layout_consumer;
+  observer()->AddConsumer(&first_layout_consumer);
+  first_layout_consumer.PopulateRequest(
+      /*max_size=*/1024,
+      /*events=*/{mojom::TextDumpEvent::kFirstLayout});
+
+  TestConsumer on_load_consumer;
+  observer()->AddConsumer(&on_load_consumer);
+  on_load_consumer.PopulateRequest(
+      /*max_size=*/1024,
+      /*events=*/{mojom::TextDumpEvent::kFinishedLoad});
+
+  GURL url(embedded_test_server()->GetURL("a.com", "/hello_world.html"));
+  ui_test_utils::NavigateToURL(browser(), url);
+
+  ASSERT_TRUE(first_layout_consumer.was_called());
+  ASSERT_TRUE(on_load_consumer.was_called());
+
+  first_layout_consumer.WaitForPageText();
+  on_load_consumer.WaitForPageText();
+
+  ASSERT_TRUE(first_layout_consumer.text());
+  // This check can be a bit flaky on some platforms. Check that "hello" is
+  // present, since the text captured may already be "hello world".
+  EXPECT_TRUE(base::Contains(*first_layout_consumer.text(),
+                             base::ASCIIToUTF16("hello")));
+
+  ASSERT_TRUE(on_load_consumer.text());
+  EXPECT_EQ(base::ASCIIToUTF16("hello\n\nworld"), *on_load_consumer.text());
+}
+
+}  // namespace optimization_guide
diff --git a/chrome/browser/page_load_metrics/observers/security_state_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/security_state_page_load_metrics_observer_unittest.cc
index 3ea6572..e4cc07e 100644
--- a/chrome/browser/page_load_metrics/observers/security_state_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/security_state_page_load_metrics_observer_unittest.cc
@@ -9,7 +9,6 @@
 #include "base/test/simple_test_tick_clock.h"
 #include "chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h"
 #include "chrome/browser/ssl/security_state_tab_helper.h"
-#include "chrome/browser/ssl/tls_deprecation_config.h"
 #include "chrome/browser/ssl/tls_deprecation_test_utils.h"
 #include "components/page_load_metrics/browser/page_load_tracker.h"
 #include "content/public/browser/web_contents.h"
@@ -57,8 +56,6 @@
 };
 
 TEST_F(SecurityStatePageLoadMetricsObserverTest, LegacyTLSMetrics) {
-  InitializeEmptyLegacyTLSConfig();
-
   auto navigation =
       CreateLegacyTLSNavigation(GURL(kLegacyTLSURL), web_contents());
   navigation->Commit();
@@ -82,35 +79,7 @@
   EXPECT_LE(kMinForegroundTime.InMilliseconds(), samples.front().min);
 }
 
-TEST_F(SecurityStatePageLoadMetricsObserverTest, LegacyTLSControlSiteMetrics) {
-  InitializeLegacyTLSConfigWithControl();
-
-  auto navigation =
-      CreateLegacyTLSNavigation(GURL(kLegacyTLSURL), web_contents());
-  navigation->Commit();
-
-  tester()->histogram_tester().ExpectBucketCount("Security.LegacyTLS.OnCommit",
-                                                 false, 1);
-
-  const base::TimeDelta kMinForegroundTime =
-      base::TimeDelta::FromMilliseconds(10);
-  AdvancePageDuration(kMinForegroundTime);
-
-  navigation->Reload(web_contents());
-
-  tester()->histogram_tester().ExpectBucketCount(
-      "Security.PageEndReason.LegacyTLS_NotTriggered",
-      page_load_metrics::END_RELOAD, 1);
-
-  auto samples = tester()->histogram_tester().GetAllSamples(
-      "Security.TimeOnPage2.LegacyTLS_NotTriggered");
-  EXPECT_EQ(1u, samples.size());
-  EXPECT_LE(kMinForegroundTime.InMilliseconds(), samples.front().min);
-}
-
 TEST_F(SecurityStatePageLoadMetricsObserverTest, LegacyTLSGoodSiteMetrics) {
-  InitializeEmptyLegacyTLSConfig();
-
   auto navigation =
       CreateNonlegacyTLSNavigation(GURL("https://good.test"), web_contents());
   navigation->Commit();
diff --git a/chrome/browser/performance_manager/policies/working_set_trimmer_policy_chromeos.cc b/chrome/browser/performance_manager/policies/working_set_trimmer_policy_chromeos.cc
index c50a524c..8dd8cc5b 100644
--- a/chrome/browser/performance_manager/policies/working_set_trimmer_policy_chromeos.cc
+++ b/chrome/browser/performance_manager/policies/working_set_trimmer_policy_chromeos.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/performance_manager/policies/working_set_trimmer_policy_chromeos.h"
 
 #include "base/bind.h"
+#include "base/location.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/rand_util.h"
 #include "chrome/browser/chromeos/arc/arc_util.h"
@@ -19,11 +20,49 @@
 #include "components/performance_manager/public/graph/frame_node.h"
 #include "components/performance_manager/public/graph/graph.h"
 #include "components/performance_manager/public/graph/page_node.h"
+#include "components/performance_manager/public/performance_manager.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
 #include "url/gurl.h"
 
 namespace performance_manager {
 namespace policies {
 
+namespace {
+enum ArcProcessType { kApp, kSystem };
+void GetArcProcessListOnUIThread(
+    ArcProcessType type,
+    base::WeakPtr<
+        performance_manager::policies::WorkingSetTrimmerPolicyChromeOS> ptr,
+    int processes_per_trim) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  arc::ArcProcessService* arc_process_service = arc::ArcProcessService::Get();
+  if (!arc_process_service) {
+    return;
+  }
+
+  // Now we need to bounce back to the PM sequence so we can do stuff with the
+  // process list.
+  auto callback = base::BindOnce(
+      [](decltype(ptr) ptr, decltype(processes_per_trim) processes_per_trim,
+         arc::ArcProcessService::OptionalArcProcessList opt_proc_list) {
+        PerformanceManager::CallOnGraph(
+            FROM_HERE,
+            base::BindOnce(
+                &WorkingSetTrimmerPolicyChromeOS::TrimReceivedArcProcesses, ptr,
+                processes_per_trim, std::move(opt_proc_list)));
+      },
+      ptr, processes_per_trim);
+
+  if (type == kApp) {
+    arc_process_service->RequestAppProcessList(std::move(callback));
+  } else if (type == kSystem) {
+    arc_process_service->RequestSystemProcessList(std::move(callback));
+  }
+}
+
+}  // namespace
+
 WorkingSetTrimmerPolicyChromeOS::WorkingSetTrimmerPolicyChromeOS() {
   trim_on_memory_pressure_ =
       base::FeatureList::IsEnabled(features::kTrimOnMemoryPressure);
@@ -201,26 +240,27 @@
   }
 }
 
+// TrimArcProcesses will be called on the PM Sequence, we'll need to bounce to
+// the UI thread to get the Arc process list and we'll bounce back to the PM
+// sequence to do the actual trimming and book keeping.
 void WorkingSetTrimmerPolicyChromeOS::TrimArcProcesses() {
   last_arc_process_fetch_ = base::TimeTicks::Now();
 
-  arc::ArcProcessService* arc_process_service = arc::ArcProcessService::Get();
-  if (!arc_process_service) {
-    return;
+  // The fetching of the ARC process list must happen on the UI thread.
+  if (params_.trim_arc_system_processes) {
+    content::GetUIThreadTaskRunner({})->PostTask(
+        FROM_HERE,
+        base::BindOnce(&GetArcProcessListOnUIThread, ArcProcessType::kSystem,
+                       weak_ptr_factory_.GetWeakPtr(),
+                       params_.arc_max_number_processes_per_trim));
   }
 
   if (params_.trim_arc_app_processes) {
-    arc_process_service->RequestAppProcessList(base::BindOnce(
-        &WorkingSetTrimmerPolicyChromeOS::TrimReceivedArcProcesses,
-        weak_ptr_factory_.GetWeakPtr(),
-        params_.arc_max_number_processes_per_trim));
-  }
-
-  if (params_.trim_arc_system_processes) {
-    arc_process_service->RequestSystemProcessList(base::BindOnce(
-        &WorkingSetTrimmerPolicyChromeOS::TrimReceivedArcProcesses,
-        weak_ptr_factory_.GetWeakPtr(),
-        params_.arc_max_number_processes_per_trim));
+    content::GetUIThreadTaskRunner({})->PostTask(
+        FROM_HERE,
+        base::BindOnce(&GetArcProcessListOnUIThread, ArcProcessType::kApp,
+                       weak_ptr_factory_.GetWeakPtr(),
+                       params_.arc_max_number_processes_per_trim));
   }
 }
 
diff --git a/chrome/browser/performance_manager/policies/working_set_trimmer_policy_chromeos.h b/chrome/browser/performance_manager/policies/working_set_trimmer_policy_chromeos.h
index 17eadcf6..40ca73c 100644
--- a/chrome/browser/performance_manager/policies/working_set_trimmer_policy_chromeos.h
+++ b/chrome/browser/performance_manager/policies/working_set_trimmer_policy_chromeos.h
@@ -55,6 +55,10 @@
     return trim_arc_on_memory_pressure_;
   }
 
+  virtual void TrimReceivedArcProcesses(
+      int allowed_to_trim,
+      arc::ArcProcessService::OptionalArcProcessList arc_processes);
+
  protected:
   friend class WorkingSetTrimmerPolicyChromeOSTest;
 
@@ -75,9 +79,6 @@
   // TrimArcProcesses will walk procfs looking for ARC container processes which
   // can be trimmed. These are virtual for testing.
   virtual void TrimArcProcesses();
-  virtual void TrimReceivedArcProcesses(
-      int allowed_to_trim,
-      arc::ArcProcessService::OptionalArcProcessList arc_processes);
   virtual bool IsArcProcessEligibleForReclaim(
       const arc::ArcProcess& arc_process);
   virtual bool TrimArcProcess(base::ProcessId pid);
diff --git a/chrome/browser/prefetch/no_state_prefetch/prerender_test_utils.cc b/chrome/browser/prefetch/no_state_prefetch/prerender_test_utils.cc
index 567b7a4..3200315 100644
--- a/chrome/browser/prefetch/no_state_prefetch/prerender_test_utils.cc
+++ b/chrome/browser/prefetch/no_state_prefetch/prerender_test_utils.cc
@@ -183,16 +183,19 @@
   return true;
 }
 
-void TestNoStatePrefetchContents::RenderFrameCreated(
-    content::RenderFrameHost* frame_host) {
-  if (!frame_host->GetParent()) {
+void TestNoStatePrefetchContents::RenderFrameHostChanged(
+    content::RenderFrameHost* old_frame_host,
+    content::RenderFrameHost* new_frame_host) {
+  // Watch for the speculative main frame being committed.
+  if (!new_frame_host->GetParent()) {
     // Used to make sure the main frame widget is hidden and, if used,
     // subsequently shown.
-    observer_.Add(frame_host->GetRenderWidgetHost());
-    new_main_frame_ = frame_host;
+    observer_.Add(new_frame_host->GetRenderWidgetHost());
+    new_main_frame_ = new_frame_host;
   }
 
-  NoStatePrefetchContents::RenderFrameCreated(frame_host);
+  NoStatePrefetchContents::RenderFrameHostChanged(old_frame_host,
+                                                  new_frame_host);
 }
 
 void TestNoStatePrefetchContents::RenderWidgetHostVisibilityChanged(
diff --git a/chrome/browser/prefetch/no_state_prefetch/prerender_test_utils.h b/chrome/browser/prefetch/no_state_prefetch/prerender_test_utils.h
index 2d4c750e..871cb3e4 100644
--- a/chrome/browser/prefetch/no_state_prefetch/prerender_test_utils.h
+++ b/chrome/browser/prefetch/no_state_prefetch/prerender_test_utils.h
@@ -80,7 +80,6 @@
   DISALLOW_COPY_AND_ASSIGN(FakeSafeBrowsingDatabaseManager);
 };
 
-// NoStatePrefetchContents that stops the UI message loop on DidStopLoading().
 class TestNoStatePrefetchContents : public NoStatePrefetchContents,
                                     public content::RenderWidgetHostObserver {
  public:
@@ -110,7 +109,9 @@
 
  private:
   // WebContentsObserver overrides.
-  void RenderFrameCreated(content::RenderFrameHost* frame_host) override;
+  void RenderFrameHostChanged(
+      content::RenderFrameHost* old_frame_host,
+      content::RenderFrameHost* new_frame_host) override;
 
   // RenderWidgetHostObserver overrides.
   void RenderWidgetHostVisibilityChanged(content::RenderWidgetHost* widget_host,
diff --git a/chrome/browser/prefetch/search_prefetch/streaming_search_prefetch_url_loader.cc b/chrome/browser/prefetch/search_prefetch/streaming_search_prefetch_url_loader.cc
index 4c9d581..012a2ea 100644
--- a/chrome/browser/prefetch/search_prefetch/streaming_search_prefetch_url_loader.cc
+++ b/chrome/browser/prefetch/search_prefetch/streaming_search_prefetch_url_loader.cc
@@ -185,6 +185,11 @@
 
 void StreamingSearchPrefetchURLLoader::OnDataComplete() {
   drain_complete_ = true;
+
+  // Disconnect if all content is served.
+  if (bytes_of_raw_data_to_transfer_ - write_position_ == 0) {
+    Finish();
+  }
 }
 
 void StreamingSearchPrefetchURLLoader::OnStartLoadingResponseBodyFromData() {
diff --git a/chrome/browser/resources/chromeos/BUILD.gn b/chrome/browser/resources/chromeos/BUILD.gn
index cea0b925..a620e72 100644
--- a/chrome/browser/resources/chromeos/BUILD.gn
+++ b/chrome/browser/resources/chromeos/BUILD.gn
@@ -9,7 +9,9 @@
 assert(is_chromeos, "Only Chrome OS resources in //c/b/resources//chromeos.")
 
 grit("multidevice_setup_resources") {
-  source = "multidevice_setup/multidevice_setup_resources.grd"
+  multidevice_setup_gen_dir =
+      "$root_gen_dir/chrome/browser/resources/chromeos/multidevice_setup"
+  source = "$multidevice_setup_gen_dir/multidevice_setup_resources.grd"
 
   defines = chrome_grit_defines
   outputs = [
@@ -18,6 +20,8 @@
     "grit/multidevice_setup_resources_map.h",
     "multidevice_setup_resources.pak",
   ]
+  deps = [ "//chrome/browser/resources/chromeos/multidevice_setup:build_grd" ]
+  enable_input_discovery_for_gn_analyze = false
   output_dir = "$root_gen_dir/chrome"
 }
 
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
index 3e5dd5d..7ca539f 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
@@ -355,6 +355,11 @@
     }
   }
 
+  /** @override */
+  readNextClipboardDataChange() {
+    this.lastClipboardEvent_ = 'copy';
+  }
+
   /**
    * Processes the copy clipboard event.
    * @param {!Event} evt
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 e57e28f..1a9ee729 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/chromevox_state.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/chromevox_state.js
@@ -169,6 +169,11 @@
   destroyUserActionMonitor() {
     this.userActionMonitor_ = null;
   },
+
+  /**
+   * Forces the reading of the next change to the clipboard.
+   */
+  readNextClipboardDataChange: goog.abstractMethod,
 };
 
 /** @type {!Array<ChromeVoxStateObserver>} */
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 f6cb8de..cd076deb 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js
@@ -322,6 +322,13 @@
     case 'resetTextToSpeechSettings':
       ChromeVox.tts.resetTextToSpeechSettings();
       return false;
+    case 'copy':
+      EventGenerator.sendKeyPress(KeyCode.C, {ctrl: true});
+
+      // The above command doesn't trigger document clipboard events, so we need
+      // to set this manually.
+      ChromeVoxState.instance.readNextClipboardDataChange();
+      return false;
   }
 
   // Require a current range.
@@ -1454,8 +1461,7 @@
 
     if (request.openTutorial) {
       let launchTutorial = function(desktop, evt) {
-        desktop.removeEventListener(
-            chrome.automation.EventType.FOCUS, launchTutorial, true);
+        desktop.removeEventListener(EventType.FOCUS, launchTutorial, true);
         CommandHandler.onCommand('help');
       };
 
@@ -1464,8 +1470,7 @@
       // show our tutorial.
       chrome.automation.getDesktop(function(desktop) {
         launchTutorial = launchTutorial.bind(this, desktop);
-        desktop.addEventListener(
-            chrome.automation.EventType.FOCUS, launchTutorial, true);
+        desktop.addEventListener(EventType.FOCUS, launchTutorial, true);
       });
     }
   });
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/keymaps/key_map.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/keymaps/key_map.js
index 7b9828b..c23ddcf 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/keymaps/key_map.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/keymaps/key_map.js
@@ -22,6 +22,8 @@
 
 goog.provide('KeyMap');
 
+goog.require('KeyCode');
+
 // TODO(dtseng): Only needed for sticky mode.
 goog.require('KeyUtil');
 
@@ -220,542 +222,555 @@
 /** @private {!Object} */
 KeyMap.BINDINGS_ = [
   {
-    'command': 'previousObject',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [37]}}
+    command: 'previousObject',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.LEFT]}}
   },
   {
-    'command': 'previousLine',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [38]}}
+    command: 'previousLine',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.UP]}}
   },
   {
-    'command': 'nextObject',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [39]}}
+    command: 'nextObject',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.RIGHT]}}
   },
   {
-    'command': 'nextLine',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [40]}}
+    command: 'nextLine',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.DOWN]}}
   },
   {
-    'command': 'nextCharacter',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [39], 'shiftKey': [true]}}
+    command: 'nextCharacter',
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.RIGHT], shiftKey: [true]}}
   },
   {
-    'command': 'previousCharacter',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [37], 'shiftKey': [true]}}
+    command: 'previousCharacter',
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.LEFT], shiftKey: [true]}}
   },
   {
-    'command': 'nextWord',
-    'sequence': {
-      'cvoxModifier': true,
-      'keys': {'keyCode': [39], 'ctrlKey': [true], 'shiftKey': [true]}
+    command: 'nextWord',
+    sequence: {
+      cvoxModifier: true,
+      keys: {keyCode: [KeyCode.RIGHT], ctrlKey: [true], shiftKey: [true]}
     }
   },
   {
-    'command': 'previousWord',
-    'sequence': {
-      'cvoxModifier': true,
-      'keys': {'keyCode': [37], 'ctrlKey': [true], 'shiftKey': [true]}
+    command: 'previousWord',
+    sequence: {
+      cvoxModifier: true,
+      keys: {keyCode: [KeyCode.LEFT], ctrlKey: [true], shiftKey: [true]}
     }
   },
   {
-    'command': 'nextButton',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [66]}}
+    command: 'nextButton',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.B]}}
   },
   {
-    'command': 'previousButton',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [66], 'shiftKey': [true]}}
+    command: 'previousButton',
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.B], shiftKey: [true]}}
   },
   {
-    'command': 'nextCheckbox',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [88]}}
+    command: 'nextCheckbox',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.X]}}
   },
   {
-    'command': 'previousCheckbox',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [88], 'shiftKey': [true]}}
+    command: 'previousCheckbox',
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.X], shiftKey: [true]}}
   },
   {
-    'command': 'nextComboBox',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [67]}}
+    command: 'nextComboBox',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.C]}}
   },
   {
-    'command': 'previousComboBox',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [67], 'shiftKey': [true]}}
+    command: 'previousComboBox',
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.C], shiftKey: [true]}}
   },
   {
-    'command': 'nextEditText',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [69]}}
+    command: 'nextEditText',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.E]}}
   },
   {
-    'command': 'previousEditText',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [69], 'shiftKey': [true]}}
+    command: 'previousEditText',
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.E], shiftKey: [true]}}
   },
   {
-    'command': 'nextFormField',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [70]}}
+    command: 'nextFormField',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.F]}}
   },
   {
-    'command': 'previousFormField',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [70], 'shiftKey': [true]}}
+    command: 'previousFormField',
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.F], shiftKey: [true]}}
   },
   {
-    'command': 'previousGraphic',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [71], 'shiftKey': [true]}}
+    command: 'previousGraphic',
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.G], shiftKey: [true]}}
   },
   {
-    'command': 'nextGraphic',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [71]}}
+    command: 'nextGraphic',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.G]}}
   },
   {
-    'command': 'nextHeading',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [72]}}
+    command: 'nextHeading',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.H]}}
   },
   {
-    'command': 'nextHeading1',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [49]}}
+    command: 'nextHeading1',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.ONE]}}
   },
   {
-    'command': 'nextHeading2',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [50]}}
+    command: 'nextHeading2',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.TWO]}}
   },
   {
-    'command': 'nextHeading3',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [51]}}
+    command: 'nextHeading3',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.THREE]}}
   },
   {
-    'command': 'nextHeading4',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [52]}}
+    command: 'nextHeading4',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.FOUR]}}
   },
   {
-    'command': 'nextHeading5',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [53]}}
+    command: 'nextHeading5',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.FIVE]}}
   },
   {
-    'command': 'nextHeading6',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [54]}}
+    command: 'nextHeading6',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.SIX]}}
   },
   {
-    'command': 'previousHeading',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [72], 'shiftKey': [true]}}
+    command: 'previousHeading',
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.H], shiftKey: [true]}}
   },
   {
-    'command': 'previousHeading1',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [49], 'shiftKey': [true]}}
+    command: 'previousHeading1',
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.ONE], shiftKey: [true]}}
   },
   {
-    'command': 'previousHeading2',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [50], 'shiftKey': [true]}}
+    command: 'previousHeading2',
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.TWO], shiftKey: [true]}}
   },
   {
-    'command': 'previousHeading3',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [51], 'shiftKey': [true]}}
+    command: 'previousHeading3',
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.THREE], shiftKey: [true]}}
   },
   {
-    'command': 'previousHeading4',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [52], 'shiftKey': [true]}}
+    command: 'previousHeading4',
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.FOUR], shiftKey: [true]}}
   },
   {
-    'command': 'previousHeading5',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [53], 'shiftKey': [true]}}
+    command: 'previousHeading5',
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.FIVE], shiftKey: [true]}}
   },
   {
-    'command': 'previousHeading6',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [54], 'shiftKey': [true]}}
+    command: 'previousHeading6',
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.SIX], shiftKey: [true]}}
   },
   {
-    'command': 'nextLink',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [76]}}
+    command: 'nextLink',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.L]}}
   },
   {
-    'command': 'previousLink',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [76], 'shiftKey': [true]}}
+    command: 'previousLink',
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.L], shiftKey: [true]}}
   },
   {
-    'command': 'nextTable',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [84]}}
+    command: 'nextTable',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.T]}}
   },
   {
-    'command': 'previousTable',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [84], 'shiftKey': [true]}}
+    command: 'previousTable',
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.T], shiftKey: [true]}}
   },
   {
-    'command': 'nextVisitedLink',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [86]}}
+    command: 'nextVisitedLink',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.V]}}
   },
   {
-    'command': 'previousVisitedLink',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [86], 'shiftKey': [true]}}
+    command: 'previousVisitedLink',
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.V], shiftKey: [true]}}
   },
   {
-    'command': 'nextLandmark',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [186]}}
+    command: 'nextLandmark',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.OEM_1]}}
   },
   {
-    'command': 'previousLandmark',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [186], 'shiftKey': [true]}}
+    command: 'previousLandmark',
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.OEM_1], shiftKey: [true]}}
   },
   {
-    'command': 'jumpToBottom',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [39], 'ctrlKey': [true]}}
-  },
-  {
-    'command': 'jumpToTop',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [37], 'ctrlKey': [true]}}
-  },
-  {
-    'command': 'forceClickOnCurrentItem',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [32]}}
-  },
-  {
-    'command': 'contextMenu',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [77]}}
-  },
-  {
-    'command': 'readFromHere',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [82]}}
-  },
-  {
-    'command': 'toggleStickyMode',
-    'sequence':
-        {'skipStripping': false, 'doubleTap': true, 'keys': {'keyCode': [91]}}
-  },
-  {
-    'command': 'passThroughMode',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [27], 'shiftKey': [true]}}
-  },
-  {
-    'command': 'toggleKeyboardHelp',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [190]}}
-  },
-  {
-    'command': 'stopSpeech',
-    'sequence':
-        {'cvoxModifier': false, 'keys': {'ctrlKey': [true], 'keyCode': [17]}}
-  },
-  {
-    'command': 'decreaseTtsRate',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [219], 'shiftKey': [true]}}
-  },
-  {
-    'command': 'increaseTtsRate',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [219]}}
-  },
-  {
-    'command': 'decreaseTtsPitch',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [221], 'shiftKey': [true]}}
-  },
-  {
-    'command': 'increaseTtsPitch',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [221]}}
-  },
-  {
-    'command': 'stopSpeech',
-    'sequence': {'keys': {'ctrlKey': [true], 'keyCode': [17]}}
-  },
-  {
-    'command': 'cyclePunctuationEcho',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [65, 80]}}
-  },
-  {
-    'command': 'showKbExplorerPage',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [79, 75]}}
-  },
-  {
-    'command': 'cycleTypingEcho',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [65, 84]}}
-  },
-  {
-    'command': 'showOptionsPage',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [79, 79]}}
-  },
-  {
-    'command': 'showLogPage',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [79, 87]}}
-  },
-  {
-    'command': 'enableLogging',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [79, 69]}}
-  },
-  {
-    'command': 'disableLogging',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [79, 68]}}
-  },
-  {
-    'command': 'dumpTree',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [68, 84], 'ctrlKey': [true]}}
-  },
-  {
-    'command': 'help',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [79, 84]}}
-  },
-  {
-    'command': 'toggleEarcons',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [65, 69]}}
-  },
-  {
-    'command': 'speakTimeAndDate',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [65, 68]}}
-  },
-  {
-    'command': 'readCurrentTitle',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [65, 87]}}
-  },
-  {
-    'command': 'readCurrentURL',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [65, 85]}}
-  },
-  {
-    'command': 'reportIssue',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [65, 73]}}
-  },
-  {
-    'command': 'toggleSearchWidget',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [191]}}
-  },
-  {
-    'command': 'showHeadingsList',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [72], 'ctrlKey': [true]}}
-  },
-  {
-    'command': 'showFormsList',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [70], 'ctrlKey': [true]}}
-  },
-  {
-    'command': 'showLandmarksList',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [186], 'ctrlKey': [true]}}
-  },
-  {
-    'command': 'showLinksList',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [76], 'ctrlKey': [true]}}
-  },
-  {
-    'command': 'showTablesList',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [84], 'ctrlKey': [true]}}
-  },
-  {
-    'command': 'toggleBrailleCaptions',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [65, 66]}}
+    command: 'jumpToBottom',
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.RIGHT], ctrlKey: [true]}}
   },
   {
-    'command': 'toggleBrailleTable',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [65, 71]}}
+    command: 'jumpToTop',
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.LEFT], ctrlKey: [true]}}
   },
   {
-    'command': 'viewGraphicAsBraille',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [71], 'altKey': [true]}}
+    command: 'forceClickOnCurrentItem',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.SPACE]}}
   },
   {
-    'command': 'toggleSelection',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [83]}}
+    command: 'contextMenu',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.M]}}
   },
   {
-    'command': 'fullyDescribe',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [75]}}
+    command: 'readFromHere',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.R]}}
   },
   {
-    'command': 'previousRow',
-    'sequence': {
-      'cvoxModifier': true,
-      'keys': {'keyCode': [38], 'ctrlKey': [true], 'altKey': [true]}
+    command: 'toggleStickyMode',
+    sequence: {
+      skipStripping: false,
+      doubleTap: true,
+      keys: {keyCode: [KeyCode.SEARCH]}
     }
   },
   {
-    'command': 'nextRow',
-    'sequence': {
-      'cvoxModifier': true,
-      'keys': {'keyCode': [40], 'ctrlKey': [true], 'altKey': [true]}
+    command: 'passThroughMode',
+    sequence: {
+      cvoxModifier: true,
+      keys: {keyCode: [KeyCode.ESCAPE], shiftKey: [true]}
     }
   },
   {
-    'command': 'nextCol',
-    'sequence': {
-      'cvoxModifier': true,
-      'keys': {'keyCode': [39], 'ctrlKey': [true], 'altKey': [true]}
+    command: 'toggleKeyboardHelp',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.OEM_PERIOD]}}
+  },
+  {
+    command: 'stopSpeech',
+    sequence: {
+      cvoxModifier: false,
+      keys: {ctrlKey: [true], keyCode: [KeyCode.CONTROL]}
     }
   },
   {
-    'command': 'previousCol',
-    'sequence': {
-      'cvoxModifier': true,
-      'keys': {'keyCode': [37], 'ctrlKey': [true], 'altKey': [true]}
+    command: 'decreaseTtsRate',
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.OEM_4], shiftKey: [true]}}
+  },
+  {
+    command: 'increaseTtsRate',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.OEM_4]}}
+  },
+  {
+    command: 'decreaseTtsPitch',
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.OEM_6], shiftKey: [true]}}
+  },
+  {
+    command: 'increaseTtsPitch',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.OEM_6]}}
+  },
+  {
+    command: 'stopSpeech',
+    sequence: {keys: {ctrlKey: [true], keyCode: [KeyCode.CONTROL]}}
+  },
+  {
+    command: 'cyclePunctuationEcho',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.P]}}
+  },
+  {
+    command: 'showKbExplorerPage',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.O, KeyCode.K]}}
+  },
+  {
+    command: 'cycleTypingEcho',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.T]}}
+  },
+  {
+    command: 'showOptionsPage',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.O, KeyCode.O]}}
+  },
+  {
+    command: 'showLogPage',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.O, KeyCode.W]}}
+  },
+  {
+    command: 'enableLogging',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.O, KeyCode.E]}}
+  },
+  {
+    command: 'disableLogging',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.O, KeyCode.D]}}
+  },
+  {
+    command: 'dumpTree',
+    sequence: {
+      cvoxModifier: true,
+      keys: {keyCode: [KeyCode.D, KeyCode.T], ctrlKey: [true]}
     }
   },
   {
-    'command': 'goToRowFirstCell',
-    'sequence': {
-      'cvoxModifier': true,
-      'keys': {
-        'keyCode': [37],
-        'ctrlKey': [true],
-        'altKey': [true],
-        'shiftKey': [true]
+    command: 'help',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.O, KeyCode.T]}}
+  },
+  {
+    command: 'toggleEarcons',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.E]}}
+  },
+  {
+    command: 'speakTimeAndDate',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.D]}}
+  },
+  {
+    command: 'readCurrentTitle',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.W]}}
+  },
+  {
+    command: 'readCurrentURL',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.U]}}
+  },
+  {
+    command: 'reportIssue',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.I]}}
+  },
+  {
+    command: 'toggleSearchWidget',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.OEM_2]}}
+  },
+  {
+    command: 'showHeadingsList',
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.H], ctrlKey: [true]}}
+  },
+  {
+    command: 'showFormsList',
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.F], ctrlKey: [true]}}
+  },
+  {
+    command: 'showLandmarksList',
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.OEM_1], ctrlKey: [true]}}
+  },
+  {
+    command: 'showLinksList',
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.L], ctrlKey: [true]}}
+  },
+  {
+    command: 'showTablesList',
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.T], ctrlKey: [true]}}
+  },
+  {
+    command: 'toggleBrailleCaptions',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.B]}}
+  },
+  {
+    command: 'toggleBrailleTable',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.G]}}
+  },
+  {
+    command: 'viewGraphicAsBraille',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.G], altKey: [true]}}
+  },
+  {
+    command: 'toggleSelection',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.S]}}
+  },
+  {
+    command: 'fullyDescribe',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.K]}}
+  },
+  {
+    command: 'previousRow',
+    sequence: {
+      cvoxModifier: true,
+      keys: {keyCode: [KeyCode.UP], ctrlKey: [true], altKey: [true]}
+    }
+  },
+  {
+    command: 'nextRow',
+    sequence: {
+      cvoxModifier: true,
+      keys: {keyCode: [KeyCode.DOWN], ctrlKey: [true], altKey: [true]}
+    }
+  },
+  {
+    command: 'nextCol',
+    sequence: {
+      cvoxModifier: true,
+      keys: {keyCode: [KeyCode.RIGHT], ctrlKey: [true], altKey: [true]}
+    }
+  },
+  {
+    command: 'previousCol',
+    sequence: {
+      cvoxModifier: true,
+      keys: {keyCode: [KeyCode.LEFT], ctrlKey: [true], altKey: [true]}
+    }
+  },
+  {
+    command: 'goToRowFirstCell',
+    sequence: {
+      cvoxModifier: true,
+      keys: {
+        keyCode: [KeyCode.LEFT],
+        ctrlKey: [true],
+        altKey: [true],
+        shiftKey: [true]
       }
     }
   },
   {
-    'command': 'goToColFirstCell',
-    'sequence': {
-      'cvoxModifier': true,
-      'keys': {
-        'keyCode': [38],
-        'ctrlKey': [true],
-        'altKey': [true],
-        'shiftKey': [true]
+    command: 'goToColFirstCell',
+    sequence: {
+      cvoxModifier: true,
+      keys: {
+        keyCode: [KeyCode.UP],
+        ctrlKey: [true],
+        altKey: [true],
+        shiftKey: [true]
       }
     }
   },
   {
-    'command': 'goToColLastCell',
-    'sequence': {
-      'cvoxModifier': true,
-      'keys': {
-        'keyCode': [40],
-        'ctrlKey': [true],
-        'altKey': [true],
-        'shiftKey': [true]
+    command: 'goToColLastCell',
+    sequence: {
+      cvoxModifier: true,
+      keys: {
+        keyCode: [KeyCode.DOWN],
+        ctrlKey: [true],
+        altKey: [true],
+        shiftKey: [true]
       }
     }
   },
   {
-    'command': 'goToFirstCell',
-    'sequence': {
-      'cvoxModifier': true,
-      'keys': {'keyCode': [37], 'altKey': [true], 'shiftKey': [true]}
+    command: 'goToFirstCell',
+    sequence: {
+      cvoxModifier: true,
+      keys: {keyCode: [KeyCode.LEFT], altKey: [true], shiftKey: [true]}
     }
   },
   {
-    'command': 'goToLastCell',
-    'sequence': {
-      'cvoxModifier': true,
-      'keys': {'keyCode': [39], 'altKey': [true], 'shiftKey': [true]}
+    command: 'goToLastCell',
+    sequence: {
+      cvoxModifier: true,
+      keys: {keyCode: [KeyCode.RIGHT], altKey: [true], shiftKey: [true]}
     }
   },
   {
-    'command': 'goToRowLastCell',
-    'sequence': {
-      'cvoxModifier': true,
-      'keys': {
-        'keyCode': [39],
-        'ctrlKey': [true],
-        'altKey': [true],
-        'shiftKey': [true]
+    command: 'goToRowLastCell',
+    sequence: {
+      cvoxModifier: true,
+      keys: {
+        keyCode: [KeyCode.RIGHT],
+        ctrlKey: [true],
+        altKey: [true],
+        shiftKey: [true]
       }
     }
   },
   {
-    'command': 'previousGroup',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [38], 'ctrlKey': [true]}}
+    command: 'previousGroup',
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.UP], ctrlKey: [true]}}
   },
   {
-    'command': 'nextGroup',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [40], 'ctrlKey': [true]}}
+    command: 'nextGroup',
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.DOWN], ctrlKey: [true]}}
   },
   {
-    'command': 'previousSimilarItem',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [73], 'shiftKey': [true]}}
+    command: 'previousSimilarItem',
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.I], shiftKey: [true]}}
   },
   {
-    'command': 'nextSimilarItem',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [73]}}
+    command: 'nextSimilarItem',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.I]}}
   },
   {
-    'command': 'jumpToDetails',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [65, 74]}}
+    command: 'jumpToDetails',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.J]}}
   },
   {
-    'command': 'toggleDarkScreen',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [217]}}
+    command: 'toggleDarkScreen',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.BRIGHTNESS_UP]}}
   },
   {
-    'command': 'toggleSpeechOnOrOff',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [173]}}
+    command: 'toggleSpeechOnOrOff',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.VOLUME_MUTE]}}
   },
   {
-    'command': 'enableChromeVoxArcSupportForCurrentApp',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [65, 219]}}
+    command: 'enableChromeVoxArcSupportForCurrentApp',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.OEM_4]}}
   },
   {
-    'command': 'disableChromeVoxArcSupportForCurrentApp',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [65, 221]}}
+    command: 'disableChromeVoxArcSupportForCurrentApp',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.OEM_6]}}
   },
   {
-    'command': 'forceClickOnCurrentItem',
-    'sequence':
-        {'cvoxModifier': true, 'keys': {'keyCode': [32]}, 'doubleTap': true}
+    command: 'forceClickOnCurrentItem',
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.SPACE]}, doubleTap: true}
   },
   {
-    'command': 'showTtsSettings',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [79, 83]}}
+    command: 'showTtsSettings',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.O, KeyCode.S]}}
   },
   {
-    'command': 'announceBatteryDescription',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [79, 66]}}
+    command: 'announceBatteryDescription',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.O, KeyCode.B]}}
   },
   {
-    'command': 'announceRichTextDescription',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [65, 70]}}
+    command: 'announceRichTextDescription',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.F]}}
   },
   {
-    'command': 'readPhoneticPronunciation',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [65, 67]}}
+    command: 'readPhoneticPronunciation',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.C]}}
   },
   {
-    'command': 'readLinkURL',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [65, 76]}}
+    command: 'readLinkURL',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.A, KeyCode.L]}}
   },
   {
-    'command': 'nextList',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [74, 76]}}
+    command: 'nextList',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.J, KeyCode.L]}}
   },
   {
-    'command': 'previousList',
-    'sequence': {
-      'cvoxModifier': true,
-      'keys': {'keyCode': [74, 76], 'shiftKey': [true]}
+    command: 'previousList',
+    sequence: {
+      cvoxModifier: true,
+      keys: {keyCode: [KeyCode.J, KeyCode.L], shiftKey: [true]}
     }
   },
   {
-    'command': 'resetTextToSpeechSettings',
-    'sequence': {
-      'cvoxModifier': true,
-      'keys': {'keyCode': [220], 'ctrlKey': [true], 'shiftKey': [true]}
+    command: 'resetTextToSpeechSettings',
+    sequence: {
+      cvoxModifier: true,
+      keys: {keyCode: [KeyCode.OEM_5], ctrlKey: [true], shiftKey: [true]}
     }
   },
   {
-    'command': 'logLanguageInformationForCurrentNode',
-    'sequence': {'cvoxModifier': true, 'keys': {'keyCode': [80, 76]}}
-  }
+    command: 'logLanguageInformationForCurrentNode',
+    sequence: {cvoxModifier: true, keys: {keyCode: [KeyCode.P, KeyCode.L]}}
+  },
+  {
+    command: 'copy',
+    sequence:
+        {cvoxModifier: true, keys: {keyCode: [KeyCode.C], ctrlKey: [true]}}
+  },
 ];
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/switch_access/BUILD.gn
index 343ba62..2c43f84 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/BUILD.gn
@@ -60,9 +60,9 @@
     "icons/showContextMenu.svg",
     "icons/textSelectionEnd.svg",
     "icons/textSelectionStart.svg",
+    "item_scan_manager.js",
     "menu_manager.js",
     "metrics.js",
-    "navigation_manager.js",
     "navigator.js",
     "nodes/back_button_node.js",
     "nodes/basic_node.js",
@@ -106,7 +106,7 @@
   test_type = "extension"
   sources = [
     "auto_scan_manager_test.js",
-    "navigation_manager_test.js",
+    "item_scan_manager_test.js",
     "nodes/basic_node_test.js",
     "nodes/desktop_node_test.js",
     "nodes/group_node_test.js",
@@ -147,11 +147,11 @@
     ":focus_ring_manager",
     ":group_node",
     ":history",
+    ":item_scan_manager",
     ":keyboard_node",
     ":menu_manager",
     ":metrics",
     ":modal_dialog_node",
-    ":navigation_manager",
     ":navigator",
     ":preference_manager",
     ":slider_node",
@@ -228,7 +228,7 @@
   deps = [
     ":action_manager",
     ":auto_scan_manager",
-    ":navigation_manager",
+    ":item_scan_manager",
   ]
   externs_list = [ "$externs_path/accessibility_private.js" ]
 }
@@ -335,7 +335,7 @@
   externs_list = [ "$externs_path/automation.js" ]
 }
 
-js_library("navigation_manager") {
+js_library("item_scan_manager") {
   deps = [
     ":action_manager",
     ":basic_node",
@@ -406,8 +406,8 @@
   deps = [
     ":auto_scan_manager",
     ":commands",
+    ":item_scan_manager",
     ":menu_manager",
-    ":navigation_manager",
     ":preference_manager",
     ":switch_access_constants",
     "../common:constants",
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/navigation_manager.js b/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager.js
similarity index 99%
rename from chrome/browser/resources/chromeos/accessibility/switch_access/navigation_manager.js
rename to chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager.js
index 0c09301b..9a062f5 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/navigation_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager.js
@@ -22,7 +22,7 @@
 const AutomationNode = chrome.automation.AutomationNode;
 
 /** This class handles navigation amongst the elements onscreen. */
-export class NavigationManager extends NavigatorInterface {
+export class ItemScanManager extends NavigatorInterface {
   /**
    * @param {!AutomationNode} desktop
    */
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/navigation_manager_test.js b/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager_test.js
similarity index 95%
rename from chrome/browser/resources/chromeos/accessibility/switch_access/navigation_manager_test.js
rename to chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager_test.js
index 04b48d1..1229344 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/navigation_manager_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager_test.js
@@ -4,8 +4,8 @@
 
 GEN_INCLUDE(['switch_access_e2e_test_base.js']);
 
-/** Test fixture for the navigation manager. */
-SwitchAccessNavigationManagerTest = class extends SwitchAccessE2ETest {
+/** Test fixture for the item scan manager. */
+SwitchAccessItemScanManagerTest = class extends SwitchAccessE2ETest {
   /** @override */
   setUp() {
     var runTest = this.deferRunTest(WhenTestDone.EXPECT);
@@ -55,7 +55,7 @@
   return Navigator.instance.node_;
 }
 
-TEST_F('SwitchAccessNavigationManagerTest', 'MoveTo', function() {
+TEST_F('SwitchAccessItemScanManagerTest', 'MoveTo', function() {
   const website = `<div id="outerGroup">
                      <div id="group">
                        <input type="text">
@@ -118,7 +118,7 @@
   });
 });
 
-TEST_F('SwitchAccessNavigationManagerTest', 'JumpTo', function() {
+TEST_F('SwitchAccessItemScanManagerTest', 'JumpTo', function() {
   const website = `<div id="group1">
                      <input type="text">
                      <button></button>
@@ -158,7 +158,7 @@
   });
 });
 
-TEST_F('SwitchAccessNavigationManagerTest', 'SelectButton', function() {
+TEST_F('SwitchAccessItemScanManagerTest', 'SelectButton', function() {
   const website = `<button id="test" aria-pressed=false>First Button</button>
       <button>Second Button</button>
       <script>
@@ -189,7 +189,7 @@
   });
 });
 
-TEST_F('SwitchAccessNavigationManagerTest', 'EnterGroup', function() {
+TEST_F('SwitchAccessItemScanManagerTest', 'EnterGroup', function() {
   const website = `<div id="group">
                      <button></button>
                      <button></button>
@@ -219,7 +219,7 @@
   });
 });
 
-TEST_F('SwitchAccessNavigationManagerTest', 'MoveForward', function() {
+TEST_F('SwitchAccessItemScanManagerTest', 'MoveForward', function() {
   const website = `<div>
                      <button id="button1"></button>
                      <button id="button2"></button>
@@ -274,7 +274,7 @@
   });
 });
 
-TEST_F('SwitchAccessNavigationManagerTest', 'MoveBackward', function() {
+TEST_F('SwitchAccessItemScanManagerTest', 'MoveBackward', function() {
   const website = `<div>
                      <button id="button1"></button>
                      <button id="button2"></button>
@@ -328,7 +328,7 @@
 });
 
 TEST_F(
-    'SwitchAccessNavigationManagerTest', 'NodeUndefinedBeforeTreeChangeRemoved',
+    'SwitchAccessItemScanManagerTest', 'NodeUndefinedBeforeTreeChangeRemoved',
     function() {
       const website = `<div>
                      <button id="button1"></button>
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 2b4647e..cdd75cb5 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access.js
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import {Commands} from './commands.js';
-import {NavigationManager} from './navigation_manager.js';
+import {ItemScanManager} from './item_scan_manager.js';
 import {Navigator} from './navigator.js';
 import {KeyboardRootNode} from './nodes/keyboard_node.js';
 import {PreferenceManager} from './preference_manager.js';
@@ -21,8 +21,8 @@
     SwitchAccess.instance = new SwitchAccess();
 
     chrome.automation.getDesktop((desktop) => {
-      // NavigationManager must be initialized first.
-      Navigator.setSingletonInstance(new NavigationManager(desktop));
+      // ItemScanManager must be initialized first.
+      Navigator.setSingletonInstance(new ItemScanManager(desktop));
 
       Commands.initialize();
       KeyboardRootNode.startWatchingVisibility();
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_e2e_test_base.js b/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_e2e_test_base.js
index 15f332f..4a6ee17 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_e2e_test_base.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_e2e_test_base.js
@@ -63,6 +63,6 @@
    */
   waitForPredicate(predicate, callback) {
     this.listenUntil(
-        predicate, NavigationManager.desktopNode, 'childrenChanged', callback);
+        predicate, Navigator.instance.desktopNode, 'childrenChanged', callback);
   }
 };
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/BUILD.gn b/chrome/browser/resources/chromeos/multidevice_setup/BUILD.gn
index 6a550ce7..d820ae32 100644
--- a/chrome/browser/resources/chromeos/multidevice_setup/BUILD.gn
+++ b/chrome/browser/resources/chromeos/multidevice_setup/BUILD.gn
@@ -2,10 +2,83 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//chrome/common/features.gni")
 import("//third_party/closure_compiler/compile_js.gni")
+import("//tools/grit/preprocess_if_expr.gni")
+import("//tools/polymer/html_to_js.gni")
+import("//ui/webui/resources/tools/generate_grd.gni")
+import("../../tools/optimize_webui.gni")
+
+preprocess_folder = "preprocessed"
+preprocess_manifest = "preprocessed_manifest.json"
+preprocess_gen_manifest = "preprocessed_gen_manifest.json"
+
+if (optimize_webui) {
+  build_manifest = "build_manifest.json"
+
+  optimize_webui("build") {
+    host = "multidevice-setup"
+    js_out_files = [ "multidevice_setup_post_oobe.rollup.js" ]
+    js_module_in_files = [ "multidevice_setup_post_oobe.js" ]
+    input = rebase_path("$target_gen_dir/$preprocess_folder", root_build_dir)
+
+    out_manifest = "$target_gen_dir/$build_manifest"
+    excludes = [
+      "chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js",
+      "chrome://resources/mojo/mojo/public/mojom/base/time.mojom-lite.js",
+      "chrome://resources/mojo/chromeos/components/multidevice/mojom/multidevice_types.mojom-lite.js",
+      "chrome://resources/mojo/chromeos/services/device_sync/public/mojom/device_sync.mojom-lite.js",
+      "chrome://resources/mojo/chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom-lite.js",
+    ]
+
+    deps = [
+      ":preprocess",
+      ":preprocess_generated",
+      "../../../../../ui/webui/resources:preprocess",
+    ]
+  }
+}
+
+generate_grd("build_grd") {
+  input_files = [ "multidevice_setup_dialog.html" ]
+  input_files_base_dir = rebase_path(".", "//")
+  if (optimize_webui) {
+    deps = [ ":build" ]
+    resource_path_rewrites = [
+      "multidevice_setup_post_oobe.rollup.js|multidevice_setup_post_oobe.js",
+    ]
+    manifest_files = [ "$target_gen_dir/$build_manifest" ]
+  } else {
+    deps = [
+      ":preprocess",
+      ":preprocess_generated",
+    ]
+    manifest_files = [
+      "$target_gen_dir/$preprocess_manifest",
+      "$target_gen_dir/$preprocess_gen_manifest",
+    ]
+  }
+  grd_prefix = "multidevice_setup"
+  out_grd = "$target_gen_dir/${grd_prefix}_resources.grd"
+}
+
+preprocess_if_expr("preprocess") {
+  in_folder = "./"
+  out_folder = "$target_gen_dir/$preprocess_folder"
+  out_manifest = "$target_gen_dir/$preprocess_manifest"
+  in_files = [ "post_oobe_delegate.js" ]
+}
+
+preprocess_if_expr("preprocess_generated") {
+  deps = [ ":web_components" ]
+  in_folder = target_gen_dir
+  out_folder = "$target_gen_dir/$preprocess_folder"
+  out_manifest = "$target_gen_dir/$preprocess_gen_manifest"
+  in_files = [ "multidevice_setup_post_oobe.js" ]
+}
 
 js_type_check("closure_compile") {
-  uses_legacy_modules = true
+  is_polymer3 = true
   deps = [
     ":multidevice_setup_post_oobe",
     ":post_oobe_delegate",
@@ -15,14 +88,20 @@
 js_library("multidevice_setup_post_oobe") {
   deps = [
     ":post_oobe_delegate",
-    "//ui/webui/resources/cr_components/chromeos/multidevice_setup:multidevice_setup",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/cr_components/chromeos/multidevice_setup:multidevice_setup.m",
+    "//ui/webui/resources/cr_components/chromeos/multidevice_setup:multidevice_setup_delegate.m",
+    "//ui/webui/resources/cr_components/chromeos/multidevice_setup:multidevice_setup_shared_css.m",
   ]
 }
 
 js_library("post_oobe_delegate") {
   deps = [
-    "//ui/webui/resources/cr_components/chromeos/multidevice_setup:mojo_api",
-    "//ui/webui/resources/cr_components/chromeos/multidevice_setup:multidevice_setup_delegate",
-    "//ui/webui/resources/js:cr",
+    "//ui/webui/resources/cr_components/chromeos/multidevice_setup:mojo_api.m",
+    "//ui/webui/resources/cr_components/chromeos/multidevice_setup:multidevice_setup_delegate.m",
   ]
 }
+
+html_to_js("web_components") {
+  js_files = [ "multidevice_setup_post_oobe.js" ]
+}
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_dialog.html b/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_dialog.html
index 4653440..a8bfa919 100644
--- a/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_dialog.html
+++ b/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_dialog.html
@@ -3,6 +3,8 @@
 <head>
   <meta charset="utf8">
   <base href="chrome://multidevice-setup">
+  <link rel="stylesheet" href="chrome://resources/css/md_colors.css">
+  <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
   <title>$i18n{dialogAccessibilityTitle}</title>
   <style>
     html,
@@ -16,11 +18,7 @@
   </style>
 </head>
 <body>
-  <link rel="stylesheet" href="chrome://resources/css/md_colors.css">
-  <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
-  <link rel="import" href="chrome://resources/html/util.html">
-  <link rel="import" href="i18n_setup.html">
-  <link rel="import" href="multidevice_setup_post_oobe.html">
   <multidevice-setup-post-oobe></multidevice-setup-post-oobe>
+  <script type="module" src="multidevice_setup_post_oobe.js"></script>
 </body>
 </html>
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_post_oobe.html b/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_post_oobe.html
index ebac41f..5aa5ad6 100644
--- a/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_post_oobe.html
+++ b/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_post_oobe.html
@@ -1,56 +1,41 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<style include="multidevice-setup-shared">
+  :host {
+    width: 100%;
+  }
 
-<link rel="import" href="chrome://resources/html/cr.html">
-<link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/multidevice_setup_shared_css.html">
-<link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/multidevice_setup.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
-<link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="post_oobe_delegate.html">
+  multidevice-setup {
+    --multidevice-setup-width: 768px;
+    --iron-pages-overflow: visible;
+  }
 
-<dom-module id="multidevice-setup-post-oobe">
-  <template>
-    <style include="multidevice-setup-shared">
-      :host {
-        width: 100%;
-      }
-
-      multidevice-setup {
-        --multidevice-setup-width: 768px;
-        --iron-pages-overflow: visible;
-      }
-
-      #backward-button,
-      #cancel-button,
-      #forward-button {
-        align-items: center;
-        display: flex;
-        justify-content: center;
-        text-transform: none;
-      }
-    </style>
-    <multidevice-setup delegate="[[delegate_]]"
-        on-setup-exited="onExitRequested_"
-        on-forward-button-focus-requested="onForwardButtonFocusRequested_"
-        on-visible-page-name-changed="onVisiblePageNameChanged_"
-        forward-button-text-id="{{forwardButtonTextId_}}"
-        forward-button-disabled="{{forwardButtonDisabled_}}"
-        cancel-button-text-id="{{cancelButtonTextId_}}"
-        backward-button-text-id="{{backwardButtonTextId_}}">
-      <cr-button id="backward-button"
-          slot="backward-button" class="cancel-button">
-        [[i18n(backwardButtonTextId_)]]
-      </cr-button>
-      <cr-button id="cancel-button"
-          slot="cancel-button" class="cancel-button">
-        [[i18n(cancelButtonTextId_)]]
-      </cr-button>
-      <cr-button id="forward-button"
-          slot="forward-button" class="action-button"
-          disabled$="[[forwardButtonDisabled_]]">
-        [[i18n(forwardButtonTextId_)]]
-      </cr-button>
-    </multidevice-setup>
-  </template>
-  <script src="multidevice_setup_post_oobe.js">
-  </script>
-</dom-module>
+  #backward-button,
+  #cancel-button,
+  #forward-button {
+    align-items: center;
+    display: flex;
+    justify-content: center;
+    text-transform: none;
+  }
+</style>
+<multidevice-setup delegate="[[delegate_]]"
+    on-setup-exited="onExitRequested_"
+    on-forward-button-focus-requested="onForwardButtonFocusRequested_"
+    on-visible-page-name-changed="onVisiblePageNameChanged_"
+    forward-button-text-id="{{forwardButtonTextId_}}"
+    forward-button-disabled="{{forwardButtonDisabled_}}"
+    cancel-button-text-id="{{cancelButtonTextId_}}"
+    backward-button-text-id="{{backwardButtonTextId_}}">
+  <cr-button id="backward-button"
+      slot="backward-button" class="cancel-button">
+    [[i18n(backwardButtonTextId_)]]
+  </cr-button>
+  <cr-button id="cancel-button"
+      slot="cancel-button" class="cancel-button">
+    [[i18n(cancelButtonTextId_)]]
+  </cr-button>
+  <cr-button id="forward-button"
+      slot="forward-button" class="action-button"
+      disabled$="[[forwardButtonDisabled_]]">
+    [[i18n(forwardButtonTextId_)]]
+  </cr-button>
+</multidevice-setup>
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_post_oobe.js b/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_post_oobe.js
index b80c392..33e4434c 100644
--- a/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_post_oobe.js
+++ b/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_post_oobe.js
@@ -2,25 +2,28 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-cr.define('multidevice_setup_post_oobe', function() {
-  /**
-   * This enum is tied directly to a UMA enum defined in
-   * //tools/metrics/histograms/enums.xml, and should always reflect it (do not
-   * change one without changing the other).
-   * @enum {number}
-   */
-  PageNameValue = {
-    UNKNOWN: 0,
-    START: 1,
-    PASSWORD: 2,
-    SUCCESS: 3,
-    MAX_VALUE: 4,
-  };
+import './strings.m.js';
 
-  return {
-    PageNameValue: PageNameValue,
-  };
-});
+import {PageName} from 'chrome://resources/cr_components/chromeos/multidevice_setup/multidevice_setup.m.js';
+import {MultiDeviceSetupDelegate} from 'chrome://resources/cr_components/chromeos/multidevice_setup/multidevice_setup_delegate.m.js';
+import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
+import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {PostOobeDelegate} from './post_oobe_delegate.js';
+
+/**
+ * This enum is tied directly to a UMA enum defined in
+ * //tools/metrics/histograms/enums.xml, and should always reflect it (do not
+ * change one without changing the other).
+ * @enum {number}
+ */
+const PageNameValue = {
+  UNKNOWN: 0,
+  START: 1,
+  PASSWORD: 2,
+  SUCCESS: 3,
+  MAX_VALUE: 4,
+};
 
 /**
  * MultiDevice setup flow which is shown after OOBE has completed.
@@ -28,8 +31,10 @@
 Polymer({
   is: 'multidevice-setup-post-oobe',
 
+  _template: html`{__html_template__}`,
+
   properties: {
-    /** @private {!multidevice_setup.MultiDeviceSetupDelegate} */
+    /** @private {!MultiDeviceSetupDelegate} */
     delegate_: Object,
 
     /**
@@ -70,7 +75,7 @@
 
   /** @override */
   attached() {
-    this.delegate_ = new multidevice_setup.PostOobeDelegate();
+    this.delegate_ = new PostOobeDelegate();
     this.$$('multidevice-setup').initializeSetupFlow();
   },
 
@@ -85,30 +90,30 @@
   },
 
   /**
-   * @param {!CustomEvent<!{value: multidevice_setup.PageName}>} event
+   * @param {!CustomEvent<!{value: PageName}>} event
    * @private
    */
   onVisiblePageNameChanged_(event) {
     let pageNameValue;
     switch (event.detail.value) {
-      case multidevice_setup.PageName.START:
-        pageNameValue = multidevice_setup_post_oobe.PageNameValue.START;
+      case PageName.START:
+        pageNameValue = PageNameValue.START;
         break;
-      case multidevice_setup.PageName.PASSWORD:
-        pageNameValue = multidevice_setup_post_oobe.PageNameValue.PASSWORD;
+      case PageName.PASSWORD:
+        pageNameValue = PageNameValue.PASSWORD;
         break;
-      case multidevice_setup.PageName.SUCCESS:
-        pageNameValue = multidevice_setup_post_oobe.PageNameValue.SUCCESS;
+      case PageName.SUCCESS:
+        pageNameValue = PageNameValue.SUCCESS;
         break;
       default:
         console.warn('Unexpected PageName.');
-        pageNameValue = multidevice_setup_post_oobe.PageNameValue.UNKNOWN;
+        pageNameValue = PageNameValue.UNKNOWN;
         break;
     }
 
     chrome.send('metricsHandler:recordInHistogram', [
       'MultiDevice.PostOOBESetupFlow.PageShown', pageNameValue,
-      multidevice_setup_post_oobe.PageNameValue.MAX_VALUE
+      PageNameValue.MAX_VALUE
     ]);
   }
 });
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_resources.grd b/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_resources.grd
deleted file mode 100644
index 81ae952..0000000
--- a/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_resources.grd
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<grit latest_public_release="0" current_release="1" output_all_resource_defines="false">
-  <outputs>
-    <output filename="grit/multidevice_setup_resources.h" type="rc_header">
-      <emit emit_type='prepend'></emit>
-    </output>
-    <output filename="grit/multidevice_setup_resources_map.cc"
-            type="resource_file_map_source" />
-    <output filename="grit/multidevice_setup_resources_map.h"
-            type="resource_map_header" />
-    <output filename="multidevice_setup_resources.pak" type="data_package" />
-  </outputs>
-  <release seq="1">
-    <structures>
-      <structure name="IDR_MULTIDEVICE_SETUP_I18N_SETUP_HTML"
-                 file="i18n_setup.html"
-                 type="chrome_html" />
-      <structure name="IDR_MULTIDEVICE_SETUP_MULTIDEVICE_SETUP_DIALOG_HTML"
-                 file="multidevice_setup_dialog.html"
-                 flattenhtml="true"
-                 allowexternalscript="true"
-                 type="chrome_html" />
-      <structure name="IDR_MULTIDEVICE_SETUP_MULTIDEVICE_SETUP_POST_OOBE_HTML"
-                 file="multidevice_setup_post_oobe.html"
-                 type="chrome_html" />
-      <structure name="IDR_MULTIDEVICE_SETUP_MULTIDEVICE_SETUP_POST_OOBE_JS"
-                 file="multidevice_setup_post_oobe.js"
-                 type="chrome_html" />
-      <structure name="IDR_MULTIDEVICE_SETUP_POST_OOBE_DELEGATE_HTML"
-                 file="post_oobe_delegate.html"
-                 type="chrome_html" />
-      <structure name="IDR_MULTIDEVICE_SETUP_POST_OOBE_DELEGATE_JS"
-                 file="post_oobe_delegate.js"
-                 type="chrome_html" />
-    </structures>
-  </release>
-</grit>
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/post_oobe_delegate.html b/chrome/browser/resources/chromeos/multidevice_setup/post_oobe_delegate.html
deleted file mode 100644
index 1fc839f..0000000
--- a/chrome/browser/resources/chromeos/multidevice_setup/post_oobe_delegate.html
+++ /dev/null
@@ -1,3 +0,0 @@
-<link rel="import" href="chrome://resources/html/cr.html">
-<link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/mojo_api.html">
-<script src="post_oobe_delegate.js"></script>
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/post_oobe_delegate.js b/chrome/browser/resources/chromeos/multidevice_setup/post_oobe_delegate.js
index 1f948e5..d8250f4 100644
--- a/chrome/browser/resources/chromeos/multidevice_setup/post_oobe_delegate.js
+++ b/chrome/browser/resources/chromeos/multidevice_setup/post_oobe_delegate.js
@@ -2,41 +2,41 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-cr.define('multidevice_setup', function() {
-  /** @implements {multidevice_setup.MultiDeviceSetupDelegate} */
-  class PostOobeDelegate {
-    /** @override */
-    isPasswordRequiredToSetHost() {
-      return true;
-    }
+import {MojoInterfaceProviderImpl} from 'chrome://resources/cr_components/chromeos/multidevice_setup/mojo_api.m.js';
+import {MultiDeviceSetupDelegate} from 'chrome://resources/cr_components/chromeos/multidevice_setup/multidevice_setup_delegate.m.js';
+import {assert} from 'chrome://resources/js/assert.m.js';
+import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
+import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-    /** @override */
-    setHostDevice(hostInstanceIdOrLegacyDeviceId, opt_authToken) {
-      // An authentication token is required to set the host device post-OOBE.
-      assert(!!opt_authToken);
-
-      // Note: A cast is needed here because currently all Mojo functions which
-      // return a promise are typed only as {Promise}. The setHostDevice()
-      // function always returns a {!Promise<{success: boolean}>} (see
-      // multidevice_setup.mojom).
-      return /** @type {!Promise<{success: boolean}>} */ (
-          multidevice_setup.MojoInterfaceProviderImpl.getInstance()
-              .getMojoServiceRemote()
-              .setHostDevice(hostInstanceIdOrLegacyDeviceId, opt_authToken));
-    }
-
-    /** @override */
-    shouldExitSetupFlowAfterSettingHost() {
-      return false;
-    }
-
-    /** @override */
-    getStartSetupCancelButtonTextId() {
-      return 'cancel';
-    }
+/** @implements {MultiDeviceSetupDelegate} */
+export class PostOobeDelegate {
+  /** @override */
+  isPasswordRequiredToSetHost() {
+    return true;
   }
 
-  return {
-    PostOobeDelegate: PostOobeDelegate,
-  };
-});
+  /** @override */
+  setHostDevice(hostInstanceIdOrLegacyDeviceId, opt_authToken) {
+    // An authentication token is required to set the host device post-OOBE.
+    assert(!!opt_authToken);
+
+    // Note: A cast is needed here because currently all Mojo functions which
+    // return a promise are typed only as {Promise}. The setHostDevice()
+    // function always returns a {!Promise<{success: boolean}>} (see
+    // multidevice_setup.mojom).
+    return /** @type {!Promise<{success: boolean}>} */ (
+        MojoInterfaceProviderImpl.getInstance()
+            .getMojoServiceRemote()
+            .setHostDevice(hostInstanceIdOrLegacyDeviceId, opt_authToken));
+  }
+
+  /** @override */
+  shouldExitSetupFlowAfterSettingHost() {
+    return false;
+  }
+
+  /** @override */
+  getStartSetupCancelButtonTextId() {
+    return 'cancel';
+  }
+}
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_browser_proxy.js b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_browser_proxy.js
index 2fc9496..83d8c15 100644
--- a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_browser_proxy.js
+++ b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_browser_proxy.js
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // #import {addSingletonGetter, sendWithPromise} from 'chrome://resources/js/cr.m.js';
-// #import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+// #import {loadTimeData} from '../../i18n_setup.js';
 // #import 'chrome://resources/cr_elements/cr_input/cr_input.m.js';
 
 // Identifiers for the default Crostini VM and container.
diff --git a/chrome/browser/resources/settings/chromeos/ensure_lazy_loaded.m.js b/chrome/browser/resources/settings/chromeos/ensure_lazy_loaded.m.js
index 18619fd..a121c12 100644
--- a/chrome/browser/resources/settings/chromeos/ensure_lazy_loaded.m.js
+++ b/chrome/browser/resources/settings/chromeos/ensure_lazy_loaded.m.js
@@ -15,8 +15,9 @@
       'os-settings-powerwash-dialog',
       'os-settings-reset-page',
       'os-settings-files-page',
-      'settings-smb-shares-page',
       'os-printing-page',
+      'settings-crostini-page',
+      'settings-smb-shares-page',
     ];
     if (!loadTimeData.getBoolean('isAccountManagementFlowsV2Enabled')) {
       lazyLoadPages.push('os-settings-privacy-page');
diff --git a/chrome/browser/resources/settings/chromeos/lazy_load.html b/chrome/browser/resources/settings/chromeos/lazy_load.html
index 7cdc415..585422e 100644
--- a/chrome/browser/resources/settings/chromeos/lazy_load.html
+++ b/chrome/browser/resources/settings/chromeos/lazy_load.html
@@ -1,6 +1,7 @@
 <html>
 <head></head>
 <body>
+  <link rel="import" href="crostini_page/crostini_page.html">
   <link rel="import" href="date_time_page/date_time_page.html">
   <link rel="import" href="date_time_page/timezone_selector.html">
   <link rel="import" href="on_startup_page/on_startup_page.html">
diff --git a/chrome/browser/resources/settings/chromeos/lazy_load.js b/chrome/browser/resources/settings/chromeos/lazy_load.js
index 4ee6c14..ce48577 100644
--- a/chrome/browser/resources/settings/chromeos/lazy_load.js
+++ b/chrome/browser/resources/settings/chromeos/lazy_load.js
@@ -3,8 +3,21 @@
 // found in the LICENSE file.
 
 // Uncomment as these modules are migrated to Polymer 3.
+import './crostini_page/crostini_arc_adb.m.js';
+import './crostini_page/crostini_arc_adb_confirmation_dialog.m.js';
+import './crostini_page/crostini_disk_resize_confirmation_dialog.m.js';
+import './crostini_page/crostini_disk_resize_dialog.m.js';
+import './crostini_page/crostini_export_import.m.js';
+import './crostini_page/crostini_import_confirmation_dialog.m.js';
+import './crostini_page/crostini_mic_sharing_dialog.m.js';
+import './crostini_page/crostini_page.m.js';
+import './crostini_page/crostini_port_forwarding.m.js';
+import './crostini_page/crostini_port_forwarding_add_port_dialog.m.js';
+import './crostini_page/crostini_subpage.m.js';
 import './date_time_page/date_time_page.m.js';
 import './date_time_page/timezone_selector.m.js';
+import './guest_os/guest_os_shared_usb_devices.m.js';
+import './guest_os/guest_os_shared_paths.m.js';
 import './os_a11y_page/os_a11y_page.m.js';
 import './on_startup_page/on_startup_page.m.js';
 import './os_a11y_page/manage_a11y_page.m.js';
@@ -39,6 +52,8 @@
 import './os_reset_page/os_reset_page.m.js';
 import './os_files_page/smb_shares_page.m.js';
 
+export {CrostiniBrowserProxy, CrostiniBrowserProxyImpl} from './crostini_page/crostini_browser_proxy.m.js';
+export {CROSTINI_TYPE, GuestOsBrowserProxy, GuestOsBrowserProxyImpl, GuestOsSharedUsbDevice, PLUGIN_VM_TYPE} from './guest_os/guest_os_browser_proxy.m.js';
 export {SmbBrowserProxyImpl, SmbMountResult} from 'chrome://resources/cr_components/chromeos/smb_shares/smb_browser_proxy.m.js';
 export {LanguagesBrowserProxy, LanguagesBrowserProxyImpl} from '../languages_page/languages_browser_proxy.m.js';
 export {LifetimeBrowserProxyImpl} from '../lifetime_browser_proxy.m.js';
diff --git a/chrome/browser/resources/settings/chromeos/metrics_recorder.js b/chrome/browser/resources/settings/chromeos/metrics_recorder.js
index 07a8969..4e645ce 100644
--- a/chrome/browser/resources/settings/chromeos/metrics_recorder.js
+++ b/chrome/browser/resources/settings/chromeos/metrics_recorder.js
@@ -4,6 +4,7 @@
 
 // clang-format off
 // #import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js'
+// #import '../constants/setting.mojom-lite.js';
 // #import '../search/user_action_recorder.mojom-lite.js';
 // clang-format on
 
diff --git a/chrome/browser/resources/settings/chromeos/os_settings.js b/chrome/browser/resources/settings/chromeos/os_settings.js
index b4c05e8..a4a2ab9 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings.js
+++ b/chrome/browser/resources/settings/chromeos/os_settings.js
@@ -8,17 +8,6 @@
 import './bluetooth_page/bluetooth_page.m.js';
 import './bluetooth_page/bluetooth_subpage.m.js';
 import './bluetooth_page/bluetooth_device_list_item.m.js';
-import './crostini_page/crostini_arc_adb.m.js';
-import './crostini_page/crostini_arc_adb_confirmation_dialog.m.js';
-import './crostini_page/crostini_disk_resize_confirmation_dialog.m.js';
-import './crostini_page/crostini_disk_resize_dialog.m.js';
-import './crostini_page/crostini_export_import.m.js';
-import './crostini_page/crostini_import_confirmation_dialog.m.js';
-import './crostini_page/crostini_mic_sharing_dialog.m.js';
-import './crostini_page/crostini_page.m.js';
-import './crostini_page/crostini_port_forwarding.m.js';
-import './crostini_page/crostini_port_forwarding_add_port_dialog.m.js';
-import './crostini_page/crostini_subpage.m.js';
 import './device_page/device_page.m.js';
 import './device_page/display.m.js';
 import './device_page/display_layout.m.js';
@@ -31,8 +20,6 @@
 import './device_page/storage_external.m.js';
 import './device_page/storage_external_entry.m.js';
 import './device_page/stylus.m.js';
-import './guest_os/guest_os_shared_usb_devices.m.js';
-import './guest_os/guest_os_shared_paths.m.js';
 import './internet_page/cellular_setup_dialog.m.js';
 import './internet_page/esim_remove_profile_dialog.m.js';
 import './internet_page/internet_config.m.js';
@@ -103,11 +90,9 @@
 export {AmbientModeBrowserProxyImpl} from './ambient_mode_page/ambient_mode_browser_proxy.m.js';
 export {AmbientModeTemperatureUnit, AmbientModeTopicSource} from './ambient_mode_page/constants.m.js';
 export {bluetoothApis} from './bluetooth_page/bluetooth_page.m.js';
-export {CrostiniBrowserProxy, CrostiniBrowserProxyImpl} from './crostini_page/crostini_browser_proxy.m.js';
 export {DevicePageBrowserProxy, DevicePageBrowserProxyImpl, IdleBehavior, LidClosedBehavior, NoteAppLockScreenSupport, setDisplayApiForTesting, StorageSpaceState} from './device_page/device_page_browser_proxy.m.js';
 export {GoogleAssistantBrowserProxyImpl} from './google_assistant_page/google_assistant_browser_proxy.m.js';
 export {ConsentStatus, DspHotwordState} from './google_assistant_page/google_assistant_page.m.js';
-export {CROSTINI_TYPE, GuestOsBrowserProxy, GuestOsBrowserProxyImpl, GuestOsSharedUsbDevice, PLUGIN_VM_TYPE} from './guest_os/guest_os_browser_proxy.m.js';
 export {InternetPageBrowserProxy, InternetPageBrowserProxyImpl} from './internet_page/internet_page_browser_proxy.m.js';
 export {KerberosAccountsBrowserProxyImpl, KerberosConfigErrorCode, KerberosErrorType} from './kerberos_page/kerberos_accounts_browser_proxy.m.js';
 export {MultiDeviceBrowserProxy, MultiDeviceBrowserProxyImpl} from './multidevice_page/multidevice_browser_proxy.m.js';
diff --git a/chrome/browser/resources/settings/privacy_sandbox/app.html b/chrome/browser/resources/settings/privacy_sandbox/app.html
index 1708092..6d0e3706 100644
--- a/chrome/browser/resources/settings/privacy_sandbox/app.html
+++ b/chrome/browser/resources/settings/privacy_sandbox/app.html
@@ -52,7 +52,7 @@
 
 <settings-prefs prefs="{{prefs}}"></settings-prefs>
 <div id="page-container">
-  <h1 id="page-heading">$i18n{privacySandboxPageHeading}</h1>
+  <h1 id="page-heading">$i18n{privacySandboxTitle}</h1>
   <picture>
     <source class="banner"
         srcset="chrome://settings/images/privacy_sandbox_banner_dark.svg"
@@ -65,41 +65,31 @@
       $i18n{privacySandboxPageHeading}
     </h2>
   </div>
-  <div>$i18n{privacySandboxPageHeading}</div>
-  <div>
-    <h2 class="section-header" tabindex="-1">
-      $i18n{privacySandboxPageHeading}
-    </h2>
-  </div>
-  <div>$i18n{privacySandboxPageHeading}</div>
-  <div>
-    <ul>
-      <li><span>$i18n{privacySandboxPageHeading}</span></li>
-      <li><span>$i18n{privacySandboxPageHeading}</span></li>
-      <li><span>$i18n{privacySandboxPageHeading}</span></li>
-    </ul>
-  </div>
-  <div>
-    <h2 class="section-header" tabindex="-1">
-      $i18n{privacySandboxPageHeading}
-    </h2>
-  </div>
+  <p>$i18n{privacySandboxPageExplanation1}</p>
+  <p>$i18n{privacySandboxPageExplanation2}</p>
+  <p>$i18n{privacySandboxPageExplanation3}</p>
+  <p>$i18nRaw{privacySandboxPageExplanation4}</p>
   <div class="card">
     <settings-toggle-button id="apiToggleButton" class="hr"
         pref="{{prefs.privacy_sandbox.apis_enabled}}"
-        label="$i18n{privacySandboxPageHeading}"
+        label="$i18n{privacySandboxPageSettingTitle}"
         on-change="onApiToggleButtonChange_">
     </settings-toggle-button>
     <div class="cr-row continuation">
-        <div class="secondary">$i18n{privacySandboxPageHeading}</div>
+      <div class="secondary">$i18n{privacySandboxPageSettingExplanation1}</div>
     </div>
     <div class="cr-row continuation">
-        <div class="secondary">$i18n{privacySandboxPageHeading}</div>
+      <div class="secondary">
+        <ul>
+          <li><span>$i18n{privacySandboxPageSettingExplanation2}</span></li>
+          <li><span>$i18n{privacySandboxPageSettingExplanation3}</span></li>
+        </ul>
+      </div>
     </div>
     <div class="cr-row continuation">
       <cr-button class="cr-button" id="learnMoreButton" role="button"
           tabindex="0" on-click="onLearnMoreButtonClick_">
-        $i18n{passwordViewDetails}
+        $i18n{privacySandboxPageDetails}
       </cr-button>
     </div>
   </div>
diff --git a/chrome/browser/resources_util.cc b/chrome/browser/resources_util.cc
index 88e0b09..5c15c99 100644
--- a/chrome/browser/resources_util.cc
+++ b/chrome/browser/resources_util.cc
@@ -41,19 +41,19 @@
     storage.reserve(storage_size);
 
     for (size_t i = 0; i < kComponentsScaledResourcesSize; ++i) {
-      storage.emplace_back(kComponentsScaledResources[i].name,
-                           kComponentsScaledResources[i].value);
+      storage.emplace_back(kComponentsScaledResources[i].path,
+                           kComponentsScaledResources[i].id);
     }
     for (size_t i = 0; i < kThemeResourcesSize; ++i) {
-      storage.emplace_back(kThemeResources[i].name, kThemeResources[i].value);
+      storage.emplace_back(kThemeResources[i].path, kThemeResources[i].id);
     }
     for (size_t i = 0; i < kUiResourcesSize; ++i) {
-      storage.emplace_back(kUiResources[i].name, kUiResources[i].value);
+      storage.emplace_back(kUiResources[i].path, kUiResources[i].id);
     }
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     for (size_t i = 0; i < kUiChromeosResourcesSize; ++i) {
-      storage.emplace_back(kUiChromeosResources[i].name,
-                           kUiChromeosResources[i].value);
+      storage.emplace_back(kUiChromeosResources[i].path,
+                           kUiChromeosResources[i].id);
     }
 #endif
 
diff --git a/chrome/browser/safe_browsing/safe_browsing_metrics_collector.cc b/chrome/browser/safe_browsing/safe_browsing_metrics_collector.cc
index 29d036c..0cc90a04 100644
--- a/chrome/browser/safe_browsing/safe_browsing_metrics_collector.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_metrics_collector.cc
@@ -7,6 +7,7 @@
 #include "base/logging.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/time/time.h"
 #include "base/util/values/values_util.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
@@ -20,24 +21,29 @@
 
 const int kMetricsLoggingIntervalDay = 1;
 
+// The max length of event timestamps stored in pref.
 const int kTimestampsMaxLength = 30;
+// The quota for ESB disabled metrics. ESB disabled metrics should not be logged
+// more than the quota in a week.
+const int kEsbDisabledMetricsQuota = 3;
 
 std::string EventTypeToPrefKey(const EventType& type) {
   return base::NumberToString(static_cast<int>(type));
 }
 
-std::string SafeBrowsingStateToPrefKey(const SafeBrowsingState state) {
-  switch (state) {
+std::string UserStateToPrefKey(const UserState& user_state) {
+  return base::NumberToString(static_cast<int>(user_state));
+}
+
+UserState SafeBrowsingStateToUserState(const SafeBrowsingState& sb_state) {
+  switch (sb_state) {
     case SafeBrowsingState::ENHANCED_PROTECTION:
-      return base::NumberToString(
-          static_cast<int>(UserState::ENHANCED_PROTECTION));
+      return UserState::ENHANCED_PROTECTION;
     case SafeBrowsingState::STANDARD_PROTECTION:
-      return base::NumberToString(
-          static_cast<int>(UserState::STANDARD_PROTECTION));
+      return UserState::STANDARD_PROTECTION;
     case SafeBrowsingState::NO_SAFE_BROWSING:
       NOTREACHED() << "Unexpected Safe Browsing state.";
-      return base::NumberToString(
-          static_cast<int>(UserState::STANDARD_PROTECTION));
+      return UserState::STANDARD_PROTECTION;
   }
 }
 
@@ -107,20 +113,26 @@
 
 void SafeBrowsingMetricsCollector::AddSafeBrowsingEventToPref(
     EventType event_type) {
-  DictionaryPrefUpdate update(pref_service_,
-                              prefs::kSafeBrowsingEventTimestamps);
-  base::DictionaryValue* mutable_state_dict = update.Get();
-
   SafeBrowsingState sb_state = GetSafeBrowsingState(*pref_service_);
   // Safe Browsing events should not be triggered when Safe Browsing is
   // disabled.
   DCHECK(sb_state != SafeBrowsingState::NO_SAFE_BROWSING);
-  base::Value* event_dict =
-      mutable_state_dict->FindDictKey(SafeBrowsingStateToPrefKey(sb_state));
+  AddSafeBrowsingEventAndUserStateToPref(SafeBrowsingStateToUserState(sb_state),
+                                         event_type);
+}
 
+void SafeBrowsingMetricsCollector::AddSafeBrowsingEventAndUserStateToPref(
+    UserState user_state,
+    EventType event_type) {
+  DictionaryPrefUpdate update(pref_service_,
+                              prefs::kSafeBrowsingEventTimestamps);
+  base::DictionaryValue* mutable_state_dict = update.Get();
+
+  base::Value* event_dict =
+      mutable_state_dict->FindDictKey(UserStateToPrefKey(user_state));
   if (!event_dict) {
     event_dict =
-        mutable_state_dict->SetKey(SafeBrowsingStateToPrefKey(sb_state),
+        mutable_state_dict->SetKey(UserStateToPrefKey(user_state),
                                    base::Value(base::Value::Type::DICTIONARY));
   }
 
@@ -141,15 +153,25 @@
 
 void SafeBrowsingMetricsCollector::OnEnhancedProtectionPrefChanged() {
   if (!pref_service_->GetBoolean(prefs::kSafeBrowsingEnhanced)) {
-    LogEnhancedProtectionDisabledMetrics();
+    AddSafeBrowsingEventAndUserStateToPref(UserState::ENHANCED_PROTECTION,
+                                           EventType::USER_STATE_DISABLED);
+    int disabled_times_last_week = GetEventCountSince(
+        UserState::ENHANCED_PROTECTION, EventType::USER_STATE_DISABLED,
+        base::Time::Now() - base::TimeDelta::FromDays(7));
+    if (disabled_times_last_week <= kEsbDisabledMetricsQuota) {
+      LogEnhancedProtectionDisabledMetrics();
+    }
+  } else {
+    AddSafeBrowsingEventAndUserStateToPref(UserState::ENHANCED_PROTECTION,
+                                           EventType::USER_STATE_ENABLED);
   }
 }
 
 void SafeBrowsingMetricsCollector::LogEnhancedProtectionDisabledMetrics() {
   const base::DictionaryValue* state_dict =
       pref_service_->GetDictionary(prefs::kSafeBrowsingEventTimestamps);
-  const base::Value* event_dict = state_dict->FindDictKey(
-      SafeBrowsingStateToPrefKey(SafeBrowsingState::ENHANCED_PROTECTION));
+  const base::Value* event_dict = state_dict->FindDictKey(UserStateToPrefKey(
+      SafeBrowsingStateToUserState(SafeBrowsingState::ENHANCED_PROTECTION)));
   if (!event_dict) {
     return;
   }
@@ -181,6 +203,29 @@
   }
 }
 
+int SafeBrowsingMetricsCollector::GetEventCountSince(UserState user_state,
+                                                     EventType event_type,
+                                                     base::Time since_time) {
+  const base::DictionaryValue* state_dict =
+      pref_service_->GetDictionary(prefs::kSafeBrowsingEventTimestamps);
+  const base::Value* event_dict =
+      state_dict->FindDictKey(UserStateToPrefKey(user_state));
+  if (!event_dict) {
+    return 0;
+  }
+  const base::Value* timestamps =
+      event_dict->FindListKey(EventTypeToPrefKey(event_type));
+  if (!timestamps) {
+    return 0;
+  }
+
+  return std::count_if(timestamps->GetList().begin(),
+                       timestamps->GetList().end(),
+                       [&](const base::Value& timestamp) {
+                         return PrefValueToTime(timestamp) > since_time;
+                       });
+}
+
 bool SafeBrowsingMetricsCollector::IsBypassEventType(const EventType& type) {
   switch (type) {
     case EventType::USER_STATE_DISABLED:
diff --git a/chrome/browser/safe_browsing/safe_browsing_metrics_collector.h b/chrome/browser/safe_browsing/safe_browsing_metrics_collector.h
index f3678c5..8a1195b 100644
--- a/chrome/browser/safe_browsing/safe_browsing_metrics_collector.h
+++ b/chrome/browser/safe_browsing/safe_browsing_metrics_collector.h
@@ -80,6 +80,12 @@
   void OnEnhancedProtectionPrefChanged();
   void LogEnhancedProtectionDisabledMetrics();
 
+  void AddSafeBrowsingEventAndUserStateToPref(UserState user_state,
+                                              EventType event_type);
+  int GetEventCountSince(UserState user_state,
+                         EventType event_type,
+                         base::Time since_time);
+
   PrefService* pref_service_;
   PrefChangeRegistrar pref_change_registrar_;
   base::OneShotTimer metrics_collector_timer_;
diff --git a/chrome/browser/safe_browsing/safe_browsing_metrics_collector_unittest.cc b/chrome/browser/safe_browsing/safe_browsing_metrics_collector_unittest.cc
index b7b4023..2c2be653 100644
--- a/chrome/browser/safe_browsing/safe_browsing_metrics_collector_unittest.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_metrics_collector_unittest.cc
@@ -279,4 +279,43 @@
                               /* expected_count */ 0);
 }
 
+TEST_F(SafeBrowsingMetricsCollectorTest,
+       LogEnhancedProtectionDisabledMetrics_NotLoggedIfHitQuotaLimit) {
+  base::HistogramTester histograms;
+  SetSafeBrowsingState(&pref_service_, SafeBrowsingState::ENHANCED_PROTECTION);
+
+  FastForwardAndAddEvent(base::TimeDelta::FromHours(1),
+                         EventType::DATABASE_INTERSTITIAL_BYPASS);
+  SetSafeBrowsingState(&pref_service_, SafeBrowsingState::STANDARD_PROTECTION);
+  histograms.ExpectTotalCount("SafeBrowsing.EsbDisabled.LastBypassEventType",
+                              /* expected_count */ 1);
+
+  task_environment_->FastForwardBy(base::TimeDelta::FromDays(1));
+  SetSafeBrowsingState(&pref_service_, SafeBrowsingState::ENHANCED_PROTECTION);
+  SetSafeBrowsingState(&pref_service_, SafeBrowsingState::STANDARD_PROTECTION);
+  histograms.ExpectTotalCount("SafeBrowsing.EsbDisabled.LastBypassEventType",
+                              /* expected_count */ 2);
+
+  task_environment_->FastForwardBy(base::TimeDelta::FromDays(1));
+  SetSafeBrowsingState(&pref_service_, SafeBrowsingState::ENHANCED_PROTECTION);
+  SetSafeBrowsingState(&pref_service_, SafeBrowsingState::STANDARD_PROTECTION);
+  histograms.ExpectTotalCount("SafeBrowsing.EsbDisabled.LastBypassEventType",
+                              /* expected_count */ 3);
+
+  task_environment_->FastForwardBy(base::TimeDelta::FromDays(1));
+  SetSafeBrowsingState(&pref_service_, SafeBrowsingState::ENHANCED_PROTECTION);
+  SetSafeBrowsingState(&pref_service_, SafeBrowsingState::STANDARD_PROTECTION);
+  // The metric is not logged because it is already logged 3 times in a week.
+  histograms.ExpectTotalCount("SafeBrowsing.EsbDisabled.LastBypassEventType",
+                              /* expected_count */ 3);
+
+  task_environment_->FastForwardBy(base::TimeDelta::FromDays(7));
+  SetSafeBrowsingState(&pref_service_, SafeBrowsingState::ENHANCED_PROTECTION);
+  SetSafeBrowsingState(&pref_service_, SafeBrowsingState::STANDARD_PROTECTION);
+  // The metric is logged again because the oldest entry is more than 7 days
+  // ago.
+  histograms.ExpectTotalCount("SafeBrowsing.EsbDisabled.LastBypassEventType",
+                              /* expected_count */ 4);
+}
+
 }  // namespace safe_browsing
diff --git a/chrome/browser/ssl/security_state_tab_helper.cc b/chrome/browser/ssl/security_state_tab_helper.cc
index 7049178..e85187f 100644
--- a/chrome/browser/ssl/security_state_tab_helper.cc
+++ b/chrome/browser/ssl/security_state_tab_helper.cc
@@ -20,7 +20,6 @@
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/browser/safe_browsing/ui_manager.h"
 #include "chrome/browser/ssl/known_interception_disclosure_infobar_delegate.h"
-#include "chrome/browser/ssl/tls_deprecation_config.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
@@ -145,23 +144,6 @@
   if (state->connection_info_initialized) {
     state->connection_used_legacy_tls =
         IsLegacyTLS(state->url, state->connection_status);
-    if (state->connection_used_legacy_tls) {
-      // We cache the results of the lookup for the duration of a navigation
-      // entry.
-      int navigation_id =
-          web_contents()->GetController().GetVisibleEntry()->GetUniqueID();
-      if (cached_should_suppress_legacy_tls_warning_ &&
-          cached_should_suppress_legacy_tls_warning_.value().first ==
-              navigation_id) {
-        state->should_suppress_legacy_tls_warning =
-            cached_should_suppress_legacy_tls_warning_.value().second;
-      } else {
-        state->should_suppress_legacy_tls_warning =
-            ShouldSuppressLegacyTLSWarning(state->url);
-        cached_should_suppress_legacy_tls_warning_ = std::pair<int, bool>(
-            navigation_id, state->should_suppress_legacy_tls_warning);
-      }
-    }
   }
 
   // Malware status might already be known even if connection security
diff --git a/chrome/browser/ssl/security_state_tab_helper.h b/chrome/browser/ssl/security_state_tab_helper.h
index fa77a371..ce11511 100644
--- a/chrome/browser/ssl/security_state_tab_helper.h
+++ b/chrome/browser/ssl/security_state_tab_helper.h
@@ -8,7 +8,6 @@
 #include <memory>
 
 #include "base/callback.h"
-#include "base/optional.h"
 #include "components/security_state/core/security_state.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
@@ -52,15 +51,6 @@
   bool UsedPolicyInstalledCertificate() const;
   security_state::MaliciousContentStatus GetMaliciousContentStatus() const;
 
-  // Caches the legacy TLS warning suppression status for the duration of a page
-  // load (bound to a specific navigation ID) to ensure that we show consistent
-  // security UI (e.g., security indicator and page info). This is because the
-  // status depends on external state (a component loading from disk), which can
-  // cause inconsistent state across a page load if it isn't cached.
-  base::Optional<std::pair<int /* navigation entry ID */,
-                           bool /* should_suppress_legacy_tls_warning */>>
-      cached_should_suppress_legacy_tls_warning_;
-
   base::OnceClosure get_security_level_callback_for_tests_;
 
   WEB_CONTENTS_USER_DATA_KEY_DECL();
diff --git a/chrome/browser/ssl/security_state_tab_helper_browsertest.cc b/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
index c4878b19..3aadf3b 100644
--- a/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
+++ b/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
@@ -31,7 +31,6 @@
 #include "chrome/browser/reputation/reputation_web_contents_observer.h"
 #include "chrome/browser/safe_browsing/chrome_password_protection_service.h"
 #include "chrome/browser/ssl/cert_verifier_browser_test.h"
-#include "chrome/browser/ssl/tls_deprecation_config.h"
 #include "chrome/browser/ssl/tls_deprecation_test_utils.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
@@ -1780,7 +1779,6 @@
 IN_PROC_BROWSER_TEST_F(
     BrowserTestNonsecureURLRequestWithLegacyTLSWarnings,
     DidChangeVisibleSecurityStateObserverObsoleteTLSSettings) {
-  InitializeEmptyLegacyTLSConfig();
 
   content::WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
@@ -1810,28 +1808,11 @@
   EXPECT_NE(std::string::npos, explanation.recommendations[1].find("GCM"));
 }
 
-// Tests that a connection with legacy TLS version (TLS 1.0/1.1) is not
-// downgraded to SecurityLevel WARNING if no config proto is set (i.e., so we
-// don't accidentally show the warning on control sites, see crbug.com/1011089).
-IN_PROC_BROWSER_TEST_F(BrowserTestNonsecureURLRequestWithLegacyTLSWarnings,
-                       LegacyTLSNoProto) {
-  auto* tab = browser()->tab_strip_model()->GetActiveWebContents();
-  auto* helper = SecurityStateTabHelper::FromWebContents(tab);
-
-  ui_test_utils::NavigateToURL(browser(), GURL(kLegacyTLSURL));
-
-  EXPECT_TRUE(helper->GetVisibleSecurityState()->connection_used_legacy_tls);
-  EXPECT_TRUE(
-      helper->GetVisibleSecurityState()->should_suppress_legacy_tls_warning);
-  EXPECT_EQ(security_state::SECURE, helper->GetSecurityLevel());
-}
 
 // Tests that a connection with legacy TLS versions (TLS 1.0/1.1) gets
 // downgraded to SecurityLevel WARNING and |connection_used_legacy_tls| is set.
 IN_PROC_BROWSER_TEST_F(BrowserTestNonsecureURLRequestWithLegacyTLSWarnings,
                        LegacyTLSDowngradesSecurityLevel) {
-  // Set an empty config (otherwise all sites are treated as control).
-  InitializeEmptyLegacyTLSConfig();
 
   auto* tab = browser()->tab_strip_model()->GetActiveWebContents();
   auto* helper = SecurityStateTabHelper::FromWebContents(tab);
@@ -1839,32 +1820,9 @@
   ui_test_utils::NavigateToURL(browser(), GURL(kLegacyTLSURL));
 
   EXPECT_TRUE(helper->GetVisibleSecurityState()->connection_used_legacy_tls);
-  EXPECT_FALSE(
-      helper->GetVisibleSecurityState()->should_suppress_legacy_tls_warning);
   EXPECT_EQ(security_state::WARNING, helper->GetSecurityLevel());
 }
 
-// Tests that a site in the set of control sites does not get downgraded to
-// SecurityLevel::WARNING even if it was loaded over TLS 1.0/1.1.
-IN_PROC_BROWSER_TEST_F(BrowserTestNonsecureURLRequestWithLegacyTLSWarnings,
-                       LegacyTLSControlSiteNotDowngraded) {
-  // Set up new experiment config proto.
-  InitializeLegacyTLSConfigWithControl();
-
-  auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
-  auto* helper = SecurityStateTabHelper::FromWebContents(web_contents);
-
-  ui_test_utils::NavigateToURL(browser(), GURL(kLegacyTLSURL));
-
-  EXPECT_TRUE(helper->GetVisibleSecurityState()->connection_used_legacy_tls);
-  EXPECT_TRUE(
-      helper->GetVisibleSecurityState()->should_suppress_legacy_tls_warning);
-  EXPECT_EQ(helper->GetSecurityLevel(), security_state::SECURE);
-
-  // Reset the config to be empty.
-  InitializeEmptyLegacyTLSConfig();
-  ASSERT_FALSE(ShouldSuppressLegacyTLSWarning(GURL(kLegacyTLSURL)));
-}
 
 // Tests that the SSLVersionMin policy can disable the Legacy TLS security
 // warning.
@@ -1882,46 +1840,12 @@
   ui_test_utils::NavigateToURL(browser(), GURL(kLegacyTLSURL));
 
   EXPECT_FALSE(helper->GetVisibleSecurityState()->connection_used_legacy_tls);
-  EXPECT_FALSE(
-      helper->GetVisibleSecurityState()->should_suppress_legacy_tls_warning);
   EXPECT_EQ(helper->GetSecurityLevel(), security_state::SECURE);
 
   g_browser_process->local_state()->SetString(prefs::kSSLVersionMin,
                                               original_pref);
 }
 
-// Tests that a page has consistent security state despite the config proto
-// getting loaded during the page visit lifetime (see crbug.com/1011089).
-IN_PROC_BROWSER_TEST_F(BrowserTestNonsecureURLRequestWithLegacyTLSWarnings,
-                       LegacyTLSDelayedConfigLoad) {
-  // Navigate to an affected page before the config proto has been set.
-  auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
-  auto* helper = SecurityStateTabHelper::FromWebContents(web_contents);
-
-  ui_test_utils::NavigateToURL(browser(), GURL(kLegacyTLSURL));
-
-  EXPECT_TRUE(helper->GetVisibleSecurityState()->connection_used_legacy_tls);
-  EXPECT_TRUE(
-      helper->GetVisibleSecurityState()->should_suppress_legacy_tls_warning);
-  EXPECT_EQ(helper->GetSecurityLevel(), security_state::SECURE);
-
-  // Set the config proto.
-  InitializeEmptyLegacyTLSConfig();
-
-  // Security state for the current page should not change.
-  EXPECT_TRUE(helper->GetVisibleSecurityState()->connection_used_legacy_tls);
-  EXPECT_TRUE(
-      helper->GetVisibleSecurityState()->should_suppress_legacy_tls_warning);
-  EXPECT_EQ(helper->GetSecurityLevel(), security_state::SECURE);
-
-  // Refreshing the page should update the page's security state to now show a
-  // warning.
-  ui_test_utils::NavigateToURL(browser(), GURL(kLegacyTLSURL));
-  EXPECT_TRUE(helper->GetVisibleSecurityState()->connection_used_legacy_tls);
-  EXPECT_FALSE(
-      helper->GetVisibleSecurityState()->should_suppress_legacy_tls_warning);
-  EXPECT_EQ(helper->GetSecurityLevel(), security_state::WARNING);
-}
 
 // Tests that the Not Secure chip does not show for error pages on http:// URLs.
 // Regression test for https://crbug.com/760647.
@@ -2236,13 +2160,6 @@
 };
 
 IN_PROC_BROWSER_TEST_P(SignedExchangeSecurityStateTest, SecurityLevelIsSecure) {
-  // Initialize an empty config for the legacy TLS experiment. Without a config,
-  // all sites are treated as control. This test specifically enables the legacy
-  // TLS experiment as a regression test for https://crbug.com/1041773, in which
-  // the legacy TLS experiment code incorrectly labeled SXGs as using a
-  // deprecated TLS version.
-  InitializeEmptyLegacyTLSConfig();
-
   embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
   ASSERT_TRUE(embedded_test_server()->Start());
 
@@ -2269,13 +2186,6 @@
 
 IN_PROC_BROWSER_TEST_P(SignedExchangeSecurityStateTest,
                        SecurityLevelIsSecureAfterPrefetch) {
-  // Initialize an empty config for the legacy TLS experiment. Without a config,
-  // all sites are treated as control. This test specifically enables the legacy
-  // TLS experiment as a regression test for https://crbug.com/1041773, in which
-  // the legacy TLS experiment code incorrectly labeled SXGs as using a
-  // deprecated TLS version.
-  InitializeEmptyLegacyTLSConfig();
-
   embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
   ASSERT_TRUE(embedded_test_server()->Start());
 
diff --git a/chrome/browser/ssl/security_state_tab_helper_unittest.cc b/chrome/browser/ssl/security_state_tab_helper_unittest.cc
index 653a134..1d0b310 100644
--- a/chrome/browser/ssl/security_state_tab_helper_unittest.cc
+++ b/chrome/browser/ssl/security_state_tab_helper_unittest.cc
@@ -111,7 +111,6 @@
 // uses legacy TLS (TLS 1.0/1.1).
 TEST_F(SecurityStateTabHelperHistogramTest, LegacyTLSFormSubmissionHistogram) {
   base::HistogramTester histograms;
-  InitializeEmptyLegacyTLSConfig();
 
   auto navigation =
       CreateLegacyTLSNavigation(GURL(kLegacyTLSURL), web_contents());
@@ -123,28 +122,10 @@
 }
 
 // Tests that form submission histograms are recorded as not coming from a page
-// that triggered legacy TLS warnings for a page that uses legacy TLS but is
-// marked as a control site that should suppress legacy TLS warnings.
-TEST_F(SecurityStateTabHelperHistogramTest,
-       LegacyTLSControlSiteFormSubmissionHistogram) {
-  base::HistogramTester histograms;
-  InitializeLegacyTLSConfigWithControl();
-
-  auto navigation =
-      CreateLegacyTLSNavigation(GURL(kLegacyTLSURL), web_contents());
-  navigation->Commit();
-
-  StartNavigation(/*is_form=*/true, /*is_main_frame=*/true);
-
-  histograms.ExpectUniqueSample("Security.LegacyTLS.FormSubmission", false, 1);
-}
-
-// Tests that form submission histograms are recorded as not coming from a page
 // that triggered legacy TLS warnings for a page that uses modern TLS.
 TEST_F(SecurityStateTabHelperHistogramTest,
        LegacyTLSGoodSiteFormSubmissionHistogram) {
   base::HistogramTester histograms;
-  InitializeEmptyLegacyTLSConfig();
 
   auto navigation =
       CreateNonlegacyTLSNavigation(GURL("https://good.test"), web_contents());
diff --git a/chrome/browser/ssl/ssl_browsertest.cc b/chrome/browser/ssl/ssl_browsertest.cc
index 70efd701..ed5203c 100644
--- a/chrome/browser/ssl/ssl_browsertest.cc
+++ b/chrome/browser/ssl/ssl_browsertest.cc
@@ -4075,6 +4075,13 @@
 // without interstitial page showing.
 #if !BUILDFLAG(IS_CHROMEOS_ASH)  // Chrome OS does not support the flag.
 IN_PROC_BROWSER_TEST_F(SSLUITestIgnoreCertErrorsBySPKIWSS, TestWSSExpired) {
+  // TODO(crbug.com/1176880): Return early on macOS 10.11, because the
+  // WSS SpawnedTestServer does not support modern TLS on the 10.11 bot.
+#if defined(OS_MAC)
+  if (base::mac::IsAtMostOS10_11())
+    return;
+#endif
+
   ASSERT_TRUE(wss_server_expired_.Start());
 
   // Setup page title observer.
@@ -7764,6 +7771,13 @@
       "*The connection used to load resources from https://%s:%s*",
       url.host().c_str(), url.port().c_str()));
   ui_test_utils::NavigateToURL(browser(), url);
+
+  // Click through the interstitial (the console warning is only shown on the
+  // affected page, not the interstitial).
+  auto* tab = browser()->tab_strip_model()->GetActiveWebContents();
+  WaitForInterstitial(tab);
+  ProceedThroughInterstitial(tab);
+
   console_observer.Wait();
   EXPECT_TRUE(base::MatchPattern(console_observer.GetMessageAt(0u),
                                  "*will be disabled in the future*"));
@@ -7783,6 +7797,13 @@
       "*The connection used to load resources from https://%s:%s*",
       url.host().c_str(), url.port().c_str()));
   ui_test_utils::NavigateToURL(browser(), url);
+
+  // Click through the interstitial (the console warning is only shown on the
+  // affected page, not the interstitial).
+  auto* tab = browser()->tab_strip_model()->GetActiveWebContents();
+  WaitForInterstitial(tab);
+  ProceedThroughInterstitial(tab);
+
   console_observer.Wait();
   EXPECT_TRUE(base::MatchPattern(console_observer.GetMessageAt(0u),
                                  "*will be disabled in the future*"));
@@ -7790,53 +7811,12 @@
                                  "*should enable TLS 1.2 or later*"));
 }
 
-// Warnings should show for subresources, but cap after a limit.
-IN_PROC_BROWSER_TEST_F(TLSLegacyVersionSSLUITest, ManySubresources) {
-  SetTLSVersion(net::SSL_PROTOCOL_VERSION_TLS1);
-  content::SetupCrossSiteRedirector(https_server());
-  ASSERT_TRUE(https_server()->Start());
-  GURL url(https_server()->GetURL("/ssl/page_with_many_subresources.html"));
-  WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
-
-  // Observe the message for a cross-site subresource.
-  {
-    content::WebContentsConsoleObserver console_observer(tab);
-    console_observer.SetPattern("*https://a.test*");
-    ui_test_utils::NavigateToURL(browser(), url);
-    console_observer.Wait();
-    EXPECT_TRUE(base::MatchPattern(console_observer.GetMessageAt(0u),
-                                   "*will be disabled in the future*"));
-    EXPECT_TRUE(base::MatchPattern(console_observer.GetMessageAt(0u),
-                                   "*should enable TLS 1.2 or later*"));
-  }
-  // Observe that the message caps out after some number of subresources.
-  {
-    content::WebContentsConsoleObserver console_observer(tab);
-    console_observer.SetPattern("*Additional resources*");
-    ui_test_utils::NavigateToURL(browser(), url);
-    console_observer.Wait();
-    EXPECT_TRUE(base::MatchPattern(console_observer.GetMessageAt(0u),
-                                   "*will be disabled in the future*"));
-    EXPECT_TRUE(base::MatchPattern(console_observer.GetMessageAt(0u),
-                                   "*should enable TLS 1.2 or later*"));
-  }
-}
-
 class LegacyTLSInterstitialTest : public TLSLegacyVersionSSLUITest {
  public:
   LegacyTLSInterstitialTest() {
     feature_list_.InitAndEnableFeature(net::features::kLegacyTLSEnforced);
   }
 
-  void SetUpOnMainThread() override {
-    TLSLegacyVersionSSLUITest::SetUpOnMainThread();
-
-    // Set up browser and network service configs to be empty by default.
-    InitializeEmptyLegacyTLSConfig();
-    base::RunLoop run_loop;
-    InitializeEmptyLegacyTLSConfigNetworkService(&run_loop);
-  }
-
  private:
   base::test::ScopedFeatureList feature_list_;
 
@@ -7981,27 +7961,6 @@
       security_interstitials::MetricsHelper::DONT_PROCEED, 1);
 }
 
-// Check that we don't show the interstitial for sites in the control set.
-IN_PROC_BROWSER_TEST_F(LegacyTLSInterstitialTest, ControlSiteNoInterstitial) {
-  InitializeLegacyTLSConfigWithControl();
-  base::RunLoop run_loop;
-  InitializeLegacyTLSConfigWithControlNetworkService(&run_loop);
-
-  SetTLSVersion(net::SSL_PROTOCOL_VERSION_TLS1);
-  ASSERT_TRUE(https_server()->Start());
-
-  base::HistogramTester histograms;
-
-  ui_test_utils::NavigateToURL(
-      browser(), https_server()->GetURL(kLegacyTLSHost, "/ssl/google.html"));
-  auto* tab = browser()->tab_strip_model()->GetActiveWebContents();
-  EXPECT_FALSE(
-      chrome_browser_interstitials::IsShowingLegacyTLSInterstitial(tab));
-
-  // Interstitial metrics should not have been recorded from this navigation.
-  histograms.ExpectTotalCount(SSLErrorHandler::GetHistogramNameForTesting(), 0);
-}
-
 // A WebContentsObserver that allows the user to wait for a navigation, and
 // check whether the resource for the navigation was loaded from cache or not.
 class CacheNavigationObserver : public content::WebContentsObserver {
@@ -8050,63 +8009,6 @@
   observer.WaitForNavigation();  // Will fail if resource is loaded from cache.
 }
 
-// Tests that resources from control sites are cached, even if they are loaded
-// over legacy TLS.
-IN_PROC_BROWSER_TEST_F(LegacyTLSInterstitialTest, ControlSitePagesCached) {
-  InitializeLegacyTLSConfigWithControl();
-  base::RunLoop run_loop;
-  InitializeLegacyTLSConfigWithControlNetworkService(&run_loop);
-
-  // Connect over TLS 1.0 to a site in the control list.
-  SetTLSVersion(net::SSL_PROTOCOL_VERSION_TLS1);
-  ASSERT_TRUE(https_server()->Start());
-  ui_test_utils::NavigateToURL(
-      browser(), https_server()->GetURL(kLegacyTLSHost, "/cachetime"));
-  auto* tab1 = browser()->tab_strip_model()->GetActiveWebContents();
-  EXPECT_FALSE(
-      chrome_browser_interstitials::IsShowingLegacyTLSInterstitial(tab1));
-
-  // Open a new tab and navigate so that resources are fetched via the disk
-  // cache. Navigating to the same URL in the same tab triggers a refresh which
-  // will not check the disk cache.
-  ui_test_utils::NavigateToURLWithDisposition(
-      browser(), GURL("about:blank"), WindowOpenDisposition::NEW_FOREGROUND_TAB,
-      ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB |
-          ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
-  auto* tab2 = browser()->tab_strip_model()->GetActiveWebContents();
-
-  CacheNavigationObserver observer(tab2, true);
-  ui_test_utils::NavigateToURL(
-      browser(), https_server()->GetURL(kLegacyTLSHost, "/cachetime"));
-  observer.WaitForNavigation();  // Will fail if not loaded from cache.
-}
-
-// Tests that resources loaded over legacy TLS but are control sites should be
-// cached.
-IN_PROC_BROWSER_TEST_F(LegacyTLSInterstitialTest, NormalPagesCached) {
-  // Connect to a non-legacy TLS site.
-  ASSERT_TRUE(https_server()->Start());
-  ui_test_utils::NavigateToURL(
-      browser(), https_server()->GetURL(kLegacyTLSHost, "/cachetime"));
-  auto* tab1 = browser()->tab_strip_model()->GetActiveWebContents();
-  EXPECT_FALSE(
-      chrome_browser_interstitials::IsShowingLegacyTLSInterstitial(tab1));
-
-  // Open a new tab and navigate so that resources are fetched via the disk
-  // cache. Navigating to the same URL in the same tab triggers a refresh which
-  // will not check the disk cache.
-  ui_test_utils::NavigateToURLWithDisposition(
-      browser(), GURL("about:blank"), WindowOpenDisposition::NEW_FOREGROUND_TAB,
-      ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB |
-          ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
-  auto* tab2 = browser()->tab_strip_model()->GetActiveWebContents();
-
-  CacheNavigationObserver observer(tab2, true);
-  ui_test_utils::NavigateToURL(
-      browser(), https_server()->GetURL(kLegacyTLSHost, "/cachetime"));
-  observer.WaitForNavigation();  // Will fail if not loaded from cache.
-}
-
 // Tests that a page with legacy TLS and HSTS shows a bypassable interstitial
 // rather than a hard non-bypassable HSTS warning.
 IN_PROC_BROWSER_TEST_F(LegacyTLSInterstitialTest, LegacyTLSNotFatal) {
@@ -8134,30 +8036,6 @@
   EXPECT_EQ(security_interstitials::CMD_TEXT_FOUND, result);
 }
 
-// Tests that the legacy TLS control config applies to subdomains if the
-// registrable domain is in the control config.
-IN_PROC_BROWSER_TEST_F(LegacyTLSInterstitialTest,
-                       ControlConfigIncludesSubdomains) {
-  InitializeLegacyTLSConfigWithControl();
-  base::RunLoop run_loop;
-  InitializeLegacyTLSConfigWithControlNetworkService(&run_loop);
-
-  SetTLSVersion(net::SSL_PROTOCOL_VERSION_TLS1);
-  ASSERT_TRUE(https_server()->Start());
-
-  base::HistogramTester histograms;
-
-  ui_test_utils::NavigateToURL(
-      browser(), https_server()->GetURL(std::string("www.") + kLegacyTLSHost,
-                                        "/ssl/google.html"));
-  auto* tab = browser()->tab_strip_model()->GetActiveWebContents();
-  EXPECT_FALSE(
-      chrome_browser_interstitials::IsShowingLegacyTLSInterstitial(tab));
-
-  // Interstitial metrics should not have been recorded from this navigation.
-  histograms.ExpectTotalCount(SSLErrorHandler::GetHistogramNameForTesting(), 0);
-}
-
 // Checks that SimpleURLLoader, which uses services/network/url_loader.cc, goes
 // through the new NetworkServiceClient interface to deliver cert error
 // notifications to the browser which then overrides the certificate error.
diff --git a/chrome/browser/ssl/tls_deprecation_config.cc b/chrome/browser/ssl/tls_deprecation_config.cc
deleted file mode 100644
index ae6ba0c..0000000
--- a/chrome/browser/ssl/tls_deprecation_config.cc
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ssl/tls_deprecation_config.h"
-
-#include <algorithm>
-#include <memory>
-#include <string>
-#include <utility>
-
-#include "base/logging.h"
-#include "base/no_destructor.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/string_util.h"
-#include "crypto/sha2.h"
-#include "services/network/public/proto/tls_deprecation_config.pb.h"
-#include "url/gurl.h"
-
-namespace {
-
-class TLSDeprecationConfigSingleton {
- public:
-  void SetProto(
-      std::unique_ptr<chrome_browser_ssl::LegacyTLSExperimentConfig> proto) {
-    // Check that the version id is set (otherwise tests can fail in confusing
-    // ways).
-    DCHECK(proto->has_version_id());
-    proto_ = std::move(proto);
-  }
-
-  chrome_browser_ssl::LegacyTLSExperimentConfig* GetProto() const {
-    return proto_.get();
-  }
-
-  void Reset() { proto_.reset(); }
-
-  static TLSDeprecationConfigSingleton& GetInstance() {
-    static base::NoDestructor<TLSDeprecationConfigSingleton> instance;
-    return *instance;
-  }
-
- private:
-  std::unique_ptr<chrome_browser_ssl::LegacyTLSExperimentConfig> proto_;
-};
-
-}  // namespace
-
-void SetRemoteTLSDeprecationConfig(const std::string& binary_config) {
-  auto proto =
-      std::make_unique<chrome_browser_ssl::LegacyTLSExperimentConfig>();
-  if (binary_config.empty() || !proto->ParseFromString(binary_config)) {
-    DVLOG(1) << "Failed parsing legacy TLS config proto";
-    return;
-  }
-  TLSDeprecationConfigSingleton::GetInstance().SetProto(std::move(proto));
-}
-
-bool ShouldSuppressLegacyTLSWarning(const GURL& url) {
-  if (!url.has_host() || !url.SchemeIsCryptographic())
-    return false;
-
-  auto* proto = TLSDeprecationConfigSingleton::GetInstance().GetProto();
-  // If the config is not yet loaded, we err on the side of not showing warnings
-  // for any sites.
-  if (!proto)
-    return true;
-
-  // Convert bytes from crypto::SHA256 so we can compare to the proto contents.
-  std::string host_hash_bytes = crypto::SHA256HashString(url.host_piece());
-  std::string host_hash = base::ToLowerASCII(
-      base::HexEncode(host_hash_bytes.c_str(), host_hash_bytes.size()));
-  const auto& control_site_hashes = proto->control_site_hashes();
-
-  // Perform binary search on the sorted list of control site hashes to check
-  // if the input URL's hostname is included.
-  auto lower = std::lower_bound(control_site_hashes.begin(),
-                                control_site_hashes.end(), host_hash);
-
-  return lower != control_site_hashes.end() && *lower == host_hash;
-}
-
-void ResetTLSDeprecationConfigForTesting() {
-  TLSDeprecationConfigSingleton::GetInstance().Reset();
-}
diff --git a/chrome/browser/ssl/tls_deprecation_config.h b/chrome/browser/ssl/tls_deprecation_config.h
deleted file mode 100644
index 613a1c7..0000000
--- a/chrome/browser/ssl/tls_deprecation_config.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_SSL_TLS_DEPRECATION_CONFIG_H_
-#define CHROME_BROWSER_SSL_TLS_DEPRECATION_CONFIG_H_
-
-#include <string>
-
-class GURL;
-
-void SetRemoteTLSDeprecationConfig(const std::string& binary_config);
-
-bool ShouldSuppressLegacyTLSWarning(const GURL& url);
-
-void ResetTLSDeprecationConfigForTesting();
-
-#endif  // CHROME_BROWSER_SSL_TLS_DEPRECATION_CONFIG_H_
diff --git a/chrome/browser/ssl/tls_deprecation_config_unittest.cc b/chrome/browser/ssl/tls_deprecation_config_unittest.cc
deleted file mode 100644
index 6d957ce..0000000
--- a/chrome/browser/ssl/tls_deprecation_config_unittest.cc
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ssl/tls_deprecation_config.h"
-
-#include <memory>
-#include <string>
-
-#include "chrome/browser/ssl/tls_deprecation_test_utils.h"
-#include "services/network/public/proto/tls_deprecation_config.pb.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-using chrome_browser_ssl::LegacyTLSExperimentConfig;
-
-class TLSDeprecationConfigTest : public testing::Test {
- protected:
-  void SetUp() override { ResetTLSDeprecationConfigForTesting(); }
-  void TearDown() override { ResetTLSDeprecationConfigForTesting(); }
-};
-
-// Tests the case where no proto has been set by the component installer.
-TEST_F(TLSDeprecationConfigTest, NoProto) {
-  EXPECT_TRUE(ShouldSuppressLegacyTLSWarning(GURL("https://example.test")));
-}
-
-// This tests that when no sites are in the control set,
-// IsTLSDeprecationControlSite() returns false.
-TEST_F(TLSDeprecationConfigTest, NoControlSites) {
-  GURL control_site("https://control.test");
-  std::string control_site_hex =
-      "f12b47771bb3c2bcc85a5347d195523013ec5a23b4c761b5d6aacf04bafc5e23";
-
-  // Setup proto (as if read from component installer), but don't add any
-  // control sites to it.
-  auto config = std::make_unique<LegacyTLSExperimentConfig>();
-  config->set_version_id(1);
-  SetRemoteTLSDeprecationConfig(config->SerializeAsString());
-
-  EXPECT_FALSE(ShouldSuppressLegacyTLSWarning(control_site));
-}
-
-// This tests that when only a single control site is in the control set,
-// IsTLSDeprecationControlSite() works correctly for the site in the control and
-// for sites not in the control.
-TEST_F(TLSDeprecationConfigTest, SingleControlSite) {
-  GURL control_site("https://control.test");
-  std::string control_site_hex =
-      "f12b47771bb3c2bcc85a5347d195523013ec5a23b4c761b5d6aacf04bafc5e23";
-  GURL non_control_site("https://not-control.test");
-  std::string non_control_site_hex =
-      "a11de9f014acb9e53c8997d9c48b10ed23c8b2fa7e790b125ea5fb78af5359cb";
-  GURL http_site("http://noncryptographic.test");
-  std::string http_site_hex =
-      "fd288ddecdac673aedb343e41117443f1ce88114dcc7bfa3c4a65a1e725d8fbe";
-
-  // Setup proto (as if read from component installer).
-  auto config = std::make_unique<LegacyTLSExperimentConfig>();
-  config->set_version_id(1);
-  config->add_control_site_hashes(control_site_hex);
-
-  SetRemoteTLSDeprecationConfig(config->SerializeAsString());
-
-  EXPECT_TRUE(ShouldSuppressLegacyTLSWarning(control_site));
-  EXPECT_FALSE(ShouldSuppressLegacyTLSWarning(non_control_site));
-
-  // And HTTP sites should not count either.
-  EXPECT_FALSE(ShouldSuppressLegacyTLSWarning(http_site));
-}
-
-// This tests that the binary search in IsTLSDeprecationControlSite() works for
-// both a site that is in the control set and a site that is not.
-TEST_F(TLSDeprecationConfigTest, ManyControlSites) {
-  const struct {
-    GURL url;
-    std::string hash;
-  } kControlSites[] = {
-      // These must be in alphanumeric (0-9a-z) order by their hashes.
-      {GURL("https://control6.test"),
-       "27ce26e09cae16b1bd1914678d82e98326362f4a06b1b3e81c8ea96018be3b08"},
-      {GURL("https://control9.test"),
-       "2904bff8d79d14e7b90d7cb499ce0bcd619f76818d9512f4c855d2ab738f42e8"},
-      {GURL("https://control2.test"),
-       "4c37847b9688f807d4853ac9fefca228decbc3bb3851be9f75a8898b5f483435"},
-      {GURL("https://control4.test"),
-       "57e66a432b7661a426f4e9f470f7f5ff259870931277ac4e00853d3a237895f1"},
-      {GURL("https://control0.test"),
-       "7653aed3ed0e08c4abd4035e6f24ad1b4705387bffed42c5955abdcabda5b2fc"},
-      {GURL("https://control5.test"),
-       "8cca9122cb6f459552422aef9536daf9fce3a4648729fe2ab17bc63a5c8cd9d9"},
-      {GURL("https://control7.test"),
-       "8d9ff576c2e4a834dd75b1a0b2acbc0b3ce89035feb1c1645e96d24e803a08a5"},
-      {GURL("https://control1.test"),
-       "d4a33fe8bbb13fce2300bd1788b2df1722605f8f30bc1119c8ca3f8aea32e1e5"},
-      {GURL("https://control8.test"),
-       "e161ddfb174571df3db808e18f8177c9bbfdb545108616bd62434280b5ca2b37"},
-      {GURL("https://control3.test"),
-       "f8bb464ff13462fde12aac03f9e66f3d37a3b9359992fca01428480bd1418e02"}};
-  GURL non_control_site("https://example-not-control.test");
-
-  // Setup proto (as if read from component installer).
-  auto config = std::make_unique<LegacyTLSExperimentConfig>();
-  config->set_version_id(1);
-  for (auto& site : kControlSites) {
-    config->add_control_site_hashes(site.hash);
-  }
-  SetRemoteTLSDeprecationConfig(config->SerializeAsString());
-
-  for (auto& site : kControlSites) {
-    EXPECT_TRUE(ShouldSuppressLegacyTLSWarning(site.url));
-  }
-
-  EXPECT_FALSE(ShouldSuppressLegacyTLSWarning(non_control_site));
-}
diff --git a/chrome/browser/ssl/tls_deprecation_test_utils.cc b/chrome/browser/ssl/tls_deprecation_test_utils.cc
index 57158a6..c53a4ec 100644
--- a/chrome/browser/ssl/tls_deprecation_test_utils.cc
+++ b/chrome/browser/ssl/tls_deprecation_test_utils.cc
@@ -4,9 +4,6 @@
 
 #include "chrome/browser/ssl/tls_deprecation_test_utils.h"
 
-#include "base/run_loop.h"
-#include "chrome/browser/ssl/tls_deprecation_config.h"
-#include "content/public/browser/network_service_instance.h"
 #include "content/public/test/navigation_simulator.h"
 #include "net/cert/x509_certificate.h"
 #include "net/ssl/ssl_config.h"
@@ -14,50 +11,8 @@
 #include "net/ssl/ssl_info.h"
 #include "net/test/cert_test_util.h"
 #include "net/test/test_data_directory.h"
-#include "services/network/public/mojom/network_service.mojom.h"
-#include "services/network/public/proto/tls_deprecation_config.pb.h"
 #include "url/gurl.h"
 
-void InitializeEmptyLegacyTLSConfig() {
-  auto config =
-      std::make_unique<chrome_browser_ssl::LegacyTLSExperimentConfig>();
-  config->set_version_id(1);
-  // Set browser-side config.
-  SetRemoteTLSDeprecationConfig(config->SerializeAsString());
-}
-
-void InitializeEmptyLegacyTLSConfigNetworkService(base::RunLoop* run_loop) {
-  auto config =
-      std::make_unique<chrome_browser_ssl::LegacyTLSExperimentConfig>();
-  config->set_version_id(1);
-  // Push config to network service.
-  content::GetNetworkService()->UpdateLegacyTLSConfig(
-      base::as_bytes(base::make_span(config->SerializeAsString())),
-      run_loop->QuitClosure());
-  run_loop->Run();
-}
-
-void InitializeLegacyTLSConfigWithControl() {
-  auto config =
-      std::make_unique<chrome_browser_ssl::LegacyTLSExperimentConfig>();
-  config->set_version_id(1);
-  config->add_control_site_hashes(kLegacyTlsControlUrlHash);
-  SetRemoteTLSDeprecationConfig(config->SerializeAsString());
-}
-
-void InitializeLegacyTLSConfigWithControlNetworkService(
-    base::RunLoop* run_loop) {
-  auto config =
-      std::make_unique<chrome_browser_ssl::LegacyTLSExperimentConfig>();
-  config->set_version_id(1);
-  config->add_control_site_hashes(kLegacyTlsControlUrlHash);
-  // Push config to network service.
-  content::GetNetworkService()->UpdateLegacyTLSConfig(
-      base::as_bytes(base::make_span(config->SerializeAsString())),
-      run_loop->QuitClosure());
-  run_loop->Run();
-}
-
 std::unique_ptr<content::NavigationSimulator> CreateTLSNavigation(
     const GURL& url,
     content::WebContents* web_contents,
diff --git a/chrome/browser/ssl/tls_deprecation_test_utils.h b/chrome/browser/ssl/tls_deprecation_test_utils.h
index 95a13ff78..7fbed48 100644
--- a/chrome/browser/ssl/tls_deprecation_test_utils.h
+++ b/chrome/browser/ssl/tls_deprecation_test_utils.h
@@ -7,10 +7,6 @@
 
 #include <memory>
 
-namespace base {
-class RunLoop;
-}  // namespace base
-
 namespace content {
 class NavigationSimulator;
 class WebContents;
@@ -20,18 +16,6 @@
 
 const char kLegacyTLSHost[] = "example-nonsecure.test";
 const char kLegacyTLSURL[] = "https://example-nonsecure.test";
-// SHA-256 hash of kLegacyTLSURL for use in setting a control site in the
-// LegacyTLSExperimentConfig for Legacy TLS tests. Generated with
-// `echo -n "example-nonsecure.test" | openssl sha256`.
-const char kLegacyTlsControlUrlHash[] =
-    "aaa334d67e96314a14d5679b2309e72f96bf30f9fe9b218e5db3d57be8baa94c";
-
-void InitializeEmptyLegacyTLSConfig();
-void InitializeEmptyLegacyTLSConfigNetworkService(base::RunLoop* run_loop);
-
-void InitializeLegacyTLSConfigWithControl();
-void InitializeLegacyTLSConfigWithControlNetworkService(
-    base::RunLoop* run_loop);
 
 // Creates and starts a simulated navigation using the specified SSL protocol
 // version (e.g., net::SSL_CONNECTION_VERSION_TLS1_2).
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabData.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabData.java
index 76783898..a80b1bb8 100644
--- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabData.java
+++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabData.java
@@ -21,6 +21,7 @@
 import org.chromium.chrome.browser.page_annotations.PageAnnotation;
 import org.chromium.chrome.browser.page_annotations.PageAnnotationUtils;
 import org.chromium.chrome.browser.page_annotations.PageAnnotationsServiceFactory;
+import org.chromium.chrome.browser.page_annotations.ProductPriceUpdatePageAnnotation;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.proto.ShoppingPersistedTabData.ShoppingPersistedTabDataProto;
@@ -90,6 +91,17 @@
     @VisibleForTesting
     protected EmptyTabObserver mUrlUpdatedObserver;
 
+    @IntDef({PriceDropMethod.NONE, PriceDropMethod.LEGACY, PriceDropMethod.NEW})
+    @Retention(RetentionPolicy.SOURCE)
+    protected @interface PriceDropMethod {
+        int NONE = 0;
+        int LEGACY = 1;
+        int NEW = 2;
+    }
+
+    @VisibleForTesting
+    protected @PriceDropMethod int mPriceDropMethod = PriceDropMethod.NEW;
+
     // Lazy initialization of OptimizationGuideBridgeFactory
     private static class OptimizationGuideBridgeFactoryHolder {
         private static final OptimizationGuideBridgeFactory sOptimizationGuideBridgeFactory;
@@ -209,12 +221,14 @@
     /**
      * Whether a BuyableProductAnnotation was found or not
      */
-    @IntDef({FoundBuyableProductAnnotation.NOT_FOUND, FoundBuyableProductAnnotation.FOUND})
+    @IntDef({FoundBuyableProductAnnotation.NOT_FOUND, FoundBuyableProductAnnotation.FOUND,
+            FoundBuyableProductAnnotation.FOUND_WITH_PRICE_UPDATE})
     @Retention(RetentionPolicy.SOURCE)
     @interface FoundBuyableProductAnnotation {
         int NOT_FOUND = 0;
         int FOUND = 1;
-        int NUM_ENTRIES = 2;
+        int FOUND_WITH_PRICE_UPDATE = 2;
+        int NUM_ENTRIES = 3;
     }
 
     /**
@@ -240,7 +254,17 @@
 
         BuyableProductPageAnnotation buyableProduct =
                 PageAnnotationUtils.getAnnotation(annotations, BuyableProductPageAnnotation.class);
-        if (buyableProduct != null) {
+
+        ProductPriceUpdatePageAnnotation productPriceUpdate = PageAnnotationUtils.getAnnotation(
+                annotations, ProductPriceUpdatePageAnnotation.class);
+
+        if (buyableProduct != null && productPriceUpdate != null) {
+            res.setPriceMicros(productPriceUpdate.getNewPriceMicros());
+            res.setPreviousPriceMicros(productPriceUpdate.getOldPriceMicros());
+            res.setCurrencyCode(productPriceUpdate.getCurrencyCode());
+            res.setLastUpdatedMs(System.currentTimeMillis());
+            foundBuyableProductAnnotation = FoundBuyableProductAnnotation.FOUND_WITH_PRICE_UPDATE;
+        } else if (buyableProduct != null) {
             res.setPriceMicros(
                     buyableProduct.getCurrentPriceMicros(), previousShoppingPersistedTabData);
             res.setCurrencyCode(buyableProduct.getCurrencyCode());
@@ -253,7 +277,9 @@
                 foundBuyableProductAnnotation, FoundBuyableProductAnnotation.NUM_ENTRIES);
         // Only persist this ShoppingPersistedTabData if it was correctly populated from the
         // response
-        if (foundBuyableProductAnnotation == FoundBuyableProductAnnotation.FOUND) {
+        if (foundBuyableProductAnnotation == FoundBuyableProductAnnotation.FOUND
+                || foundBuyableProductAnnotation
+                        == FoundBuyableProductAnnotation.FOUND_WITH_PRICE_UPDATE) {
             res.enableSaving();
             return res;
         }
@@ -282,6 +308,12 @@
         save();
     }
 
+    @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+    public void setPriceMicros(long priceMicros) {
+        mPriceMicros = priceMicros;
+        save();
+    }
+
     protected void setCurrencyCode(String currencyCode) {
         mCurrencyCode = currencyCode;
         save();
@@ -325,7 +357,8 @@
      * Deprecate getPrice and getPriceString(). Change price and previousPriceString
      * representations to be numeric to make drop comparison easier.
      */
-    public PriceDrop getPriceDrop() {
+    public PriceDrop getPriceDropLegacy() {
+        assert mPriceDropMethod == PriceDropMethod.LEGACY;
         if (mPriceMicros == NO_PRICE_KNOWN || mPreviousPriceMicros == NO_PRICE_KNOWN
                 || !isQualifyingPriceDrop() || isPriceChangeStale()) {
             return null;
@@ -338,6 +371,32 @@
         return new PriceDrop(formattedPrice, formattedPreviousPrice);
     }
 
+    /**
+     * @return {@link PriceDrop} relating to the main offer in the page.
+     */
+    public PriceDrop getPriceDrop() {
+        assert mPriceDropMethod == PriceDropMethod.NEW;
+        if (!isValidPriceDropUpdate() || isPriceChangeStale()) {
+            return null;
+        }
+        return createPriceDrop(mPriceMicros, mPreviousPriceMicros);
+    }
+
+    private boolean isValidPriceDropUpdate() {
+        return mPriceMicros != NO_PRICE_KNOWN && mPreviousPriceMicros != NO_PRICE_KNOWN
+                && mPriceMicros < mPreviousPriceMicros;
+    }
+
+    private PriceDrop createPriceDrop(long priceMicros, long previousPriceMicros) {
+        String formattedPrice = formatPrice(priceMicros);
+        String formattedPreviousPrice = formatPrice(previousPriceMicros);
+        if (formattedPrice.equals(formattedPreviousPrice)) {
+            return null;
+        }
+
+        return new PriceDrop(formattedPrice, formattedPreviousPrice);
+    }
+
     private boolean isPriceChangeStale() {
         return mLastPriceChangeTimeMs != NO_TRANSITIONS_OCCURRED
                 && System.currentTimeMillis() - mLastPriceChangeTimeMs > DISPLAY_TIME_MS.getValue();
diff --git a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelector.java b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelector.java
index e7a0b76..1bf4fdf 100644
--- a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelector.java
+++ b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelector.java
@@ -163,11 +163,6 @@
     boolean isTabStateInitialized();
 
     /**
-     * Merges the tab states from two tab models.
-     */
-    void mergeState();
-
-    /**
      * Prevents the TabModelSelector from destroying its tabs to allow for reparenting.
      *
      * This is only safe to be called immediately before destruction. After entering reparenting
diff --git a/chrome/browser/translate/android/translate_bridge.cc b/chrome/browser/translate/android/translate_bridge.cc
index 60e841a..18d8615 100644
--- a/chrome/browser/translate/android/translate_bridge.cc
+++ b/chrome/browser/translate/android/translate_bridge.cc
@@ -6,8 +6,8 @@
 
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
-#include "chrome/android/chrome_jni_headers/TranslateBridge_jni.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/language/android/jni_headers/TranslateBridge_jni.h"
 #include "chrome/browser/language/language_model_manager_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index f774e1bc..82bd45aa 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -2634,6 +2634,7 @@
       "//chrome/browser/resources:bluetooth_pairing_dialog_resources",
       "//chrome/browser/resources:internet_config_dialog_resources",
       "//chrome/browser/resources:internet_detail_dialog_resources",
+      "//chrome/browser/resources/chromeos:multidevice_setup_resources",
       "//chrome/browser/ui/app_list/search/cros_action_history:cros_action_proto",
       "//chrome/browser/ui/app_list/search/search_result_ranker:app_launch_event_logger_proto",
       "//chrome/browser/ui/app_list/search/search_result_ranker:app_launch_predictor_proto",
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index a2dc77f3..59050401 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -2187,9 +2187,6 @@
       <message name="IDS_APP_BANNER_ADD" desc="A button in an app banner to confirm adding a site to homescreen">
         Add
       </message>
-      <message name="IDS_ADDED_TO_HOMESCREEN" desc="Indicates that the website with the specified name was added to the user's Home screen.">
-        <ph name="NAME">%1$s<ex>Google</ex></ph> was added to your Home screen
-      </message>
       <message name="IDS_WEBAPK_INSTALL_IN_PROGRESS" desc="Indicates that an installation for the WebAPK for the specific website is already in progress.">
         Still adding previous site
       </message>
@@ -4099,6 +4096,58 @@
          Google Chrome as a Security Key
       </message>
 
+      <message name="IDS_CABLEV2_REGISTRATION_SUCCEEDED"
+        desc="The text of a small pop-up, that appears at the bottom of the screen on Android, informing the user that their attempt to register their phone as an authentication device was successful. The translation of the verb should hopefully match that in TC ID 1295743531323106866. (The attached image doesn't show the pop-up at the bottom because it's too fleeting to screenshot, so I've drawn in roughly what it looks like.)">
+         Registration succeeded
+      </message>
+
+      <message name="IDS_CABLEV2_REGISTRATION_FAILED"
+        desc="The text of a small pop-up, that appears at the bottom of the screen on Android, informing the user that their attempt to register their phone as an authentication device was unsuccessful. The translation of the verb should hopefully match that in TC ID 1295743531323106866. (The attached image doesn't show the pop-up at the bottom because it's too fleeting to screenshot, so I've drawn in roughly what it looks like.)">
+         Registration failed
+      </message>
+
+      <message name="IDS_CABLEV2_SIGN_IN_SUCCEEDED"
+        desc="The text of a small pop-up, that appears at the bottom of the screen on Android, informing the user that their attempt to sign-in using their phone as an authentication device was successful. The translation of 'sign-in' should hopefully match that in TC ID 6840924527405570820. (The attached image doesn't show the pop-up at the bottom because it's too fleeting to screenshot, so I've drawn in roughly what it looks like.)">
+         Sign-in succeeded
+      </message>
+
+      <message name="IDS_CABLEV2_SIGN_IN_FAILED"
+        desc="The text of a small pop-up, that appears at the bottom of the screen on Android, informing the user that their attempt to sign-in using their phone as an authentication device was unsuccessful. The translation of 'sign-in' should hopefully match that in TC ID 6840924527405570820. (The attached image doesn't show the pop-up at the bottom because it's too fleeting to screenshot, so I've drawn in roughly what it looks like.)">
+         Sign-in failed
+      </message>
+
+      <message name="IDS_CABLEV2_USB_PROMPT_TITLE" desc="The title of a screen that advises the user to try connecting their phone with a USB cable. This appears when the user is trying to sign in to a desktop/laptop by using their phone. If the connection isn't working (for example because they have no internet service on their phone) then we suggest that they can use a physical cable to connect the phone with the desktop/laptop. This title is rather generic and a different message is shown below to give precise instructions.">
+         Connection seems to be taking quite long…
+      </message>
+
+      <message name="IDS_CABLEV2_USB_PROMPT_BODY" desc="The body of a screen that advises the user to try connecting their phone with a USB cable. This appears when the user is trying to sign in to a desktop/laptop by using their phone. If the connection isn't working (for example because they have no internet service on their phone) then we suggest that they can use a physical cable to connect the phone with the desktop/laptop.">
+         Try connecting your phone with a USB cable
+      </message>
+
+      <message name="IDS_CABLEV2_USB_DISCON_TITLE" desc="The title of a screen that advises the user to disconnect their phone. This shows when the user has connected their phone to their computer using a USB cable and needs to disconnect it once they have finished their current actions.">
+         Connected with USB cable
+      </message>
+
+      <message name="IDS_CABLEV2_USB_DISCON_BODY" desc="The body of a screen that advises the user to disconnect their phone. This shows when the user has connected their phone to their computer using a USB cable and needs to disconnect it once they have finished their current actions.">
+         Disconnect when you’re done
+      </message>
+
+      <message name="IDS_CABLEV2_SERVERLINK_CONNECTING_TO_YOUR_DEVICE" desc="The title of a screen that is shown when a user is connecting their phone to a desktop or laptop computer in order to sign in with it.">
+         Connecting to your device…
+      </message>
+
+      <message name="IDS_CABLEV2_SERVERLINK_STATUS_CONNECTING" desc="A small subheader on a screen that is shown when a user is connecting their phone to a desktop or laptop computer in order to sign in with it.">
+        This may take a minute
+      </message>
+
+      <message name="IDS_CABLEV2_SERVERLINK_STATUS_CONNECTED" desc="A small subheader on a screen that is shown when a user is connecting their phone to a desktop or laptop computer in order to sign in with it. (In the attached image, this text will appear in place of the text 'This may take a minute'.) This text is shown once the connection to the desktop/laptop has completed and the phone is awaiting instructions.">
+        Connected to device
+      </message>
+
+      <message name="IDS_CABLEV2_SERVERLINK_STATUS_PROCESSING" desc="A small subheader on a screen that is shown when a user is connecting their phone to a desktop or laptop computer in order to sign in with it. (In the attached image, this text will appear in place of the text 'This may take a minute'.) This text is shown once a challenge from the desktop/laptop has been received.">
+        Processing request
+      </message>
+
       <!-- QR Code -->
       <message name="IDS_QR_CODE_SHARE_ICON_LABEL" desc="Icon label for sharing with QR Code activity.">
         QR Code
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_REGISTRATION_FAILED.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_REGISTRATION_FAILED.png.sha1
new file mode 100644
index 0000000..75c7c9a
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_REGISTRATION_FAILED.png.sha1
@@ -0,0 +1 @@
+c6b2bfbcabc6b89e8cc63289e7d24aeaecf5d519
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_REGISTRATION_SUCCEEDED.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_REGISTRATION_SUCCEEDED.png.sha1
new file mode 100644
index 0000000..75c7c9a
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_REGISTRATION_SUCCEEDED.png.sha1
@@ -0,0 +1 @@
+c6b2bfbcabc6b89e8cc63289e7d24aeaecf5d519
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_SERVERLINK_CONNECTING_TO_YOUR_DEVICE.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_SERVERLINK_CONNECTING_TO_YOUR_DEVICE.png.sha1
new file mode 100644
index 0000000..75411e5
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_SERVERLINK_CONNECTING_TO_YOUR_DEVICE.png.sha1
@@ -0,0 +1 @@
+f0ac2c19f6fb5dae246ae078f98df2c60d35d1e5
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_SERVERLINK_STATUS_CONNECTED.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_SERVERLINK_STATUS_CONNECTED.png.sha1
new file mode 100644
index 0000000..75411e5
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_SERVERLINK_STATUS_CONNECTED.png.sha1
@@ -0,0 +1 @@
+f0ac2c19f6fb5dae246ae078f98df2c60d35d1e5
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_SERVERLINK_STATUS_CONNECTING.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_SERVERLINK_STATUS_CONNECTING.png.sha1
new file mode 100644
index 0000000..75411e5
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_SERVERLINK_STATUS_CONNECTING.png.sha1
@@ -0,0 +1 @@
+f0ac2c19f6fb5dae246ae078f98df2c60d35d1e5
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_SERVERLINK_STATUS_PROCESSING.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_SERVERLINK_STATUS_PROCESSING.png.sha1
new file mode 100644
index 0000000..75411e5
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_SERVERLINK_STATUS_PROCESSING.png.sha1
@@ -0,0 +1 @@
+f0ac2c19f6fb5dae246ae078f98df2c60d35d1e5
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_SIGN_IN_FAILED.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_SIGN_IN_FAILED.png.sha1
new file mode 100644
index 0000000..75c7c9a
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_SIGN_IN_FAILED.png.sha1
@@ -0,0 +1 @@
+c6b2bfbcabc6b89e8cc63289e7d24aeaecf5d519
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_SIGN_IN_SUCCEEDED.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_SIGN_IN_SUCCEEDED.png.sha1
new file mode 100644
index 0000000..75c7c9a
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_SIGN_IN_SUCCEEDED.png.sha1
@@ -0,0 +1 @@
+c6b2bfbcabc6b89e8cc63289e7d24aeaecf5d519
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_USB_DISCON_BODY.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_USB_DISCON_BODY.png.sha1
new file mode 100644
index 0000000..f51b0788
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_USB_DISCON_BODY.png.sha1
@@ -0,0 +1 @@
+bb21f493a5e88b246ab3c082545d618a4e4ecf09
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_USB_DISCON_TITLE.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_USB_DISCON_TITLE.png.sha1
new file mode 100644
index 0000000..f51b0788
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_USB_DISCON_TITLE.png.sha1
@@ -0,0 +1 @@
+bb21f493a5e88b246ab3c082545d618a4e4ecf09
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_USB_PROMPT_BODY.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_USB_PROMPT_BODY.png.sha1
new file mode 100644
index 0000000..927cac4
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_USB_PROMPT_BODY.png.sha1
@@ -0,0 +1 @@
+71f4a1790fdbeaea43d07e1e9d5eaf5a73848d3b
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_USB_PROMPT_TITLE.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_USB_PROMPT_TITLE.png.sha1
new file mode 100644
index 0000000..927cac4
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_USB_PROMPT_TITLE.png.sha1
@@ -0,0 +1 @@
+71f4a1790fdbeaea43d07e1e9d5eaf5a73848d3b
\ No newline at end of file
diff --git a/chrome/browser/ui/app_list/search/chrome_search_result.cc b/chrome/browser/ui/app_list/search/chrome_search_result.cc
index 58f0a1d..4ce9ccf 100644
--- a/chrome/browser/ui/app_list/search/chrome_search_result.cc
+++ b/chrome/browser/ui/app_list/search/chrome_search_result.cc
@@ -95,6 +95,11 @@
   SetSearchResultMetadata();
 }
 
+void ChromeSearchResult::SetOmniboxType(OmniboxType omnibox_type) {
+  metadata_->omnibox_type = omnibox_type;
+  SetSearchResultMetadata();
+}
+
 void ChromeSearchResult::SetPositionPriority(float position_priority) {
   metadata_->position_priority = position_priority;
   SetSearchResultMetadata();
@@ -110,11 +115,6 @@
   SetSearchResultMetadata();
 }
 
-void ChromeSearchResult::SetIsAnswer(bool is_answer) {
-  metadata_->is_answer = is_answer;
-  SetSearchResultMetadata();
-}
-
 void ChromeSearchResult::SetQueryUrl(const GURL& url) {
   metadata_->query_url = url;
   auto* updater = model_updater();
diff --git a/chrome/browser/ui/app_list/search/chrome_search_result.h b/chrome/browser/ui/app_list/search/chrome_search_result.h
index 415086a..bc96ae5 100644
--- a/chrome/browser/ui/app_list/search/chrome_search_result.h
+++ b/chrome/browser/ui/app_list/search/chrome_search_result.h
@@ -39,6 +39,7 @@
   using Action = ash::SearchResultAction;
   using Actions = ash::SearchResultActions;
   using DisplayIndex = ash::SearchResultDisplayIndex;
+  using OmniboxType = ash::SearchResultOmniboxType;
 
   ChromeSearchResult();
   virtual ~ChromeSearchResult();
@@ -61,12 +62,12 @@
   }
   MetricsType metrics_type() const { return metadata_->metrics_type; }
   DisplayIndex display_index() const { return metadata_->display_index; }
+  OmniboxType omnibox_type() const { return metadata_->omnibox_type; }
   float position_priority() const { return metadata_->position_priority; }
   const Actions& actions() const { return metadata_->actions; }
   double display_score() const { return metadata_->display_score; }
   bool is_installing() const { return metadata_->is_installing; }
   bool is_recommendation() const { return metadata_->is_recommendation; }
-  bool is_answer() const { return metadata_->is_answer; }
   const base::Optional<GURL>& query_url() const { return metadata_->query_url; }
   const base::Optional<std::string>& equivalent_result_id() const {
     return metadata_->equivalent_result_id;
@@ -92,12 +93,12 @@
   void SetResultType(ResultType result_type);
   void SetMetricsType(MetricsType metrics_type);
   void SetDisplayIndex(DisplayIndex display_index);
+  void SetOmniboxType(OmniboxType omnibox_type);
   void SetPositionPriority(float position_priority);
   void SetDisplayScore(double display_score);
   void SetActions(const Actions& actions);
   void SetIsOmniboxSearch(bool is_omnibox_search);
   void SetIsRecommendation(bool is_recommendation);
-  void SetIsAnswer(bool is_answer);
   void SetIsInstalling(bool is_installing);
   void SetQueryUrl(const GURL& url);
   void SetEquivalentResutlId(const std::string& equivlanet_result_id);
diff --git a/chrome/browser/ui/app_list/search/files/drive_zero_state_provider.cc b/chrome/browser/ui/app_list/search/files/drive_zero_state_provider.cc
index 3a10b79..0488d73 100644
--- a/chrome/browser/ui/app_list/search/files/drive_zero_state_provider.cc
+++ b/chrome/browser/ui/app_list/search/files/drive_zero_state_provider.cc
@@ -63,7 +63,7 @@
 base::FilePath ReparentToDriveMount(
     const base::FilePath& path,
     const drive::DriveIntegrationService* drive_service) {
-  DCHECK(path.IsAbsolute());
+  DCHECK(!path.IsAbsolute());
   return drive_service->GetMountPointPath().Append(path.value());
 }
 
diff --git a/chrome/browser/ui/app_list/search/omnibox_result.cc b/chrome/browser/ui/app_list/search/omnibox_result.cc
index ab0008f..40574884 100644
--- a/chrome/browser/ui/app_list/search/omnibox_result.cc
+++ b/chrome/browser/ui/app_list/search/omnibox_result.cc
@@ -13,6 +13,7 @@
 #include "base/optional.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/bitmap_fetcher/bitmap_fetcher.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
@@ -23,6 +24,8 @@
 #include "components/omnibox/browser/autocomplete_match_type.h"
 #include "components/omnibox/browser/vector_icons.h"
 #include "components/search_engines/util.h"
+#include "extensions/common/image_util.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/image/image_skia_operations.h"
@@ -38,6 +41,32 @@
 
 constexpr SkColor kListIconColor = gfx::kGoogleGrey700;
 
+constexpr net::NetworkTrafficAnnotationTag kTrafficAnnotation =
+    net::DefineNetworkTrafficAnnotation("cros_launcher_omnibox", R"(
+        semantics {
+          sender: "Chrome OS Launcher"
+          description:
+            "Chrome OS provides search suggestions when a user types a query "
+            "into the launcher. This request downloads an image icon for a "
+            "suggested result in order to provide more information."
+          trigger:
+            "Change of results for the query typed by the user into the "
+            "launcher."
+          data:
+            "URL of the image to be downloaded. This URL corresponds to "
+            "search suggestions for the user's query."
+          destination: GOOGLE_OWNED_SERVICE
+        }
+        policy {
+          cookies_allowed: NO
+          setting:
+            "Search autocomplete and suggestions can be disabled in Chrome OS "
+            "settings. Image icons cannot be disabled separately to this."
+          policy_exception_justification:
+            "No content is uploaded or saved, this request downloads a "
+            "publicly available image."
+        })");
+
 int ACMatchStyleToTagStyle(int styles) {
   int tag_styles = 0;
   if (styles & ACMatchClassification::URL)
@@ -151,7 +180,7 @@
 gfx::ImageSkia CreateAnswerIcon(const gfx::VectorIcon& vector_icon) {
   const auto& icon = gfx::CreateVectorIcon(vector_icon, SK_ColorWHITE);
   const int dimension =
-      ash::AppListConfig::instance().search_list_icon_dimension();
+      ash::AppListConfig::instance().search_list_answer_icon_dimension();
   return gfx::ImageSkiaOperations::CreateImageWithCircleBackground(
       dimension / 2, gfx::kGoogleBlue600, icon);
 }
@@ -203,10 +232,12 @@
   SetMetricsType(GetSearchResultType());
 
   if (app_list_features::IsOmniboxRichEntitiesEnabled()) {
-    SetIsAnswer(match_.answer.has_value());
-    if (is_answer()) {
+    if (match_.answer.has_value()) {
+      SetOmniboxType(OmniboxType::kAnswer);
       // The answer subtype overrides the match subtype.
       set_result_subtype(static_cast<int>(match_.answer->type()));
+    } else if (!match_.image_url.is_empty()) {
+      SetOmniboxType(OmniboxType::kRichImage);
     }
   }
 
@@ -248,6 +279,11 @@
   }
 }
 
+void OmniboxResult::OnFetchComplete(const GURL& url, const SkBitmap* bitmap) {
+  if (bitmap)
+    SetIcon(gfx::ImageSkia::CreateFrom1xBitmap(*bitmap));
+}
+
 ash::SearchResultType OmniboxResult::GetSearchResultType() const {
   switch (match_.type) {
     case AutocompleteMatchType::URL_WHAT_YOU_TYPED:
@@ -310,13 +346,22 @@
 void OmniboxResult::UpdateIcon() {
   if (app_list_features::IsOmniboxRichEntitiesEnabled() &&
       IsRichEntityResult()) {
+    // Determine if we have a local icon. Calculator and non-weather answer
+    // results have local icons.
     if (match_.type == AutocompleteMatchType::CALCULATOR) {
       SetIcon(CreateAnswerIcon(omnibox::kCalculatorIcon));
     } else if (match_.answer) {
-      SetIcon(CreateAnswerIcon(TypeToAnswerIcon(match_.answer->type())));
+      if (match_.answer->type() == SuggestionAnswer::ANSWER_TYPE_WEATHER &&
+          !match_.answer->image_url().is_empty()) {
+        // Weather icons are downloaded. Check this first so that the local
+        // default answer icon can be used as a fallback if the URL is missing.
+        FetchRichEntityImage(match_.answer->image_url());
+      } else {
+        SetIcon(CreateAnswerIcon(TypeToAnswerIcon(match_.answer->type())));
+      }
     } else if (!match_.image_url.is_empty()) {
-      // TODO(crbug.com/1130372): If |match_.image_url| exists, download the
-      // image and set it as the result icon.
+      // All remaining rich entity icons will have their image downloaded.
+      FetchRichEntityImage(match_.image_url);
     }
   } else {
     BookmarkModel* bookmark_model =
@@ -334,7 +379,7 @@
 
 void OmniboxResult::UpdateTitleAndDetails() {
   if (app_list_features::IsOmniboxRichEntitiesEnabled()) {
-    if (is_answer()) {
+    if (match_.answer.has_value()) {
       const auto& additional_text =
           GetAdditionalText(match_.answer->first_line());
       // TODO(crbug.com/1130372): Use placeholders or a l10n-friendly way to
@@ -400,6 +445,17 @@
          !match_.image_url.is_empty();
 }
 
+void OmniboxResult::FetchRichEntityImage(const GURL& url) {
+  if (!bitmap_fetcher_) {
+    bitmap_fetcher_ =
+        std::make_unique<BitmapFetcher>(url, this, kTrafficAnnotation);
+  }
+  bitmap_fetcher_->Init(/*referrer=*/std::string(),
+                        net::ReferrerPolicy::NEVER_CLEAR,
+                        network::mojom::CredentialsMode::kOmit);
+  bitmap_fetcher_->Start(profile_->GetURLLoaderFactory().get());
+}
+
 void OmniboxResult::SetZeroSuggestionActions() {
   Actions zero_suggestion_actions;
 
diff --git a/chrome/browser/ui/app_list/search/omnibox_result.h b/chrome/browser/ui/app_list/search/omnibox_result.h
index 28c9560bd..fb802085 100644
--- a/chrome/browser/ui/app_list/search/omnibox_result.h
+++ b/chrome/browser/ui/app_list/search/omnibox_result.h
@@ -9,12 +9,14 @@
 
 #include "ash/public/cpp/app_list/app_list_metrics.h"
 #include "base/macros.h"
+#include "chrome/browser/bitmap_fetcher/bitmap_fetcher_delegate.h"
 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "url/gurl.h"
 
 class AppListControllerDelegate;
 class AutocompleteController;
+class BitmapFetcher;
 class Profile;
 
 // These are used in histograms, do not remove/renumber entries. If you're
@@ -28,7 +30,7 @@
 
 namespace app_list {
 
-class OmniboxResult : public ChromeSearchResult {
+class OmniboxResult : public ChromeSearchResult, public BitmapFetcherDelegate {
  public:
   OmniboxResult(Profile* profile,
                 AppListControllerDelegate* list_controller,
@@ -37,10 +39,13 @@
                 bool is_zero_suggestion);
   ~OmniboxResult() override;
 
-  // ChromeSearchResult overrides:
+  // ChromeSearchResult:
   void Open(int event_flags) override;
   void InvokeAction(int action_index) override;
 
+  // BitmapFetcherDelegate:
+  void OnFetchComplete(const GURL& url, const SkBitmap* bitmap) override;
+
   // Returns the URL that will be navigated to by this search result.
   GURL DestinationURL() const;
 
@@ -55,6 +60,7 @@
   bool IsUrlResultWithDescription() const;
 
   bool IsRichEntityResult() const;
+  void FetchRichEntityImage(const GURL& url);
 
   void SetZeroSuggestionActions();
 
@@ -67,6 +73,7 @@
   AutocompleteController* autocomplete_controller_;
   AutocompleteMatch match_;
   const bool is_zero_suggestion_;
+  std::unique_ptr<BitmapFetcher> bitmap_fetcher_;
 
   DISALLOW_COPY_AND_ASSIGN(OmniboxResult);
 };
diff --git a/chrome/browser/ui/ash/assistant/assistant_context_browsertest.cc b/chrome/browser/ui/ash/assistant/assistant_context_browsertest.cc
index 4846ab7b..793959d4 100644
--- a/chrome/browser/ui/ash/assistant/assistant_context_browsertest.cc
+++ b/chrome/browser/ui/ash/assistant/assistant_context_browsertest.cc
@@ -88,14 +88,18 @@
   ui::AssistantNode* root = assistant_tree->nodes[0].get();
 
   ASSERT_EQ(root->children_indices.size(), 1ul);
-  ui::AssistantNode* child =
+  ui::AssistantNode* div =
       assistant_tree->nodes[root->children_indices[0]].get();
 
-  ui::AssistantNode* grad_child =
-      assistant_tree->nodes[child->children_indices[0]].get();
-  ASSERT_EQ(base::UTF16ToUTF8(grad_child->text), "Hello");
-  ASSERT_EQ(grad_child->rect.x(), 20);
-  ASSERT_EQ(grad_child->rect.y(), 20);
+  ui::AssistantNode* para =
+      assistant_tree->nodes[div->children_indices[0]].get();
+  EXPECT_TRUE(para->text.empty());
+  EXPECT_EQ(para->rect.x(), 20);
+  EXPECT_EQ(para->rect.y(), 20);
+
+  ui::AssistantNode* static_text =
+      assistant_tree->nodes[para->children_indices[0]].get();
+  ASSERT_EQ(base::UTF16ToUTF8(static_text->text), "Hello");
 }
 
 }  // namespace assistant
diff --git a/chrome/browser/ui/page_info/page_info_unittest.cc b/chrome/browser/ui/page_info/page_info_unittest.cc
index 6414a7e1a..967bf084 100644
--- a/chrome/browser/ui/page_info/page_info_unittest.cc
+++ b/chrome/browser/ui/page_info/page_info_unittest.cc
@@ -961,7 +961,6 @@
   visible_security_state_.connection_status = status;
   visible_security_state_.connection_info_initialized = true;
   visible_security_state_.connection_used_legacy_tls = true;
-  visible_security_state_.should_suppress_legacy_tls_warning = false;
 
   SetDefaultUIExpectations(mock_ui());
 
@@ -970,34 +969,6 @@
   EXPECT_EQ(PageInfo::SITE_IDENTITY_STATUS_CERT,
             page_info()->site_identity_status());
 }
-
-// Tests that the site connection status is not set to LEGACY_TLS when a site
-// using legacy TLS is marked as a control site in the visible security state,
-// when the kLegacyTLSWarnings feature is enabled.
-TEST_F(PageInfoTest, LegacyTLSControlSite) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      security_state::features::kLegacyTLSWarnings);
-
-  security_level_ = security_state::SECURE;
-  visible_security_state_.url = GURL("https://scheme-is-cryptographic.test");
-  visible_security_state_.certificate = cert();
-  visible_security_state_.cert_status = 0;
-  int status = 0;
-  status = SetSSLVersion(status, net::SSL_CONNECTION_VERSION_TLS1);
-  status = SetSSLVersion(status, CR_TLS_RSA_WITH_AES_256_CBC_SHA256);
-  visible_security_state_.connection_status = status;
-  visible_security_state_.connection_info_initialized = true;
-  visible_security_state_.connection_used_legacy_tls = true;
-  visible_security_state_.should_suppress_legacy_tls_warning = true;
-
-  SetDefaultUIExpectations(mock_ui());
-
-  EXPECT_EQ(PageInfo::SITE_CONNECTION_STATUS_ENCRYPTED,
-            page_info()->site_connection_status());
-  EXPECT_EQ(PageInfo::SITE_IDENTITY_STATUS_CERT,
-            page_info()->site_identity_status());
-}
 #endif
 
 #if !defined(OS_ANDROID)
@@ -1378,26 +1349,20 @@
 TEST_F(PageInfoTest, LegacyTLSMetrics) {
   const struct TestCase {
     const bool connection_used_legacy_tls;
-    const bool should_suppress_legacy_tls_warning;
     const std::string histogram_suffix;
   } kTestCases[] = {
-      {true, false, "LegacyTLS_Triggered"},
-      {true, true, "LegacyTLS_NotTriggered"},
-      {false, false, "LegacyTLS_NotTriggered"},
+      {true, "LegacyTLS_Triggered"},
+      {false, "LegacyTLS_NotTriggered"},
   };
 
   const std::string kHistogramPrefix("Security.LegacyTLS.PageInfo.Action");
   const char kGenericHistogram[] = "WebsiteSettings.Action";
 
-  InitializeEmptyLegacyTLSConfig();
-
   for (const auto& test : kTestCases) {
     base::HistogramTester histograms;
     SetURL("https://example.test");
     visible_security_state_.connection_used_legacy_tls =
         test.connection_used_legacy_tls;
-    visible_security_state_.should_suppress_legacy_tls_warning =
-        test.should_suppress_legacy_tls_warning;
     ResetMockUI();
     ClearPageInfo();
     SetDefaultUIExpectations(mock_ui());
@@ -1426,33 +1391,24 @@
 TEST_F(PageInfoTest, LegacyTLSTimeOpenMetrics) {
   const struct TestCase {
     const bool connection_used_legacy_tls;
-    const bool should_suppress_legacy_tls_warning;
     const std::string legacy_tls_status_name;
     const PageInfo::PageInfoAction action;
   } kTestCases[] = {
       // PAGE_INFO_COUNT used as shorthand for "take no action".
-      {true, false, "LegacyTLS_Triggered", PageInfo::PAGE_INFO_COUNT},
-      {true, true, "LegacyTLS_NotTriggered", PageInfo::PAGE_INFO_COUNT},
-      {false, false, "LegacyTLS_NotTriggered", PageInfo::PAGE_INFO_COUNT},
-      {true, false, "LegacyTLS_Triggered",
-       PageInfo::PAGE_INFO_SITE_SETTINGS_OPENED},
-      {true, true, "LegacyTLS_NotTriggered",
-       PageInfo::PAGE_INFO_SITE_SETTINGS_OPENED},
-      {false, false, "LegacyTLS_NotTriggered",
+      {true, "LegacyTLS_Triggered", PageInfo::PAGE_INFO_COUNT},
+      {false, "LegacyTLS_NotTriggered", PageInfo::PAGE_INFO_COUNT},
+      {true, "LegacyTLS_Triggered", PageInfo::PAGE_INFO_SITE_SETTINGS_OPENED},
+      {false, "LegacyTLS_NotTriggered",
        PageInfo::PAGE_INFO_SITE_SETTINGS_OPENED},
   };
 
   const std::string kHistogramPrefix("Security.PageInfo.TimeOpen.");
 
-  InitializeEmptyLegacyTLSConfig();
-
   for (const auto& test : kTestCases) {
     base::HistogramTester histograms;
     SetURL("https://example.test");
     visible_security_state_.connection_used_legacy_tls =
         test.connection_used_legacy_tls;
-    visible_security_state_.should_suppress_legacy_tls_warning =
-        test.should_suppress_legacy_tls_warning;
     ResetMockUI();
     ClearPageInfo();
     SetDefaultUIExpectations(mock_ui());
diff --git a/chrome/browser/ui/tabs/tab_strip_model.cc b/chrome/browser/ui/tabs/tab_strip_model.cc
index 4b84e7a..c0cfbb2 100644
--- a/chrome/browser/ui/tabs/tab_strip_model.cc
+++ b/chrome/browser/ui/tabs/tab_strip_model.cc
@@ -1857,6 +1857,14 @@
   selection.new_model = new_model;
   selection.reason = reason;
 
+#if DCHECK_IS_ON()
+  // Validate that |new_model| only selects tabs that actually exist.
+  DCHECK(ContainsIndex(new_model.active()));
+  for (int selected_index : new_model.selected_indices()) {
+    DCHECK(ContainsIndex(selected_index));
+  }
+#endif
+
   // This is done after notifying TabDeactivated() because caller can assume
   // that TabStripModel::active_index() would return the index for
   // |selection.old_contents|.
diff --git a/chrome/browser/ui/ui_features.cc b/chrome/browser/ui/ui_features.cc
index 0dfe4e5..ba272740 100644
--- a/chrome/browser/ui/ui_features.cc
+++ b/chrome/browser/ui/ui_features.cc
@@ -82,10 +82,6 @@
 const base::Feature kTabGroupsAutoCreate{"TabGroupsAutoCreate",
                                          base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Enables tab groups to be collapsed and expanded. https://crbug.com/1018230
-const base::Feature kTabGroupsCollapse{"TabGroupsCollapse",
-                                       base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Enables tabs to be frozen when collapsed. https://crbug.com/1110108
 const base::Feature kTabGroupsCollapseFreezing{
     "TabGroupsCollapseFreezing", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chrome/browser/ui/ui_features.h b/chrome/browser/ui/ui_features.h
index 37b4dde..ba949da 100644
--- a/chrome/browser/ui/ui_features.h
+++ b/chrome/browser/ui/ui_features.h
@@ -56,8 +56,6 @@
 
 extern const base::Feature kTabGroupsAutoCreate;
 
-extern const base::Feature kTabGroupsCollapse;
-
 extern const base::Feature kTabGroupsCollapseFreezing;
 
 extern const base::Feature kTabGroupsFeedback;
diff --git a/chrome/browser/ui/views/frame/tab_strip_region_view.cc b/chrome/browser/ui/views/frame/tab_strip_region_view.cc
index 84d8510..5022f05 100644
--- a/chrome/browser/ui/views/frame/tab_strip_region_view.cc
+++ b/chrome/browser/ui/views/frame/tab_strip_region_view.cc
@@ -121,11 +121,6 @@
     canvas->DrawRect(GetContentsBounds(), flags);
   }
 
-  void OnThemeChanged() override {
-    View::OnThemeChanged();
-    SchedulePaint();
-  }
-
  private:
   TabStrip* tab_strip_;
   views::OverflowIndicatorAlignment side_;
@@ -159,16 +154,18 @@
     tab_strip_scroll_container->SetContents(std::move(tab_strip));
 
     tab_strip_scroll_container->SetDrawOverflowIndicator(true);
-    tab_strip_scroll_container->SetCustomOverflowIndicator(
-        views::OverflowIndicatorAlignment::kLeft,
-        std::make_unique<TabStripContainerOverflowIndicator>(
-            tab_strip_, views::OverflowIndicatorAlignment::kLeft),
-        TabStripContainerOverflowIndicator::kTotalWidth, false);
-    tab_strip_scroll_container->SetCustomOverflowIndicator(
-        views::OverflowIndicatorAlignment::kRight,
-        std::make_unique<TabStripContainerOverflowIndicator>(
-            tab_strip_, views::OverflowIndicatorAlignment::kRight),
-        TabStripContainerOverflowIndicator::kTotalWidth, false);
+    left_overflow_indicator_ =
+        tab_strip_scroll_container->SetCustomOverflowIndicator(
+            views::OverflowIndicatorAlignment::kLeft,
+            std::make_unique<TabStripContainerOverflowIndicator>(
+                tab_strip_, views::OverflowIndicatorAlignment::kLeft),
+            TabStripContainerOverflowIndicator::kTotalWidth, false);
+    right_overflow_indicator_ =
+        tab_strip_scroll_container->SetCustomOverflowIndicator(
+            views::OverflowIndicatorAlignment::kRight,
+            std::make_unique<TabStripContainerOverflowIndicator>(
+                tab_strip_, views::OverflowIndicatorAlignment::kRight),
+            TabStripContainerOverflowIndicator::kTotalWidth, false);
 
     // This base::Unretained is safe because the callback is called by the
     // layout manager, which is cleaned up before view children like
@@ -304,6 +301,10 @@
                                            foreground_color);
   }
   tab_strip_->FrameColorsChanged();
+  if (base::FeatureList::IsEnabled(features::kScrollableTabStrip)) {
+    left_overflow_indicator_->SchedulePaint();
+    right_overflow_indicator_->SchedulePaint();
+  }
   SchedulePaint();
 }
 
diff --git a/chrome/browser/ui/views/frame/tab_strip_region_view.h b/chrome/browser/ui/views/frame/tab_strip_region_view.h
index 07b0875..7bf4005 100644
--- a/chrome/browser/ui/views/frame/tab_strip_region_view.h
+++ b/chrome/browser/ui/views/frame/tab_strip_region_view.h
@@ -87,6 +87,10 @@
   TabSearchButton* tab_search_button_ = nullptr;
   views::ImageButton* leading_scroll_button_;
   views::ImageButton* trailing_scroll_button_;
+  // The views, owned by |scroll_container_|, that indicate that there are more
+  // tabs overflowing to the left or right.
+  views::View* left_overflow_indicator_;
+  views::View* right_overflow_indicator_;
   TipMarqueeView* tip_marquee_view_ = nullptr;
 
   const base::CallbackListSubscription subscription_ =
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
index 75ffbe5..a2db12ad 100644
--- a/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
@@ -268,8 +268,10 @@
   ukm_recorder.ExpectEntrySourceHasUrl(entry, origin_url);
   EXPECT_EQ(*ukm_recorder.GetEntryMetric(entry, "Source"),
             static_cast<int64_t>(permissions::PermissionSourceUI::OIB));
+  size_t num_values = 0;
   EXPECT_EQ(*ukm_recorder.GetEntryMetric(entry, "PermissionType"),
-            static_cast<int64_t>(ContentSettingsType::NOTIFICATIONS));
+            ContentSettingTypeToHistogramValue(
+                ContentSettingsType::NOTIFICATIONS, &num_values));
   EXPECT_EQ(*ukm_recorder.GetEntryMetric(entry, "Action"),
             static_cast<int64_t>(permissions::PermissionAction::REVOKED));
 }
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller.cc b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
index 7371042e..9ffdbcd 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
@@ -1678,6 +1678,13 @@
   if (selection_model.empty())
     return;
 
+  // Tabs in |source_context_| may have closed since the drag began. In that
+  // case, |initial_selection_model_| may include indices that are no longer
+  // valid in |source_context_|. Abort restoring the selection if so.
+  if (!source_context_->GetTabStripModel()->ContainsIndex(
+          *(selection_model.selected_indices().rbegin())))
+    return;
+
   // The anchor/active may have been among the tabs that were dragged out. Force
   // the anchor/active to be valid.
   if (selection_model.anchor() == ui::ListSelectionModel::kUnselectedIndex)
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
index e2c0105..8f155c7 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
@@ -2313,23 +2313,11 @@
             gfx::Range(0, 1));
 }
 
-class DetachToBrowserTabDragControllerTestWithTabGroupsCollapseEnabled
-    : public DetachToBrowserTabDragControllerTest {
- public:
-  DetachToBrowserTabDragControllerTestWithTabGroupsCollapseEnabled() {
-    scoped_feature_list_.InitWithFeatures({features::kTabGroupsCollapse}, {});
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
 // Creates a browser with four tabs where the second and third tab is in a
 // collapsed group. Drag the fourth tab to the left past the group header. The
 // fourth tab should swap places with the collapsed group header.
-IN_PROC_BROWSER_TEST_P(
-    DetachToBrowserTabDragControllerTestWithTabGroupsCollapseEnabled,
-    DragTabLeftPastCollapsedGroupHeader) {
+IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
+                       DragTabLeftPastCollapsedGroupHeader) {
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
   TabStripModel* model = browser()->tab_strip_model();
 
@@ -2362,9 +2350,8 @@
 // Creates a browser with four tabs where the second and third tab is in a
 // collapsed group. Drag the first tab to the right past the group header. The
 // first tab should swap places with the collapsed group header.
-IN_PROC_BROWSER_TEST_P(
-    DetachToBrowserTabDragControllerTestWithTabGroupsCollapseEnabled,
-    DragTabRightPastCollapsedGroupHeader) {
+IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
+                       DragTabRightPastCollapsedGroupHeader) {
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
   TabStripModel* model = browser()->tab_strip_model();
 
@@ -2397,9 +2384,8 @@
 
 // Drags a tab group by the header and while detached presses escape to revert
 // the drag.
-IN_PROC_BROWSER_TEST_P(
-    DetachToBrowserTabDragControllerTestWithTabGroupsCollapseEnabled,
-    RevertCollapsedHeaderDragWhileDetached) {
+IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
+                       RevertCollapsedHeaderDragWhileDetached) {
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
   TabStripModel* model = browser()->tab_strip_model();
   AddTabsAndResetBrowser(browser(), 1);
@@ -2431,9 +2417,8 @@
 // Creates a browser with four tabs. The first two tabs belong in Tab Group 1.
 // Dragging the collapsed group header of Tab Group 1 will result in Tab Group 1
 // expanding.
-IN_PROC_BROWSER_TEST_P(
-    DetachToBrowserTabDragControllerTestWithTabGroupsCollapseEnabled,
-    DragCollapsedGroupHeaderExpandsGroup) {
+IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
+                       DragCollapsedGroupHeaderExpandsGroup) {
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
   TabStripModel* model = browser()->tab_strip_model();
   TabGroupModel* group_model = model->group_model();
@@ -2473,9 +2458,8 @@
 #endif
 
 // Creates two browsers, then drags a collapsed group from one to the other.
-IN_PROC_BROWSER_TEST_P(
-    DetachToBrowserTabDragControllerTestWithTabGroupsCollapseEnabled,
-    MAYBE_DragCollapsedGroupHeaderToSeparateWindow) {
+IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
+                       MAYBE_DragCollapsedGroupHeaderToSeparateWindow) {
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
   TabStripModel* model = browser()->tab_strip_model();
   AddTabsAndResetBrowser(browser(), 2);
@@ -4500,10 +4484,6 @@
                          ::testing::Values("mouse", "touch"));
 INSTANTIATE_TEST_SUITE_P(
     TabDragging,
-    DetachToBrowserTabDragControllerTestWithTabGroupsCollapseEnabled,
-    ::testing::Values("mouse", "touch"));
-INSTANTIATE_TEST_SUITE_P(
-    TabDragging,
     DetachToBrowserTabDragControllerTestWithScrollableTabStripEnabled,
     ::testing::Values("mouse", "touch"));
 INSTANTIATE_TEST_SUITE_P(TabDragging,
@@ -4529,10 +4509,6 @@
                          ::testing::Values("mouse"));
 INSTANTIATE_TEST_SUITE_P(
     TabDragging,
-    DetachToBrowserTabDragControllerTestWithTabGroupsCollapseEnabled,
-    ::testing::Values("mouse"));
-INSTANTIATE_TEST_SUITE_P(
-    TabDragging,
     DetachToBrowserTabDragControllerTestWithScrollableTabStripEnabled,
     ::testing::Values("mouse"));
 #endif
diff --git a/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view_browsertest.cc b/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view_browsertest.cc
index 9ba5c0a0..6ff39ae9 100644
--- a/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/tabs/tab_group_editor_bubble_view_browsertest.cc
@@ -178,8 +178,7 @@
  public:
   TabGroupEditorBubbleViewDialogBrowserTestWithFreezingEnabled() {
     scoped_feature_list_.InitWithFeatures(
-        {features::kTabGroupsCollapse, features::kTabGroupsCollapseFreezing},
-        {});
+        {features::kTabGroupsCollapseFreezing}, {});
   }
 
  private:
diff --git a/chrome/browser/ui/views/tabs/tab_group_header.cc b/chrome/browser/ui/views/tabs/tab_group_header.cc
index 539efa5..3972b4b7 100644
--- a/chrome/browser/ui/views/tabs/tab_group_header.cc
+++ b/chrome/browser/ui/views/tabs/tab_group_header.cc
@@ -117,23 +117,16 @@
   if ((event.key_code() == ui::VKEY_SPACE ||
        event.key_code() == ui::VKEY_RETURN) &&
       !editor_bubble_tracker_.is_open()) {
-    if (base::FeatureList::IsEnabled(features::kTabGroupsCollapse)) {
-      // The collapse feature changes the behavior from showing the
-      // editor bubble to toggling the collapsed state of the group.
-      bool successful_toggle =
-          tab_strip_->controller()->ToggleTabGroupCollapsedState(
-              group().value(), ToggleTabGroupCollapsedStateOrigin::kKeyboard);
-      if (successful_toggle) {
+    bool successful_toggle =
+        tab_strip_->controller()->ToggleTabGroupCollapsedState(
+            group().value(), ToggleTabGroupCollapsedStateOrigin::kKeyboard);
+    if (successful_toggle) {
 #if defined(OS_WIN)
         NotifyAccessibilityEvent(ax::mojom::Event::kSelection, true);
 #else
         NotifyAccessibilityEvent(ax::mojom::Event::kAlert, true);
 #endif
         LogCollapseTime();
-      }
-    } else {
-      editor_bubble_tracker_.Opened(TabGroupEditorBubbleView::Show(
-          tab_strip_->controller()->GetBrowser(), group().value(), this));
     }
     return true;
   }
@@ -181,21 +174,13 @@
 }
 
 void TabGroupHeader::OnMouseReleased(const ui::MouseEvent& event) {
-  if (base::FeatureList::IsEnabled(features::kTabGroupsCollapse)) {
-    // The collapse feature changes the left click behavior from showing the
-    // editor bubble to toggling the collapsed state of the group.
-    if (event.IsLeftMouseButton() && !dragging()) {
-      bool successful_toggle =
-          tab_strip_->controller()->ToggleTabGroupCollapsedState(
-              group().value(), ToggleTabGroupCollapsedStateOrigin::kMouse);
-      if (successful_toggle)
-        LogCollapseTime();
-    }
-
-  } else if (!dragging() && !editor_bubble_tracker_.is_open()) {
-    // (TODO): Delete this else statement once collapse launches since
-    // ShowContextMenuForViewImpl() will handle spawning the bubble on right
-    // clicks.
+  if (event.IsLeftMouseButton() && !dragging()) {
+    bool successful_toggle =
+        tab_strip_->controller()->ToggleTabGroupCollapsedState(
+            group().value(), ToggleTabGroupCollapsedStateOrigin::kMouse);
+    if (successful_toggle)
+      LogCollapseTime();
+  } else if (event.IsRightMouseButton() && !dragging()) {
     editor_bubble_tracker_.Opened(TabGroupEditorBubbleView::Show(
         tab_strip_->controller()->GetBrowser(), group().value(), this));
   }
@@ -218,18 +203,11 @@
   tab_strip_->UpdateHoverCard(nullptr);
   switch (event->type()) {
     case ui::ET_GESTURE_TAP: {
-      if (base::FeatureList::IsEnabled(features::kTabGroupsCollapse)) {
-        // The collapse feature changes the behavior from showing the
-        // editor bubble to toggling the collapsed state of the group.
-        bool successful_toggle =
-            tab_strip_->controller()->ToggleTabGroupCollapsedState(
-                group().value(), ToggleTabGroupCollapsedStateOrigin::kGesture);
-        if (successful_toggle)
-          LogCollapseTime();
-      } else {
-        editor_bubble_tracker_.Opened(TabGroupEditorBubbleView::Show(
-            tab_strip_->controller()->GetBrowser(), group().value(), this));
-      }
+      bool successful_toggle =
+          tab_strip_->controller()->ToggleTabGroupCollapsedState(
+              group().value(), ToggleTabGroupCollapsedStateOrigin::kGesture);
+      if (successful_toggle)
+        LogCollapseTime();
       break;
     }
 
diff --git a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_interactive_uitest.cc b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_interactive_uitest.cc
index 8164843..f5db329 100644
--- a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_interactive_uitest.cc
+++ b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_interactive_uitest.cc
@@ -47,7 +47,10 @@
 // Verifies that for minimal-ui web apps, the toolbar keyboard focus cycles
 // among the toolbar buttons: the reload button, the extensions menu button, and
 // the app menu button, in that order.
-IN_PROC_BROWSER_TEST_P(WebAppFrameToolbarInteractiveUITest, CycleFocus) {
+//
+// TODO(https://crbug.com/1176121): Re-enable after fixing flakiness.
+IN_PROC_BROWSER_TEST_P(WebAppFrameToolbarInteractiveUITest,
+                       DISABLED_CycleFocus) {
   ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("simple_with_icon/")));
 
   const GURL app_url("https://test.org");
diff --git a/chrome/browser/ui/webui/chromeos/emoji/emoji_dialog.cc b/chrome/browser/ui/webui/chromeos/emoji/emoji_dialog.cc
index 1756539d..823ba5e3 100644
--- a/chrome/browser/ui/webui/chromeos/emoji/emoji_dialog.cc
+++ b/chrome/browser/ui/webui/chromeos/emoji/emoji_dialog.cc
@@ -19,8 +19,7 @@
 
 constexpr gfx::Size kDefaultWindowSize(340, 390);
 
-ui::TextInputClient* EmojiPickerDialog::input_client = nullptr;
-gfx::Range EmojiPickerDialog::selection_range = gfx::Range();
+gfx::NativeWindow EmojiPickerDialog::window = nullptr;
 
 EmojiPickerDialog::EmojiPickerDialog() {
   set_can_resize(false);
@@ -29,15 +28,11 @@
 void EmojiPickerDialog::Show() {
   ui::InputMethod* input_method =
       ui::IMEBridge::Get()->GetInputContextHandler()->GetInputMethod();
-  if (input_method) {
-    input_client = input_method->GetTextInputClient();
-  }
-  if (input_client) {
-    input_client->GetEditableSelectionRange(&selection_range);
-  }
-  const auto caret_bounds =
+  const ui::TextInputClient* input_client =
+      input_method ? input_method->GetTextInputClient() : nullptr;
+  const gfx::Rect caret_bounds =
       input_client ? input_client->GetCaretBounds() : gfx::Rect();
-  gfx::NativeWindow window = chrome::ShowWebDialog(
+  window = chrome::ShowWebDialog(
       nullptr, ProfileManager::GetActiveUserProfile(), new EmojiPickerDialog());
   // For now, this can overflow the screen.
   if (input_client) {
@@ -75,7 +70,7 @@
 }
 
 void EmojiPickerDialog::OnDialogClosed(const std::string& json_retval) {
-  input_client = nullptr;
+  window = nullptr;
   delete this;
 }
 
diff --git a/chrome/browser/ui/webui/chromeos/emoji/emoji_dialog.h b/chrome/browser/ui/webui/chromeos/emoji/emoji_dialog.h
index d99f35e..9109609 100644
--- a/chrome/browser/ui/webui/chromeos/emoji/emoji_dialog.h
+++ b/chrome/browser/ui/webui/chromeos/emoji/emoji_dialog.h
@@ -36,10 +36,8 @@
 
   content::WebUI* webui_ = nullptr;
 
-  // Input field which was focused before the EmojiDialog was opened.
-  static ui::TextInputClient* input_client;
-  // Selection range of input_client before the EmojiDialog was opened.
-  static gfx::Range selection_range;
+  // Window for the emoji picker.  Used by the handler to close the window.
+  static gfx::NativeWindow window;
   friend class EmojiHandler;
 
   DISALLOW_COPY_AND_ASSIGN(EmojiPickerDialog);
diff --git a/chrome/browser/ui/webui/chromeos/emoji/emoji_handler.cc b/chrome/browser/ui/webui/chromeos/emoji/emoji_handler.cc
index fbbe46e..8b3dc8f 100644
--- a/chrome/browser/ui/webui/chromeos/emoji/emoji_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/emoji/emoji_handler.cc
@@ -8,10 +8,12 @@
 #include "base/strings/utf_string_conversions.h"
 
 #include "chrome/browser/ui/webui/chromeos/emoji/emoji_dialog.h"
+#include "ui/aura/window.h"
+#include "ui/base/ime/chromeos/ime_bridge.h"
 
 namespace chromeos {
 
-EmojiHandler::EmojiHandler() : selection_range_set(false) {}
+EmojiHandler::EmojiHandler() {}
 EmojiHandler::~EmojiHandler() = default;
 
 void EmojiHandler::RegisterMessages() {
@@ -29,7 +31,19 @@
 
   const std::string& emoji = args->GetList()[0].GetString();
 
-  ui::TextInputClient* input_client = EmojiPickerDialog::input_client;
+  // Hide emoji picker window to restore focus to original text field
+  if (EmojiPickerDialog::window) {
+    EmojiPickerDialog::window->Hide();
+  }
+
+  ui::InputMethod* input_method =
+      ui::IMEBridge::Get()->GetInputContextHandler()->GetInputMethod();
+  if (!input_method) {
+    LOG(WARNING) << "no input_method found";
+    return;
+  }
+
+  ui::TextInputClient* input_client = input_method->GetTextInputClient();
 
   if (!input_client) {
     LOG(WARNING) << "no input_client found";
@@ -39,11 +53,7 @@
   if (input_client->GetTextInputType() ==
       ui::TextInputType::TEXT_INPUT_TYPE_NONE) {
     LOG(WARNING) << "attempt to insert into input_client with type none";
-  }
-
-  if (!selection_range_set) {
-    input_client->SetEditableSelectionRange(EmojiPickerDialog::selection_range);
-    selection_range_set = true;
+    return;
   }
 
   input_client->InsertText(
diff --git a/chrome/browser/ui/webui/chromeos/emoji/emoji_handler.h b/chrome/browser/ui/webui/chromeos/emoji/emoji_handler.h
index 25a0277b..26f74ca7 100644
--- a/chrome/browser/ui/webui/chromeos/emoji/emoji_handler.h
+++ b/chrome/browser/ui/webui/chromeos/emoji/emoji_handler.h
@@ -22,8 +22,6 @@
 
   void HandleInsertEmoji(const base::ListValue* args);
 
-  bool selection_range_set;
-
   DISALLOW_COPY_AND_ASSIGN(EmojiHandler);
 };
 
diff --git a/chrome/browser/ui/webui/chromeos/login/assistant_optin_flow_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/assistant_optin_flow_screen_handler.cc
index 1d46da0..3c8a3cc 100644
--- a/chrome/browser/ui/webui/chromeos/login/assistant_optin_flow_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/assistant_optin_flow_screen_handler.cc
@@ -55,7 +55,7 @@
 }
 
 AssistantOptInFlowScreenHandler::~AssistantOptInFlowScreenHandler() {
-  if (voice_match_enrollment_started_)
+  if (assistant::AssistantSettings::Get() && voice_match_enrollment_started_)
     StopSpeakerIdEnrollment();
   if (ash::AssistantState::Get())
     ash::AssistantState::Get()->RemoveObserver(this);
diff --git a/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.cc b/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.cc
index c419ad5..f26c58b 100644
--- a/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.cc
@@ -511,7 +511,8 @@
   }
 
   auto notification = phonehub::Notification(
-      id, app_metadata, timestamp, importance, inline_reply_id, opt_title,
+      id, app_metadata, timestamp, importance, inline_reply_id,
+      phonehub::Notification::InteractionBehavior::kNone, opt_title,
       opt_text_content, opt_shared_image, opt_contact_image);
 
   PA_LOG(VERBOSE) << "Set notification" << notification;
diff --git a/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.cc b/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.cc
index df9d873..1d53bb9 100644
--- a/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.cc
+++ b/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_handler.h"
 #include "chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_localized_strings_provider.h"
 #include "chrome/browser/ui/webui/metrics_handler.h"
+#include "chrome/browser/ui/webui/webui_util.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/grit/generated_resources.h"
@@ -123,16 +124,12 @@
 
   chromeos::multidevice_setup::AddLocalizedStrings(source);
   source->UseStringsJs();
-  source->SetDefaultResource(
-      IDR_MULTIDEVICE_SETUP_MULTIDEVICE_SETUP_DIALOG_HTML);
 
-  // Note: The |kMultiDeviceSetupResourcesSize| and |kMultideviceSetupResources|
-  // fields are defined in the generated file
-  // chrome/grit/multidevice_setup_resources_map.h.
-  for (size_t i = 0; i < kMultideviceSetupResourcesSize; ++i) {
-    source->AddResourcePath(kMultideviceSetupResources[i].name,
-                            kMultideviceSetupResources[i].value);
-  }
+  webui::SetupWebUIDataSource(
+      source,
+      base::make_span(kMultideviceSetupResources,
+                      kMultideviceSetupResourcesSize),
+      IDR_MULTIDEVICE_SETUP_MULTIDEVICE_SETUP_DIALOG_HTML);
 
   web_ui->AddMessageHandler(std::make_unique<MultideviceSetupHandler>());
   web_ui->AddMessageHandler(std::make_unique<MetricsHandler>());
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index cf4406e..4d50cdf 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -1448,10 +1448,32 @@
     static constexpr webui::LocalizedString kLocalizedStringsBehindFlag[] = {
         {"privacySandboxPageHeading",
          IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_HEADING},
+        {"privacySandboxPageExplanation1",
+         IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_EXPLANATION1},
+        {"privacySandboxPageExplanation2",
+         IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_EXPLANATION2},
+        {"privacySandboxPageExplanation3",
+         IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_EXPLANATION3},
+        {"privacySandboxPageSettingTitle",
+         IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_SETTING_TITLE},
+        {"privacySandboxPageSettingExplanation1",
+         IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_SETTING_EXPLANATION1},
+        {"privacySandboxPageSettingExplanation2",
+         IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_SETTING_EXPLANATION2},
+        {"privacySandboxPageSettingExplanation3",
+         IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_SETTING_EXPLANATION3},
+        {"privacySandboxPageDetails",
+         IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_DETAILS},
     };
     html_source->AddLocalizedStrings(kLocalizedStringsBehindFlag);
 
     html_source->AddString("privacySandboxURL", chrome::kPrivacySandboxURL);
+
+    // TODO(crbug/1152336): Replace with the target URL once it's available.
+    html_source->AddString("privacySandboxPageExplanation4",
+                           l10n_util::GetStringFUTF16(
+                               IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_EXPLANATION4,
+                               base::ASCIIToUTF16(chrome::kPrivacySandboxURL)));
   }
 }
 
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc b/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
index b54f5d0d..955e994 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
@@ -1200,8 +1200,10 @@
   EXPECT_EQ(
       *ukm_recorder.GetEntryMetric(entry, "Source"),
       static_cast<int64_t>(permissions::PermissionSourceUI::SITE_SETTINGS));
+  size_t num_values = 0;
   EXPECT_EQ(*ukm_recorder.GetEntryMetric(entry, "PermissionType"),
-            static_cast<int64_t>(ContentSettingsType::NOTIFICATIONS));
+            ContentSettingTypeToHistogramValue(
+                ContentSettingsType::NOTIFICATIONS, &num_values));
   EXPECT_EQ(*ukm_recorder.GetEntryMetric(entry, "Action"),
             static_cast<int64_t>(permissions::PermissionAction::REVOKED));
 }
diff --git a/chrome/browser/ui/webui/settings/site_settings_helper.cc b/chrome/browser/ui/webui/settings/site_settings_helper.cc
index af39c45..0d2b861c 100644
--- a/chrome/browser/ui/webui/settings/site_settings_helper.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_helper.cc
@@ -151,9 +151,7 @@
     {ContentSettingsType::DISPLAY_CAPTURE, nullptr},
 };
 
-// TODO(crbug.com/1149878): After removing
-// ContentSettingsType::DEPRECATED_PLUGINS, remove +1.
-static_assert(base::size(kContentSettingsTypeGroupNames) + 1 ==
+static_assert(base::size(kContentSettingsTypeGroupNames) ==
                   // ContentSettingsType starts at -1, so add 1 here.
                   static_cast<int32_t>(ContentSettingsType::NUM_TYPES) + 1,
               "kContentSettingsTypeGroupNames should have "
diff --git a/chrome/browser/ui/webui/webui_util.cc b/chrome/browser/ui/webui/webui_util.cc
index 60410989..85a5608 100644
--- a/chrome/browser/ui/webui/webui_util.cc
+++ b/chrome/browser/ui/webui/webui_util.cc
@@ -42,7 +42,7 @@
                           int default_resource) {
   SetJSModuleDefaults(source);
   for (const GritResourceMap& resource : resources) {
-    source->AddResourcePath(resource.name, resource.value);
+    source->AddResourcePath(resource.path, resource.id);
   }
   source->AddResourcePath("", default_resource);
 }
@@ -56,7 +56,7 @@
 void AddResourcePathsBulk(content::WebUIDataSource* source,
                           base::span<const GritResourceMap> resources) {
   for (const auto& resource : resources)
-    source->AddResourcePath(resource.name, resource.value);
+    source->AddResourcePath(resource.path, resource.id);
 }
 
 bool IsEnterpriseManaged() {
diff --git a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
index e980924..005da5a 100644
--- a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
+++ b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
@@ -25,6 +25,7 @@
 #include "chrome/browser/webauthn/authenticator_request_dialog_model.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
+#include "chrome/grit/generated_resources.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
@@ -37,6 +38,7 @@
 #include "device/fido/features.h"
 #include "device/fido/fido_authenticator.h"
 #include "device/fido/fido_discovery_factory.h"
+#include "ui/base/l10n/l10n_util.h"
 
 #if defined(OS_MAC)
 #include "device/fido/mac/authenticator.h"
@@ -389,7 +391,9 @@
     mojo::Remote<device::mojom::UsbDeviceManager> usb_device_manager;
     content::GetDeviceService().BindUsbDeviceManager(
         usb_device_manager.BindNewPipeAndPassReceiver());
-    discovery_factory->set_usb_device_manager(std::move(usb_device_manager));
+    discovery_factory->set_android_accessory_params(
+        std::move(usb_device_manager),
+        l10n_util::GetStringUTF8(IDS_WEBAUTHN_CABLEV2_AOA_REQUEST_DESCRIPTION));
     discovery_factory->set_network_context(
         SystemNetworkContextManager::GetInstance()->GetContext());
   }
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 3bc7ee3..fc20f51 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-master-1613044789-15ec05d079ab67299de34510cb04d649684043bf.profdata
+chrome-linux-master-1613087985-fc4fe9a8085af8a2e14f6cb006d99076160b6ef9.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index d868440..f224d0b 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-master-1613044789-816b8279568428b4275c365c959703c7954160e2.profdata
+chrome-mac-master-1613066326-a8be7ed4ac6e8b275d3e0de16c8117ef671116f8.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index ecb475ee..b395b02 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-master-1612947407-8c5a94fcc3e3ef9c39759f054c99635e96a1c26a.profdata
+chrome-win32-master-1613076822-da42213c1412143209eb39f3f855127cf28cf482.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index e400c8fd..1b17284 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-master-1613055160-5242692ac3423259c37a60e2fdfebfd341a8ade9.profdata
+chrome-win64-master-1613076822-eec9c2b6cca2c08403e38cea005aaf3483636fd1.profdata
diff --git a/chrome/common/extensions/api/accessibility_private.json b/chrome/common/extensions/api/accessibility_private.json
index 46f2867..20b1199 100644
--- a/chrome/common/extensions/api/accessibility_private.json
+++ b/chrome/common/extensions/api/accessibility_private.json
@@ -79,7 +79,7 @@
       {
         "id": "SwitchAccessMenuAction",
         "type": "string",
-        "enum": [ "copy", "cut", "decrement", "dictation", "endTextSelection", "increment", "jumpToBeginningOfText", "jumpToEndOfText", "keyboard", "leftClick", "moveBackwardOneCharOfText", "moveBackwardOneWordOfText", "moveCursor", "moveDownOneLineOfText", "moveForwardOneCharOfText", "moveForwardOneWordOfText", "moveUpOneLineOfText", "paste", "pointScan", "rightClick", "scrollDown", "scrollLeft", "scrollRight", "scrollUp", "select", "settings", "startTextSelection" ],
+        "enum": [ "copy", "cut", "decrement", "dictation", "endTextSelection", "increment", "itemScan", "jumpToBeginningOfText", "jumpToEndOfText", "keyboard", "leftClick", "moveBackwardOneCharOfText", "moveBackwardOneWordOfText", "moveCursor", "moveDownOneLineOfText", "moveForwardOneCharOfText", "moveForwardOneWordOfText", "moveUpOneLineOfText", "paste", "pointScan", "rightClick", "scrollDown", "scrollLeft", "scrollRight", "scrollUp", "select", "settings", "startTextSelection" ],
         "description": "Available actions to be shown in the Switch Access menu. Must be kept in sync with the strings in ash/system/accessibility/switch_access_menu_view.cc"
       },
       {
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 10cd8e8..f385a83 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1204,6 +1204,7 @@
       "../browser/optimization_guide/blink/blink_optimization_guide_browsertest.cc",
       "../browser/optimization_guide/hints_fetcher_browsertest.cc",
       "../browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc",
+      "../browser/optimization_guide/page_text_observer_browsertest.cc",
       "../browser/optimization_guide/prediction/machine_learning_service_browsertest.cc",
       "../browser/optimization_guide/prediction/prediction_manager_browsertest.cc",
       "../browser/page_load_metrics/observers/ad_metrics/ad_density_intervention_browsertest.cc",
@@ -1769,6 +1770,7 @@
       ]
 
       sources += [
+        "../browser/apps/app_service/media_access_browsertest.cc",
         "../browser/apps/app_service/notifications_browsertest.cc",
         "../browser/apps/app_service/web_apps_base_browsertest.cc",
         "../browser/apps/icon_standardizer_unittest.cc",
@@ -3813,7 +3815,6 @@
     "../browser/ssl/insecure_sensitive_input_driver_unittest.cc",
     "../browser/ssl/security_state_tab_helper_unittest.cc",
     "../browser/ssl/ssl_config_service_manager_pref_unittest.cc",
-    "../browser/ssl/tls_deprecation_config_unittest.cc",
     "../browser/status_icons/status_icon_menu_model_unittest.cc",
     "../browser/status_icons/status_icon_unittest.cc",
     "../browser/status_icons/status_tray_unittest.cc",
@@ -6796,6 +6797,10 @@
       ]
     }
 
+    if (is_chromeos_lacros) {
+      sources += [ "../browser/lacros/browser_interactive_uitest.cc" ]
+    }
+
     if (is_win) {
       sources += [
         "../browser/downgrade/user_data_downgrade_browsertest.cc",
diff --git a/chrome/test/android/BUILD.gn b/chrome/test/android/BUILD.gn
index 5f0fff2..668c8ee 100644
--- a/chrome/test/android/BUILD.gn
+++ b/chrome/test/android/BUILD.gn
@@ -266,6 +266,7 @@
     "//chrome/android:base_module_java",
     "//chrome/android:chrome_java",
     "//chrome/browser/flags:java",
+    "//chrome/browser/language/android:java",
     "//chrome/browser/preferences:java",
     "//chrome/browser/profiles/android:java",
     "//chrome/browser/settings:test_support_java",
diff --git a/chrome/test/data/extensions/api_test/content_settings/disablepluginsapi/manifest.json b/chrome/test/data/extensions/api_test/content_settings/disablepluginsapi/manifest.json
deleted file mode 100644
index 0bb1a1b1..0000000
--- a/chrome/test/data/extensions/api_test/content_settings/disablepluginsapi/manifest.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  "name" : "Content Settings API Test Extension",
-  "version" : "0.1",
-  "manifest_version": 2,
-  "description" : "Tries to set flash content settings, and ensures flash content settings are not set",
-  "background": {
-    "scripts": ["test.js"],
-    "persistent": false
-  },
-  "permissions": [ "contentSettings" ]
-}
diff --git a/chrome/test/data/extensions/api_test/content_settings/disablepluginsapi/test.js b/chrome/test/data/extensions/api_test/content_settings/disablepluginsapi/test.js
deleted file mode 100644
index b00e40a7..0000000
--- a/chrome/test/data/extensions/api_test/content_settings/disablepluginsapi/test.js
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-var cs = chrome.contentSettings;
-
-function setPluginsSetting() {
-  cs['plugins'].set({
-    'primaryPattern': 'https://www.example.com/*',
-    'secondaryPattern': '<all_urls>',
-    'setting': 'allow'
-  });
-}
-
-function expect(expected, message) {
-  return chrome.test.callbackPass(function(value) {
-    chrome.test.assertEq(expected, value, message);
-  });
-}
-
-chrome.test.runTests([
-  function testPluginsApi() {
-    cs['plugins'].set(
-        {
-          'primaryPattern': 'https://*.google.com:443/*',
-          'secondaryPattern': '<all_urls>',
-          'setting': 'allow'
-        },
-        chrome.test.callbackFail(
-            '`chrome.contentSettings.plugins.set()` API is no longer supported.'));
-    cs['plugins'].get(
-        {'primaryUrl': 'https://drive.google.com:443/*'},
-        chrome.test.callbackFail(
-            '`chrome.contentSettings.plugins.get()` API is no longer supported.'));
-    cs['plugins'].clear(
-        {},
-        chrome.test.callbackFail(
-            '`chrome.contentSettings.plugins.clear()` API is no longer supported.'));
-  },
-]);
\ No newline at end of file
diff --git a/chrome/test/data/optimization_guide/hello.html b/chrome/test/data/optimization_guide/hello.html
new file mode 100644
index 0000000..50616bf
--- /dev/null
+++ b/chrome/test/data/optimization_guide/hello.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Test Page</title>
+</head>
+<body>
+  <p>hello</p>
+</body>
+</html>
diff --git a/chrome/test/data/optimization_guide/hello_world.html b/chrome/test/data/optimization_guide/hello_world.html
new file mode 100644
index 0000000..f5e81f64
--- /dev/null
+++ b/chrome/test/data/optimization_guide/hello_world.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Test Page</title>
+</head>
+<body>
+  <p>hello</p>
+
+  <script async type="text/javascript" src="hello_world.js"></script>
+</body>
+</html>
diff --git a/chrome/test/data/optimization_guide/hello_world.js b/chrome/test/data/optimization_guide/hello_world.js
new file mode 100644
index 0000000..10492407
--- /dev/null
+++ b/chrome/test/data/optimization_guide/hello_world.js
@@ -0,0 +1,7 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+var p = document.createElement('p');
+p.innerHTML = 'world';
+document.body.appendChild(p);
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index eea99d1..2eea86a 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -289,6 +289,9 @@
         "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_cellular_setup_remote.m.js",
         "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_cellular_setup_delegate.m.js",
         "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_esim_manager_remote.m.js",
+        "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/multidevice_setup/integration_test.m.js",
+        "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/multidevice_setup/setup_succeeded_page_test.m.js",
+        "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/multidevice_setup/start_setup_page_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/network/cr_policy_network_behavior_mojo_tests.m.js",
         "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/network/cr_policy_network_indicator_mojo_tests.m.js",
         "$root_gen_dir/chrome/test/data/webui/cr_components/chromeos/network/network_apnlist_test.m.js",
@@ -446,7 +449,6 @@
         "chromeos/machine_learning_internals_browsertest.js",
         "chromeos/print_management/print_management_browsertest.js",
         "chromeos/scanning/scanning_app_browsertest.js",
-        "multidevice_setup/multidevice_setup_browsertest.js",
         "nearby_share/nearby_browsertest.js",
         "nearby_share/shared/nearby_shared_v3_browsertest.js",
       ]
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 c95afbe..2510e04 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
@@ -19,6 +19,7 @@
       'showFileInLocation',
       'getPluralString',
       'recordScanJobSettings',
+      'getMyFilesPath',
     ]);
 
     /** @private {?SelectedPath} */
@@ -26,6 +27,9 @@
 
     /** @private {?string} */
     this.pathToFile_ = null;
+
+    /** @private {string} */
+    this.myFilesPath_ = '';
   }
 
   /** @override */
@@ -65,6 +69,12 @@
   /** @override */
   recordScanJobSettings() {}
 
+  /** @override */
+  getMyFilesPath() {
+    this.methodCalled('getMyFilesPath');
+    return Promise.resolve(this.myFilesPath_);
+  }
+
   /** @param {!SelectedPath} selectedPath */
   setSelectedPath(selectedPath) {
     this.selectedPath_ = selectedPath;
@@ -74,4 +84,9 @@
   setPathToFile(pathToFile) {
     this.pathToFile_ = pathToFile;
   }
+
+  /** @param {string} myFilesPath */
+  setMyFilesPath(myFilesPath) {
+    this.myFilesPath_ = myFilesPath;
+  }
 }
diff --git a/chrome/test/data/webui/cr_components/chromeos/BUILD.gn b/chrome/test/data/webui/cr_components/chromeos/BUILD.gn
index dc5d52f..1700bfbf 100644
--- a/chrome/test/data/webui/cr_components/chromeos/BUILD.gn
+++ b/chrome/test/data/webui/cr_components/chromeos/BUILD.gn
@@ -7,6 +7,7 @@
 group("modulize") {
   deps = [
     "cellular_setup:modulize",
+    "multidevice_setup:modulize",
     "network:modulize",
     "network_health:modulize",
     "//chrome/test/data/webui/chromeos:modulize",
diff --git a/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_browsertest.js b/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_browsertest.js
index 4cf049f..fb0d8e3 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_browsertest.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cr_components_chromeos_browsertest.js
@@ -63,6 +63,26 @@
 ].forEach(test => registerTest('NetworkComponents', 'oobe/login', ...test));
 
 [
+  ['Integration', 'multidevice_setup/integration_test.js', [
+    '../../test_browser_proxy.js',
+    '../../fake_chrome_event.js',  // Necessary for
+                                // fake_quick_unlock_private.js
+    '../../settings/chromeos/fake_quick_unlock_private.js',
+    '../../test_util.js',
+    'multidevice_setup/setup_succeeded_page_test.js',
+  ]],
+  ['SetupSucceededPage', 'multidevice_setup/setup_succeeded_page_test.js', [
+    '../../test_browser_proxy.js',
+  ]],
+  ['StartSetupPage', 'multidevice_setup/start_setup_page_test.js', [
+    '../../test_browser_proxy.js',
+    '../../test_util.js',
+  ]],
+]
+    .forEach(
+        test => registerTest('MultiDeviceSetup', 'oobe/login', ...test));
+
+[
   // TODO(https://crbug.com/1173345): Reenable flaky test suite.
   // ['ActivationCodePage', 'cellular_setup/activation_code_page_test.js',[
   //   './cellular_setup/fake_media_devices.js',
@@ -114,7 +134,7 @@
 
     /** @override */
     get extraLibraries() {
-      return super.extraLibraries.concat(module).concat(deps);
+      return super.extraLibraries.concat(deps).concat(module);
     }
 
     /** @override */
diff --git a/chrome/test/data/webui/cr_components/chromeos/multidevice_setup/BUILD.gn b/chrome/test/data/webui/cr_components/chromeos/multidevice_setup/BUILD.gn
new file mode 100644
index 0000000..038b872
--- /dev/null
+++ b/chrome/test/data/webui/cr_components/chromeos/multidevice_setup/BUILD.gn
@@ -0,0 +1,15 @@
+# Copyright 2020 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//ui/webui/resources/cr_components/chromeos/os_cr_components.gni")
+import("//ui/webui/resources/tools/js_modulizer.gni")
+
+js_modulizer("modulize") {
+  input_files = [
+    "integration_test.js",
+    "setup_succeeded_page_test.js",
+    "start_setup_page_test.js",
+  ]
+  namespace_rewrites = cr_components_chromeos_namespace_rewrites
+}
diff --git a/chrome/test/data/webui/multidevice_setup/DIR_METADATA b/chrome/test/data/webui/cr_components/chromeos/multidevice_setup/DIR_METADATA
similarity index 100%
rename from chrome/test/data/webui/multidevice_setup/DIR_METADATA
rename to chrome/test/data/webui/cr_components/chromeos/multidevice_setup/DIR_METADATA
diff --git a/chrome/test/data/webui/multidevice_setup/OWNERS b/chrome/test/data/webui/cr_components/chromeos/multidevice_setup/OWNERS
similarity index 100%
rename from chrome/test/data/webui/multidevice_setup/OWNERS
rename to chrome/test/data/webui/cr_components/chromeos/multidevice_setup/OWNERS
diff --git a/chrome/test/data/webui/multidevice_setup/integration_test.js b/chrome/test/data/webui/cr_components/chromeos/multidevice_setup/integration_test.js
similarity index 96%
rename from chrome/test/data/webui/multidevice_setup/integration_test.js
rename to chrome/test/data/webui/cr_components/chromeos/multidevice_setup/integration_test.js
index 53141152..b11277b 100644
--- a/chrome/test/data/webui/multidevice_setup/integration_test.js
+++ b/chrome/test/data/webui/cr_components/chromeos/multidevice_setup/integration_test.js
@@ -106,7 +106,12 @@
   const CORRECT_PASSWORD = 'correctPassword';
   const WRONG_PASSWORD = 'wrongPassword';
 
-  setup(() => {
+  setup(async () => {
+    // The OOBE host uses polyfill which requires the test to wait until HTML
+    // imports have finished loading before initiating any tests. The Polymer 3
+    // version of the test does not use the OOBE host so this line should not
+    // execute.
+    /* #ignore */ await cr.ui.Oobe.waitForOobeToLoad();
     browserProxy = new TestMultideviceSetupBrowserProxy();
     multidevice_setup.BrowserProxyImpl.instance_ = browserProxy;
 
diff --git a/chrome/test/data/webui/multidevice_setup/setup_succeeded_page_test.js b/chrome/test/data/webui/cr_components/chromeos/multidevice_setup/setup_succeeded_page_test.js
similarity index 94%
rename from chrome/test/data/webui/multidevice_setup/setup_succeeded_page_test.js
rename to chrome/test/data/webui/cr_components/chromeos/multidevice_setup/setup_succeeded_page_test.js
index b9cbc35..7117791f 100644
--- a/chrome/test/data/webui/multidevice_setup/setup_succeeded_page_test.js
+++ b/chrome/test/data/webui/cr_components/chromeos/multidevice_setup/setup_succeeded_page_test.js
@@ -36,7 +36,8 @@
   /** @type {?TestMultideviceSetupBrowserProxy} */
   let browserProxy = null;
 
-  setup(() => {
+  setup(async () => {
+    /* #ignore */ await cr.ui.Oobe.waitForOobeToLoad();
     browserProxy = new TestMultideviceSetupBrowserProxy();
     multidevice_setup.BrowserProxyImpl.instance_ = browserProxy;
 
diff --git a/chrome/test/data/webui/multidevice_setup/start_setup_page_test.js b/chrome/test/data/webui/cr_components/chromeos/multidevice_setup/start_setup_page_test.js
similarity index 74%
rename from chrome/test/data/webui/multidevice_setup/start_setup_page_test.js
rename to chrome/test/data/webui/cr_components/chromeos/multidevice_setup/start_setup_page_test.js
index 12f2fbfb7..bce5d18a 100644
--- a/chrome/test/data/webui/multidevice_setup/start_setup_page_test.js
+++ b/chrome/test/data/webui/cr_components/chromeos/multidevice_setup/start_setup_page_test.js
@@ -34,28 +34,35 @@
   // TODO(https://crbug.com/1019206): When v1 DeviceSync is turned off, all
   // devices should have an Instance ID.
   const DEVICES = [
+    // TODO(crbug.com/1022196) Replace the hard-coded values with the deviceSync
+    // enum. This is currently causing an import error where chromeos is not
+    // defined.
     {
       remoteDevice: {deviceName: 'Pixel XL', deviceId: 'legacy-id-1'},
-      connectivityStatus: chromeos.deviceSync.mojom.ConnectivityStatus.kOnline
+      connectivityStatus: 0  // kOnline
     },
     {
       remoteDevice: {deviceName: 'Nexus 6P', instanceId: 'iid-2'},
-      connectivityStatus: chromeos.deviceSync.mojom.ConnectivityStatus.kOffline
+      connectivityStatus: 1  // kOffline
     },
     {
       remoteDevice:
           {deviceName: 'Nexus 5', deviceId: 'legacy-id-3', instanceId: 'iid-3'},
-      connectivityStatus:
-          chromeos.deviceSync.mojom.ConnectivityStatus.kUnknownConnectivity
+      connectivityStatus: 2  // kUnknownConnectivity
     },
     {
       remoteDevice:
           {deviceName: 'Pixel 4', deviceId: 'legacy-id-4', instanceId: ''},
-      connectivityStatus: chromeos.deviceSync.mojom.ConnectivityStatus.kOnline
+      connectivityStatus: 3  // kOnline
     },
   ];
 
-  setup(() => {
+  setup(async () => {
+    // The OOBE host uses polyfill which requires the test to wait until HTML
+    // imports have finished loading before initiating any tests. The Polymer 3
+    // version of the test does not use the OOBE host so this line should not
+    // execute.
+    /* #ignore */ await cr.ui.Oobe.waitForOobeToLoad();
     startSetupPageElement = document.createElement('start-setup-page');
     document.body.appendChild(startSetupPageElement);
     startSetupPageElement.devices = DEVICES;
@@ -76,16 +83,15 @@
 
   // TODO(https://crbug.com/1019206): When v1 DeviceSync is turned off, all
   // selected IDs will be Instance IDs.
-  test(
-      'Finding devices populates dropdown and defines selected device', () => {
-        assertEquals(
-            startSetupPageElement.$.deviceDropdown.querySelectorAll('option')
-                .length,
-            DEVICES.length);
-        assertEquals(
-            startSetupPageElement.selectedInstanceIdOrLegacyDeviceId,
-            'legacy-id-1');
-      });
+  test('Finding devices populates dropdown and defines selected device', () => {
+    assertEquals(
+        startSetupPageElement.$.deviceDropdown.querySelectorAll('option')
+            .length,
+        DEVICES.length);
+    assertEquals(
+        startSetupPageElement.selectedInstanceIdOrLegacyDeviceId,
+        'legacy-id-1');
+  });
 
   // TODO(https://crbug.com/1019206): When v1 DeviceSync is turned off, all
   // selected IDs will be Instance IDs.
diff --git a/chrome/test/data/webui/multidevice_setup/multidevice_setup_browsertest.js b/chrome/test/data/webui/multidevice_setup/multidevice_setup_browsertest.js
deleted file mode 100644
index b89ccbf2..0000000
--- a/chrome/test/data/webui/multidevice_setup/multidevice_setup_browsertest.js
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/** @fileoverview Tests for MultiDevice unified setup WebUI. Chrome OS only. */
-
-// Polymer BrowserTest fixture.
-GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']);
-
-GEN('#include "content/public/test/browser_test.h"');
-
-// clang-format off
-[
-  ['Integration', 'integration_test.js', []],
-  ['SetupSucceededPage', 'setup_succeeded_page_test.js', []],
-  ['StartSetupPage', 'start_setup_page_test.js', []],
-]
-    .forEach(
-        test => registerTest('MultiDeviceSetup', ...test));
-// clang-format on
-
-function registerTest(componentName, testName, module, deps) {
-  const className = `${componentName}${testName}Test`;
-  this[className] = class extends Polymer2DeprecatedTest {
-    /** @override */
-    get browsePreload() {
-      return `chrome://multidevice-setup/`;
-    }
-
-    /** @override */
-    get extraLibraries() {
-      return [
-        ...Polymer2DeprecatedTest.prototype.extraLibraries,
-        '../test_browser_proxy.js',
-        '../fake_chrome_event.js',  // Necessary for
-                                    // fake_quick_unlock_private.js
-        '../settings/chromeos/fake_quick_unlock_private.js',
-        '../test_util.js',
-        'setup_succeeded_page_test.js',
-      ];
-    }
-  };
-
-  TEST_F(className, 'All', () => mocha.run());
-}
diff --git a/chrome/test/data/webui/settings/chromeos/crostini_page_test.js b/chrome/test/data/webui/settings/chromeos/crostini_page_test.js
index 9513abf5..c16e3e92 100644
--- a/chrome/test/data/webui/settings/chromeos/crostini_page_test.js
+++ b/chrome/test/data/webui/settings/chromeos/crostini_page_test.js
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 // clang-format off
+// #import 'chrome://os-settings/chromeos/lazy_load.js';
 // #import 'chrome://os-settings/chromeos/os_settings.js';
 
 // #import {TestGuestOsBrowserProxy} from './test_guest_os_browser_proxy.m.js';
@@ -14,7 +15,7 @@
 // #import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
 // #import {Router, Route, routes} from 'chrome://os-settings/chromeos/os_settings.js';
 // #import {eventToPromise, flushTasks, waitAfterNextRender} from 'chrome://test/test_util.m.js';
-// #import {GuestOsBrowserProxyImpl, CrostiniBrowserProxy, CrostiniBrowserProxyImpl} from 'chrome://os-settings/chromeos/os_settings.js';
+// #import {GuestOsBrowserProxyImpl, CrostiniBrowserProxy, CrostiniBrowserProxyImpl} from 'chrome://os-settings/chromeos/lazy_load.js';
 // #import {webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
 // clang-format on
 
diff --git a/chrome/test/data/webui/settings/chromeos/guest_os_shared_paths_test.js b/chrome/test/data/webui/settings/chromeos/guest_os_shared_paths_test.js
index bae1060..23f68c6 100644
--- a/chrome/test/data/webui/settings/chromeos/guest_os_shared_paths_test.js
+++ b/chrome/test/data/webui/settings/chromeos/guest_os_shared_paths_test.js
@@ -9,7 +9,7 @@
 // #import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
 // #import {assert} from 'chrome://resources/js/assert.m.js';
 // #import {TestBrowserProxy} from '../../test_browser_proxy.m.js';
-// #import {GuestOsBrowserProxyImpl} from 'chrome://os-settings/chromeos/os_settings.js';
+// #import {GuestOsBrowserProxyImpl} from 'chrome://os-settings/chromeos/lazy_load.js';
 // clang-format on
 
 /** @implements {settings.GuestOsBrowserProxy} */
diff --git a/chrome/test/data/webui/settings/chromeos/guest_os_shared_usb_devices_test.js b/chrome/test/data/webui/settings/chromeos/guest_os_shared_usb_devices_test.js
index 4ffdcb44..9353848 100644
--- a/chrome/test/data/webui/settings/chromeos/guest_os_shared_usb_devices_test.js
+++ b/chrome/test/data/webui/settings/chromeos/guest_os_shared_usb_devices_test.js
@@ -9,7 +9,7 @@
 // #import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
 // #import {assert} from 'chrome://resources/js/assert.m.js';
 // #import {TestBrowserProxy} from '../../test_browser_proxy.m.js';
-// #import {GuestOsBrowserProxyImpl} from 'chrome://os-settings/chromeos/os_settings.js';
+// #import {GuestOsBrowserProxyImpl} from 'chrome://os-settings/chromeos/lazy_load.js';
 // #import {eventToPromise, flushTasks} from 'chrome://test/test_util.m.js';
 // clang-format on
 
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index ba98125d..d5e9b4a 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-13780.0.0
\ No newline at end of file
+13782.0.0
\ No newline at end of file
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd
index e93bfcc..410daff 100644
--- a/chromeos/chromeos_strings.grd
+++ b/chromeos/chromeos_strings.grd
@@ -780,7 +780,7 @@
         See Report
       </message>
       <message name="IDS_DIANOSTICS_ROUTINE_ENTRY_TEXT" desc="The text that shows the name of a test.">
-        <ph name="TEST_NAME">$1<ex>Stress</ex></ph> Test
+        <ph name="TEST_NAME">$1<ex>Stress</ex></ph> test
       </message>
       <message name="IDS_DIAGNOSTICS_CHARGE_RUN_TESTS_BUTTON_TEXT" desc="The text for the button used to run the battery charge test.">
         Run Charge test
diff --git a/chromeos/chromeos_strings_grd/IDS_DIANOSTICS_ROUTINE_ENTRY_TEXT.png.sha1 b/chromeos/chromeos_strings_grd/IDS_DIANOSTICS_ROUTINE_ENTRY_TEXT.png.sha1
index 8e33f78..8142159d 100644
--- a/chromeos/chromeos_strings_grd/IDS_DIANOSTICS_ROUTINE_ENTRY_TEXT.png.sha1
+++ b/chromeos/chromeos_strings_grd/IDS_DIANOSTICS_ROUTINE_ENTRY_TEXT.png.sha1
@@ -1 +1 @@
-29f7c4c9cd69276dea9875b9963de8f252d7f50b
\ No newline at end of file
+c6b2bf65d6403f9e4adfb04d6a2adbc36729e516
\ No newline at end of file
diff --git a/chromeos/components/camera_app_ui/BUILD.gn b/chromeos/components/camera_app_ui/BUILD.gn
index 6b13d4f8..2d6cdff6 100644
--- a/chromeos/components/camera_app_ui/BUILD.gn
+++ b/chromeos/components/camera_app_ui/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.
 
-import("//chromeos/components/camera_app_ui/resources/css/css.gni")
-import("//chromeos/components/camera_app_ui/resources/images/images.gni")
-import("//chromeos/components/camera_app_ui/resources/js/js.gni")
-import("//chromeos/components/camera_app_ui/resources/sounds/sounds.gni")
-import("//chromeos/components/camera_app_ui/resources/views/views.gni")
 import("//mojo/public/tools/bindings/mojom.gni")
 import("//third_party/closure_compiler/compile_js.gni")
-import("//ui/webui/resources/tools/generate_grd.gni")
 
 assert(is_chromeos, "Camera App is Chrome OS only")
 
@@ -65,186 +59,3 @@
 
   deps = [ "//components/arc/mojom:camera_intent" ]
 }
-
-cca_grd_prefix = "chromeos_camera_app"
-resources_grd_file = "$target_gen_dir/${cca_grd_prefix}_resources.grd"
-
-mojo_grdp_file = "$target_gen_dir/mojo.grdp"
-generated_grdp_file = "$target_gen_dir/generated.grdp"
-platform_app_compatible_grdp_file =
-    "$target_gen_dir/platform_app_compatible.grdp"
-
-generate_grd("build_grd") {
-  deps = [
-    ":build_generated_grdp",
-    ":build_mojo_grdp",
-    ":build_platform_app_compatible_grdp",
-  ]
-
-  grd_prefix = cca_grd_prefix
-  out_grd = resources_grd_file
-  grdp_files = [
-    mojo_grdp_file,
-    generated_grdp_file,
-    platform_app_compatible_grdp_file,
-  ]
-  input_files_base_dir = rebase_path("resources", "//")
-  input_files = []
-
-  # CSS Files.
-  foreach(css, css_files) {
-    input_files += [ "css/$css" ]
-  }
-
-  # View Files.
-  foreach(view, view_files) {
-    input_files += [ "views/$view" ]
-  }
-
-  # Image Files.
-  foreach(image, in_app_images) {
-    input_files += [ "images/$image" ]
-  }
-  foreach(image, icon_images) {
-    input_files += [ "images/$image" ]
-  }
-
-  # JS Files.
-  foreach(js, compile_js_files) {
-    input_files += [ "js/$js" ]
-  }
-  foreach(js, no_compile_js_files) {
-    input_files += [ "js/$js" ]
-  }
-  foreach(wasm, wasm_files) {
-    input_files += [ "js/$wasm" ]
-  }
-
-  # Sound Files.
-  foreach(sound, sound_files) {
-    input_files += [ "sounds/$sound" ]
-  }
-
-  # Other Files.
-  input_files += [
-    # We put test.html outside of views/ directory to avoid test page and the
-    # main window sharing the same shelf icon.
-    "test/test.html",
-  ]
-}
-
-generate_grd("build_mojo_grdp") {
-  deps = [
-    "//chromeos/components/camera_app_ui:mojo_bindings_js",
-    "//components/arc/mojom:camera_intent_js",
-    "//media/capture/mojom:image_capture_js",
-    "//media/capture/video/chromeos/mojom:cros_camera_js",
-    "//mojo/public/js:bindings_lite",
-    "//mojo/public/mojom/base:base_js",
-    "//third_party/blink/public/mojom:mojom_platform_js",
-    "//ui/gfx/geometry/mojom:mojom_js",
-    "//ui/gfx/range/mojom:mojom_js",
-  ]
-
-  grd_prefix = "chromeos_camera_app_js_mojo"
-  input_files_base_dir = rebase_path("$root_gen_dir", "//")
-
-  input_mappings = [
-    {
-      path = "chromeos/components/camera_app_ui/camera_app_helper.mojom-lite.js"
-      rewrite_path = "js/mojo/camera_app_helper.mojom-lite.js"
-    },
-    {
-      path = "components/arc/mojom/camera_intent.mojom-lite.js"
-      rewrite_path = "js/mojo/camera_intent.mojom-lite.js"
-    },
-    {
-      path = "media/capture/mojom/image_capture.mojom-lite.js"
-      rewrite_path = "js/mojo/image_capture.mojom-lite.js"
-    },
-    {
-      path = "media/capture/video/chromeos/mojom/camera_app.mojom-lite.js"
-      rewrite_path = "js/mojo/camera_app.mojom-lite.js"
-    },
-    {
-      path = "media/capture/video/chromeos/mojom/camera_common.mojom-lite.js"
-      rewrite_path = "js/mojo/camera_common.mojom-lite.js"
-    },
-    {
-      path = "media/capture/video/chromeos/mojom/camera_metadata.mojom-lite.js"
-      rewrite_path = "js/mojo/camera_metadata.mojom-lite.js"
-    },
-    {
-      path = "media/capture/video/chromeos/mojom/camera_metadata_tags.mojom-lite.js"
-      rewrite_path = "js/mojo/camera_metadata_tags.mojom-lite.js"
-    },
-    {
-      path = "mojo/public/js/mojo_bindings_lite.js"
-      rewrite_path = "js/mojo/mojo_bindings_lite.js"
-    },
-    {
-      path = "mojo/public/mojom/base/time.mojom-lite.js"
-      rewrite_path = "js/mojo/time.mojom-lite.js"
-    },
-    {
-      path = "ui/gfx/geometry/mojom/geometry.mojom-lite.js"
-      rewrite_path = "js/mojo/geometry.mojom-lite.js"
-    },
-    {
-      path = "ui/gfx/range/mojom/range.mojom-lite.js"
-      rewrite_path = "js/mojo/range.mojom-lite.js"
-    },
-    {
-      path = "third_party/blink/public/mojom/idle/idle_manager.mojom-lite.js"
-      rewrite_path = "js/mojo/idle_manager.mojom-lite.js"
-    },
-  ]
-
-  input_files = []
-  resource_path_rewrites = []
-  foreach(input, input_mappings) {
-    input_files += [ "${input.path}" ]
-    resource_path_rewrites += [ "${input.path}|${input.rewrite_path}" ]
-  }
-
-  out_grd = mojo_grdp_file
-}
-
-generate_grd("build_generated_grdp") {
-  deps = [
-    "//chromeos/components/camera_app_ui/resources/js:gen_preload_images_js",
-  ]
-  grd_prefix = "chromeos_camera_app_js"
-  input_files_base_dir =
-      rebase_path("${root_gen_dir}/chromeos/components/camera_app_ui/resources",
-                  "//")
-  input_files = [ "js/preload_images.js" ]
-  out_grd = generated_grdp_file
-}
-
-# TODO(b/172343409): Remove this rule once the cleanup for platform app logic in
-# CCA is done.
-generate_grd("build_platform_app_compatible_grdp") {
-  grd_prefix = "chromeos_camera_app_js"
-  input_files_base_dir = rebase_path("resources", "//")
-
-  input_mappings = [
-    {
-      path = "js/browser_proxy/webui_browser_proxy.js"
-      rewrite_path = "js/browser_proxy/browser_proxy.js"
-    },
-    {
-      path = "js/window_controller/mojo_window_controller.js"
-      rewrite_path = "js/window_controller/window_controller.js"
-    },
-  ]
-
-  input_files = []
-  resource_path_rewrites = []
-  foreach(input, input_mappings) {
-    input_files += [ "${input.path}" ]
-    resource_path_rewrites += [ "${input.path}|${input.rewrite_path}" ]
-  }
-
-  out_grd = platform_app_compatible_grdp_file
-}
diff --git a/chromeos/components/camera_app_ui/camera_app_ui.cc b/chromeos/components/camera_app_ui/camera_app_ui.cc
index 8d5a50f4..91f833d 100644
--- a/chromeos/components/camera_app_ui/camera_app_ui.cc
+++ b/chromeos/components/camera_app_ui/camera_app_ui.cc
@@ -44,8 +44,8 @@
 
   // Add all settings resources.
   for (size_t i = 0; i < kChromeosCameraAppResourcesSize; i++) {
-    source->AddResourcePath(kChromeosCameraAppResources[i].name,
-                            kChromeosCameraAppResources[i].value);
+    source->AddResourcePath(kChromeosCameraAppResources[i].path,
+                            kChromeosCameraAppResources[i].id);
   }
 
   source->AddResourcePath("js/mojo/mojo_bindings_lite.js",
@@ -79,8 +79,8 @@
   content::WebUIDataSource* untrusted_source =
       content::WebUIDataSource::Create(kChromeUIUntrustedCameraAppURL);
   for (size_t i = 0; i < kChromeosCameraAppResourcesSize; i++) {
-    untrusted_source->AddResourcePath(kChromeosCameraAppResources[i].name,
-                                      kChromeosCameraAppResources[i].value);
+    untrusted_source->AddResourcePath(kChromeosCameraAppResources[i].path,
+                                      kChromeosCameraAppResources[i].id);
   }
   untrusted_source->AddFrameAncestor(GURL(kChromeUICameraAppURL));
 
diff --git a/chromeos/components/camera_app_ui/resources/README.md b/chromeos/components/camera_app_ui/resources/README.md
index b806cdf..f960f20 100644
--- a/chromeos/components/camera_app_ui/resources/README.md
+++ b/chromeos/components/camera_app_ui/resources/README.md
@@ -22,12 +22,6 @@
 For more details, please check the usage of individual commands with the
 `--help` flag.
 
-## Adding files
-
-When adding a file (e.g. CSS/HTML/JS/Sound/Image), please also add the file name
-into the list of corresponding .gni file. For example, when adding a "foo.js",
-please also add "foo.js" into the list in "js/js.gni".
-
 ## Known issues
 
 <https://crbug.com/?q=component%3APlatform%3EApps%3ECamera>
diff --git a/chromeos/components/camera_app_ui/resources/camera_app_resources.grd b/chromeos/components/camera_app_ui/resources/camera_app_resources.grd
new file mode 100644
index 0000000..de0ed735
--- /dev/null
+++ b/chromeos/components/camera_app_ui/resources/camera_app_resources.grd
@@ -0,0 +1,236 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grit latest_public_release="0" current_release="1" output_all_resource_defines="false">
+  <outputs>
+    <output filename="grit/chromeos_camera_app_resources.h" type="rc_header">
+      <emit emit_type='prepend'></emit>
+    </output>
+    <output filename="grit/chromeos_camera_app_resources_map.cc"
+            type="resource_file_map_source" />
+    <output filename="grit/chromeos_camera_app_resources_map.h"
+            type="resource_map_header" />
+    <output filename="chromeos_camera_app_resources.pak" type="data_package" />
+  </outputs>
+  <release seq="1">
+    <structures>
+      <structure name="IDR_CAMERA_ANALYTICS_JS" file="js/lib/analytics.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_APP_WINDOW_JS" file="js/app_window.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_ANIMATE_JS" file="js/animation.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_ASYNC_INTERVAL_JS" file="js/models/async_interval.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_ASYNC_JOB_QUEUE_JS" file="js/async_job_queue.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_ASYNC_WRITER_JS" file="js/models/async_writer.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_BACKGROUND_JS" file="js/background.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_BACKGROUND_OPS_JS" file="js/background_ops.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_BARCODE_JS" file="js/models/barcode.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_BARCODE_CHIP_JS" file="js/barcode_chip.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_BARCODE_WORKER_JS" file="js/models/barcode_worker.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_BARCODE_WORKER_INTERFACE_JS" file="js/models/barcode_worker_interface.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_BROWSER_PROXY_INTERFACE_JS" file="js/browser_proxy/browser_proxy_interface.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_CAMERA3_DEVICE_INFO_JS" file="js/device/camera3_device_info.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_CAMERA_INTENT_JS" file="js/views/camera_intent.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_CAMERA_JS" file="js/views/camera.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_CHROME_FILE_SYSTEM_ENTRY_JS" file="js/models/chrome_file_system_entry.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_CHROME_HELPER_JS" file="js/mojo/chrome_helper.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_CHROME_UTIL_JS" file="js/chrome_util.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_COMLINK_JS" file="js/lib/comlink.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_CONSTRAINTS_PREFERRER_JS" file="js/device/constraints_preferrer.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_DEVICE_INFO_UPDATER_JS" file="js/device/device_info_updater.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_DEVICE_OPERATOR_JS" file="js/mojo/device_operator.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_DIALOG_JS" file="js/views/dialog.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_DOM_JS" file="js/dom.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_DYNAMIC_IMPORT_JS" file="js/dynamic_import.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_ERROR_JS" file="js/error.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_FACE_JS" file="js/face.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_FFMPEG_JS" file="js/lib/ffmpeg.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_FILE_NAMER_JS" file="js/models/file_namer.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_FILE_SYSTEM_ENTRY_JS" file="js/models/file_system_entry.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_FILE_SYSTEM_JS" file="js/models/file_system.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_FILE_UTIL_JS" file="js/models/file_util.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_GALLERYBUTTON_JS" file="js/gallerybutton.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_IDB_JS" file="js/models/idb.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_H264_JS" file="js/h264.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_IMAGECAPTURE_JS" file="js/mojo/image_capture.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_INIT_JS" file="js/init.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_INTENT_JS" file="js/intent.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_LAYOUT_JS" file="js/views/camera/layout.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_LAZY_DIRECTORY_ENTRY_JS" file="js/models/lazy_directory_entry.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_MAIN_CSS" file="css/main.css" type="chrome_html" />
+      <structure name="IDR_CAMERA_MAIN_HTML" file="views/main.html" type="chrome_html" />
+      <structure name="IDR_CAMERA_MAIN_JS" file="js/main.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_METRICS_JS" file="js/metrics.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_MODE_MODE_BASE_JS" file="js/views/camera/mode/mode_base.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_MODE_INDEX_JS" file="js/views/camera/mode/index.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_MODE_PHOTO_JS" file="js/views/camera/mode/photo.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_MODE_PORTRAIT_JS" file="js/views/camera/mode/portrait.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_MODE_RECORD_TIME_JS" file="js/views/camera/mode/record_time.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_MODE_SQUARE_JS" file="js/views/camera/mode/square.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_MODE_VIDEO_JS" file="js/views/camera/mode/video.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_MP4_VIDEO_PROCESSOR_JS" file="js/models/mp4_video_processor.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_NATIVE_FILE_SYSTEM_ENTRY_JS" file="js/models/native_file_system_entry.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_NAV_JS" file="js/nav.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_OPTIONS_JS" file="js/views/camera/options.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_PERF_JS" file="js/perf.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_PREVIEW_JS" file="js/views/camera/preview.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_RESULT_SAVER_JS" file="js/models/result_saver.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_REVIEW_RESULT_JS" file="js/views/camera/review_result.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_SETTINGS_JS" file="js/views/settings.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_SNACKBAR_JS" file="js/snackbar.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_SOUND_JS" file="js/sound.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_STATE_JS" file="js/state.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_TEST_BRIDGE_JS" file="js/test_bridge.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_TEST_HTML" file="test/test.html" type="chrome_html" />
+      <structure name="IDR_CAMERA_THUMBNAILER_JS" file="js/thumbnailer.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_TIMER_JS" file="js/timer.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_TIMERTICK_JS" file="js/views/camera/timertick.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_TOAST_JS" file="js/toast.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_TOOLTIP_JS" file="js/tooltip.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_TYPE_JS" file="js/type.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_UNTRUSTED_GA_HELPER_JS" file="js/untrusted_ga_helper.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_UNTRUSTED_HELPER_INTERFACES_JS" file="js/untrusted_helper_interfaces.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_UNTRUSTED_SCRIPT_LOADER_HTML" file="views/untrusted_script_loader.html" type="chrome_html" />
+      <structure name="IDR_CAMERA_UNTRUSTED_SCRIPT_LOADER_JS" file="js/untrusted_script_loader.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_UNTRUSTED_VIDEO_PROCESSOR_HELPER_JS" file="js/untrusted_video_processor_helper.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_UTIL_JS" file="js/util.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_VIDEO_ENCODER_OPTIONS_JS" file="js/views/camera/video_encoder_options.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_VIDEO_SAVER_JS" file="js/models/video_saver.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_VIDEO_PROCESSOR_INTERFACE_JS" file="js/models/video_processor_interface.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_VIEW_JS" file="js/views/view.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_WAITABLE_EVENT_JS" file="js/waitable_event.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_WARNING_JS" file="js/views/warning.js" type="chrome_html" />
+      <structure name="IDR_CAMERA_WINDOW_CONTROLLER_INTERFACE_JS" file="js/window_controller/window_controller_interface.js" type="chrome_html" />
+    </structures>
+    <includes>
+      <include name="IDR_CAMERA_MOJO_WINDOW_CONTROLLER_JS"
+          file="js/window_controller/mojo_window_controller.js"
+          resource_path="js/window_controller/window_controller.js"
+          type="chrome_html" />
+      <include name="IDR_CAMERA_WEBUI_BROWSER_PROXY_JS"
+          file="js/browser_proxy/webui_browser_proxy.js"
+          resource_path="js/browser_proxy/browser_proxy.js"
+          type="chrome_html" />
+      <!-- Mojo Lite Bindings -->
+      <include name="IDR_CAMERA_CAMERA_APP_HELPER_MOJOM_LITE_JS"
+          file="${root_gen_dir}/chromeos/components/camera_app_ui/camera_app_helper.mojom-lite.js"
+          resource_path="js/mojo/camera_app_helper.mojom-lite.js"
+          use_base_dir="false"
+          type="BINDATA"/>
+      <include name="IDR_CAMERA_CAMERA_APP_MOJOM_LITE_JS"
+          file="${root_gen_dir}/media/capture/video/chromeos/mojom/camera_app.mojom-lite.js"
+          resource_path="js/mojo/camera_app.mojom-lite.js"
+          use_base_dir="false"
+          type="BINDATA"/>
+      <include name="IDR_CAMERA_CAMERA_COMMON_MOJOM_LITE_JS"
+          file="${root_gen_dir}/media/capture/video/chromeos/mojom/camera_common.mojom-lite.js"
+          resource_path="js/mojo/camera_common.mojom-lite.js"
+          use_base_dir="false"
+          type="BINDATA"/>
+      <include name="IDR_CAMERA_CAMERA_INTENT_MOJOM_LITE_JS"
+          file="${root_gen_dir}/components/arc/mojom/camera_intent.mojom-lite.js"
+          resource_path="js/mojo/camera_intent.mojom-lite.js"
+          use_base_dir="false"
+          type="BINDATA"/>
+      <include name="IDR_CAMERA_CAMERA_METADATA_MOJOM_LITE_JS"
+          file="${root_gen_dir}/media/capture/video/chromeos/mojom/camera_metadata.mojom-lite.js"
+          resource_path="js/mojo/camera_metadata.mojom-lite.js"
+          use_base_dir="false"
+          type="BINDATA"/>
+      <include name="IDR_CAMERA_CAMERA_METADATA_TAGS_MOJOM_LITE_JS"
+          file="${root_gen_dir}/media/capture/video/chromeos/mojom/camera_metadata_tags.mojom-lite.js"
+          resource_path="js/mojo/camera_metadata_tags.mojom-lite.js"
+          use_base_dir="false"
+          type="BINDATA"/>
+      <include name="IDR_CAMERA_GEOMETRY_MOJOM_LITE_JS"
+          file="${root_gen_dir}/ui/gfx/geometry/mojom/geometry.mojom-lite.js"
+          resource_path="js/mojo/geometry.mojom-lite.js"
+          use_base_dir="false"
+          type="BINDATA"/>
+      <include name="IDR_CAMERA_IDLE_MANAGER_MOJOM_LITE_JS"
+          file="${root_gen_dir}/third_party/blink/public/mojom/idle/idle_manager.mojom-lite.js"
+          resource_path="js/mojo/idle_manager.mojom-lite.js"
+          use_base_dir="false"
+          type="BINDATA"/>
+      <include name="IDR_CAMERA_IMAGE_CAPTURE_MOJOM_LITE_JS"
+          file="${root_gen_dir}/media/capture/mojom/image_capture.mojom-lite.js"
+          resource_path="js/mojo/image_capture.mojom-lite.js"
+          use_base_dir="false"
+          type="BINDATA"/>
+      <include name="IDR_CAMERA_RANGE_MOJOM_LITE_JS"
+          file="${root_gen_dir}/ui/gfx/range/mojom/range.mojom-lite.js"
+          resource_path="js/mojo/range.mojom-lite.js"
+          use_base_dir="false"
+          type="BINDATA"/>
+      <include name="IDR_CAMERA_TIME_MOJOM_LITE_JS"
+          file="${root_gen_dir}/mojo/public/mojom/base/time.mojom-lite.js"
+          resource_path="js/mojo/time.mojom-lite.js"
+          use_base_dir="false"
+          type="BINDATA"/>
+
+      <!-- Dynamic generated -->
+      <include name="IDR_CAMERA_PRELOAD_IMAGES_JS"
+          file="${root_gen_dir}/chromeos/components/camera_app_ui/resources/js/preload_images.js"
+          resource_path="js/preload_images.js"
+          use_base_dir="false"
+          type="chrome_html"/>
+
+      <include name="IDR_CAMERA_FFMPEG_WASM" file="js/lib/ffmpeg.wasm" type="BINDATA" />
+
+      <include name="IDR_CAMERA_RECORD_END_OGG" file="sounds/record_end.ogg" type="BINDATA" />
+      <include name="IDR_CAMERA_RECORD_PAUSE_OGG" file="sounds/record_pause.ogg" type="BINDATA" />
+      <include name="IDR_CAMERA_RECORD_START_OGG" file="sounds/record_start.ogg" type="BINDATA" />
+      <include name="IDR_CAMERA_SHUTTER_OGG" file="sounds/shutter.ogg" type="BINDATA" />
+      <include name="IDR_CAMERA_TICK_FINAL_OGG" file="sounds/tick_final.ogg" type="BINDATA" />
+      <include name="IDR_CAMERA_TICK_INC_OGG" file="sounds/tick_inc.ogg" type="BINDATA" />
+      <include name="IDR_CAMERA_TICK_START_OGG" file="sounds/tick_start.ogg" type="BINDATA" />
+
+      <include name="IDR_CAMERA_CAMERA_APP_ICONS_128_PNG" file="images/camera_app_icons_128.png" type="BINDATA" />
+      <include name="IDR_CAMERA_CAMERA_APP_ICONS_192_PNG" file="images/camera_app_icons_192.png" type="BINDATA" />
+      <include name="IDR_CAMERA_CAMERA_APP_ICONS_48_PNG" file="images/camera_app_icons_48.png" type="BINDATA" />
+      <include name="IDR_CAMERA_BARCODE_CHEVRON_DOWN_SVG" file="images/barcode_chevron_down.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_BARCODE_CHEVRON_UP_SVG" file="images/barcode_chevron_up.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_BARCODE_COPY_SVG" file="images/barcode_copy.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_BARCODE_SCAN_BOX_BORDER_MASK_SVG" file="images/barcode_scan_box_border_mask.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_BARCODE_TOGGLE_OFF_SVG" file="images/barcode_toggle_off.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_BARCODE_TOGGLE_ON_SVG" file="images/barcode_toggle_on.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_BARCODE_URL_SVG" file="images/barcode_url.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_CAMERA_BUTTON_FPS_30_SVG" file="images/camera_button_fps_30.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_CAMERA_BUTTON_FPS_60_SVG" file="images/camera_button_fps_60.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_CAMERA_BUTTON_GRID_OFF_SVG" file="images/camera_button_grid_off.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_CAMERA_BUTTON_GRID_ON_SVG" file="images/camera_button_grid_on.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_CAMERA_BUTTON_MIC_OFF_SVG" file="images/camera_button_mic_off.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_CAMERA_BUTTON_MIC_ON_SVG" file="images/camera_button_mic_on.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_CAMERA_BUTTON_MIRROR_OFF_SVG" file="images/camera_button_mirror_off.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_CAMERA_BUTTON_MIRROR_ON_SVG" file="images/camera_button_mirror_on.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_CAMERA_BUTTON_SETTINGS_SVG" file="images/camera_button_settings.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_CAMERA_BUTTON_SWITCH_DEVICE_SVG" file="images/camera_button_switch_device.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_CAMERA_BUTTON_SWITCH_PHOTO_SVG" file="images/camera_button_switch_photo.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_CAMERA_BUTTON_SWITCH_VIDEO_SVG" file="images/camera_button_switch_video.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_CAMERA_BUTTON_TIMER_OFF_SVG" file="images/camera_button_timer_off.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_CAMERA_BUTTON_TIMER_ON_10S_SVG" file="images/camera_button_timer_on_10s.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_CAMERA_BUTTON_TIMER_ON_3S_SVG" file="images/camera_button_timer_on_3s.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_CAMERA_FOCUS_AIM_SVG" file="images/camera_focus_aim.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_CAMERA_INTENT_PLAY_VIDEO_SVG" file="images/camera_intent_play_video.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_CAMERA_INTENT_RESULT_CANCEL_SVG" file="images/camera_intent_result_cancel.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_CAMERA_INTENT_RESULT_CONFIRM_SVG" file="images/camera_intent_result_confirm.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_CAMERA_INTRO_BANNER_CLOSE_SVG" file="images/camera_intro_banner_close.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_CAMERA_INTRO_BANNER_ICON_SVG" file="images/camera_intro_banner_icon.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_CAMERA_MODE_PHOTO_SVG" file="images/camera_mode_photo.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_CAMERA_MODE_PORTRAIT_SVG" file="images/camera_mode_portrait.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_CAMERA_MODE_SQUARE_SVG" file="images/camera_mode_square.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_CAMERA_MODE_VIDEO_SVG" file="images/camera_mode_video.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_CAMERA_SHUTTER_PHOTO_START_ACTIVE_SVG" file="images/camera_shutter_photo_start_active.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_CAMERA_SHUTTER_PHOTO_START_HOVER_SVG" file="images/camera_shutter_photo_start_hover.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_CAMERA_SHUTTER_PHOTO_START_SVG" file="images/camera_shutter_photo_start.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_CAMERA_SHUTTER_PHOTO_STOP_HOVER_SVG" file="images/camera_shutter_photo_stop_hover.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_CAMERA_SHUTTER_PHOTO_STOP_SVG" file="images/camera_shutter_photo_stop.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_CAMERA_SHUTTER_VIDEO_PAUSE_SVG" file="images/camera_shutter_video_pause.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_SETTINGS_BUTTON_BACK_SVG" file="images/settings_button_back.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_SETTINGS_BUTTON_EXPAND_SVG" file="images/settings_button_expand.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_SETTINGS_EXPERT_SVG" file="images/settings_expert.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_SETTINGS_FEEDBACK_SVG" file="images/settings_feedback.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_SETTINGS_GRID_TYPE_SVG" file="images/settings_grid_type.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_SETTINGS_HELP_SVG" file="images/settings_help.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_SETTINGS_RESOLUTION_SVG" file="images/settings_resolution.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_SETTINGS_TIMER_DURATION_SVG" file="images/settings_timer_duration.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_SPINNER_SVG" file="images/spinner.svg" type="BINDATA" />
+    </includes>
+  </release>
+</grit>
diff --git a/chromeos/components/camera_app_ui/resources/css/css.gni b/chromeos/components/camera_app_ui/resources/css/css.gni
deleted file mode 100644
index c57f5ee..0000000
--- a/chromeos/components/camera_app_ui/resources/css/css.gni
+++ /dev/null
@@ -1,5 +0,0 @@
-# Copyright 2021 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-css_files = [ "main.css" ]
diff --git a/chromeos/components/camera_app_ui/resources/images/images.gni b/chromeos/components/camera_app_ui/resources/images/images.gni
index aa5e8ee..d0f394d 100644
--- a/chromeos/components/camera_app_ui/resources/images/images.gni
+++ b/chromeos/components/camera_app_ui/resources/images/images.gni
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-in_app_images = [
+in_app_assets = [
   "barcode_chevron_down.svg",
   "barcode_chevron_up.svg",
   "barcode_copy.svg",
@@ -51,9 +51,3 @@
   "settings_timer_duration.svg",
   "spinner.svg",
 ]
-
-icon_images = [
-  "camera_app_icons_128.png",
-  "camera_app_icons_192.png",
-  "camera_app_icons_48.png",
-]
diff --git a/chromeos/components/camera_app_ui/resources/js/BUILD.gn b/chromeos/components/camera_app_ui/resources/js/BUILD.gn
index 3bf4d369..a65b589c 100644
--- a/chromeos/components/camera_app_ui/resources/js/BUILD.gn
+++ b/chromeos/components/camera_app_ui/resources/js/BUILD.gn
@@ -3,7 +3,6 @@
 # found in the LICENSE file.
 
 import("//chromeos/components/camera_app_ui/resources/images/images.gni")
-import("//chromeos/components/camera_app_ui/resources/js/js.gni")
 import("//third_party/closure_compiler/compile_js.gni")
 
 js_type_check("closure_compile") {
@@ -30,8 +29,8 @@
 action("gen_preload_images_js") {
   script = "../utils/gen_preload_images_js.py"
   inputs = []
-  foreach(image, in_app_images) {
-    inputs += [ "../images/$image" ]
+  foreach(asset, in_app_assets) {
+    inputs += [ "../images/$asset" ]
   }
 
   outputs = [ "$target_gen_dir/preload_images.js" ]
@@ -50,8 +49,91 @@
 }
 
 js_library("compile_resources") {
-  sources = compile_js_files
-
+  sources = [
+    "animation.js",
+    "app_window.js",
+    "async_job_queue.js",
+    "background.js",
+    "background_ops.js",
+    "barcode_chip.js",
+    "browser_proxy/browser_proxy.js",
+    "browser_proxy/browser_proxy_interface.js",
+    "browser_proxy/webui_browser_proxy.js",
+    "chrome_util.js",
+    "device/camera3_device_info.js",
+    "device/constraints_preferrer.js",
+    "device/device_info_updater.js",
+    "dom.js",
+    "error.js",
+    "face.js",
+    "gallerybutton.js",
+    "h264.js",
+    "init.js",
+    "intent.js",
+    "lib/comlink.js",
+    "lib/ffmpeg.js",
+    "main.js",
+    "metrics.js",
+    "models/async_interval.js",
+    "models/async_writer.js",
+    "models/barcode.js",
+    "models/barcode_worker.js",
+    "models/barcode_worker_interface.js",
+    "models/chrome_file_system_entry.js",
+    "models/file_namer.js",
+    "models/file_system.js",
+    "models/file_system_entry.js",
+    "models/file_util.js",
+    "models/idb.js",
+    "models/lazy_directory_entry.js",
+    "models/mp4_video_processor.js",
+    "models/native_file_system_entry.js",
+    "models/result_saver.js",
+    "models/video_processor_interface.js",
+    "models/video_saver.js",
+    "mojo/chrome_helper.js",
+    "mojo/device_operator.js",
+    "mojo/image_capture.js",
+    "nav.js",
+    "perf.js",
+    "snackbar.js",
+    "sound.js",
+    "state.js",
+    "test_bridge.js",
+    "thumbnailer.js",
+    "timer.js",
+    "toast.js",
+    "tooltip.js",
+    "type.js",
+    "untrusted_ga_helper.js",
+    "untrusted_helper_interfaces.js",
+    "untrusted_script_loader.js",
+    "untrusted_video_processor_helper.js",
+    "util.js",
+    "views/camera.js",
+    "views/camera/layout.js",
+    "views/camera/mode/index.js",
+    "views/camera/mode/mode_base.js",
+    "views/camera/mode/photo.js",
+    "views/camera/mode/portrait.js",
+    "views/camera/mode/record_time.js",
+    "views/camera/mode/square.js",
+    "views/camera/mode/video.js",
+    "views/camera/options.js",
+    "views/camera/preview.js",
+    "views/camera/review_result.js",
+    "views/camera/timertick.js",
+    "views/camera/video_encoder_options.js",
+    "views/camera_intent.js",
+    "views/dialog.js",
+    "views/settings.js",
+    "views/view.js",
+    "views/warning.js",
+    "waitable_event.js",
+    "window_controller/mojo_window_controller.js",
+    "window_controller/window_controller.js",
+    "window_controller/window_controller_interface.js",
+  ]
   deps = [
     ":preload_images",
     "//chromeos/components/camera_app_ui:mojo_bindings_js_library_for_compile",
@@ -61,6 +143,11 @@
     "//third_party/blink/public/mojom:mojom_platform_js_library_for_compile",
   ]
   externs_list = [
+    "externs/chrome.js",
+    "externs/swa.js",
+    "externs/typescript.js",
+    "externs/universal_analytics_api.js",
+    "externs/w3c_api.js",
     "$externs_path/chrome_extensions.js",
     "$externs_path/file_manager_private.js",
     "$externs_path/file_system_provider.js",
@@ -70,5 +157,4 @@
     # compiler.
     "//chromeos/components/web_applications/externs/file_handling.externs.js",
   ]
-  externs_list += externs_files
 }
diff --git a/chromeos/components/camera_app_ui/resources/js/background.js b/chromeos/components/camera_app_ui/resources/js/background.js
new file mode 100644
index 0000000..408faa1
--- /dev/null
+++ b/chromeos/components/camera_app_ui/resources/js/background.js
@@ -0,0 +1,619 @@
+// Copyright (c) 2013 The Chromium 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 {
+  AppWindow,  // eslint-disable-line no-unused-vars
+  getDefaultWindowSize,
+} from './app_window.js';
+import {
+  BackgroundOps,  // eslint-disable-line no-unused-vars
+  ForegroundOps,  // eslint-disable-line no-unused-vars
+} from './background_ops.js';
+import {browserProxy} from './browser_proxy/browser_proxy.js';
+import {Intent} from './intent.js';
+import {initMetrics, setMetricsEnabled} from './metrics.js';
+import {PerfLogger} from './perf.js';
+import {
+  PerfEvent,
+  TestingErrorCallback,  // eslint-disable-line no-unused-vars
+} from './type.js';
+
+/**
+ * Fixed minimum width of the window inner-bounds in pixels.
+ * @type {number}
+ */
+const MIN_WIDTH = 505;
+
+/**
+ * Fixed minimum height of the window inner-bounds in pixels.
+ * @type {number}
+ */
+const MIN_HEIGHT = 460;
+
+/**
+ * Top bar color of the window.
+ * @type {string}
+ */
+const TOPBAR_COLOR = '#000000';
+
+/**
+ * The origin of the test app used in Tast.
+ * @type {string}
+ */
+const TEST_API_ORIGIN = 'chrome-extension://behllobkkfkfnphdnhnkndlbkcpglgmj';
+
+/**
+ * It's used in test to catch the perf event before the creation of app window
+ * for time measurement before launch.
+ * @type {?PerfLogger}
+ */
+let perfLoggerForTesting = null;
+
+/**
+ * Background object for handling launch event.
+ * @type {?Background}
+ */
+let background = null;
+
+/**
+ * State of CCAWindow.
+ * @enum {string}
+ */
+const WindowState = {
+  UNINIT: 'uninitialized',
+  LAUNCHING: 'launching',
+  ACTIVE: 'active',
+  SUSPENDING: 'suspending',
+  SUSPENDED: 'suspended',
+  RESUMING: 'resuming',
+  CLOSING: 'closing',
+  CLOSED: 'closed',
+};
+
+/**
+ * Callbacks called when specific window events happened.
+ * @typedef {{
+ *   onActive: function(!CCAWindow),
+ *   onSuspended: function(!CCAWindow),
+ *   onClosed: function(!CCAWindow),
+ * }}
+ */
+let WindowEventCallbacks;  // eslint-disable-line no-unused-vars
+
+/**
+ * Callbacks called when specific window events happened in test run.
+ * onCreated()/onClosed() is called when AppWindow created/closed with window
+ * url. onError() is called with error happening in AppWindow.
+ * @typedef {{
+ *   onCreated: function(string),
+ *   onClosed: function(string),
+ *   onError: !TestingErrorCallback,
+ * }}
+ */
+let WindowTestEventCallbacks;  // eslint-disable-line no-unused-vars
+
+/**
+ * Set in test mode for notifying test events.
+ * @type {?WindowTestEventCallbacks}
+ */
+let windowTestEventCallbacks = null;
+
+/**
+ * Wrapper of AppWindow for tracking its state.
+ * @implements {BackgroundOps}
+ */
+class CCAWindow {
+  /**
+   * @param {!WindowEventCallbacks} callbacks
+   * @param {?WindowTestEventCallbacks} testingCallbacks
+   * @param {?PerfLogger} perfLogger The logger for perf events. If it
+   *     is null, we will create a new one for the window.
+   * @param {?Intent=} intent Intent to be handled by the app window.
+   *     Set to null for app window not launching from intent.
+   */
+  constructor(callbacks, testingCallbacks, perfLogger, intent = null) {
+    /**
+     * @type {!WindowEventCallbacks}
+     */
+    this.callbacks_ = callbacks;
+
+    /**
+     * @type {?WindowTestEventCallbacks}
+     */
+    this.testingCallbacks_ = testingCallbacks;
+
+    /**
+     * @type {?Intent}
+     * @private
+     */
+    this.intent_ = intent;
+
+    /**
+     * @type {!PerfLogger}
+     * @private
+     */
+    this.perfLogger_ = perfLogger || new PerfLogger();
+
+    /**
+     * @type {?chrome.app.window.AppWindow}
+     * @private
+     */
+    this.appWindow_ = null;
+
+    /**
+     * @type {?AppWindow}
+     * @private
+     */
+    this.testAppWindow_ = null;
+
+    /**
+     * @type {?ForegroundOps}
+     * @private
+     */
+    this.foregroundOps_ = null;
+
+    /**
+     * @type {!WindowState}
+     * @private
+     */
+    this.state_ = WindowState.UNINIT;
+  }
+
+  /**
+   * Gets state of the window.
+   * @return {!WindowState}
+   */
+  get state() {
+    return this.state_;
+  }
+
+  /**
+   * Creates app window and launches app.
+   * @return {!Promise}
+   */
+  async launch() {
+    // Disables the metrics sending if it is testing window.
+    if (this.testingCallbacks_ !== null) {
+      await setMetricsEnabled(false);
+    }
+
+    this.state_ = WindowState.LAUNCHING;
+
+    const windowId =
+        this.intent_ !== null ? `main-${this.intent_.intentId}` : 'main';
+    const windowUrl = 'views/main.html' +
+        (this.intent_ !== null ? this.intent_.url.search : '');
+    const isPortrait = screen.orientation.type.startsWith('portrait');
+    const {width: defaultWidth, height: defaultHeight} =
+        getDefaultWindowSize(isPortrait ? 9 / 16 : 16 / 9);
+
+    chrome.app.window.create(
+        windowUrl, {
+          id: windowId,
+          frame: {color: TOPBAR_COLOR},
+          hidden: true,  // Will be shown from main.js once loaded.
+          innerBounds: {
+            width: defaultWidth,
+            height: defaultHeight,
+            minHeight: MIN_HEIGHT,
+            minWidth: MIN_WIDTH,
+            left: Math.round((window.screen.availWidth - defaultWidth) / 2),
+            top: Math.round((window.screen.availHeight - defaultHeight) / 2),
+          },
+        },
+        (appWindow) => {
+          this.perfLogger_.start(PerfEvent.LAUNCHING_FROM_WINDOW_CREATION);
+          this.appWindow_ = appWindow;
+          this.appWindow_.onClosed.addListener(() => {
+            browserProxy.localStorageSet({maximized: appWindow.isMaximized()});
+            this.state_ = WindowState.CLOSED;
+            if (this.intent_ !== null && !this.intent_.done) {
+              this.intent_.cancel();
+            }
+            this.callbacks_.onClosed(this);
+            if (this.testingCallbacks_ !== null) {
+              this.testingCallbacks_.onClosed(windowUrl);
+            }
+            if (this.testAppWindow_ !== null) {
+              this.testAppWindow_.notifyClosed();
+            }
+          });
+          appWindow.contentWindow['backgroundOps'] = this;
+          if (this.testingCallbacks_ !== null) {
+            this.testingCallbacks_.onCreated(windowUrl);
+          }
+        });
+  }
+
+  /**
+   * @override
+   */
+  bindForegroundOps(ops) {
+    this.foregroundOps_ = ops;
+  }
+
+  /**
+   * @override
+   */
+  bindAppWindow(appWindow) {
+    this.testAppWindow_ = appWindow;
+  }
+
+  /**
+   * @override
+   */
+  getIntent() {
+    return this.intent_;
+  }
+
+  /**
+   * @override
+   */
+  notifyActivation() {
+    this.state_ = WindowState.ACTIVE;
+    this.callbacks_.onActive(this);
+  }
+
+  /**
+   * @override
+   */
+  notifySuspension() {
+    this.state_ = WindowState.SUSPENDED;
+    this.callbacks_.onSuspended(this);
+  }
+
+  /**
+   * @override
+   */
+  getPerfLogger() {
+    return this.perfLogger_;
+  }
+
+  /**
+   * @override
+   */
+  getTestingErrorCallback() {
+    return this.testingCallbacks_ ? this.testingCallbacks_.onError : null;
+  }
+
+  /**
+   * Suspends the app window.
+   */
+  suspend() {
+    if (this.state_ === WindowState.LAUNCHING) {
+      console.error('Call suspend() while window is still launching.');
+      return;
+    }
+    this.state_ = WindowState.SUSPENDING;
+    this.foregroundOps_.suspend();
+  }
+
+  /**
+   * Resumes the app window.
+   */
+  resume() {
+    this.state_ = WindowState.RESUMING;
+    this.foregroundOps_.resume();
+  }
+
+  /**
+   * Closes the app window.
+   */
+  close() {
+    this.state_ = WindowState.CLOSING;
+    this.appWindow_.close();
+  }
+
+  /**
+   * Minimize or restore the app window.
+   */
+  minimizeOrRestore() {
+    if (this.appWindow_.isMinimized()) {
+      this.appWindow_.restore();
+      this.appWindow_.focus();
+    } else {
+      this.appWindow_.minimize();
+    }
+  }
+}
+
+/**
+ * Launch event handler runs in background.
+ */
+class Background {
+  /**
+   * @public
+   */
+  constructor() {
+    /**
+     * Launch window handles launch event triggered from app launcher.
+     * @type {?CCAWindow}
+     * @private
+     */
+    this.launcherWindow_ = null;
+
+    /**
+     * Intent window handles launch event triggered from ARC++ intent.
+     * @type {?CCAWindow}
+     * @private
+     */
+    this.intentWindow_ = null;
+
+    /**
+     * The pending intent arrived when foreground window is busy.
+     * @type {?Intent}
+     */
+    this.pendingIntent_ = null;
+
+    // By default, we enable the metrics sending on background page and will
+    // turn it off when launching testing windows.
+    initMetrics();
+  }
+
+  /**
+   * Checks and logs any violation of background transition logic.
+   * @param {boolean} assertion Condition to be asserted.
+   * @param {string|function(): string} message Logged message.
+   * @private
+   */
+  assert_(assertion, message) {
+    if (!assertion) {
+      console.error(typeof message === 'string' ? message : message());
+    }
+    // TODO(inker): Cleans up states and starts over after any violation.
+  }
+
+  /**
+   * Processes the pending intent.
+   * @private
+   */
+  processPendingIntent_() {
+    if (!this.pendingIntent_) {
+      console.error('Call processPendingIntent_() without intent present.');
+      return;
+    }
+    this.intentWindow_ = this.createIntentWindow_(this.pendingIntent_);
+    this.pendingIntent_ = null;
+    this.intentWindow_.launch();
+  }
+
+  /**
+   * Returns a Window object handling launch event triggered from app launcher.
+   * @return {!CCAWindow}
+   * @private
+   */
+  createLauncherWindow_() {
+    const onActive = (wnd) => {
+      this.assert_(wnd === this.launcherWindow_, 'Wrong active launch window.');
+      this.assert_(
+          !this.intentWindow_,
+          'Launch window is active while handling intent window.');
+      if (this.pendingIntent_ !== null) {
+        wnd.suspend();
+      }
+    };
+    const onSuspended = (wnd) => {
+      this.assert_(
+          wnd === this.launcherWindow_, 'Wrong suspended launch window.');
+      this.assert_(
+          !this.intentWindow_,
+          'Launch window is suspended while handling intent window.');
+      if (this.pendingIntent_ === null) {
+        this.assert_(
+            false, 'Launch window is not suspended by some pending intent');
+        wnd.resume();
+        return;
+      }
+      this.processPendingIntent_();
+    };
+    const onClosed = (wnd) => {
+      this.assert_(wnd === this.launcherWindow_, 'Wrong closed launch window.');
+      this.launcherWindow_ = null;
+      if (this.pendingIntent_ !== null) {
+        this.processPendingIntent_();
+      }
+    };
+
+    const wnd = new CCAWindow(
+        {onActive, onSuspended, onClosed}, windowTestEventCallbacks,
+        perfLoggerForTesting);
+    windowTestEventCallbacks = null;
+    perfLoggerForTesting = null;
+    return wnd;
+  }
+
+  /**
+   * Returns a Window object handling launch event triggered from ARC++ intent.
+   * @param {!Intent} intent Intent forwarding from ARC++.
+   * @return {!CCAWindow}
+   * @private
+   */
+  createIntentWindow_(intent) {
+    const onActive = (wnd) => {
+      this.assert_(wnd === this.intentWindow_, 'Wrong active intent window.');
+      this.assert_(
+          !this.launcherWindow_ ||
+              this.launcherWindow_.state === WindowState.SUSPENDED,
+          () => `Launch window is ${
+              this.launcherWindow_.state} when intent window is active.`);
+      if (this.pendingIntent_) {
+        wnd.close();
+      }
+    };
+    const onSuspended = (wnd) => {
+      this.assert_(
+          wnd === this.intentWindow_, 'Wrong suspended intent window.');
+      this.assert_(false, 'Intent window should not be suspended.');
+    };
+    const onClosed = (wnd) => {
+      this.assert_(wnd === this.intentWindow_, 'Wrong closed intent window.');
+      this.assert_(
+          !this.launcherWindow_ ||
+              this.launcherWindow_.state === WindowState.SUSPENDED,
+          () => `Launch window is ${
+              this.launcherWindow_.state} when intent window is closed.`);
+      this.intentWindow_ = null;
+      if (this.pendingIntent_) {
+        this.processPendingIntent_();
+      } else if (this.launcherWindow_) {
+        this.launcherWindow_.resume();
+      }
+    };
+
+    const wnd = new CCAWindow(
+        {onActive, onSuspended, onClosed}, windowTestEventCallbacks,
+        perfLoggerForTesting, intent);
+    windowTestEventCallbacks = null;
+    perfLoggerForTesting = null;
+    return wnd;
+  }
+
+  /**
+   * Handles launch event triggered from app launcher.
+   */
+  launchApp() {
+    if (this.launcherWindow_ || this.intentWindow_) {
+      const activeWindow = [this.launcherWindow_, this.intentWindow_].find(
+          (wnd) => wnd !== null && wnd.state_ === WindowState.ACTIVE);
+      if (activeWindow !== undefined) {
+        activeWindow.minimizeOrRestore();
+      }
+      return;
+    }
+    this.assert_(
+        !this.pendingIntent_,
+        'Pending intent is not processed when launch new window.');
+    this.launcherWindow_ = this.createLauncherWindow_();
+    this.launcherWindow_.launch();
+  }
+
+  /**
+   * Closes the existing pending intent and replaces it with a new incoming
+   * intent.
+   * @param {!Intent} intent New incoming intent.
+   * @private
+   */
+  replacePendingIntent_(intent) {
+    if (this.pendingIntent_) {
+      this.pendingIntent_.cancel();
+    }
+    this.pendingIntent_ = intent;
+  }
+
+  /**
+   * Handles launch event triggered from ARC++ intent.
+   * @param {!Intent} intent Intent forwarding from ARC++.
+   */
+  launchIntent(intent) {
+    if (this.intentWindow_) {
+      switch (this.intentWindow_.state) {
+        case WindowState.LAUNCHING:
+        case WindowState.CLOSING:
+          this.replacePendingIntent_(intent);
+          break;
+        case WindowState.ACTIVE:
+          this.replacePendingIntent_(intent);
+          this.intentWindow_.close();
+          break;
+        default:
+          this.assert_(
+              false,
+              `Intent window is ${
+                  this.intentWindow_.state} when launch new intent window.`);
+      }
+    } else if (this.launcherWindow_) {
+      switch (this.launcherWindow_.state) {
+        case WindowState.LAUNCHING:
+        case WindowState.SUSPENDING:
+        case WindowState.RESUMING:
+        case WindowState.CLOSING:
+          this.replacePendingIntent_(intent);
+          break;
+        case WindowState.ACTIVE:
+          this.assert_(
+              !this.pendingIntent_,
+              'Pending intent is not processed when launch window is active.');
+          this.replacePendingIntent_(intent);
+          this.launcherWindow_.suspend();
+          break;
+        default:
+          this.assert_(
+              false,
+              `Launch window is ${
+                  this.launcherWindow_.state} when launch new intent window.`);
+      }
+    } else {
+      this.intentWindow_ = this.createIntentWindow_(intent);
+      this.intentWindow_.launch();
+    }
+  }
+}
+
+/**
+ * Handles connection from the test extension used in Tast.
+ * @param {!Port} port The port that used to do two-way communication.
+ */
+function handleExternalConnectionFromTest(port) {
+  if (port.sender.origin !== TEST_API_ORIGIN) {
+    console.warn(`Unknown sender id: ${port.sender.id}`);
+    return;
+  }
+  switch (port.name) {
+    // TODO(crbug.com/980846): Remove the old error reporting logic once the
+    // implementation using TestBridge on Tast side is ready.
+    case 'SET_PERF_CONNECTION':
+      port.onMessage.addListener((event) => {
+        if (perfLoggerForTesting === null) {
+          perfLoggerForTesting = new PerfLogger();
+
+          perfLoggerForTesting.addListener(({event, duration, perfInfo}) => {
+            port.postMessage({event, duration, extras: perfInfo});
+          });
+        }
+
+        const {name} = event;
+        if (name !== PerfEvent.LAUNCHING_FROM_LAUNCH_APP_COLD &&
+            name !== PerfEvent.LAUNCHING_FROM_LAUNCH_APP_WARM) {
+          console.warn(`Unknown event name from test: ${name}`);
+          return;
+        }
+        perfLoggerForTesting.start(name);
+      });
+      return;
+    case 'SET_TEST_CONNECTION':
+      // TODO(crbug.com/1082133): Documents or adds typing to the test
+      // connection port message.
+      windowTestEventCallbacks = {
+        onCreated: (windowUrl) => {
+          port.postMessage({name: 'connect', windowUrl});
+        },
+        onClosed: (windowUrl) => {
+          port.postMessage({name: 'disconnect', windowUrl});
+        },
+        onError: (errorInfo) => port.postMessage({name: 'error', errorInfo}),
+      };
+      return;
+    default:
+      console.warn(`Unknown port name: ${port.name}`);
+  }
+}
+
+chrome.app.runtime.onLaunched.addListener((launchData) => {
+  if (!background) {
+    background = new Background();
+  }
+  try {
+    const /** (string|undefined) */ url = launchData['url'];
+    if (url !== undefined) {
+      const intent = Intent.create(new URL(url));
+      background.launchIntent(intent);
+    } else {
+      background.launchApp();
+    }
+  } catch (e) {
+    console.error(e.stack);
+  }
+});
+
+chrome.runtime.onConnectExternal.addListener(handleExternalConnectionFromTest);
diff --git a/chromeos/components/camera_app_ui/resources/js/background_ops.js b/chromeos/components/camera_app_ui/resources/js/background_ops.js
new file mode 100644
index 0000000..69bb7323
--- /dev/null
+++ b/chromeos/components/camera_app_ui/resources/js/background_ops.js
@@ -0,0 +1,102 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// eslint-disable-next-line no-unused-vars
+import {AppWindow} from './app_window.js';
+// eslint-disable-next-line no-unused-vars
+import {Intent} from './intent.js';
+// eslint-disable-next-line no-unused-vars
+import {PerfLogger} from './perf.js';
+// eslint-disable-next-line no-unused-vars
+import {TestingErrorCallback} from './type.js';
+
+/**
+ * Operations supported by foreground window.
+ * @interface
+ */
+export class ForegroundOps {
+  /**
+   * Suspend foreground window.
+   * @return {!Promise}
+   * @abstract
+   */
+  async suspend() {}
+
+  /**
+   * Resume foreground window.
+   * @abstract
+   */
+  resume() {}
+}
+
+/**
+ * Operations supported by background window.
+ * @interface
+ */
+export class BackgroundOps {
+  /**
+   * Sets the implementation of ForegroundOps from foreground window.
+   * @param {!ForegroundOps} ops
+   */
+  bindForegroundOps(ops) {}
+
+  /**
+   * Sets the app window which is associated to the foreground window.
+   * @param {?AppWindow} appWindow
+   */
+  bindAppWindow(appWindow) {}
+
+  /**
+   * Gets intent associate with CCA Window object.
+   * @return {?Intent}
+   * @abstract
+   */
+  getIntent() {}
+
+  /**
+   * Gets the perf logger associate with CCA Window object.
+   * @return {!PerfLogger}
+   * @abstract
+   */
+  getPerfLogger() {}
+
+  /**
+   * Gets callback for reporting error in testing run. Returns null in non
+   * testing run.
+   * @return {?TestingErrorCallback} callbacks
+   * @abstract
+   */
+  getTestingErrorCallback() {}
+
+  /**
+   * Called by foreground window when it's active.
+   * @abstract
+   */
+  notifyActivation() {}
+
+  /**
+   * Called by foreground window when it's suspended.
+   * @abstract
+   */
+  notifySuspension() {}
+}
+
+/**
+ * Creates a fake background ops.
+ * @return {!BackgroundOps}
+ */
+export function createFakeBackgroundOps() {
+  const perfLogger = new PerfLogger();
+  const url = window.location.href;
+  const intent = url.includes('intent') ? Intent.create(new URL(url)) : null;
+  return /** @type {!BackgroundOps} */ ({
+    bindForegroundOps: (ops) => {},
+    bindAppWindow: (appWindow) => {},
+    getIntent: () => intent,
+    getPerfLogger: () => perfLogger,
+    getTestingErrorCallback: () => null,
+    notifyActivation: () => {},
+    notifySuspension: () => {},
+  });
+}
diff --git a/chromeos/components/camera_app_ui/resources/js/error.js b/chromeos/components/camera_app_ui/resources/js/error.js
index 1711646..2cbe755 100644
--- a/chromeos/components/camera_app_ui/resources/js/error.js
+++ b/chromeos/components/camera_app_ui/resources/js/error.js
@@ -97,14 +97,22 @@
 }
 
 /**
+ * @type {?TestingErrorCallback}
+ */
+let onTestingError = null;
+
+/**
  * @type {?AppWindow}
  */
 const appWindow = window['appWindow'];
 
 /**
  * Initializes error collecting functions.
+ * @param {?TestingErrorCallback} onError Callback for reporting error in
+ *     testing run. Set to null in non testing run.
  */
-export function initialize() {
+export function initialize(onError) {
+  onTestingError = onError;
   window.addEventListener('unhandledrejection', (e) => {
     reportError(
         ErrorType.UNCAUGHT_PROMISE, ErrorLevel.ERROR,
@@ -149,6 +157,12 @@
   }
   triggeredErrorSet.add(hash);
 
+  // TODO(crbug.com/980846): Remove the old error reporting logic once the
+  // implementation using TestBridge on Tast side is ready.
+  if (onTestingError !== null) {
+    onTestingError({type, level, stack: formatErrorStack(error), time});
+    return;
+  }
   if (appWindow !== null) {
     appWindow.reportError({type, level, stack: formatErrorStack(error), time});
     return;
diff --git a/chromeos/components/camera_app_ui/resources/js/js.gni b/chromeos/components/camera_app_ui/resources/js/js.gni
deleted file mode 100644
index 334be86..0000000
--- a/chromeos/components/camera_app_ui/resources/js/js.gni
+++ /dev/null
@@ -1,102 +0,0 @@
-# Copyright 2021 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-compile_js_files = [
-  "animation.js",
-  "app_window.js",
-  "async_job_queue.js",
-  "barcode_chip.js",
-  "browser_proxy/browser_proxy.js",
-  "browser_proxy/browser_proxy_interface.js",
-  "browser_proxy/webui_browser_proxy.js",
-  "chrome_util.js",
-  "device/camera3_device_info.js",
-  "device/constraints_preferrer.js",
-  "device/device_info_updater.js",
-  "dom.js",
-  "error.js",
-  "face.js",
-  "gallerybutton.js",
-  "h264.js",
-  "init.js",
-  "intent.js",
-  "lib/comlink.js",
-  "lib/ffmpeg.js",
-  "main.js",
-  "metrics.js",
-  "models/async_interval.js",
-  "models/async_writer.js",
-  "models/barcode.js",
-  "models/barcode_worker.js",
-  "models/barcode_worker_interface.js",
-  "models/chrome_file_system_entry.js",
-  "models/file_namer.js",
-  "models/file_system.js",
-  "models/file_system_entry.js",
-  "models/file_util.js",
-  "models/idb.js",
-  "models/lazy_directory_entry.js",
-  "models/mp4_video_processor.js",
-  "models/native_file_system_entry.js",
-  "models/result_saver.js",
-  "models/video_processor_interface.js",
-  "models/video_saver.js",
-  "mojo/chrome_helper.js",
-  "mojo/device_operator.js",
-  "mojo/image_capture.js",
-  "nav.js",
-  "perf.js",
-  "snackbar.js",
-  "sound.js",
-  "state.js",
-  "test_bridge.js",
-  "thumbnailer.js",
-  "timer.js",
-  "toast.js",
-  "tooltip.js",
-  "type.js",
-  "untrusted_ga_helper.js",
-  "untrusted_helper_interfaces.js",
-  "untrusted_script_loader.js",
-  "untrusted_video_processor_helper.js",
-  "util.js",
-  "views/camera.js",
-  "views/camera/layout.js",
-  "views/camera/mode/index.js",
-  "views/camera/mode/mode_base.js",
-  "views/camera/mode/photo.js",
-  "views/camera/mode/portrait.js",
-  "views/camera/mode/record_time.js",
-  "views/camera/mode/square.js",
-  "views/camera/mode/video.js",
-  "views/camera/options.js",
-  "views/camera/preview.js",
-  "views/camera/review_result.js",
-  "views/camera/timertick.js",
-  "views/camera/video_encoder_options.js",
-  "views/camera_intent.js",
-  "views/dialog.js",
-  "views/settings.js",
-  "views/view.js",
-  "views/warning.js",
-  "waitable_event.js",
-  "window_controller/mojo_window_controller.js",
-  "window_controller/window_controller.js",
-  "window_controller/window_controller_interface.js",
-]
-
-no_compile_js_files = [
-  "lib/analytics.js",
-  "dynamic_import.js",
-]
-
-wasm_files = [ "lib/ffmpeg.wasm" ]
-
-externs_files = [
-  "externs/chrome.js",
-  "externs/swa.js",
-  "externs/typescript.js",
-  "externs/universal_analytics_api.js",
-  "externs/w3c_api.js",
-]
diff --git a/chromeos/components/camera_app_ui/resources/js/main.js b/chromeos/components/camera_app_ui/resources/js/main.js
index 58e20ffc..19d4da5 100644
--- a/chromeos/components/camera_app_ui/resources/js/main.js
+++ b/chromeos/components/camera_app_ui/resources/js/main.js
@@ -6,6 +6,11 @@
   AppWindow,  // eslint-disable-line no-unused-vars
   getDefaultWindowSize,
 } from './app_window.js';
+import {
+  BackgroundOps,  // eslint-disable-line no-unused-vars
+  createFakeBackgroundOps,
+  ForegroundOps,  // eslint-disable-line no-unused-vars
+} from './background_ops.js';
 import {browserProxy} from './browser_proxy/browser_proxy.js';
 import {assert, assertInstanceof} from './chrome_util.js';
 import {
@@ -16,12 +21,10 @@
 import * as dom from './dom.js';
 import * as error from './error.js';
 import {GalleryButton} from './gallerybutton.js';
-import {Intent} from './intent.js';
 import * as metrics from './metrics.js';
 import * as filesystem from './models/file_system.js';
 import {notifyCameraResourceReady} from './mojo/device_operator.js';
 import * as nav from './nav.js';
-import {PerfLogger} from './perf.js';
 import {preloadImagesList} from './preload_images.js';
 import * as state from './state.js';
 import * as tooltip from './tooltip.js';
@@ -50,27 +53,18 @@
 
 /**
  * Creates the Camera App main object.
+ * @implements {ForegroundOps}
  */
 export class App {
   /**
-   * @param {{
-   *     perfLogger: !PerfLogger,
-   *     intent: ?Intent,
-   * }} params
-   * @public
+   * @param {!BackgroundOps} backgroundOps
    */
-  constructor({perfLogger, intent}) {
+  constructor(backgroundOps) {
     /**
-     * @type {!PerfLogger}
+     * @type {!BackgroundOps}
      * @private
      */
-    this.perfLogger_ = perfLogger;
-
-    /**
-     * @type {?Intent}
-     * @private
-     */
-    this.intent_ = intent;
+    this.backgroundOps_ = backgroundOps;
 
     /**
      * @type {!PhotoConstraintsPreferrer}
@@ -104,16 +98,18 @@
      * @private
      */
     this.cameraView_ = (() => {
-      if (this.intent_ !== null && this.intent_.shouldHandleResult) {
+      const intent = this.backgroundOps_.getIntent();
+      const perfLogger = this.backgroundOps_.getPerfLogger();
+      if (intent !== null && intent.shouldHandleResult) {
         state.set(state.State.SHOULD_HANDLE_INTENT_RESULT, true);
         return new CameraIntent(
-            this.intent_, this.infoUpdater_, this.photoPreferrer_,
-            this.videoPreferrer_, this.perfLogger_);
+            intent, this.infoUpdater_, this.photoPreferrer_,
+            this.videoPreferrer_, perfLogger);
       } else {
-        const mode = this.intent_ !== null ? this.intent_.mode : Mode.PHOTO;
+        const mode = intent !== null ? intent.mode : Mode.PHOTO;
         return new Camera(
             this.galleryButton_, this.infoUpdater_, this.photoPreferrer_,
-            this.videoPreferrer_, mode, this.perfLogger_);
+            this.videoPreferrer_, mode, perfLogger);
       }
     })();
 
@@ -151,6 +147,8 @@
     ]);
 
     nav.open(ViewName.SPLASH);
+    this.backgroundOps_.bindForegroundOps(this);
+    this.backgroundOps_.bindAppWindow(appWindow);
   }
 
   /**
@@ -230,10 +228,13 @@
 
     const showWindow = (async () => {
       windowController.enable();
+      this.backgroundOps_.notifyActivation();
       // For intent only requiring open camera with specific mode without
-      // returning the capture result, finish it directly.
-      if (this.intent_ !== null && !this.intent_.shouldHandleResult) {
-        this.intent_.finish();
+      // returning the capture result, called onIntentHandled() right
+      // after app successfully launched.
+      const intent = this.backgroundOps_.getIntent();
+      if (intent !== null && !intent.shouldHandleResult) {
+        intent.finish();
       }
     })();
 
@@ -269,10 +270,10 @@
       nav.open(ViewName.CAMERA);
       await browserProxy.setLaunchingFromWindowCreationStartTime(async () => {
         const windowCreationTime = window['windowCreationTime'];
-        this.perfLogger_.start(
+        this.backgroundOps_.getPerfLogger().start(
             PerfEvent.LAUNCHING_FROM_WINDOW_CREATION, windowCreationTime);
       });
-      this.perfLogger_.stop(
+      this.backgroundOps_.getPerfLogger().stop(
           PerfEvent.LAUNCHING_FROM_WINDOW_CREATION, {hasError: !isSuccess});
       if (appWindow !== null) {
         appWindow.onAppLaunched();
@@ -322,6 +323,7 @@
     state.set(state.State.SUSPEND, true);
     await this.cameraView_.start();
     windowController.disable();
+    this.backgroundOps_.notifySuspension();
     nav.open(ViewName.WARNING, WarningType.CAMERA_PAUSED);
   }
 
@@ -331,6 +333,7 @@
   resume() {
     state.set(state.State.SUSPEND, false);
     windowController.enable();
+    this.backgroundOps_.notifyActivation();
     nav.close(ViewName.WARNING, WarningType.CAMERA_PAUSED);
   }
 }
@@ -349,12 +352,16 @@
     return;
   }
 
-  const perfLogger = new PerfLogger();
-  const url = new URL(window.location.href);
-  const intent =
-      url.searchParams.get('intentId') !== null ? Intent.create(url) : null;
+  let bgOps;
+  if (window['backgroundOps'] !== undefined) {
+    bgOps = window['backgroundOps'];
+  } else {
+    // TODO(crbug.com/980846): Refactor after migrating to SWA since there is no
+    // background page for SWA.
+    bgOps = createFakeBackgroundOps();
+  }
 
-  state.set(state.State.INTENT, intent !== null);
+  state.set(state.State.INTENT, bgOps.getIntent() !== null);
 
   browserProxy.setupUnloadListener(() => {
     // For SWA, we don't cancel the unhandled intent here since there is no
@@ -366,13 +373,16 @@
     }
   });
 
+  const testErrorCallback = bgOps.getTestingErrorCallback();
   metrics.initMetrics();
-  if (appWindow !== null) {
+  if (testErrorCallback !== null || appWindow !== null) {
     metrics.setMetricsEnabled(false);
   }
 
   // TODO(crbug.com/1082585): Initializes it before any other javascript loaded.
-  error.initialize();
+  error.initialize(testErrorCallback);
+
+  const perfLogger = bgOps.getPerfLogger();
 
   // Setup listener for performance events.
   perfLogger.addListener(({event, duration, perfInfo}) => {
@@ -415,6 +425,7 @@
     });
   });
 
-  instance = new App({perfLogger, intent});
+  instance = new App(
+      /** @type {!BackgroundOps} */ (bgOps));
   await instance.start();
 })();
diff --git a/chromeos/components/camera_app_ui/resources/sounds/sounds.gni b/chromeos/components/camera_app_ui/resources/sounds/sounds.gni
deleted file mode 100644
index 71e9646..0000000
--- a/chromeos/components/camera_app_ui/resources/sounds/sounds.gni
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright 2021 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-sound_files = [
-  "record_end.ogg",
-  "record_pause.ogg",
-  "record_start.ogg",
-  "shutter.ogg",
-  "tick_final.ogg",
-  "tick_inc.ogg",
-  "tick_start.ogg",
-]
diff --git a/chromeos/components/camera_app_ui/resources/utils/cca.py b/chromeos/components/camera_app_ui/resources/utils/cca.py
index 3e8c06ea..5702830 100755
--- a/chromeos/components/camera_app_ui/resources/utils/cca.py
+++ b/chromeos/components/camera_app_ui/resources/utils/cca.py
@@ -40,11 +40,11 @@
 
 def build_preload_images_js(outdir):
     with open('images/images.gni') as f:
-        in_app_images = ast.literal_eval(
-            re.search(r'in_app_images\s*=\s*(\[.*?\])', f.read(),
+        in_app_assets = ast.literal_eval(
+            re.search(r'in_app_assets\s*=\s*(\[.*\])', f.read(),
                       re.DOTALL).group(1))
     with tempfile.NamedTemporaryFile('w') as f:
-        f.writelines(asset + '\n' for asset in in_app_images)
+        f.writelines(asset + '\n' for asset in in_app_assets)
         f.flush()
         cmd = [
             'utils/gen_preload_images_js.py',
@@ -67,19 +67,13 @@
     build_pak_cmd = [
         'tools/grit/grit.py',
         '-i',
-        os.path.join(
-            target_dir, 'gen/chromeos/components/camera_app_ui/' +
-            'chromeos_camera_app_resources.grd'),
+        os.path.join(cca_root, 'camera_app_resources.grd'),
         'build',
         '-o',
         os.path.join(target_dir, 'gen/chromeos'),
         '-f',
         os.path.join(target_dir,
                      'gen/tools/gritsettings/default_resource_ids'),
-        '-D',
-        f'SHARED_INTERMEDIATE_DIR={os.path.join(target_dir, "gen")}',
-        '-E',
-        f'root_src_dir={get_chromium_root()}',
         '-E',
         f'root_gen_dir={os.path.join(target_dir, "gen")}',
     ]
diff --git a/chromeos/components/camera_app_ui/resources/views/background.html b/chromeos/components/camera_app_ui/resources/views/background.html
new file mode 100644
index 0000000..f1ec880f
--- /dev/null
+++ b/chromeos/components/camera_app_ui/resources/views/background.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<!-- 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. -->
+<html>
+  <head>
+    <script src="/js/mojo/mojo_bindings_lite.js"></script>
+    <script src="/js/mojo/camera_intent.mojom-lite.js"></script>
+    <script src="/js/mojo/camera_app_helper.mojom-lite.js"></script>
+    <script type="module" src="/js/background.js"></script>
+  </head>
+</html>
diff --git a/chromeos/components/camera_app_ui/resources/views/views.gni b/chromeos/components/camera_app_ui/resources/views/views.gni
deleted file mode 100644
index d622429..0000000
--- a/chromeos/components/camera_app_ui/resources/views/views.gni
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright 2021 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-view_files = [
-  "main.html",
-  "untrusted_script_loader.html",
-]
diff --git a/chromeos/components/connectivity_diagnostics/connectivity_diagnostics_ui.cc b/chromeos/components/connectivity_diagnostics/connectivity_diagnostics_ui.cc
index 73a7173..885a245 100644
--- a/chromeos/components/connectivity_diagnostics/connectivity_diagnostics_ui.cc
+++ b/chromeos/components/connectivity_diagnostics/connectivity_diagnostics_ui.cc
@@ -28,7 +28,7 @@
                           base::span<const GritResourceMap> resources,
                           int default_resource) {
   for (const auto& resource : resources) {
-    source->AddResourcePath(resource.name, resource.value);
+    source->AddResourcePath(resource.path, resource.id);
   }
 
   source->SetDefaultResource(default_resource);
diff --git a/chromeos/components/diagnostics_ui/diagnostics_ui.cc b/chromeos/components/diagnostics_ui/diagnostics_ui.cc
index 3b70d05f..ec494e0 100644
--- a/chromeos/components/diagnostics_ui/diagnostics_ui.cc
+++ b/chromeos/components/diagnostics_ui/diagnostics_ui.cc
@@ -115,7 +115,7 @@
                           base::span<const GritResourceMap> resources,
                           int default_resource) {
   for (const auto& resource : resources) {
-    source->AddResourcePath(resource.name, resource.value);
+    source->AddResourcePath(resource.path, resource.id);
   }
 
   source->SetDefaultResource(default_resource);
diff --git a/chromeos/components/diagnostics_ui/resources/battery_status_card.html b/chromeos/components/diagnostics_ui/resources/battery_status_card.html
index 9a85f29..09064fd0 100644
--- a/chromeos/components/diagnostics_ui/resources/battery_status_card.html
+++ b/chromeos/components/diagnostics_ui/resources/battery_status_card.html
@@ -5,8 +5,11 @@
 </style>
 
 <diagnostics-card>
-  <div id="cardTitle" slot="title" aria-describedby="batteryStatusChipInfo">[[i18n('batteryTitle')]]</div>
-  <div id="batteryStatusChipInfo" slot="chip" class="diagnostics-chip" aria-hidden="true">
+  <div id="cardTitle" slot="title" aria-describedby="batteryStatusChipInfo">
+    [[i18n('batteryTitle')]]
+  </div>
+  <div id="batteryStatusChipInfo" slot="chip" class="diagnostics-chip"
+      aria-hidden="true">
     [[getDesignedFullCharge_(batteryHealth_.chargeFullDesignMilliampHours)]]
   </div>
   <iron-icon slot="icon" icon="[[batteryIcon]]" class$="[[iconClass]]">
diff --git a/chromeos/components/diagnostics_ui/resources/cpu_card.html b/chromeos/components/diagnostics_ui/resources/cpu_card.html
index 8bf446c..08a66da 100644
--- a/chromeos/components/diagnostics_ui/resources/cpu_card.html
+++ b/chromeos/components/diagnostics_ui/resources/cpu_card.html
@@ -1,7 +1,9 @@
 <style include="diagnostics-shared diagnostics-fonts"></style>
 
 <diagnostics-card>
-  <div id="cardTitle" slot="title" aria-describedby="cpuChipInfo">[[i18n('cpuTitle')]]</div>
+  <div id="cardTitle" slot="title" aria-describedby="cpuChipInfo">
+    [[i18n('cpuTitle')]]
+  </div>
   <div id="cpuChipInfo" slot="chip" aria-hidden="true">[[cpuChipInfo_]]</div>
   <iron-icon slot="icon" icon="diagnostics:cpu"></iron-icon>
   <!-- TODO(michaelcheco): Add i18n string for percent number format -->
diff --git a/chromeos/components/diagnostics_ui/resources/routine_result_entry.html b/chromeos/components/diagnostics_ui/resources/routine_result_entry.html
index 1b4e4a3d..f89f6e2 100644
--- a/chromeos/components/diagnostics_ui/resources/routine_result_entry.html
+++ b/chromeos/components/diagnostics_ui/resources/routine_result_entry.html
@@ -14,7 +14,8 @@
 </style>
 
 <div class="entryRow">
-  <text-badge id="status" badge-type="[[badgeType_]]" value="[[badgeText_]]">
+  <text-badge id="status" badge-type="[[badgeType_]]" value="[[badgeText_]]"
+      aria-hidden="true">
   </text-badge>
-  <div id="routine">[[routineType_]]</div>
+  <div id="routine" aria-describedby="status">[[routineType_]]</div>
 </div>
diff --git a/chromeos/components/file_manager/file_manager_ui.cc b/chromeos/components/file_manager/file_manager_ui.cc
index c59daba..ea91ce3 100644
--- a/chromeos/components/file_manager/file_manager_ui.cc
+++ b/chromeos/components/file_manager/file_manager_ui.cc
@@ -25,13 +25,13 @@
                           const GritResourceMap* entries,
                           size_t size) {
   for (size_t i = 0; i < size; ++i) {
-    std::string path(entries[i].name);
+    std::string path(entries[i].path);
     // Only load resources for Files app.
     if (base::StartsWith(path, "file_manager/")) {
       // Files app UI has all paths relative to //ui/file_manager/file_manager/
       // so we remove the leading file_manager/ to match the existing paths.
       base::ReplaceFirstSubstringAfterOffset(&path, 0, "file_manager/", "");
-      source->AddResourcePath(path, entries[i].value);
+      source->AddResourcePath(path, entries[i].id);
     }
   }
 }
@@ -54,8 +54,8 @@
 
   // Add chrome://file-manager content.
   for (size_t i = 0; i < kChromeosFileManagerResourcesSize; i++) {
-    source->AddResourcePath(kChromeosFileManagerResources[i].name,
-                            kChromeosFileManagerResources[i].value);
+    source->AddResourcePath(kChromeosFileManagerResources[i].path,
+                            kChromeosFileManagerResources[i].id);
   }
 
   AddFilesAppResources(source, kFileManagerResources,
diff --git a/chromeos/components/help_app_ui/help_app_untrusted_ui.cc b/chromeos/components/help_app_ui/help_app_untrusted_ui.cc
index 3e79c88..64ec22d 100644
--- a/chromeos/components/help_app_ui/help_app_untrusted_ui.cc
+++ b/chromeos/components/help_app_ui/help_app_untrusted_ui.cc
@@ -33,8 +33,8 @@
 
   // Add all resources from chromeos_media_app_bundle.pak.
   for (size_t i = 0; i < kChromeosHelpAppBundleResourcesSize; i++) {
-    source->AddResourcePath(kChromeosHelpAppBundleResources[i].name,
-                            kChromeosHelpAppBundleResources[i].value);
+    source->AddResourcePath(kChromeosHelpAppBundleResources[i].path,
+                            kChromeosHelpAppBundleResources[i].id);
   }
 
   // Add device and feature flags.
diff --git a/chromeos/components/media_app_ui/media_app_guest_ui.cc b/chromeos/components/media_app_ui/media_app_guest_ui.cc
index ee1c9d7..8e0e56b 100644
--- a/chromeos/components/media_app_ui/media_app_guest_ui.cc
+++ b/chromeos/components/media_app_ui/media_app_guest_ui.cc
@@ -43,8 +43,8 @@
 
   // Add all resources from chromeos_media_app_bundle_resources.pak.
   for (size_t i = 0; i < kChromeosMediaAppBundleResourcesSize; i++) {
-    source->AddResourcePath(kChromeosMediaAppBundleResources[i].name,
-                            kChromeosMediaAppBundleResources[i].value);
+    source->AddResourcePath(kChromeosMediaAppBundleResources[i].path,
+                            kChromeosMediaAppBundleResources[i].id);
   }
 
   // Note: go/bbsrc/flags.ts processes this.
diff --git a/chromeos/components/phonehub/fake_notification_interaction_handler.cc b/chromeos/components/phonehub/fake_notification_interaction_handler.cc
index 6b4b7cb..400f664 100644
--- a/chromeos/components/phonehub/fake_notification_interaction_handler.cc
+++ b/chromeos/components/phonehub/fake_notification_interaction_handler.cc
@@ -15,7 +15,7 @@
 
 void FakeNotificationInteractionHandler::HandleNotificationClicked(
     int64_t notification_id) {
-  NotifyNotificationClicked(notification_id);
+  handled_notification_count_++;
 }
 
 }  // namespace phonehub
diff --git a/chromeos/components/phonehub/fake_notification_interaction_handler.h b/chromeos/components/phonehub/fake_notification_interaction_handler.h
index fe409c1d..9b95f2e 100644
--- a/chromeos/components/phonehub/fake_notification_interaction_handler.h
+++ b/chromeos/components/phonehub/fake_notification_interaction_handler.h
@@ -17,8 +17,13 @@
   FakeNotificationInteractionHandler();
   ~FakeNotificationInteractionHandler() override;
 
+  size_t handled_notification_count() const {
+    return handled_notification_count_;
+  }
+
  private:
   void HandleNotificationClicked(int64_t notification_id) override;
+  size_t handled_notification_count_ = 0;
 };
 
 }  // namespace phonehub
diff --git a/chromeos/components/phonehub/notification.cc b/chromeos/components/phonehub/notification.cc
index b70ee96c..5f4b95c3 100644
--- a/chromeos/components/phonehub/notification.cc
+++ b/chromeos/components/phonehub/notification.cc
@@ -34,6 +34,7 @@
                            const base::Time& timestamp,
                            Importance importance,
                            int64_t inline_reply_id,
+                           InteractionBehavior interaction_behavior,
                            const base::Optional<base::string16>& title,
                            const base::Optional<base::string16>& text_content,
                            const base::Optional<gfx::Image>& shared_image,
@@ -43,6 +44,7 @@
       timestamp_(timestamp),
       importance_(importance),
       inline_reply_id_(inline_reply_id),
+      interaction_behavior_(interaction_behavior),
       title_(title),
       text_content_(text_content),
       shared_image_(shared_image),
@@ -59,8 +61,9 @@
 bool Notification::operator==(const Notification& other) const {
   return id_ == other.id_ && app_metadata_ == other.app_metadata_ &&
          timestamp_ == other.timestamp_ && importance_ == other.importance_ &&
-         inline_reply_id_ == other.inline_reply_id_ && title_ == other.title_ &&
-         text_content_ == other.text_content_ &&
+         inline_reply_id_ == other.inline_reply_id_ &&
+         interaction_behavior_ == other.interaction_behavior_ &&
+         title_ == other.title_ && text_content_ == other.text_content_ &&
          shared_image_ == other.shared_image_ &&
          contact_image_ == other.contact_image_;
 }
@@ -102,11 +105,26 @@
 }
 
 std::ostream& operator<<(std::ostream& stream,
+                         Notification::InteractionBehavior behavior) {
+  switch (behavior) {
+    case Notification::InteractionBehavior::kNone:
+      stream << "[None]";
+      break;
+    case Notification::InteractionBehavior::kOpenable:
+      stream << "[Openable]";
+      break;
+  }
+  return stream;
+}
+
+std::ostream& operator<<(std::ostream& stream,
                          const Notification& notification) {
   stream << "{Id: " << notification.id() << ", "
          << "App: " << notification.app_metadata() << ", "
          << "Timestamp: " << notification.timestamp() << ", "
-         << "Importance: " << notification.importance() << "}";
+         << "Importance: " << notification.importance() << ", "
+         << "InteractionBehavior: " << notification.interaction_behavior()
+         << "}";
   return stream;
 }
 
diff --git a/chromeos/components/phonehub/notification.h b/chromeos/components/phonehub/notification.h
index 42df1cd..e97a022 100644
--- a/chromeos/components/phonehub/notification.h
+++ b/chromeos/components/phonehub/notification.h
@@ -37,6 +37,15 @@
     gfx::Image icon;
   };
 
+  // Interaction behavior for integration with other features.
+  enum class InteractionBehavior {
+    // Default value. No interactions available.
+    kNone,
+
+    // Notification can be opened.
+    kOpenable
+  };
+
   // Notification importance; for more details, see
   // https://developer.android.com/reference/android/app/NotificationManager.
   enum class Importance {
@@ -70,6 +79,7 @@
       const base::Time& timestamp,
       Importance importance,
       int64_t inline_reply_id,
+      InteractionBehavior interaction_behavior,
       const base::Optional<base::string16>& title = base::nullopt,
       const base::Optional<base::string16>& text_content = base::nullopt,
       const base::Optional<gfx::Image>& shared_image = base::nullopt,
@@ -86,6 +96,9 @@
   base::Time timestamp() const { return timestamp_; }
   Importance importance() const { return importance_; }
   int64_t inline_reply_id() const { return inline_reply_id_; }
+  InteractionBehavior interaction_behavior() const {
+    return interaction_behavior_;
+  }
   const base::Optional<base::string16>& title() const { return title_; }
   const base::Optional<base::string16>& text_content() const {
     return text_content_;
@@ -103,6 +116,7 @@
   base::Time timestamp_;
   Importance importance_;
   int64_t inline_reply_id_;
+  InteractionBehavior interaction_behavior_;
   base::Optional<base::string16> title_;
   base::Optional<base::string16> text_content_;
   base::Optional<gfx::Image> shared_image_;
@@ -115,7 +129,8 @@
                          Notification::Importance importance);
 std::ostream& operator<<(std::ostream& stream,
                          const Notification& notification);
-
+std::ostream& operator<<(std::ostream& stream,
+                         const Notification::InteractionBehavior behavior);
 }  // namespace phonehub
 }  // namespace chromeos
 
diff --git a/chromeos/components/phonehub/notification_manager_impl_unittest.cc b/chromeos/components/phonehub/notification_manager_impl_unittest.cc
index b2b8a58..11d11ed 100644
--- a/chromeos/components/phonehub/notification_manager_impl_unittest.cc
+++ b/chromeos/components/phonehub/notification_manager_impl_unittest.cc
@@ -33,8 +33,8 @@
                                                     kPackageName,
                                                     /*icon=*/gfx::Image()),
       base::Time::Now(), Notification::Importance::kDefault,
-      /*inline_reply_id=*/0, base::UTF8ToUTF16(kTitle),
-      base::UTF8ToUTF16(kTextContent));
+      /*inline_reply_id=*/0, Notification::InteractionBehavior::kNone,
+      base::UTF8ToUTF16(kTitle), base::UTF8ToUTF16(kTextContent));
 }
 
 using multidevice_setup::mojom::Feature;
diff --git a/chromeos/components/phonehub/notification_processor.cc b/chromeos/components/phonehub/notification_processor.cc
index 777701a..be256aa 100644
--- a/chromeos/components/phonehub/notification_processor.cc
+++ b/chromeos/components/phonehub/notification_processor.cc
@@ -58,6 +58,12 @@
   base::Optional<int64_t> inline_reply_id = GetInlineReplyIdFromProto(proto);
   DCHECK(inline_reply_id.has_value());
 
+  auto actions_it = std::find_if(
+      proto.actions().begin(), proto.actions().end(), [](const auto& action) {
+        return action.type() == proto::Action_InputType::Action_InputType_OPEN;
+      });
+  bool includes_open_action = actions_it != proto.actions().end();
+
   base::Optional<base::string16> title = base::nullopt;
   if (!proto.title().empty())
     title = base::UTF8ToUTF16(proto.title());
@@ -74,14 +80,16 @@
   if (!contact_image.IsEmpty())
     opt_contact_image = contact_image;
 
-  return Notification(proto.id(),
-                      Notification::AppMetadata(
-                          base::UTF8ToUTF16(proto.origin_app().visible_name()),
-                          proto.origin_app().package_name(), icon),
-                      base::Time::FromJsTime(proto.epoch_time_millis()),
-                      GetNotificationImportanceFromProto(proto.importance()),
-                      *inline_reply_id, title, text_content, opt_shared_image,
-                      opt_contact_image);
+  return Notification(
+      proto.id(),
+      Notification::AppMetadata(
+          base::UTF8ToUTF16(proto.origin_app().visible_name()),
+          proto.origin_app().package_name(), icon),
+      base::Time::FromJsTime(proto.epoch_time_millis()),
+      GetNotificationImportanceFromProto(proto.importance()), *inline_reply_id,
+      includes_open_action ? Notification::InteractionBehavior::kOpenable
+                           : Notification::InteractionBehavior::kNone,
+      title, text_content, opt_shared_image, opt_contact_image);
 }
 
 }  // namespace
@@ -283,4 +291,4 @@
 }
 
 }  // namespace phonehub
-}  // namespace chromeos
\ No newline at end of file
+}  // namespace chromeos
diff --git a/chromeos/components/phonehub/notification_processor_unittest.cc b/chromeos/components/phonehub/notification_processor_unittest.cc
index 581730d..f89f0cb 100644
--- a/chromeos/components/phonehub/notification_processor_unittest.cc
+++ b/chromeos/components/phonehub/notification_processor_unittest.cc
@@ -22,6 +22,8 @@
 constexpr int64_t kInlineReplyIdA = 3;
 constexpr int64_t kInlineReplyIdB = 4;
 
+constexpr int64_t kOpenableActionId = -2;
+
 const char kIconDataA[] = "icon_a";
 const char kIconDataB[] = "icon_b";
 
@@ -112,12 +114,25 @@
     return notification_processor()->pending_notification_requests_.size();
   }
 
+  proto::Notification CreateNewInlineReplyableOpenableNotification(
+      int64_t notification_id,
+      int64_t inline_reply_id,
+      Notification::InteractionBehavior behavior) {
+    return CreateNewInlineReplyableNotification(
+        notification_id, inline_reply_id,
+        /* icon= */ std::string(),
+        /* shared_image= */ std::string(),
+        /* contact_image= */ std::string(), behavior);
+  }
+
   proto::Notification CreateNewInlineReplyableNotification(
       int64_t notification_id,
       int64_t inline_reply_id,
       std::string icon = std::string(),
       std::string shared_image = std::string(),
-      std::string contact_image = std::string()) {
+      std::string contact_image = std::string(),
+      Notification::InteractionBehavior behavior =
+          Notification::InteractionBehavior::kNone) {
     auto origin_app = std::make_unique<proto::App>();
     origin_app->set_icon(icon);
 
@@ -132,6 +147,12 @@
     mutable_action->set_id(inline_reply_id);
     mutable_action->set_type(proto::Action_InputType::Action_InputType_TEXT);
 
+    if (behavior == Notification::InteractionBehavior::kOpenable) {
+      notification.add_actions();
+      proto::Action* open_action = notification.mutable_actions(1);
+      open_action->set_id(kOpenableActionId);
+      open_action->set_type(proto::Action_InputType::Action_InputType_OPEN);
+    }
     return notification;
   }
 
@@ -326,5 +347,34 @@
   EXPECT_FALSE(fake_notification_manager()->GetNotification(kNotificationIdB));
 }
 
+TEST_F(NotificationProcessorTest, InteractionBehaviorPopulatedCorrectly) {
+  std::vector<proto::Notification> first_set_of_notifications;
+
+  // The notification should be openable if a OPEN action is specified.
+  first_set_of_notifications.emplace_back(
+      CreateNewInlineReplyableOpenableNotification(
+          kNotificationIdA, kInlineReplyIdA,
+          Notification::InteractionBehavior::kOpenable));
+  notification_processor()->AddNotifications(first_set_of_notifications);
+  image_decoder_delegate()->RunAllCallbacks();
+
+  const Notification* notification =
+      fake_notification_manager()->GetNotification(kNotificationIdA);
+  EXPECT_EQ(Notification::InteractionBehavior::kOpenable,
+            notification->interaction_behavior());
+
+  // The notification should not specify interaction behaviors if none are
+  // available.
+  first_set_of_notifications.clear();
+  first_set_of_notifications.emplace_back(
+      CreateNewInlineReplyableNotification(kNotificationIdA, kInlineReplyIdA));
+  notification_processor()->AddNotifications(first_set_of_notifications);
+  image_decoder_delegate()->RunAllCallbacks();
+
+  notification = fake_notification_manager()->GetNotification(kNotificationIdA);
+  EXPECT_EQ(Notification::InteractionBehavior::kNone,
+            notification->interaction_behavior());
+}
+
 }  // namespace phonehub
 }  // namespace chromeos
diff --git a/chromeos/components/phonehub/phone_model_test_util.cc b/chromeos/components/phonehub/phone_model_test_util.cc
index c2593bc8..f998a98 100644
--- a/chromeos/components/phonehub/phone_model_test_util.cc
+++ b/chromeos/components/phonehub/phone_model_test_util.cc
@@ -87,6 +87,7 @@
       base::Time(),
       Notification::Importance::kDefault,
       kFakeInlineReplyId,
+      Notification::InteractionBehavior::kNone,
       base::UTF8ToUTF16(kFakeNotificationTitle),
       base::UTF8ToUTF16(kFakeNotificationText)};
   return *fake_notification;
diff --git a/chromeos/components/phonehub/phone_status_processor_unittest.cc b/chromeos/components/phonehub/phone_status_processor_unittest.cc
index dc4bf07..b79a82a 100644
--- a/chromeos/components/phonehub/phone_status_processor_unittest.cc
+++ b/chromeos/components/phonehub/phone_status_processor_unittest.cc
@@ -44,10 +44,11 @@
       const std::vector<proto::Notification>& notification_protos) override {
     base::flat_set<Notification> notifications;
     for (const auto& proto : notification_protos) {
-      notifications.emplace(
-          Notification(proto.id(), CreateFakeAppMetadata(), base::Time(),
-                       Notification::Importance::kDefault, 0, base::nullopt,
-                       base::nullopt, base::nullopt, base::nullopt));
+      notifications.emplace(Notification(
+          proto.id(), CreateFakeAppMetadata(), base::Time(),
+          Notification::Importance::kDefault, /*inline_reply_id=*/0,
+          Notification::InteractionBehavior::kNone, base::nullopt,
+          base::nullopt, base::nullopt, base::nullopt));
     }
     notification_manager_->SetNotificationsInternal(notifications);
   }
diff --git a/chromeos/components/print_management/print_management_ui.cc b/chromeos/components/print_management/print_management_ui.cc
index 4907f322..e8e4dcf 100644
--- a/chromeos/components/print_management/print_management_ui.cc
+++ b/chromeos/components/print_management/print_management_ui.cc
@@ -27,7 +27,7 @@
                           base::span<const GritResourceMap> resources,
                           int default_resource) {
   for (const auto& resource : resources) {
-    source->AddResourcePath(resource.name, resource.value);
+    source->AddResourcePath(resource.path, resource.id);
   }
   source->SetDefaultResource(default_resource);
   source->AddResourcePath("test_loader.html", IDR_WEBUI_HTML_TEST_LOADER_HTML);
diff --git a/chromeos/components/scanning/resources/scan_preview.html b/chromeos/components/scanning/resources/scan_preview.html
index 9a7b426..c157e68f 100644
--- a/chromeos/components/scanning/resources/scan_preview.html
+++ b/chromeos/components/scanning/resources/scan_preview.html
@@ -54,15 +54,6 @@
     box-shadow: 0 0 0 2px rgba(var(--google-blue-600-rgb), .4);
   }
 
-  .preview::-webkit-scrollbar {
-    -webkit-appearance: none;
-    width: 4px;
-  }
-
-  .preview::-webkit-scrollbar-thumb {
-    background-color: rgba(0, 0, 0, 0.5);
-  }
-
   .preview-item {
     border: 1px solid var(--google-grey-300);
     border-radius: 4px;
@@ -71,12 +62,11 @@
   }
 
   .scanned-image {
-    display: block;
-  }
-
-  /* Add top margin to all but the first scanned image. */
-  .scanned-image:nth-of-type(n+2) {
-    margin-top: 8px;
+    box-shadow: rgba(var(--google-grey-200-rgb), .3) 0 4px 4px 0,
+                rgba(var(--google-grey-900-rgb), .15) 0 8px 12px 1px;
+    margin-bottom: 12px;
+    margin-inline-start: 12px;
+    width: calc(100% - 24px);
   }
 
   paper-progress {
diff --git a/chromeos/components/scanning/resources/scanning_app.js b/chromeos/components/scanning/resources/scanning_app.js
index c40f217..6bfae27 100644
--- a/chromeos/components/scanning/resources/scanning_app.js
+++ b/chromeos/components/scanning/resources/scanning_app.js
@@ -37,12 +37,6 @@
 import {ScanningBrowserProxy, ScanningBrowserProxyImpl} from './scanning_browser_proxy.js';
 
 /**
- * The default save directory for completed scans.
- * @const {string}
- */
-const DEFAULT_SAVE_DIRECTORY = '/home/chronos/user/MyFiles';
-
-/**
  * URL for the Scanning help page.
  * @const {string}
  */
@@ -250,10 +244,12 @@
   /** @override */
   created() {
     this.scanService_ = getScanService();
-    this.selectedFilePath = DEFAULT_SAVE_DIRECTORY;
-
     this.browserProxy_ = ScanningBrowserProxyImpl.getInstance();
     this.browserProxy_.initialize();
+    this.browserProxy_.getMyFilesPath().then(
+        /* @type {string} */ (myFilesPath) => {
+          this.selectedFilePath = myFilesPath;
+        });
   },
 
   /** @override */
diff --git a/chromeos/components/scanning/resources/scanning_browser_proxy.js b/chromeos/components/scanning/resources/scanning_browser_proxy.js
index 2073e33..dd148c6 100644
--- a/chromeos/components/scanning/resources/scanning_browser_proxy.js
+++ b/chromeos/components/scanning/resources/scanning_browser_proxy.js
@@ -60,6 +60,12 @@
    * @param {!ScanJobSettingsForMetrics} scanJobSettings
    */
   recordScanJobSettings(scanJobSettings) {}
+
+  /**
+   * Returns the MyFiles path for the current user.
+   * @return {!Promise<string>}
+   */
+  getMyFilesPath() {}
 }
 
 /** @implements {ScanningBrowserProxy} */
@@ -88,6 +94,11 @@
   recordScanJobSettings(scanJobSettings) {
     chrome.send('recordScanJobSettings', [scanJobSettings]);
   }
+
+  /** @override */
+  getMyFilesPath() {
+    return sendWithPromise('getMyFilesPath');
+  }
 }
 
 // The singleton instance_ can be replaced with a test version of this wrapper
diff --git a/chromeos/components/scanning/resources/scanning_shared_css.html b/chromeos/components/scanning/resources/scanning_shared_css.html
index a72b183..ae6b3a4 100644
--- a/chromeos/components/scanning/resources/scanning_shared_css.html
+++ b/chromeos/components/scanning/resources/scanning_shared_css.html
@@ -31,6 +31,16 @@
       background-position-x: 12px;
     }
 
+    ::-webkit-scrollbar {
+      -webkit-appearance: none;
+      width: 4px;
+    }
+
+    ::-webkit-scrollbar-thumb {
+      background-color: rgba(0, 0, 0, 0.5);
+      border-radius: 4px;
+    }
+
     @media (min-width: 600px) {
       :host {
         --container-width: 600px;
diff --git a/chromeos/components/scanning/scanning_handler.cc b/chromeos/components/scanning/scanning_handler.cc
index ebe16ed..fb0916c0 100644
--- a/chromeos/components/scanning/scanning_handler.cc
+++ b/chromeos/components/scanning/scanning_handler.cc
@@ -51,6 +51,11 @@
       "getPluralString",
       base::BindRepeating(&ScanningHandler::HandleGetPluralString,
                           base::Unretained(this)));
+
+  web_ui()->RegisterMessageCallback(
+      "getMyFilesPath",
+      base::BindRepeating(&ScanningHandler::HandleGetMyFilesPath,
+                          base::Unretained(this)));
 }
 
 void ScanningHandler::HandleInitialize(const base::ListValue* args) {
@@ -126,6 +131,19 @@
                             base::Value(localized_string));
 }
 
+void ScanningHandler::HandleGetMyFilesPath(const base::ListValue* args) {
+  if (!IsJavascriptAllowed())
+    return;
+
+  CHECK_EQ(1U, args->GetSize());
+  const std::string& callback = args->GetList()[0].GetString();
+
+  const base::FilePath my_files_path =
+      scanning_paths_provider_->GetMyFilesPath(web_ui());
+  ResolveJavascriptCallback(base::Value(callback),
+                            base::Value(my_files_path.value()));
+}
+
 // Uses the full filepath and the base directory (lowest level directory in the
 // filepath, used to display in the UI) to create a Value object to return to
 // the Scanning UI.
diff --git a/chromeos/components/scanning/scanning_handler.h b/chromeos/components/scanning/scanning_handler.h
index d9a4e5d..35420d9 100644
--- a/chromeos/components/scanning/scanning_handler.h
+++ b/chromeos/components/scanning/scanning_handler.h
@@ -77,6 +77,9 @@
   // Returns a localized, pluralized string.
   void HandleGetPluralString(const base::ListValue* args);
 
+  // Gets the MyFiles path for the current user.
+  void HandleGetMyFilesPath(const base::ListValue* args);
+
   SelectFilePolicyCreator select_file_policy_creator_;
 
   std::string scan_location_callback_id_;
diff --git a/chromeos/components/scanning/scanning_handler_unittest.cc b/chromeos/components/scanning/scanning_handler_unittest.cc
index 49f42ca2..08f11ed 100644
--- a/chromeos/components/scanning/scanning_handler_unittest.cc
+++ b/chromeos/components/scanning/scanning_handler_unittest.cc
@@ -122,6 +122,10 @@
                                   const base::FilePath& path) override {
     return path.BaseName().value();
   }
+
+  base::FilePath GetMyFilesPath(content::WebUI* web_ui) override {
+    return base::FilePath(kTestFilePath);
+  }
 };
 
 bool ShowFileInFilesApp(const base::FilePath& drive_path,
@@ -233,4 +237,18 @@
   EXPECT_TRUE(call_data.arg3()->GetBool());
 }
 
+// Validates that invoking the getMyFilesPath Web UI event returns the correct
+// path.
+TEST_F(ScanningHandlerTest, GetMyFilesPath) {
+  const size_t call_data_count_before_call = web_ui_.call_data().size();
+  base::ListValue args;
+  args.Append(kHandlerFunctionName);
+  web_ui_.HandleReceivedMessage("getMyFilesPath", &args);
+
+  const content::TestWebUI::CallData& call_data =
+      GetCallData(call_data_count_before_call);
+  EXPECT_EQ(base::FilePath(kTestFilePath).value(),
+            call_data.arg3()->GetString());
+}
+
 }  // namespace chromeos.
diff --git a/chromeos/components/scanning/scanning_paths_provider.h b/chromeos/components/scanning/scanning_paths_provider.h
index dc751a4..f991c38 100644
--- a/chromeos/components/scanning/scanning_paths_provider.h
+++ b/chromeos/components/scanning/scanning_paths_provider.h
@@ -25,6 +25,9 @@
   // desired display name.
   virtual std::string GetBaseNameFromPath(content::WebUI* web_ui,
                                           const base::FilePath& path) = 0;
+
+  // Gets the MyFiles path for the current user.
+  virtual base::FilePath GetMyFilesPath(content::WebUI* web_ui) = 0;
 };
 
 }  // namespace chromeos
diff --git a/chromeos/components/scanning/scanning_ui.cc b/chromeos/components/scanning/scanning_ui.cc
index 66607c1..3f305213 100644
--- a/chromeos/components/scanning/scanning_ui.cc
+++ b/chromeos/components/scanning/scanning_ui.cc
@@ -35,7 +35,7 @@
                           base::span<const GritResourceMap> resources,
                           int default_resource) {
   for (const auto& resource : resources) {
-    source->AddResourcePath(resource.name, resource.value);
+    source->AddResourcePath(resource.path, resource.id);
   }
 
   source->SetDefaultResource(default_resource);
diff --git a/chromeos/resources/BUILD.gn b/chromeos/resources/BUILD.gn
index 1189db1..b770563 100644
--- a/chromeos/resources/BUILD.gn
+++ b/chromeos/resources/BUILD.gn
@@ -43,14 +43,8 @@
 
 # Resources used by chrome://camera-app.
 grit("camera_app_resources") {
-  enable_input_discovery_for_gn_analyze = false
-  defines =
-      [ "SHARED_INTERMEDIATE_DIR=" + rebase_path(root_gen_dir, root_build_dir) ]
+  source = "../components/camera_app_ui/resources/camera_app_resources.grd"
 
-  deps = [ "//chromeos/components/camera_app_ui:build_grd" ]
-
-  camera_app_gen_dir = "$root_gen_dir/chromeos/components/camera_app_ui"
-  source = "$camera_app_gen_dir/chromeos_camera_app_resources.grd"
   outputs = [
     "grit/chromeos_camera_app_resources.h",
     "grit/chromeos_camera_app_resources_map.cc",
@@ -58,6 +52,14 @@
     "chromeos_camera_app_resources.pak",
   ]
   output_dir = "$root_gen_dir/chromeos"
+
+  deps = [
+    "//chromeos/components/camera_app_ui:mojo_bindings_js",
+    "//chromeos/components/camera_app_ui/resources/js:gen_preload_images_js",
+    "//media/capture/video/chromeos/mojom:cros_camera_js",
+    "//mojo/public/js:bindings_lite",
+    "//third_party/blink/public/mojom:mojom_platform_js",
+  ]
 }
 
 # Resources used by chrome://connectivity-diagnostics
diff --git a/chromeos/services/assistant/BUILD.gn b/chromeos/services/assistant/BUILD.gn
index 155ef69..b14f17e 100644
--- a/chromeos/services/assistant/BUILD.gn
+++ b/chromeos/services/assistant/BUILD.gn
@@ -82,14 +82,10 @@
       "assistant_settings_impl.h",
       "libassistant_service_host_impl.cc",
       "libassistant_service_host_impl.h",
-      "platform/file_provider_impl.cc",
-      "platform/file_provider_impl.h",
       "platform/network_provider_impl.cc",
       "platform/network_provider_impl.h",
       "platform_api_impl.cc",
       "platform_api_impl.h",
-      "utils.cc",
-      "utils.h",
     ]
 
     if (enable_fake_assistant_microphone) {
@@ -105,7 +101,6 @@
       "//chromeos/assistant/internal:libassistant",
       "//chromeos/assistant/internal/proto/google3",
       "//chromeos/dbus",
-      "//chromeos/resources",
       "//chromeos/services/assistant/proxy",
       "//chromeos/services/assistant/public/cpp/migration",
       "//chromeos/services/libassistant",
@@ -116,7 +111,6 @@
       "//libassistant/shared/public:export",
       "//net",
       "//services/network/public/cpp",
-      "//ui/base",
       "//url",
     ]
   }
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.cc b/chromeos/services/assistant/assistant_manager_service_impl.cc
index 6d545e8..e7031ea 100644
--- a/chromeos/services/assistant/assistant_manager_service_impl.cc
+++ b/chromeos/services/assistant/assistant_manager_service_impl.cc
@@ -49,7 +49,6 @@
 #include "chromeos/services/assistant/public/cpp/migration/libassistant_v1_api.h"
 #include "chromeos/services/assistant/public/shared/utils.h"
 #include "chromeos/services/assistant/service_context.h"
-#include "chromeos/services/assistant/utils.h"
 #include "chromeos/services/libassistant/public/mojom/android_app_info.mojom.h"
 #include "chromeos/services/libassistant/public/mojom/speech_recognition_observer.mojom.h"
 #include "chromeos/strings/grit/chromeos_strings.h"
diff --git a/chromeos/services/assistant/platform_api_impl.cc b/chromeos/services/assistant/platform_api_impl.cc
index 43d8d43e..00a582e 100644
--- a/chromeos/services/assistant/platform_api_impl.cc
+++ b/chromeos/services/assistant/platform_api_impl.cc
@@ -9,14 +9,8 @@
 #include <vector>
 
 #include "chromeos/services/assistant/public/cpp/features.h"
-#include "chromeos/services/assistant/utils.h"
-#include "libassistant/shared/public/assistant_export.h"
-#include "libassistant/shared/public/platform_api.h"
-#include "libassistant/shared/public/platform_factory.h"
 
-using assistant_client::FileProvider;
 using assistant_client::NetworkProvider;
-using assistant_client::PlatformApi;
 
 namespace chromeos {
 namespace assistant {
@@ -33,10 +27,6 @@
 
 PlatformApiImpl::~PlatformApiImpl() = default;
 
-FileProvider& PlatformApiImpl::GetFileProvider() {
-  return file_provider_;
-}
-
 NetworkProvider& PlatformApiImpl::GetNetworkProvider() {
   return network_provider_;
 }
diff --git a/chromeos/services/assistant/platform_api_impl.h b/chromeos/services/assistant/platform_api_impl.h
index dcbc8c7..3111ebc 100644
--- a/chromeos/services/assistant/platform_api_impl.h
+++ b/chromeos/services/assistant/platform_api_impl.h
@@ -9,7 +9,6 @@
 #include <utility>
 #include <vector>
 
-#include "chromeos/services/assistant/platform/file_provider_impl.h"
 #include "chromeos/services/assistant/platform/network_provider_impl.h"
 #include "chromeos/services/assistant/public/cpp/assistant_service.h"
 #include "chromeos/services/assistant/public/cpp/migration/cros_platform_api.h"
@@ -31,11 +30,9 @@
       scoped_refptr<base::SequencedTaskRunner> main_thread_task_runner);
   ~PlatformApiImpl() override;
 
-  assistant_client::FileProvider& GetFileProvider() override;
   assistant_client::NetworkProvider& GetNetworkProvider() override;
 
  private:
-  FileProviderImpl file_provider_;
   NetworkProviderImpl network_provider_;
 
   DISALLOW_COPY_AND_ASSIGN(PlatformApiImpl);
diff --git a/chromeos/services/assistant/public/cpp/migration/cros_platform_api.h b/chromeos/services/assistant/public/cpp/migration/cros_platform_api.h
index d867175..43ca91c37 100644
--- a/chromeos/services/assistant/public/cpp/migration/cros_platform_api.h
+++ b/chromeos/services/assistant/public/cpp/migration/cros_platform_api.h
@@ -8,7 +8,6 @@
 #include "base/macros.h"
 
 namespace assistant_client {
-class FileProvider;
 class NetworkProvider;
 }  // namespace assistant_client
 
@@ -24,9 +23,6 @@
   CrosPlatformApi() = default;
   virtual ~CrosPlatformApi() = default;
 
-  // Returns the file provider to be used by libassistant.
-  virtual assistant_client::FileProvider& GetFileProvider() = 0;
-
   // Returns the network provider to be used by libassistant.
   virtual assistant_client::NetworkProvider& GetNetworkProvider() = 0;
 
diff --git a/chromeos/services/assistant/public/cpp/migration/fake_platform_api.cc b/chromeos/services/assistant/public/cpp/migration/fake_platform_api.cc
index a464dd8..4e06e72 100644
--- a/chromeos/services/assistant/public/cpp/migration/fake_platform_api.cc
+++ b/chromeos/services/assistant/public/cpp/migration/fake_platform_api.cc
@@ -13,11 +13,6 @@
 FakePlatformApi::FakePlatformApi() = default;
 FakePlatformApi::~FakePlatformApi() = default;
 
-assistant_client::FileProvider& FakePlatformApi::GetFileProvider() {
-  NOTIMPLEMENTED();
-  abort();
-}
-
 assistant_client::NetworkProvider& FakePlatformApi::GetNetworkProvider() {
   NOTIMPLEMENTED();
   abort();
diff --git a/chromeos/services/assistant/public/cpp/migration/fake_platform_api.h b/chromeos/services/assistant/public/cpp/migration/fake_platform_api.h
index 017dca4..9b827e34 100644
--- a/chromeos/services/assistant/public/cpp/migration/fake_platform_api.h
+++ b/chromeos/services/assistant/public/cpp/migration/fake_platform_api.h
@@ -23,7 +23,6 @@
   ~FakePlatformApi() override;
 
   // CrosPlatformApi implementation:
-  assistant_client::FileProvider& GetFileProvider() override;
   assistant_client::NetworkProvider& GetNetworkProvider() override;
 };
 
diff --git a/chromeos/services/assistant/service.cc b/chromeos/services/assistant/service.cc
index f7545eb..fa17e482 100644
--- a/chromeos/services/assistant/service.cc
+++ b/chromeos/services/assistant/service.cc
@@ -52,7 +52,6 @@
 #include "chromeos/assistant/internal/internal_constants.h"
 #include "chromeos/services/assistant/assistant_manager_service_impl.h"
 #include "chromeos/services/assistant/assistant_settings_impl.h"
-#include "chromeos/services/assistant/utils.h"
 #endif
 
 namespace chromeos {
diff --git a/chromeos/services/assistant/utils.cc b/chromeos/services/assistant/utils.cc
deleted file mode 100644
index 684bb3ec..0000000
--- a/chromeos/services/assistant/utils.cc
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chromeos/services/assistant/utils.h"
-
-#include <utility>
-
-#include "base/check_op.h"
-#include "base/files/file_util.h"
-#include "base/path_service.h"
-
-namespace chromeos {
-namespace assistant {
-
-// Get the root path for assistant files.
-base::FilePath GetRootPath() {
-  base::FilePath home_dir;
-  CHECK(base::PathService::Get(base::DIR_HOME, &home_dir));
-  // Ensures DIR_HOME is overridden after primary user sign-in.
-  CHECK_NE(base::GetHomeDir(), home_dir);
-  return home_dir;
-}
-
-base::FilePath GetBaseAssistantDir() {
-  return GetRootPath().Append(FILE_PATH_LITERAL("google-assistant-library"));
-}
-
-}  // namespace assistant
-}  // namespace chromeos
diff --git a/chromeos/services/assistant/utils.h b/chromeos/services/assistant/utils.h
deleted file mode 100644
index 24dbd3d..0000000
--- a/chromeos/services/assistant/utils.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROMEOS_SERVICES_ASSISTANT_UTILS_H_
-#define CHROMEOS_SERVICES_ASSISTANT_UTILS_H_
-
-#include <string>
-
-#include "base/macros.h"
-#include "base/optional.h"
-
-namespace base {
-class FilePath;
-}  // namespace base
-
-namespace chromeos {
-namespace assistant {
-
-// Returns the root path of all user specific files.
-base::FilePath GetRootPath();
-
-// Returns the path where all downloaded LibAssistant resources are stored.
-base::FilePath GetBaseAssistantDir();
-
-// Creates the configuration for libassistant.
-std::string CreateLibAssistantConfig(
-    base::Optional<std::string> s3_server_uri_override,
-    base::Optional<std::string> device_id_override);
-
-}  // namespace assistant
-}  // namespace chromeos
-
-#endif  // CHROMEOS_SERVICES_ASSISTANT_UTILS_H_
diff --git a/chromeos/services/cellular_setup/esim_profile.cc b/chromeos/services/cellular_setup/esim_profile.cc
index 7a2ff0be..abb8826 100644
--- a/chromeos/services/cellular_setup/esim_profile.cc
+++ b/chromeos/services/cellular_setup/esim_profile.cc
@@ -6,6 +6,8 @@
 
 #include "base/strings/utf_string_conversions.h"
 #include "chromeos/dbus/hermes/hermes_euicc_client.h"
+#include "chromeos/dbus/hermes/hermes_profile_client.h"
+#include "chromeos/dbus/hermes/hermes_response_status.h"
 #include "chromeos/network/cellular_esim_profile.h"
 #include "chromeos/network/cellular_esim_uninstall_handler.h"
 #include "chromeos/network/cellular_inhibitor.h"
@@ -49,8 +51,15 @@
 }
 
 ESimProfile::~ESimProfile() {
-  if (uninstall_callback_ || set_profile_nickname_callback_) {
-    NET_LOG(ERROR) << "Profile destroyed with unfulfilled callbacks";
+  if (install_callback_) {
+    NET_LOG(ERROR) << "Profile destroyed with unfulfilled install callback";
+  }
+  if (uninstall_callback_) {
+    NET_LOG(ERROR) << "Profile destroyed with unfulfilled uninstall callbacks";
+  }
+  if (set_profile_nickname_callback_) {
+    NET_LOG(ERROR)
+        << "Profile destroyed with unfulfilled set profile nickname callbacks";
   }
 }
 
@@ -70,15 +79,17 @@
   properties_->state = mojom::ProfileState::kInstalling;
   esim_manager_->NotifyESimProfileChanged(this);
 
-  HermesEuiccClient::Get()->InstallPendingProfile(
-      euicc_->path(), path_, confirmation_code,
-      base::BindOnce(&ESimProfile::OnPendingProfileInstallResult,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  install_callback_ = std::move(callback);
+  EnsureProfileExistsOnEuiccCallback perform_install_profile_callback =
+      base::BindOnce(&ESimProfile::PerformInstallProfile,
+                     weak_ptr_factory_.GetWeakPtr(), confirmation_code);
+  esim_manager_->cellular_inhibitor()->InhibitCellularScanning(base::BindOnce(
+      &ESimProfile::EnsureProfileExistsOnEuicc, weak_ptr_factory_.GetWeakPtr(),
+      std::move(perform_install_profile_callback)));
 }
 
 void ESimProfile::UninstallProfile(UninstallProfileCallback callback) {
-  if (properties_->state == mojom::ProfileState::kInstalling ||
-      properties_->state == mojom::ProfileState::kPending) {
+  if (!IsProfileInstalled()) {
     NET_LOG(ERROR) << "Profile uninstall failed: Profile is not installed.";
     std::move(callback).Run(mojom::ESimOperationResult::kFailure);
     return;
@@ -137,12 +148,12 @@
   }
 
   set_profile_nickname_callback_ = std::move(callback);
-  // Pass has_already_requested_installed_profiles=false so that a
-  // RequestInstalledProfiles call will be made in case this ESimProfile object
-  // was created from cached profile data.
+  EnsureProfileExistsOnEuiccCallback perform_set_profile_nickname_callback =
+      base::BindOnce(&ESimProfile::PerformSetProfileNickname,
+                     weak_ptr_factory_.GetWeakPtr(), nickname);
   esim_manager_->cellular_inhibitor()->InhibitCellularScanning(base::BindOnce(
-      &ESimProfile::PerformSetProfileNickname, weak_ptr_factory_.GetWeakPtr(),
-      nickname, /*has_already_requested_installed_profiles=*/false));
+      &ESimProfile::EnsureProfileExistsOnEuicc, weak_ptr_factory_.GetWeakPtr(),
+      std::move(perform_set_profile_nickname_callback)));
 }
 
 void ESimProfile::UpdateProperties(
@@ -170,10 +181,14 @@
     // success since the profile will be removed.
     std::move(uninstall_callback_).Run(mojom::ESimOperationResult::kSuccess);
   }
+
+  // Installation or setting nickname could trigger a request for profiles. If
+  // this profile gets removed at that point, return the pending call with
+  // failure.
+  if (install_callback_) {
+    std::move(install_callback_).Run(mojom::ProfileInstallResult::kFailure);
+  }
   if (set_profile_nickname_callback_) {
-    // Setting nickname could trigger a request for installed profiles. If this
-    // profile gets removed at that point, return pending call with failure
-    // result.
     std::move(set_profile_nickname_callback_)
         .Run(mojom::ESimOperationResult::kFailure);
   }
@@ -185,37 +200,87 @@
   return esim_profile_remote;
 }
 
-void ESimProfile::PerformSetProfileNickname(
-    const base::string16& nickname,
-    bool has_already_requested_installed_profiles,
+void ESimProfile::EnsureProfileExistsOnEuicc(
+    EnsureProfileExistsOnEuiccCallback callback,
     std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock) {
   if (!inhibit_lock) {
     NET_LOG(ERROR) << "Error inhibiting cellular device";
-    std::move(set_profile_nickname_callback_)
-        .Run(mojom::ESimOperationResult::kFailure);
+    std::move(callback).Run(/*request_profile_success=*/false,
+                            /*inhibit_lock=*/nullptr);
     return;
   }
 
-  // If a valid profile does not exist on Hermes, then we are using cached
-  // profile data. Make a request for installed profiles so that the profile
-  // will be loaded in hermes.
   if (!ProfileExistsOnEuicc()) {
-    if (has_already_requested_installed_profiles) {
-      // If the profile doesn't exists and installed profiles have already been
-      // requested then give up and return with error. This profile will get
-      // destroyed when ESimProfileHandler updates.
-      NET_LOG(ERROR) << "Unable to find profile in dbus. path="
-                     << path_.value();
-      std::move(set_profile_nickname_callback_)
-          .Run(mojom::ESimOperationResult::kFailure);
-      return;
+    if (IsProfileInstalled()) {
+      HermesEuiccClient::Get()->RequestInstalledProfiles(
+          euicc_->path(),
+          base::BindOnce(&ESimProfile::OnRequestProfiles,
+                         weak_ptr_factory_.GetWeakPtr(), std::move(callback),
+                         std::move(inhibit_lock)));
+    } else {
+      HermesEuiccClient::Get()->RequestPendingProfiles(
+          euicc_->path(), /*root_smds=*/std::string(),
+          base::BindOnce(&ESimProfile::OnRequestProfiles,
+                         weak_ptr_factory_.GetWeakPtr(), std::move(callback),
+                         std::move(inhibit_lock)));
     }
+    return;
+  }
 
-    HermesEuiccClient::Get()->RequestInstalledProfiles(
-        euicc_->path(),
-        base::BindOnce(&ESimProfile::OnRequestInstalledProfilesForSetNickname,
-                       weak_ptr_factory_.GetWeakPtr(), nickname,
-                       std::move(inhibit_lock)));
+  std::move(callback).Run(/*request_profile_success=*/true,
+                          std::move(inhibit_lock));
+}
+
+void ESimProfile::OnRequestProfiles(
+    EnsureProfileExistsOnEuiccCallback callback,
+    std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock,
+    HermesResponseStatus status) {
+  if (status != HermesResponseStatus::kSuccess) {
+    NET_LOG(ERROR) << "Error requesting profiles to ensure profile exists on "
+                      "Euicc. status="
+                   << static_cast<int>(status);
+    std::move(callback).Run(/*request_profile_success=*/false,
+                            std::move(inhibit_lock));
+    return;
+  }
+
+  // If profile does not exist on Euicc even after request for profiles then
+  // return failure. The profile was removed and this object will get destroyed
+  // when CellularESimProfileHandler updates.
+  if (!ProfileExistsOnEuicc()) {
+    NET_LOG(ERROR) << "Unable to ensure profile exists on Euicc. path="
+                   << path_.value();
+    std::move(callback).Run(/*request_profile_success=*/false,
+                            std::move(inhibit_lock));
+    return;
+  }
+
+  std::move(callback).Run(/*request_profile_success=*/true,
+                          std::move(inhibit_lock));
+}
+
+void ESimProfile::PerformInstallProfile(
+    const std::string& confirmation_code,
+    bool request_profile_success,
+    std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock) {
+  if (!request_profile_success) {
+    std::move(install_callback_).Run(mojom::ProfileInstallResult::kFailure);
+    return;
+  }
+
+  HermesEuiccClient::Get()->InstallPendingProfile(
+      euicc_->path(), path_, confirmation_code,
+      base::BindOnce(&ESimProfile::OnPendingProfileInstallResult,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(inhibit_lock)));
+}
+
+void ESimProfile::PerformSetProfileNickname(
+    const base::string16& nickname,
+    bool request_profile_success,
+    std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock) {
+  if (!request_profile_success) {
+    std::move(set_profile_nickname_callback_)
+        .Run(mojom::ESimOperationResult::kFailure);
     return;
   }
 
@@ -227,36 +292,20 @@
                      weak_ptr_factory_.GetWeakPtr(), std::move(inhibit_lock)));
 }
 
-void ESimProfile::OnRequestInstalledProfilesForSetNickname(
-    const base::string16& nickname,
-    std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock,
-    HermesResponseStatus status) {
-  if (status != HermesResponseStatus::kSuccess) {
-    NET_LOG(ERROR)
-        << "Error requesting installed profiles for set nickname. status="
-        << static_cast<int>(status);
-    std::move(set_profile_nickname_callback_)
-        .Run(mojom::ESimOperationResult::kFailure);
-    return;
-  }
-  PerformSetProfileNickname(nickname,
-                            /*has_already_requested_intalled_profiles=*/true,
-                            std::move(inhibit_lock));
-}
-
 void ESimProfile::OnPendingProfileInstallResult(
-    ProfileInstallResultCallback callback,
+    std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock,
     HermesResponseStatus status) {
   if (status != HermesResponseStatus::kSuccess) {
     NET_LOG(ERROR) << "Error Installing pending profile status="
                    << static_cast<int>(status);
     properties_->state = mojom::ProfileState::kPending;
     esim_manager_->NotifyESimProfileChanged(this);
-    std::move(callback).Run(InstallResultFromStatus(status));
+    std::move(install_callback_).Run(InstallResultFromStatus(status));
     return;
   }
 
-  std::move(callback).Run(mojom::ProfileInstallResult::kSuccess);
+  std::move(install_callback_).Run(mojom::ProfileInstallResult::kSuccess);
+  // inhibit_lock goes out of scope and will uninhibit automatically.
 }
 
 void ESimProfile::OnProfileUninstallResult(bool success) {
@@ -289,11 +338,18 @@
 bool ESimProfile::ProfileExistsOnEuicc() {
   HermesEuiccClient::Properties* euicc_properties =
       HermesEuiccClient::Get()->GetProperties(euicc_->path());
-  const std::vector<dbus::ObjectPath>& installed_profile_paths =
-      euicc_properties->installed_carrier_profiles().value();
-  auto iter = std::find(installed_profile_paths.begin(),
-                        installed_profile_paths.end(), path_);
-  return iter != installed_profile_paths.end();
+  const std::vector<dbus::ObjectPath>& profile_paths =
+      IsProfileInstalled()
+          ? euicc_properties->installed_carrier_profiles().value()
+          : euicc_properties->pending_carrier_profiles().value();
+
+  auto iter = std::find(profile_paths.begin(), profile_paths.end(), path_);
+  return iter != profile_paths.end();
+}
+
+bool ESimProfile::IsProfileInstalled() {
+  return properties_->state != mojom::ProfileState::kPending &&
+         properties_->state != mojom::ProfileState::kInstalling;
 }
 
 }  // namespace cellular_setup
diff --git a/chromeos/services/cellular_setup/esim_profile.h b/chromeos/services/cellular_setup/esim_profile.h
index 3ad6e18..e82839e 100644
--- a/chromeos/services/cellular_setup/esim_profile.h
+++ b/chromeos/services/cellular_setup/esim_profile.h
@@ -68,16 +68,31 @@
   using ESimOperationResultCallback =
       base::OnceCallback<void(mojom::ESimOperationResult)>;
 
-  void PerformSetProfileNickname(
-      const base::string16& nickname,
-      bool has_already_requested_installed_profiles,
+  // Type of callback to be passed to EnsureProfileExists method. The callback
+  // receives a boolean indicating request profile succeess status and inhibit
+  // lock that was passed to the method.
+  using EnsureProfileExistsOnEuiccCallback =
+      base::OnceCallback<void(bool,
+                              std::unique_ptr<CellularInhibitor::InhibitLock>)>;
+
+  void EnsureProfileExistsOnEuicc(
+      EnsureProfileExistsOnEuiccCallback callback,
       std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock);
-  void OnRequestInstalledProfilesForSetNickname(
-      const base::string16& nickname,
+  void OnRequestProfiles(
+      EnsureProfileExistsOnEuiccCallback callback,
       std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock,
       HermesResponseStatus status);
-  void OnPendingProfileInstallResult(ProfileInstallResultCallback callback,
-                                     HermesResponseStatus status);
+  void PerformInstallProfile(
+      const std::string& confirmation_code,
+      bool request_profile_success,
+      std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock);
+  void PerformSetProfileNickname(
+      const base::string16& nickname,
+      bool request_profile_success,
+      std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock);
+  void OnPendingProfileInstallResult(
+      std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock,
+      HermesResponseStatus status);
   void OnProfileUninstallResult(bool success);
   void OnESimOperationResult(ESimOperationResultCallback callback,
                              HermesResponseStatus status);
@@ -85,6 +100,7 @@
       std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock,
       bool success);
   bool ProfileExistsOnEuicc();
+  bool IsProfileInstalled();
 
   // Reference to Euicc that owns this profile.
   Euicc* euicc_;
@@ -92,6 +108,7 @@
   ESimManager* esim_manager_;
   UninstallProfileCallback uninstall_callback_;
   SetProfileNicknameCallback set_profile_nickname_callback_;
+  InstallProfileCallback install_callback_;
   mojo::ReceiverSet<mojom::ESimProfile> receiver_set_;
   mojom::ESimProfilePropertiesPtr properties_;
   dbus::ObjectPath path_;
diff --git a/chromeos/services/libassistant/BUILD.gn b/chromeos/services/libassistant/BUILD.gn
index 19df25e..e59032f 100644
--- a/chromeos/services/libassistant/BUILD.gn
+++ b/chromeos/services/libassistant/BUILD.gn
@@ -50,6 +50,8 @@
     "display_controller.h",
     "fake_auth_provider.cc",
     "fake_auth_provider.h",
+    "file_provider_impl.cc",
+    "file_provider_impl.h",
     "media_controller.cc",
     "media_controller.h",
     "platform_api.cc",
@@ -72,6 +74,7 @@
     "//chromeos/assistant/internal:support",
     "//chromeos/dbus",
     "//chromeos/dbus/power",
+    "//chromeos/resources:resources_grit",
     "//chromeos/services/assistant/public/cpp",
     "//chromeos/services/assistant/public/cpp/migration",
     "//chromeos/services/libassistant/public/mojom",
@@ -82,6 +85,7 @@
     "//libassistant/shared/public:export",
     "//services/network/public/cpp",
     "//services/network/public/mojom",
+    "//ui/base",
   ]
 
   defines = [ "IS_LIBASSISTANT_SERVICE_IMPL" ]
@@ -151,6 +155,7 @@
   deps = [
     ":internal",
     ":libassistant",
+    "//base/test:test_support",
     "//chromeos/services/assistant/public/cpp/migration",
     "//chromeos/services/assistant/public/cpp/migration:test_support",
     "//chromeos/services/libassistant/public/mojom",
diff --git a/chromeos/services/libassistant/DEPS b/chromeos/services/libassistant/DEPS
index 9db3e98e..d4ca3716 100644
--- a/chromeos/services/libassistant/DEPS
+++ b/chromeos/services/libassistant/DEPS
@@ -10,4 +10,5 @@
   "+services/media_session/public/mojom",
   "+services/network/public",
   "+services/network/test",
+  "+ui/base",
 ]
diff --git a/chromeos/services/libassistant/audio/audio_device_owner.cc b/chromeos/services/libassistant/audio/audio_device_owner.cc
index f8e3a606..d07f6ca 100644
--- a/chromeos/services/libassistant/audio/audio_device_owner.cc
+++ b/chromeos/services/libassistant/audio/audio_device_owner.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <utility>
 
+#include "base/sequence_checker.h"
 #include "chromeos/services/libassistant/public/mojom/audio_output_delegate.mojom.h"
 #include "media/audio/audio_device_description.h"
 #include "media/base/limits.h"
@@ -81,28 +82,31 @@
 
 }  // namespace
 
-AudioDeviceOwner::AudioDeviceOwner(
-    scoped_refptr<base::SequencedTaskRunner> task_runner,
-    scoped_refptr<base::SequencedTaskRunner> background_task_runner,
-    const std::string& device_id)
-    : main_task_runner_(task_runner),
-      background_task_runner_(background_task_runner),
-      device_id_(device_id) {}
+AudioDeviceOwner::AudioDeviceOwner(const std::string& device_id)
+    : device_id_(device_id) {}
 
 AudioDeviceOwner::~AudioDeviceOwner() {
-  DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 }
 
-void AudioDeviceOwner::StartOnMainThread(
+void AudioDeviceOwner::Start(
     mojom::AudioOutputDelegate* audio_output_delegate,
     assistant_client::AudioOutput::Delegate* delegate,
     mojo::PendingRemote<audio::mojom::StreamFactory> stream_factory,
     const assistant_client::OutputStreamFormat& format) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!output_device_);
-  DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
+
+  base::AutoLock lock(lock_);
 
   delegate_ = delegate;
   format_ = format;
+  audio_param_ = GetAudioParametersFromBufferFormat(format_);
+  audio_data_.resize(GetBufferSizeInBytesFromBufferFormat(format_));
+  // |audio_fifo_| contains 8x the number of frames to render.
+  audio_fifo_ = std::make_unique<media::AudioBlockFifo>(
+      format.pcm_num_channels, audio_param_.frames_per_buffer(), 8);
+
   // TODO(wutao): There is a bug LibAssistant sends wrong format. Do not run
   // in this case.
   if (format_.pcm_num_channels >
@@ -111,33 +115,15 @@
     return;
   }
 
-  audio_param_ = GetAudioParametersFromBufferFormat(format_);
-
-  // |audio_fifo_| contains 8x the number of frames to render.
-  audio_fifo_ = std::make_unique<media::AudioBlockFifo>(
-      format.pcm_num_channels, audio_param_.frames_per_buffer(), 8);
-  audio_data_.resize(GetBufferSizeInBytesFromBufferFormat(format_));
-
-  {
-    base::AutoLock lock(lock_);
-    ScheduleFillLocked(base::TimeTicks::Now());
-  }
+  ScheduleFillLocked(base::TimeTicks::Now());
 
   // |stream_factory| is null in unittest.
-  if (stream_factory) {
-    // |AudioDeviceOwner| is destroyed on background thread. Thus, it's safe to
-    // use base::Unretained.
-    background_task_runner_->PostTask(
-        FROM_HERE,
-        base::BindOnce(&AudioDeviceOwner::StartDeviceOnBackgroundThread,
-                       base::Unretained(this), std::move(stream_factory)));
-    audio_output_delegate->AddMediaSessionObserver(
-        session_receiver_.BindNewPipeAndPassRemote());
-  }
+  if (stream_factory)
+    StartDevice(std::move(stream_factory), audio_output_delegate);
 }
 
-void AudioDeviceOwner::StopOnBackgroundThread() {
-  DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
+void AudioDeviceOwner::Stop() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   output_device_.reset();
   base::AutoLock lock(lock_);
   if (delegate_) {
@@ -148,10 +134,7 @@
 
 void AudioDeviceOwner::MediaSessionInfoChanged(
     media_session::mojom::MediaSessionInfoPtr info) {
-  // |output_device_| is only accessed on background thread.
-  ENSURE_BACKGROUND_THREAD(&AudioDeviceOwner::MediaSessionInfoChanged,
-                           std::move(info));
-
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // We only handle media ducking case here as intended. Other media
   // operactions, such as pausing and resuming, are handled by Libassistant
   // |MediaManager| API in |AssistantManagerServiceImpl|.
@@ -163,14 +146,21 @@
     output_device_->SetVolume(is_ducking ? kDuckingVolume : 1.0);
 }
 
-void AudioDeviceOwner::StartDeviceOnBackgroundThread(
-    mojo::PendingRemote<audio::mojom::StreamFactory> stream_factory) {
-  DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
+void AudioDeviceOwner::StartDevice(
+    mojo::PendingRemote<audio::mojom::StreamFactory> stream_factory,
+    mojom::AudioOutputDelegate* audio_output_delegate) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  lock_.AssertAcquired();
+
   output_device_ = std::make_unique<audio::OutputDevice>(
       std::move(stream_factory), audio_param_, this, device_id_);
   output_device_->Play();
+
+  audio_output_delegate->AddMediaSessionObserver(
+      session_receiver_.BindNewPipeAndPassRemote());
 }
 
+// Runs on audio renderer thread (started internally in |output_device_|).
 int AudioDeviceOwner::Render(base::TimeDelta delay,
                              base::TimeTicks delay_timestamp,
                              int prior_frames_skipped,
@@ -209,6 +199,7 @@
   return dest->frames();
 }
 
+// Runs on audio renderer thread (started internally in |output_device_|).
 void AudioDeviceOwner::OnRenderError() {
   DVLOG(1) << "OnRenderError()";
   base::AutoLock lock(lock_);
@@ -218,10 +209,12 @@
 
 void AudioDeviceOwner::SetDelegate(
     assistant_client::AudioOutput::Delegate* delegate) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   base::AutoLock lock(lock_);
   delegate_ = delegate;
 }
 
+// Runs on audio renderer thread (started internally in |output_device_|).
 void AudioDeviceOwner::ScheduleFillLocked(const base::TimeTicks& time) {
   lock_.AssertAcquired();
   if (is_filling_)
@@ -242,6 +235,7 @@
       [this](int num) { this->BufferFillDone(num); });
 }
 
+// Runs on audio renderer thread (started internally in |output_device_|).
 void AudioDeviceOwner::BufferFillDone(int num_bytes) {
   base::AutoLock lock(lock_);
   is_filling_ = false;
diff --git a/chromeos/services/libassistant/audio/audio_device_owner.h b/chromeos/services/libassistant/audio/audio_device_owner.h
index bda2fa1..05d7e5f0 100644
--- a/chromeos/services/libassistant/audio/audio_device_owner.h
+++ b/chromeos/services/libassistant/audio/audio_device_owner.h
@@ -10,6 +10,8 @@
 #include <vector>
 
 #include "base/macros.h"
+#include "base/sequence_checker.h"
+#include "base/synchronization/lock.h"
 #include "chromeos/services/libassistant/public/mojom/audio_output_delegate.mojom-forward.h"
 #include "libassistant/shared/public/platform_audio_output.h"
 #include "media/base/audio_block_fifo.h"
@@ -26,19 +28,16 @@
 class AudioDeviceOwner : public media::AudioRendererSink::RenderCallback,
                          media_session::mojom::MediaSessionObserver {
  public:
-  AudioDeviceOwner(
-      scoped_refptr<base::SequencedTaskRunner> task_runner,
-      scoped_refptr<base::SequencedTaskRunner> background_task_runner,
-      const std::string& device_id);
+  explicit AudioDeviceOwner(const std::string& device_id);
   ~AudioDeviceOwner() override;
 
-  void StartOnMainThread(
+  void Start(
       chromeos::libassistant::mojom::AudioOutputDelegate* audio_output_delegate,
       assistant_client::AudioOutput::Delegate* delegate,
       mojo::PendingRemote<audio::mojom::StreamFactory> stream_factory,
       const assistant_client::OutputStreamFormat& format);
 
-  void StopOnBackgroundThread();
+  void Stop();
 
   // media_session::mojom::MediaSessionObserver overrides:
   void MediaSessionInfoChanged(
@@ -68,8 +67,9 @@
   void SetDelegate(assistant_client::AudioOutput::Delegate* delegate);
 
  private:
-  void StartDeviceOnBackgroundThread(
-      mojo::PendingRemote<audio::mojom::StreamFactory> stream_factory);
+  void StartDevice(
+      mojo::PendingRemote<audio::mojom::StreamFactory> stream_factory,
+      mojom::AudioOutputDelegate* audio_output_delegate);
 
   // Requests assistant to fill buffer with more data.
   void ScheduleFillLocked(const base::TimeTicks& time);
@@ -77,31 +77,32 @@
   // Callback for assistant to notify that it completes the filling.
   void BufferFillDone(int num_bytes);
 
-  scoped_refptr<base::SequencedTaskRunner> main_task_runner_;
-  scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
-
   base::Lock lock_;
-  std::unique_ptr<media::AudioBlockFifo> audio_fifo_;  // guarded by lock_.
+  std::unique_ptr<media::AudioBlockFifo> audio_fifo_ GUARDED_BY(lock_);
   // Whether assistant is filling the buffer -- delegate_->FillBuffer is called
   // and BufferFillDone() is not called yet.
-  // guarded by lock_.
-  bool is_filling_ = false;
+  bool is_filling_ GUARDED_BY(lock_) = false;
 
-  // Guarded by |lock_|.
-  assistant_client::AudioOutput::Delegate* delegate_;
+  media::AudioParameters audio_param_ GUARDED_BY(lock_);
+  std::vector<uint8_t> audio_data_ GUARDED_BY(lock_);
+  // Stores audio frames generated by assistant.
+  assistant_client::OutputStreamFormat format_ GUARDED_BY(lock_);
+
+  assistant_client::AudioOutput::Delegate* delegate_ GUARDED_BY(lock_);
 
   // Audio output device id used for output.
-  std::string device_id_;
-  std::unique_ptr<audio::OutputDevice> output_device_;
-  // Stores audio frames generated by assistant.
-  std::vector<uint8_t> audio_data_;
-  assistant_client::OutputStreamFormat format_;
-  media::AudioParameters audio_param_;
+  std::string device_id_ GUARDED_BY_CONTEXT(sequence_checker_);
+  std::unique_ptr<audio::OutputDevice> output_device_
+      GUARDED_BY_CONTEXT(sequence_checker_);
 
   mojo::Receiver<media_session::mojom::MediaSessionObserver> session_receiver_{
       this};
 
-  base::WeakPtrFactory<AudioDeviceOwner> weak_factory_{this};
+  // The callbacks from |RenderCallback| are called on a different sequence,
+  // so this sequence checker prevents the other methods from being called on
+  // the render sequence.
+  SEQUENCE_CHECKER(sequence_checker_);
+
   DISALLOW_COPY_AND_ASSIGN(AudioDeviceOwner);
 };
 
diff --git a/chromeos/services/libassistant/audio/audio_media_data_source.cc b/chromeos/services/libassistant/audio/audio_media_data_source.cc
index 0da9a19..3cdc0e5 100644
--- a/chromeos/services/libassistant/audio/audio_media_data_source.cc
+++ b/chromeos/services/libassistant/audio/audio_media_data_source.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/callback_helpers.h"
+#include "base/sequence_checker.h"
 #include "base/time/time.h"
 
 namespace chromeos {
@@ -22,31 +23,32 @@
 }  // namespace
 
 AudioMediaDataSource::AudioMediaDataSource(
-    mojo::PendingReceiver<AssistantMediaDataSource> receiver,
-    scoped_refptr<base::SequencedTaskRunner> task_runner)
+    mojo::PendingReceiver<AssistantMediaDataSource> receiver)
     : receiver_(this, std::move(receiver)),
-      task_runner_(task_runner),
+      task_runner_(base::SequencedTaskRunnerHandle::Get()),
       weak_factory_(this) {}
 
 AudioMediaDataSource::~AudioMediaDataSource() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
   if (read_callback_) {
     // During shutdown, it is possible we received a call to |Read()| but have
     // not received the data from Libassistant yet. In that case we must still
     // call the |read_callback_| to satisfy the mojom API contract.
-    std::move(read_callback_).Run({});
+    OnFillBuffer(0);
   }
 }
 
 void AudioMediaDataSource::Read(uint32_t size, ReadCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
   // Note: mojom calls are sequenced, so we should not receive a second call to
   // Read() before we consumed the previous |read_callback_|.
   DCHECK(!read_callback_);
   read_callback_ = std::move(callback);
 
   if (!delegate_) {
-    task_runner_->PostTask(FROM_HERE,
-                           base::BindOnce(&AudioMediaDataSource::OnFillBuffer,
-                                          weak_factory_.GetWeakPtr(), 0));
+    OnFillBuffer(0);
     return;
   }
 
@@ -67,6 +69,7 @@
 }
 
 void AudioMediaDataSource::OnFillBuffer(int bytes_filled) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(read_callback_);
   source_buffer_.resize(bytes_filled);
   std::move(read_callback_).Run(source_buffer_);
diff --git a/chromeos/services/libassistant/audio/audio_media_data_source.h b/chromeos/services/libassistant/audio/audio_media_data_source.h
index d9b5f2b..587f036 100644
--- a/chromeos/services/libassistant/audio/audio_media_data_source.h
+++ b/chromeos/services/libassistant/audio/audio_media_data_source.h
@@ -23,28 +23,29 @@
 class AudioMediaDataSource
     : public chromeos::assistant::mojom::AssistantMediaDataSource {
  public:
-  AudioMediaDataSource(
+  explicit AudioMediaDataSource(
       mojo::PendingReceiver<
-          chromeos::assistant::mojom::AssistantMediaDataSource> receiver,
-      scoped_refptr<base::SequencedTaskRunner> task_runner);
+          chromeos::assistant::mojom::AssistantMediaDataSource> receiver);
   ~AudioMediaDataSource() override;
 
   // chromeos::assistant::mojom::MediaDataSource implementation.
-  // Called by utility process. Must be called after |set_delegate()|.
+  // Must be called after |set_delegate()|.
   // The caller must wait for callback to finish before issuing the next read.
   void Read(uint32_t size, ReadCallback callback) override;
 
-  // Called by AudioStreamHandler on main thread.
   void set_delegate(assistant_client::AudioOutput::Delegate* delegate) {
     delegate_ = delegate;
   }
 
  private:
-  // Called on main thread.
   void OnFillBuffer(int bytes_filled);
 
   mojo::Receiver<AssistantMediaDataSource> receiver_;
 
+  // The callback from |delegate_| runs on a different sequence, so this
+  // sequence checker prevents the other methods from being called on the wrong
+  // sequence.
+  SEQUENCE_CHECKER(sequence_checker_);
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
 
   assistant_client::AudioOutput::Delegate* delegate_ = nullptr;
diff --git a/chromeos/services/libassistant/audio/audio_output_provider_impl.cc b/chromeos/services/libassistant/audio/audio_output_provider_impl.cc
index b75e95b..b379ed0 100644
--- a/chromeos/services/libassistant/audio/audio_output_provider_impl.cc
+++ b/chromeos/services/libassistant/audio/audio_output_provider_impl.cc
@@ -9,6 +9,7 @@
 
 #include "base/bind.h"
 #include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
 #include "chromeos/services/assistant/public/mojom/assistant_audio_decoder.mojom.h"
 #include "chromeos/services/libassistant/audio/audio_stream_handler.h"
 #include "chromeos/services/libassistant/public/mojom/platform_delegate.mojom.h"
@@ -27,42 +28,36 @@
              assistant_client::OutputStreamEncoding::STREAM_OPUS_IN_OGG;
 }
 
+// Instances of this class will be owned by Libassistant, so any public method
+// (including the constructor and destructor) can and will be called from other
+// threads.
 class AudioOutputImpl : public assistant_client::AudioOutput {
  public:
   AudioOutputImpl(
       mojo::PendingRemote<audio::mojom::StreamFactory> stream_factory,
-      scoped_refptr<base::SequencedTaskRunner> task_runner,
-      scoped_refptr<base::SequencedTaskRunner> background_task_runner,
+      scoped_refptr<base::SequencedTaskRunner> main_task_runner,
       chromeos::assistant::mojom::AssistantAudioDecoderFactory*
           audio_decoder_factory,
       mojom::AudioOutputDelegate* audio_output_delegate,
       assistant_client::OutputStreamType type,
       assistant_client::OutputStreamFormat format,
       const std::string& device_id)
-      : stream_factory_(std::move(stream_factory)),
-        main_task_runner_(task_runner),
-        background_thread_task_runner_(background_task_runner),
+      : main_task_runner_(main_task_runner),
+        stream_factory_(std::move(stream_factory)),
         audio_decoder_factory_(audio_decoder_factory),
         audio_output_delegate_(audio_output_delegate),
         stream_type_(type),
-        format_(format),
-        audio_stream_handler_(
-            std::make_unique<AudioStreamHandler>(task_runner)),
-        device_owner_(std::make_unique<AudioDeviceOwner>(task_runner,
-                                                         background_task_runner,
-                                                         device_id)) {}
+        format_(format) {
+    // The constructor runs on the Libassistant thread, so we need to detach the
+    // main sequence checker.
+    DETACH_FROM_SEQUENCE(main_sequence_checker_);
+    main_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&AudioOutputImpl::InitializeOnMainThread,
+                                  weak_ptr_factory_.GetWeakPtr(), device_id));
+  }
 
   ~AudioOutputImpl() override {
-    // This ensures that it will be executed after StartOnMainThread.
-    main_task_runner_->PostTask(
-        FROM_HERE,
-        base::BindOnce(
-            [](std::unique_ptr<AudioDeviceOwner> device_owner,
-               scoped_refptr<base::SequencedTaskRunner> background_runner) {
-              // Ensures |device_owner| is destructed on the correct thread.
-              background_runner->DeleteSoon(FROM_HERE, device_owner.release());
-            },
-            std::move(device_owner_), background_thread_task_runner_));
+    main_task_runner_->DeleteSoon(FROM_HERE, device_owner_.release());
     main_task_runner_->DeleteSoon(FROM_HERE, audio_stream_handler_.release());
   }
 
@@ -82,7 +77,16 @@
   }
 
  private:
+  void InitializeOnMainThread(const std::string& device_id) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(main_sequence_checker_);
+
+    audio_stream_handler_ = std::make_unique<AudioStreamHandler>();
+    device_owner_ = std::make_unique<AudioDeviceOwner>(device_id);
+  }
+
   void StartOnMainThread(assistant_client::AudioOutput::Delegate* delegate) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(main_sequence_checker_);
+
     // TODO(llin): Remove getting audio focus here after libassistant handles
     // acquiring audio focus for the internal media player.
     if (stream_type_ == assistant_client::OutputStreamType::STREAM_MEDIA) {
@@ -93,17 +97,19 @@
     if (IsEncodedFormat(format_)) {
       audio_stream_handler_->StartAudioDecoder(
           audio_decoder_factory_, delegate,
-          base::BindOnce(&AudioDeviceOwner::StartOnMainThread,
+          base::BindOnce(&AudioDeviceOwner::Start,
                          base::Unretained(device_owner_.get()),
                          audio_output_delegate_, audio_stream_handler_.get(),
                          std::move(stream_factory_)));
     } else {
-      device_owner_->StartOnMainThread(audio_output_delegate_, delegate,
-                                       std::move(stream_factory_), format_);
+      device_owner_->Start(audio_output_delegate_, delegate,
+                           std::move(stream_factory_), format_);
     }
   }
 
   void StopOnMainThread() {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(main_sequence_checker_);
+
     // TODO(llin): Remove abandoning audio focus here after libassistant handles
     // abandoning audio focus for the internal media player.
     if (stream_type_ == assistant_client::OutputStreamType::STREAM_MEDIA) {
@@ -114,24 +120,33 @@
       device_owner_->SetDelegate(nullptr);
       audio_stream_handler_->OnStopped();
     } else {
-      background_thread_task_runner_->PostTask(
-          FROM_HERE, base::BindOnce(&AudioDeviceOwner::StopOnBackgroundThread,
-                                    base::Unretained(device_owner_.get())));
+      device_owner_->Stop();
     }
   }
 
-  mojo::PendingRemote<audio::mojom::StreamFactory> stream_factory_;
   scoped_refptr<base::SequencedTaskRunner> main_task_runner_;
-  scoped_refptr<base::SequencedTaskRunner> background_thread_task_runner_;
+
+  mojo::PendingRemote<audio::mojom::StreamFactory> stream_factory_
+      GUARDED_BY_CONTEXT(main_sequence_checker_);
   chromeos::assistant::mojom::AssistantAudioDecoderFactory*
-      audio_decoder_factory_;
-  mojom::AudioOutputDelegate* const audio_output_delegate_;
+      audio_decoder_factory_ GUARDED_BY_CONTEXT(main_sequence_checker_);
+  mojom::AudioOutputDelegate* const audio_output_delegate_
+      GUARDED_BY_CONTEXT(main_sequence_checker_);
 
+  // Accessed from both Libassistant and main sequence, so should remain
+  // |const|.
   const assistant_client::OutputStreamType stream_type_;
-  assistant_client::OutputStreamFormat format_;
 
-  std::unique_ptr<AudioStreamHandler> audio_stream_handler_;
-  std::unique_ptr<AudioDeviceOwner> device_owner_;
+  assistant_client::OutputStreamFormat format_
+      GUARDED_BY_CONTEXT(main_sequence_checker_);
+
+  std::unique_ptr<AudioStreamHandler> audio_stream_handler_
+      GUARDED_BY_CONTEXT(main_sequence_checker_);
+  std::unique_ptr<AudioDeviceOwner> device_owner_
+      GUARDED_BY_CONTEXT(main_sequence_checker_);
+
+  // This class is used both from the Libassistant and main thread.
+  SEQUENCE_CHECKER(main_sequence_checker_);
 
   base::WeakPtrFactory<AudioOutputImpl> weak_ptr_factory_{this};
 
@@ -140,13 +155,10 @@
 
 }  // namespace
 
-AudioOutputProviderImpl::AudioOutputProviderImpl(
-    scoped_refptr<base::SequencedTaskRunner> background_task_runner,
-    const std::string& device_id)
+AudioOutputProviderImpl::AudioOutputProviderImpl(const std::string& device_id)
     : loop_back_input_(media::AudioDeviceDescription::kLoopbackInputDeviceId),
       volume_control_impl_(),
       main_task_runner_(base::SequencedTaskRunnerHandle::Get()),
-      background_task_runner_(background_task_runner),
       device_id_(device_id) {}
 
 void AudioOutputProviderImpl::Bind(
@@ -165,6 +177,7 @@
 
 AudioOutputProviderImpl::~AudioOutputProviderImpl() = default;
 
+// Called from the Libassistant thread.
 assistant_client::AudioOutput* AudioOutputProviderImpl::CreateAudioOutput(
     assistant_client::OutputStreamType type,
     const assistant_client::OutputStreamFormat& stream_format) {
@@ -176,12 +189,13 @@
                      stream_factory.InitWithNewPipeAndPassReceiver()));
   // Owned by one arbitrary thread inside libassistant. It will be destroyed
   // once assistant_client::AudioOutput::Delegate::OnStopped() is called.
-  return new AudioOutputImpl(
-      std::move(stream_factory), main_task_runner_, background_task_runner_,
-      audio_decoder_factory_.get(), audio_output_delegate_.get(), type,
-      stream_format, device_id_);
+  return new AudioOutputImpl(std::move(stream_factory), main_task_runner_,
+                             audio_decoder_factory_.get(),
+                             audio_output_delegate_.get(), type, stream_format,
+                             device_id_);
 }
 
+// Called from the Libassistant thread.
 std::vector<assistant_client::OutputStreamEncoding>
 AudioOutputProviderImpl::GetSupportedStreamEncodings() {
   return std::vector<assistant_client::OutputStreamEncoding>{
@@ -193,19 +207,23 @@
   };
 }
 
+// Called from the Libassistant thread.
 assistant_client::AudioInput* AudioOutputProviderImpl::GetReferenceInput() {
   return &loop_back_input_;
 }
 
+// Called from the Libassistant thread.
 bool AudioOutputProviderImpl::SupportsPlaybackTimestamp() const {
   // TODO(muyuanli): implement.
   return false;
 }
 
+// Called from the Libassistant thread.
 assistant_client::VolumeControl& AudioOutputProviderImpl::GetVolumeControl() {
   return volume_control_impl_;
 }
 
+// Called from the Libassistant thread.
 void AudioOutputProviderImpl::RegisterAudioEmittingStateCallback(
     AudioEmittingStateCallback callback) {
   // TODO(muyuanli): implement.
diff --git a/chromeos/services/libassistant/audio/audio_output_provider_impl.h b/chromeos/services/libassistant/audio/audio_output_provider_impl.h
index 649bc7b..6018a88 100644
--- a/chromeos/services/libassistant/audio/audio_output_provider_impl.h
+++ b/chromeos/services/libassistant/audio/audio_output_provider_impl.h
@@ -31,9 +31,7 @@
 
 class AudioOutputProviderImpl : public assistant_client::AudioOutputProvider {
  public:
-  AudioOutputProviderImpl(
-      scoped_refptr<base::SequencedTaskRunner> background_task_runner,
-      const std::string& device_id);
+  explicit AudioOutputProviderImpl(const std::string& device_id);
   ~AudioOutputProviderImpl() override;
 
   void Bind(
@@ -69,7 +67,6 @@
   AudioInputImpl loop_back_input_;
   VolumeControlImpl volume_control_impl_;
   scoped_refptr<base::SequencedTaskRunner> main_task_runner_;
-  scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
   mojo::Remote<chromeos::assistant::mojom::AssistantAudioDecoderFactory>
       audio_decoder_factory_;
   std::string device_id_;
diff --git a/chromeos/services/libassistant/audio/audio_output_provider_impl_unittest.cc b/chromeos/services/libassistant/audio/audio_output_provider_impl_unittest.cc
index 91db2e4..70677e5 100644
--- a/chromeos/services/libassistant/audio/audio_output_provider_impl_unittest.cc
+++ b/chromeos/services/libassistant/audio/audio_output_provider_impl_unittest.cc
@@ -123,12 +123,10 @@
   audio_output_delegate.set_num_of_bytes_to_fill(200);
   audio_output_delegate.Reset();
 
-  auto owner = std::make_unique<AudioDeviceOwner>(
-      base::SequencedTaskRunnerHandle::Get(),
-      base::SequencedTaskRunnerHandle::Get(), "test device");
+  auto owner = std::make_unique<AudioDeviceOwner>("test device");
   // Upon start, it will start to fill the buffer.
-  owner->StartOnMainThread(&audio_output_delegate_mojom, &audio_output_delegate,
-                           mojo::NullRemote(), format);
+  owner->Start(&audio_output_delegate_mojom, &audio_output_delegate,
+               mojo::NullRemote(), format);
   audio_output_delegate.Wait();
 
   audio_output_delegate.Reset();
diff --git a/chromeos/services/libassistant/audio/audio_stream_handler.cc b/chromeos/services/libassistant/audio/audio_stream_handler.cc
index 8b14058..3bd92d8c 100644
--- a/chromeos/services/libassistant/audio/audio_stream_handler.cc
+++ b/chromeos/services/libassistant/audio/audio_stream_handler.cc
@@ -12,24 +12,28 @@
 namespace chromeos {
 namespace libassistant {
 
-AudioStreamHandler::AudioStreamHandler(
-    scoped_refptr<base::SequencedTaskRunner> task_runner)
-    : task_runner_(task_runner), weak_factory_(this) {}
+AudioStreamHandler::AudioStreamHandler()
+    : main_task_runner_(base::SequencedTaskRunnerHandle::Get()),
+      weak_factory_(this) {}
 
-AudioStreamHandler::~AudioStreamHandler() = default;
+AudioStreamHandler::~AudioStreamHandler() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
 
 void AudioStreamHandler::StartAudioDecoder(
     chromeos::assistant::mojom::AssistantAudioDecoderFactory*
         audio_decoder_factory,
     assistant_client::AudioOutput::Delegate* delegate,
     InitCB on_inited) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
   mojo::PendingRemote<AssistantAudioDecoderClient> client;
   client_receiver_.Bind(client.InitWithNewPipeAndPassReceiver());
 
   mojo::PendingRemote<chromeos::assistant::mojom::AssistantMediaDataSource>
       data_source;
   media_data_source_ = std::make_unique<AudioMediaDataSource>(
-      data_source.InitWithNewPipeAndPassReceiver(), task_runner_);
+      data_source.InitWithNewPipeAndPassReceiver());
 
   audio_decoder_factory->CreateAssistantAudioDecoder(
       audio_decoder_.BindNewPipeAndPassReceiver(), std::move(client),
@@ -55,6 +59,7 @@
 }
 
 // TODO(wutao): Needs to pass |playback_timestamp| to LibAssistant.
+// Called from the Libassistant thread.
 void AudioStreamHandler::FillBuffer(
     void* buffer,
     int buffer_size,
@@ -62,6 +67,18 @@
     assistant_client::Callback1<int> on_filled) {
   DCHECK(!on_filled_);
 
+  main_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&AudioStreamHandler::FillBufferOnMainThread,
+                                weak_factory_.GetWeakPtr(), buffer, buffer_size,
+                                std::move(on_filled)));
+}
+
+void AudioStreamHandler::FillBufferOnMainThread(
+    void* buffer,
+    int buffer_size,
+    assistant_client::Callback1<int> on_filled) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
   on_filled_ = std::move(on_filled);
   buffer_to_copy_ = buffer;
   size_to_copy_ = buffer_size;
@@ -69,16 +86,19 @@
   FillDecodedBuffer(buffer, buffer_size);
 }
 
+// Called from the Libassistant thread.
 void AudioStreamHandler::OnEndOfStream() {
   if (delegate_)
     delegate_->OnEndOfStream();
 }
 
+// Called from the Libassistant thread.
 void AudioStreamHandler::OnError(assistant_client::AudioOutput::Error error) {
   if (delegate_)
     delegate_->OnError(error);
 }
 
+// Called from the Libassistant thread.
 void AudioStreamHandler::OnStopped() {
   stopped_ = true;
 
@@ -95,18 +115,8 @@
                                               uint32_t bytes_per_sample,
                                               uint32_t samples_per_second,
                                               uint32_t channels) {
-  task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&AudioStreamHandler::OnDecoderInitializedOnThread,
-                     weak_factory_.GetWeakPtr(), success, bytes_per_sample,
-                     samples_per_second, channels));
-}
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-void AudioStreamHandler::OnDecoderInitializedOnThread(
-    bool success,
-    uint32_t bytes_per_sample,
-    uint32_t samples_per_second,
-    uint32_t channels) {
   if (!success) {
     // In the case that both |OpenDecoder()| and |CloseDecoder()| were called,
     // there is no need to call |OnError()|, since we are going to call
@@ -132,11 +142,15 @@
 }
 
 void AudioStreamHandler::StopDelegate() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
   delegate_->OnStopped();
   delegate_ = nullptr;
 }
 
 void AudioStreamHandler::FillDecodedBuffer(void* buffer, int buffer_size) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
   if (on_filled_ && (decoded_data_.size() > 0 || no_more_data_)) {
     int size_copied = 0;
     // Fill buffer with data not more than requested.
@@ -155,26 +169,30 @@
         decoded_data_.pop_front();
     }
 
-    task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(&AudioStreamHandler::OnFillBufferOnThread,
+    main_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&AudioStreamHandler::OnFillBuffer,
                                   weak_factory_.GetWeakPtr(),
                                   std::move(on_filled_), size_copied));
   }
 
   if (decoded_data_.empty() && !no_more_data_) {
-    task_runner_->PostTask(FROM_HERE,
-                           base::BindOnce(&AudioStreamHandler::DecodeOnThread,
-                                          weak_factory_.GetWeakPtr()));
+    main_task_runner_->PostTask(FROM_HERE,
+                                base::BindOnce(&AudioStreamHandler::Decode,
+                                               weak_factory_.GetWeakPtr()));
   }
 }
 
-void AudioStreamHandler::OnFillBufferOnThread(
+void AudioStreamHandler::OnFillBuffer(
     assistant_client::Callback1<int> on_filled,
     int num_bytes) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
   on_filled(num_bytes);
 }
 
-void AudioStreamHandler::DecodeOnThread() {
+void AudioStreamHandler::Decode() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
   if (is_decoding_)
     return;
 
diff --git a/chromeos/services/libassistant/audio/audio_stream_handler.h b/chromeos/services/libassistant/audio/audio_stream_handler.h
index df35db9..709d604 100644
--- a/chromeos/services/libassistant/audio/audio_stream_handler.h
+++ b/chromeos/services/libassistant/audio/audio_stream_handler.h
@@ -25,11 +25,9 @@
   using InitCB =
       base::OnceCallback<void(const assistant_client::OutputStreamFormat&)>;
 
-  explicit AudioStreamHandler(
-      scoped_refptr<base::SequencedTaskRunner> task_runner);
+  AudioStreamHandler();
   ~AudioStreamHandler() override;
 
-  // Called on main thread.
   void StartAudioDecoder(
       chromeos::assistant::mojom::AssistantAudioDecoderFactory*
           audio_decoder_factory,
@@ -37,11 +35,9 @@
       InitCB start_device_owner_on_main_thread);
 
   // chromeos::assistant::mojom::AssistantAudioDecoderClient overrides:
-  // Called by |audio_decoder_| on utility thread.
   void OnNewBuffers(const std::vector<std::vector<uint8_t>>& buffers) override;
 
   // assistant_client::AudioOutput::Delegate overrides:
-  // Called by AudioDeviceOwner on main thread.
   void FillBuffer(void* buffer,
                   int buffer_size,
                   int64_t playback_timestamp,
@@ -51,29 +47,25 @@
   void OnStopped() override;
 
  private:
-  // Calls AudioDeviceOwner to start on main thread.
   void OnDecoderInitialized(bool success,
                             uint32_t bytes_per_sample,
                             uint32_t samples_per_second,
                             uint32_t channels);
-  void OnDecoderInitializedOnThread(bool success,
-                                    uint32_t bytes_per_sample,
-                                    uint32_t samples_per_second,
-                                    uint32_t channels);
   void StopDelegate();
 
-  // Called by |FillBuffer()| to fill available data. If no available data, it
-  // will call |DecodeOnThread()| to get more data.
+  void FillBufferOnMainThread(void* buffer,
+                              int buffer_size,
+                              assistant_client::Callback1<int> on_filled);
+
+  // Called by |FillBufferOnMainThread()| to fill available data. If no
+  // available data, it will call |Decode()| to get more data.
   void FillDecodedBuffer(void* buffer, int buffer_size);
 
-  // Fills buffer to AudioDeviceOwner on main thread.
-  void OnFillBufferOnThread(assistant_client::Callback1<int> on_decoded,
-                            int num_bytes);
+  void OnFillBuffer(assistant_client::Callback1<int> on_decoded, int num_bytes);
 
-  // Calls |audio_decoder_| to decode on main thread.
-  void DecodeOnThread();
+  void Decode();
 
-  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+  scoped_refptr<base::SequencedTaskRunner> main_task_runner_;
   assistant_client::AudioOutput::Delegate* delegate_;
 
   mojo::Receiver<AssistantAudioDecoderClient> client_receiver_{this};
@@ -100,6 +92,11 @@
 
   base::circular_deque<std::vector<uint8_t>> decoded_data_;
 
+  // The callbacks from Libassistant are called on a different sequence,
+  // so this sequence checker ensures that no other methods are called on the
+  // libassistant sequence.
+  SEQUENCE_CHECKER(sequence_checker_);
+
   base::WeakPtrFactory<AudioStreamHandler> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(AudioStreamHandler);
diff --git a/chromeos/services/assistant/platform/file_provider_impl.cc b/chromeos/services/libassistant/file_provider_impl.cc
similarity index 94%
rename from chromeos/services/assistant/platform/file_provider_impl.cc
rename to chromeos/services/libassistant/file_provider_impl.cc
index b63b6c0..ab5a6e9 100644
--- a/chromeos/services/assistant/platform/file_provider_impl.cc
+++ b/chromeos/services/libassistant/file_provider_impl.cc
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/services/assistant/platform/file_provider_impl.h"
+#include "chromeos/services/libassistant/file_provider_impl.h"
 
 #include "base/files/file_util.h"
 #include "chromeos/grit/chromeos_resources.h"
-#include "chromeos/services/assistant/utils.h"
+#include "chromeos/services/libassistant/util.h"
 #include "ui/base/resource/resource_bundle.h"
 
 namespace chromeos {
-namespace assistant {
+namespace libassistant {
 namespace {
 
 constexpr int kReadFileSizeLimitInBytes = 10 * 1024 * 1024;
@@ -99,5 +99,5 @@
   return true;
 }
 
-}  // namespace assistant
+}  // namespace libassistant
 }  // namespace chromeos
diff --git a/chromeos/services/assistant/platform/file_provider_impl.h b/chromeos/services/libassistant/file_provider_impl.h
similarity index 80%
rename from chromeos/services/assistant/platform/file_provider_impl.h
rename to chromeos/services/libassistant/file_provider_impl.h
index bbd3645..1f7c9a61 100644
--- a/chromeos/services/assistant/platform/file_provider_impl.h
+++ b/chromeos/services/libassistant/file_provider_impl.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_SERVICES_ASSISTANT_PLATFORM_FILE_PROVIDER_IMPL_H_
-#define CHROMEOS_SERVICES_ASSISTANT_PLATFORM_FILE_PROVIDER_IMPL_H_
+#ifndef CHROMEOS_SERVICES_LIBASSISTANT_FILE_PROVIDER_IMPL_H_
+#define CHROMEOS_SERVICES_LIBASSISTANT_FILE_PROVIDER_IMPL_H_
 
 #include <string>
 
@@ -12,7 +12,7 @@
 #include "libassistant/shared/public/platform_file.h"
 
 namespace chromeos {
-namespace assistant {
+namespace libassistant {
 
 class FileProviderImpl : public assistant_client::FileProvider {
  public:
@@ -35,7 +35,7 @@
   DISALLOW_COPY_AND_ASSIGN(FileProviderImpl);
 };
 
-}  // namespace assistant
+}  // namespace libassistant
 }  // namespace chromeos
 
-#endif  // CHROMEOS_SERVICES_ASSISTANT_PLATFORM_FILE_PROVIDER_IMPL_H_
+#endif  // CHROMEOS_SERVICES_LIBASSISTANT_FILE_PROVIDER_IMPL_H_
diff --git a/chromeos/services/libassistant/libassistant_service.cc b/chromeos/services/libassistant/libassistant_service.cc
index 974960b..8de7744 100644
--- a/chromeos/services/libassistant/libassistant_service.cc
+++ b/chromeos/services/libassistant/libassistant_service.cc
@@ -14,7 +14,6 @@
 #include "chromeos/services/libassistant/conversation_controller.h"
 #include "chromeos/services/libassistant/conversation_state_listener_impl.h"
 #include "chromeos/services/libassistant/display_controller.h"
-#include "chromeos/services/libassistant/fake_auth_provider.h"
 #include "chromeos/services/libassistant/media_controller.h"
 #include "chromeos/services/libassistant/platform_api.h"
 #include "chromeos/services/libassistant/service_controller.h"
@@ -28,7 +27,6 @@
     assistant::AssistantManagerServiceDelegate* delegate)
     : receiver_(this, std::move(receiver)),
       platform_api_(std::make_unique<PlatformApi>()),
-      fake_auth_provider_(std::make_unique<FakeAuthProvider>()),
       audio_input_controller_(std::make_unique<AudioInputController>()),
       service_controller_(
           std::make_unique<ServiceController>(delegate, platform_api_.get())),
@@ -52,8 +50,6 @@
     platform_api_
         ->SetAudioInputProvider(
             &audio_input_controller_->audio_input_provider())
-        .SetAuthProvider(fake_auth_provider_.get())
-        .SetFileProvider(&platform_api->GetFileProvider())
         .SetNetworkProvider(&platform_api->GetNetworkProvider());
   }
 }
diff --git a/chromeos/services/libassistant/libassistant_service.h b/chromeos/services/libassistant/libassistant_service.h
index f615684c..7e427c4dd9 100644
--- a/chromeos/services/libassistant/libassistant_service.h
+++ b/chromeos/services/libassistant/libassistant_service.h
@@ -35,7 +35,6 @@
 class ConversationController;
 class ConversationStateListenerImpl;
 class DisplayController;
-class FakeAuthProvider;
 class MediaController;
 class PlatformApi;
 class ServiceController;
@@ -83,7 +82,6 @@
   // These controllers are part of the platform api which is called from
   // Libassistant, and thus they must outlive |service_controller_|.
   std::unique_ptr<PlatformApi> platform_api_;
-  std::unique_ptr<FakeAuthProvider> fake_auth_provider_;
   std::unique_ptr<AudioInputController> audio_input_controller_;
 
   std::unique_ptr<ServiceController> service_controller_;
diff --git a/chromeos/services/libassistant/platform_api.cc b/chromeos/services/libassistant/platform_api.cc
index 847f9ba6..bce811e3 100644
--- a/chromeos/services/libassistant/platform_api.cc
+++ b/chromeos/services/libassistant/platform_api.cc
@@ -7,6 +7,8 @@
 #include "base/check.h"
 #include "chromeos/services/assistant/public/cpp/features.h"
 #include "chromeos/services/libassistant/audio/audio_output_provider_impl.h"
+#include "chromeos/services/libassistant/fake_auth_provider.h"
+#include "chromeos/services/libassistant/file_provider_impl.h"
 #include "chromeos/services/libassistant/power_manager_provider_impl.h"
 #include "chromeos/services/libassistant/system_provider_impl.h"
 #include "media/audio/audio_device_description.h"
@@ -16,8 +18,9 @@
 
 PlatformApi::PlatformApi()
     : audio_output_provider_(std::make_unique<AudioOutputProviderImpl>(
-          /*background_task_runner=*/base::SequencedTaskRunnerHandle::Get(),
-          media::AudioDeviceDescription::kDefaultDeviceId)) {
+          media::AudioDeviceDescription::kDefaultDeviceId)),
+      fake_auth_provider_(std::make_unique<FakeAuthProvider>()),
+      file_provider_(std::make_unique<FileProviderImpl>()) {
   // Only enable native power features if they are supported by the UI.
   std::unique_ptr<PowerManagerProviderImpl> provider;
   if (assistant::features::IsPowerManagerEnabled()) {
@@ -42,18 +45,6 @@
   return *this;
 }
 
-PlatformApi& PlatformApi::SetAuthProvider(
-    assistant_client::AuthProvider* provider) {
-  auth_provider_ = provider;
-  return *this;
-}
-
-PlatformApi& PlatformApi::SetFileProvider(
-    assistant_client::FileProvider* provider) {
-  file_provider_ = provider;
-  return *this;
-}
-
 PlatformApi& PlatformApi::SetNetworkProvider(
     assistant_client::NetworkProvider* provider) {
   network_provider_ = provider;
@@ -71,8 +62,8 @@
 }
 
 assistant_client::AuthProvider& PlatformApi::GetAuthProvider() {
-  DCHECK(auth_provider_);
-  return *auth_provider_;
+  DCHECK(fake_auth_provider_);
+  return *fake_auth_provider_;
 }
 
 assistant_client::FileProvider& PlatformApi::GetFileProvider() {
diff --git a/chromeos/services/libassistant/platform_api.h b/chromeos/services/libassistant/platform_api.h
index a473977..edaea191 100644
--- a/chromeos/services/libassistant/platform_api.h
+++ b/chromeos/services/libassistant/platform_api.h
@@ -17,6 +17,8 @@
 namespace libassistant {
 
 class AudioOutputProviderImpl;
+class FakeAuthProvider;
+class FileProviderImpl;
 class SystemProviderImpl;
 
 // Implementation of the Libassistant PlatformApi.
@@ -34,8 +36,6 @@
       mojom::PlatformDelegate* platform_delegate);
 
   PlatformApi& SetAudioInputProvider(assistant_client::AudioInputProvider*);
-  PlatformApi& SetAuthProvider(assistant_client::AuthProvider*);
-  PlatformApi& SetFileProvider(assistant_client::FileProvider*);
   PlatformApi& SetNetworkProvider(assistant_client::NetworkProvider*);
 
   // assistant_client::PlatformApi implementation:
@@ -50,11 +50,11 @@
   // The below are all owned by the browser side |PlatformApiImpl|,
   // which outlives us.
   assistant_client::AudioInputProvider* audio_input_provider_ = nullptr;
-  assistant_client::AuthProvider* auth_provider_ = nullptr;
-  assistant_client::FileProvider* file_provider_ = nullptr;
   assistant_client::NetworkProvider* network_provider_ = nullptr;
 
   std::unique_ptr<AudioOutputProviderImpl> audio_output_provider_;
+  std::unique_ptr<FakeAuthProvider> fake_auth_provider_;
+  std::unique_ptr<FileProviderImpl> file_provider_;
   std::unique_ptr<SystemProviderImpl> system_provider_;
 };
 
diff --git a/chromeos/services/libassistant/test_support/libassistant_service_tester.cc b/chromeos/services/libassistant/test_support/libassistant_service_tester.cc
index 46b4310..0aaabda 100644
--- a/chromeos/services/libassistant/test_support/libassistant_service_tester.cc
+++ b/chromeos/services/libassistant/test_support/libassistant_service_tester.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "chromeos/services/libassistant/test_support/libassistant_service_tester.h"
+#include "base/base_paths.h"
 #include "services/network/test/test_url_loader_factory.h"
 
 namespace chromeos {
@@ -20,7 +21,8 @@
 }  // namespace
 
 LibassistantServiceTester::LibassistantServiceTester()
-    : service_(service_remote_.BindNewPipeAndPassReceiver(),
+    : home_dir_override_(base::DIR_HOME),
+      service_(service_remote_.BindNewPipeAndPassReceiver(),
                /*platform_api=*/nullptr,
                &assistant_manager_service_delegate_) {
   BindControllers();
diff --git a/chromeos/services/libassistant/test_support/libassistant_service_tester.h b/chromeos/services/libassistant/test_support/libassistant_service_tester.h
index c5920ef..782ba93 100644
--- a/chromeos/services/libassistant/test_support/libassistant_service_tester.h
+++ b/chromeos/services/libassistant/test_support/libassistant_service_tester.h
@@ -5,6 +5,7 @@
 #ifndef CHROMEOS_SERVICES_LIBASSISTANT_TEST_SUPPORT_LIBASSISTANT_SERVICE_TESTER_H_
 #define CHROMEOS_SERVICES_LIBASSISTANT_TEST_SUPPORT_LIBASSISTANT_SERVICE_TESTER_H_
 
+#include "base/test/scoped_path_override.h"
 #include "chromeos/services/assistant/public/cpp/migration/fake_assistant_manager_service_delegate.h"
 #include "chromeos/services/libassistant/libassistant_service.h"
 #include "chromeos/services/libassistant/public/mojom/audio_input_controller.mojom.h"
@@ -77,6 +78,8 @@
   mojo::Remote<mojom::LibassistantService> service_remote_;
   assistant::FakeAssistantManagerServiceDelegate
       assistant_manager_service_delegate_;
+  // Our file provider requires the home dir to be overridden.
+  base::ScopedPathOverride home_dir_override_;
   LibassistantService service_;
 };
 
diff --git a/chromeos/tast_control.gni b/chromeos/tast_control.gni
index 5285d1a..a65d3dba 100644
--- a/chromeos/tast_control.gni
+++ b/chromeos/tast_control.gni
@@ -9,6 +9,9 @@
 # To disable a specific test in chrome_all_tast_tests, add it the following
 # list and cite a bug.
 tast_disabled_tests_from_chrome_all = [
+  # crbug.com/1170669
+  "hwsec.ChapsRSAPSS",
+
   # crbug.com/1167243
   "ui.ChromeLogin",
 
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn
index 359b77b5..02926dfd 100644
--- a/components/autofill/core/browser/BUILD.gn
+++ b/components/autofill/core/browser/BUILD.gn
@@ -754,7 +754,6 @@
     "//base/test:test_support",
     "//build:chromeos_buildflags",
     "//components/autofill/core/common",
-    "//components/infobars/core:feature_flags",
     "//components/leveldb_proto",
     "//components/os_crypt",
     "//components/os_crypt:test_support",
diff --git a/components/autofill/core/browser/address_rewriter.cc b/components/autofill/core/browser/address_rewriter.cc
index 030a5ab..c2d8533 100644
--- a/components/autofill/core/browser/address_rewriter.cc
+++ b/components/autofill/core/browser/address_rewriter.cc
@@ -35,8 +35,8 @@
   int resource_id = 0;
   std::string resource_key = GetMapKey(region);
   for (size_t i = 0; i < kAutofillAddressRewriterResourcesSize; ++i) {
-    if (kAutofillAddressRewriterResources[i].name == resource_key) {
-      resource_id = kAutofillAddressRewriterResources[i].value;
+    if (kAutofillAddressRewriterResources[i].path == resource_key) {
+      resource_id = kAutofillAddressRewriterResources[i].id;
       break;
     }
   }
diff --git a/components/autofill/core/browser/payments/credit_card_save_manager.cc b/components/autofill/core/browser/payments/credit_card_save_manager.cc
index 2addf2a..dbd58bb 100644
--- a/components/autofill/core/browser/payments/credit_card_save_manager.cc
+++ b/components/autofill/core/browser/payments/credit_card_save_manager.cc
@@ -43,7 +43,6 @@
 #include "components/autofill/core/common/autofill_payments_features.h"
 #include "components/autofill/core/common/autofill_prefs.h"
 #include "components/autofill/core/common/autofill_util.h"
-#include "components/infobars/core/infobar_feature.h"
 #include "components/prefs/pref_service.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "url/gurl.h"
@@ -216,9 +215,8 @@
     // iOS should always provide a valid expiration date when attempting to
     // upload a Saved Card. Calling LogSaveCardRequestExpirationDateReasonMetric
     // would trigger a DCHECK.
-    if (!(base::FeatureList::IsEnabled(
-              features::kAutofillSaveCardInfobarEditSupport) &&
-          base::FeatureList::IsEnabled(kIOSInfobarUIReboot))) {
+    if (!base::FeatureList::IsEnabled(
+            features::kAutofillSaveCardInfobarEditSupport)) {
       // Remove once both flags are deleted.
       LogSaveCardRequestExpirationDateReasonMetric();
     }
@@ -229,9 +227,8 @@
   }
 
 #if defined(OS_IOS)
-  if ((base::FeatureList::IsEnabled(
-           features::kAutofillSaveCardInfobarEditSupport) &&
-       base::FeatureList::IsEnabled(kIOSInfobarUIReboot))) {
+  if (base::FeatureList::IsEnabled(
+          features::kAutofillSaveCardInfobarEditSupport)) {
     // iOS's new credit card save dialog requires the user to enter both
     // cardholder name and expiration date before saving.  Regardless of what
     // Chrome thought it needed to do before, disable both of the previous
@@ -776,9 +773,8 @@
 // card unless the user provides both a valid cardholder name and expiration
 // date.
 #if defined(OS_IOS)
-  if ((base::FeatureList::IsEnabled(
-           features::kAutofillSaveCardInfobarEditSupport) &&
-       base::FeatureList::IsEnabled(kIOSInfobarUIReboot))) {
+  if (base::FeatureList::IsEnabled(
+          features::kAutofillSaveCardInfobarEditSupport)) {
     detected_values |= DetectedValue::USER_PROVIDED_NAME;
     detected_values |= DetectedValue::USER_PROVIDED_EXPIRATION_DATE;
   }
@@ -852,9 +848,8 @@
     // the user, but not through the fix flow triggered via
     // |should_request_name_from_user_|.
     DCHECK(should_request_name_from_user_ ||
-           (base::FeatureList::IsEnabled(
-                autofill::features::kAutofillSaveCardInfobarEditSupport) &&
-            base::FeatureList::IsEnabled(kIOSInfobarUIReboot)));
+           base::FeatureList::IsEnabled(
+               autofill::features::kAutofillSaveCardInfobarEditSupport));
 #else
     DCHECK(should_request_name_from_user_);
 #endif
@@ -873,9 +868,8 @@
     // the user, but not through the fix flow triggered via
     // |should_request_expiration_date_from_user_|.
     DCHECK(should_request_expiration_date_from_user_ ||
-           (base::FeatureList::IsEnabled(
-                autofill::features::kAutofillSaveCardInfobarEditSupport) &&
-            base::FeatureList::IsEnabled(kIOSInfobarUIReboot)));
+           base::FeatureList::IsEnabled(
+               autofill::features::kAutofillSaveCardInfobarEditSupport));
 #else
     DCHECK(should_request_expiration_date_from_user_);
 #endif
diff --git a/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc b/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc
index 2553358..34fb6da3 100644
--- a/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc
@@ -50,7 +50,6 @@
 #include "components/autofill/core/common/autofill_prefs.h"
 #include "components/autofill/core/common/form_data.h"
 #include "components/autofill/core/common/form_field_data.h"
-#include "components/infobars/core/infobar_feature.h"
 #include "components/prefs/pref_service.h"
 #include "components/sync/driver/test_sync_service.h"
 #include "components/ukm/test_ukm_recorder.h"
@@ -349,7 +348,6 @@
   MockPersonalDataManager personal_data_;
   MockAutocompleteHistoryManager autocomplete_history_manager_;
   syncer::TestSyncService sync_service_;
-  base::test::ScopedFeatureList scoped_feature_list_;
   // Ends up getting owned (and destroyed) by TestFormDataImporter:
   TestCreditCardSaveManager* credit_card_save_manager_;
   // Ends up getting owned (and destroyed) by TestAutofillClient:
@@ -2960,9 +2958,8 @@
   // iOS should always provide a valid expiration date when attempting to
   // upload a Saved Card due to the Messages SaveCard modal. The manager
   // shouldn't handle expired dates.
-  if ((base::FeatureList::IsEnabled(
-           features::kAutofillSaveCardInfobarEditSupport) &&
-       base::FeatureList::IsEnabled(kIOSInfobarUIReboot))) {
+  if (base::FeatureList::IsEnabled(
+          features::kAutofillSaveCardInfobarEditSupport)) {
     return;
   }
 #endif
@@ -3012,9 +3009,8 @@
   // iOS should always provide a valid expiration date when attempting to
   // upload a Saved Card due to the Messages SaveCard modal. The manager
   // shouldn't handle expired dates.
-  if ((base::FeatureList::IsEnabled(
-           features::kAutofillSaveCardInfobarEditSupport) &&
-       base::FeatureList::IsEnabled(kIOSInfobarUIReboot))) {
+  if (base::FeatureList::IsEnabled(
+          features::kAutofillSaveCardInfobarEditSupport)) {
     return;
   }
 #endif
@@ -3064,9 +3060,8 @@
   // iOS should always provide a valid expiration date when attempting to
   // upload a Saved Card due to the Messages SaveCard modal. The manager
   // shouldn't handle expired dates.
-  if ((base::FeatureList::IsEnabled(
-           features::kAutofillSaveCardInfobarEditSupport) &&
-       base::FeatureList::IsEnabled(kIOSInfobarUIReboot))) {
+  if (base::FeatureList::IsEnabled(
+          features::kAutofillSaveCardInfobarEditSupport)) {
     return;
   }
 #endif
@@ -3116,9 +3111,8 @@
   // iOS should always provide a valid expiration date when attempting to
   // upload a Saved Card due to the Messages SaveCard modal. The manager
   // shouldn't handle expired dates.
-  if ((base::FeatureList::IsEnabled(
-           features::kAutofillSaveCardInfobarEditSupport) &&
-       base::FeatureList::IsEnabled(kIOSInfobarUIReboot))) {
+  if (base::FeatureList::IsEnabled(
+          features::kAutofillSaveCardInfobarEditSupport)) {
     return;
   }
 #endif
@@ -3169,9 +3163,8 @@
   // iOS should always provide a valid expiration date when attempting to
   // upload a Saved Card due to the Messages SaveCard modal. The manager
   // shouldn't handle expired dates.
-  if ((base::FeatureList::IsEnabled(
-           features::kAutofillSaveCardInfobarEditSupport) &&
-       base::FeatureList::IsEnabled(kIOSInfobarUIReboot))) {
+  if (base::FeatureList::IsEnabled(
+          features::kAutofillSaveCardInfobarEditSupport)) {
     return;
   }
 #endif
diff --git a/components/autofill/core/browser/payments/test_strike_database.h b/components/autofill/core/browser/payments/test_strike_database.h
index 803a1f1..7fb5fb5 100644
--- a/components/autofill/core/browser/payments/test_strike_database.h
+++ b/components/autofill/core/browser/payments/test_strike_database.h
@@ -12,6 +12,7 @@
 #include <vector>
 
 #include "components/autofill/core/browser/payments/strike_database.h"
+#include "components/autofill/core/browser/proto/strike_data.pb.h"
 
 namespace autofill {
 
diff --git a/components/autofill_payments_strings.grdp b/components/autofill_payments_strings.grdp
index 6ec7952..3a6a912b 100644
--- a/components/autofill_payments_strings.grdp
+++ b/components/autofill_payments_strings.grdp
@@ -508,16 +508,23 @@
   <message name="IDS_AUTOFILL_OFFERS_CASHBACK" desc="Displays that a cashback offer will be rewarded if credit card is used on current page. Part of Autofill suggestions popup.">
     Cashback linked
   </message>
-  <message name="IDS_AUTOFILL_OFFERS_REMINDER_TITLE" desc="Title of the infobar shown on the merchant website when an offer is available to use.">
-    Don't forget your Google Pay offer
+  <message name="IDS_AUTOFILL_OFFERS_REMINDER_TITLE" desc="Title of the bubble/infobar shown on the merchant website when an offer is available to use.">
+    Google Pay offer available
   </message>
-  <message name="IDS_AUTOFILL_OFFERS_REMINDER_POSITIVE_BUTTON_LABEL" desc="Label for the positive button for the infobar shown on the merchant website when an offer is available to use.">
+  <message name="IDS_AUTOFILL_OFFERS_REMINDER_POSITIVE_BUTTON_LABEL" desc="Label for the positive button for the bubble/infobar shown on the merchant website when an offer is available to use.">
     Got it
   </message>
-  <message name="IDS_AUTOFILL_OFFERS_REMINDER_DESCRIPTION_TEXT" desc="Label for the positive button for the infobar shown on the merchant website when an offer is available to use." formatter_data="android_java">
-    Check out with
-  </message>
-  <message name="IDS_AUTOFILL_OFFERS_REMINDER_DEEP_LINK_TEXT" desc="Text to be linked to take the user to the offer in the Google Pay app." formatter_data="android_java">
-    See details.
-  </message>
+  <if expr="not is_ios and not is_android">
+    <message name="IDS_AUTOFILL_OFFERS_REMINDER_DESCRIPTION_TEXT" desc="Secondary explanatory text for the Desktop bubble shown on the merchant website when an offer is available to use.">
+      Pay with <ph name="CARD_DETAIL">$1<ex>Visa - 1234</ex></ph> at checkout
+    </message>
+  </if>
+  <if expr="is_android">
+    <message name="IDS_AUTOFILL_OFFERS_REMINDER_DESCRIPTION_TEXT" desc="Secondary explanatory text for the Clank infobar shown on the merchant website when an offer is available to use." formatter_data="android_java">
+      Pay with <ph name="CARD_DETAIL">$1<ex>Visa - 1234</ex></ph> at checkout.
+    </message>
+    <message name="IDS_AUTOFILL_OFFERS_REMINDER_DEEP_LINK_TEXT" desc="Text to be linked to take the user to the offer in the Google Pay app." formatter_data="android_java">
+      See details
+    </message>
+  </if>
 </grit-part>
diff --git a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_OFFERS_REMINDER_DEEP_LINK_TEXT.png.sha1 b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_OFFERS_REMINDER_DEEP_LINK_TEXT.png.sha1
index e5bb921..6845e38 100644
--- a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_OFFERS_REMINDER_DEEP_LINK_TEXT.png.sha1
+++ b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_OFFERS_REMINDER_DEEP_LINK_TEXT.png.sha1
@@ -1 +1 @@
-479d2c91dfda3d47d82243ac63a33c4189c3e9a1
\ No newline at end of file
+8a5dc744774a4689ed28ede66594f24e697f98a2
\ No newline at end of file
diff --git a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_OFFERS_REMINDER_DESCRIPTION_TEXT.png.sha1 b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_OFFERS_REMINDER_DESCRIPTION_TEXT.png.sha1
index e5bb921..285f3c9 100644
--- a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_OFFERS_REMINDER_DESCRIPTION_TEXT.png.sha1
+++ b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_OFFERS_REMINDER_DESCRIPTION_TEXT.png.sha1
@@ -1 +1 @@
-479d2c91dfda3d47d82243ac63a33c4189c3e9a1
\ No newline at end of file
+a5281d982b05a2bc87b92a8fe3e65742a16766e0
\ No newline at end of file
diff --git a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_OFFERS_REMINDER_TITLE.png.sha1 b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_OFFERS_REMINDER_TITLE.png.sha1
index e5bb921..285f3c9 100644
--- a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_OFFERS_REMINDER_TITLE.png.sha1
+++ b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_OFFERS_REMINDER_TITLE.png.sha1
@@ -1 +1 @@
-479d2c91dfda3d47d82243ac63a33c4189c3e9a1
\ No newline at end of file
+a5281d982b05a2bc87b92a8fe3e65742a16766e0
\ No newline at end of file
diff --git a/components/bookmarks/browser/bookmark_utils.cc b/components/bookmarks/browser/bookmark_utils.cc
index 34873ba..213e8f1 100644
--- a/components/bookmarks/browser/bookmark_utils.cc
+++ b/components/bookmarks/browser/bookmark_utils.cc
@@ -417,11 +417,10 @@
 std::vector<base::string16> ParseBookmarkQuery(
     const bookmarks::QueryFields& query) {
   std::vector<base::string16> query_words;
-  query_parser::QueryParser parser;
   if (query.word_phrase_query) {
-    parser.ParseQueryWords(base::i18n::ToLower(*query.word_phrase_query),
-                           query_parser::MatchingAlgorithm::DEFAULT,
-                           &query_words);
+    query_parser::QueryParser::ParseQueryWords(
+        base::i18n::ToLower(*query.word_phrase_query),
+        query_parser::MatchingAlgorithm::DEFAULT, &query_words);
   }
   return query_words;
 }
diff --git a/components/bookmarks/browser/titled_url_index.cc b/components/bookmarks/browser/titled_url_index.cc
index 61ffea3..36f06745 100644
--- a/components/bookmarks/browser/titled_url_index.cc
+++ b/components/bookmarks/browser/titled_url_index.cc
@@ -94,9 +94,9 @@
   // We use a QueryParser to fill in match positions for us. It's not the most
   // efficient way to go about this, but by the time we get here we know what
   // matches and so this shouldn't be performance critical.
-  query_parser::QueryParser parser;
   query_parser::QueryNodeVector query_nodes;
-  parser.ParseQueryNodes(query, matching_algorithm, &query_nodes);
+  query_parser::QueryParser::ParseQueryNodes(query, matching_algorithm,
+                                             &query_nodes);
 
   // The highest typed counts should be at the beginning of the results vector
   // so that the best matches will always be included in the results. The loop
@@ -106,8 +106,8 @@
   std::vector<TitledUrlMatch> results;
   for (TitledUrlNodes::const_iterator i = sorted_nodes.begin();
        i != sorted_nodes.end() && results.size() < max_count; ++i) {
-    base::Optional<TitledUrlMatch> match = MatchTitledUrlNodeWithQuery(
-        *i, &parser, query_nodes, match_ancestor_titles);
+    base::Optional<TitledUrlMatch> match =
+        MatchTitledUrlNodeWithQuery(*i, query_nodes, match_ancestor_titles);
     if (match)
       results.push_back(match.value());
   }
@@ -125,7 +125,6 @@
 
 base::Optional<TitledUrlMatch> TitledUrlIndex::MatchTitledUrlNodeWithQuery(
     const TitledUrlNode* node,
-    query_parser::QueryParser* parser,
     const query_parser::QueryNodeVector& query_nodes,
     bool match_ancestor_titles) {
   if (!node) {
@@ -139,14 +138,14 @@
   query_parser::QueryWordVector title_words, url_words, ancestor_words;
   const base::string16 lower_title =
       base::i18n::ToLower(Normalize(node->GetTitledUrlNodeTitle()));
-  parser->ExtractQueryWords(lower_title, &title_words);
+  query_parser::QueryParser::ExtractQueryWords(lower_title, &title_words);
   base::OffsetAdjuster::Adjustments adjustments;
-  parser->ExtractQueryWords(
+  query_parser::QueryParser::ExtractQueryWords(
       CleanUpUrlForMatching(node->GetTitledUrlNodeUrl(), &adjustments),
       &url_words);
   if (match_ancestor_titles) {
     for (auto ancestor : node->GetTitledUrlNodeAncestorTitles()) {
-      parser->ExtractQueryWords(
+      query_parser::QueryParser::ExtractQueryWords(
           base::i18n::ToLower(Normalize(base::string16(ancestor))),
           &ancestor_words);
     }
@@ -258,10 +257,9 @@
   std::vector<base::string16> terms;
   if (query.empty())
     return std::vector<base::string16>();
-  query_parser::QueryParser parser;
-  parser.ParseQueryWords(base::i18n::ToLower(query),
-                         query_parser::MatchingAlgorithm::DEFAULT,
-                         &terms);
+  query_parser::QueryParser::ParseQueryWords(
+      base::i18n::ToLower(query), query_parser::MatchingAlgorithm::DEFAULT,
+      &terms);
   return terms;
 }
 
diff --git a/components/bookmarks/browser/titled_url_index.h b/components/bookmarks/browser/titled_url_index.h
index 406dd55f..a2fcd07b 100644
--- a/components/bookmarks/browser/titled_url_index.h
+++ b/components/bookmarks/browser/titled_url_index.h
@@ -85,7 +85,6 @@
   // containing |node| and the matches.
   base::Optional<TitledUrlMatch> MatchTitledUrlNodeWithQuery(
       const TitledUrlNode* node,
-      query_parser::QueryParser* parser,
       const query_parser::QueryNodeVector& query_nodes,
       bool match_ancestor_titles);
 
diff --git a/components/browser_ui/widget/android/BUILD.gn b/components/browser_ui/widget/android/BUILD.gn
index d4e68c2..40736fb 100644
--- a/components/browser_ui/widget/android/BUILD.gn
+++ b/components/browser_ui/widget/android/BUILD.gn
@@ -98,7 +98,7 @@
     "java/src/org/chromium/components/browser_ui/widget/text/TextViewWithCompoundDrawables.java",
     "java/src/org/chromium/components/browser_ui/widget/text/VerticallyFixedEditText.java",
     "java/src/org/chromium/components/browser_ui/widget/textbubble/ArrowBubbleDrawable.java",
-    "java/src/org/chromium/components/browser_ui/widget/textbubble/ImageTextBubble.java",
+    "java/src/org/chromium/components/browser_ui/widget/textbubble/ClickableTextBubble.java",
     "java/src/org/chromium/components/browser_ui/widget/textbubble/TextBubble.java",
   ]
 
@@ -136,30 +136,35 @@
     "java/res/drawable-hdpi/btn_info.png",
     "java/res/drawable-hdpi/ic_arrow_back_white_24dp.png",
     "java/res/drawable-hdpi/ic_check_googblue_24dp.png",
+    "java/res/drawable-hdpi/ic_drag_handle_grey600_24dp.png",
     "java/res/drawable-hdpi/ic_more_vert_24dp_on_dark_bg.png",
     "java/res/drawable-hdpi/ic_more_vert_24dp_on_light_bg.png",
     "java/res/drawable-mdpi/btn_delete_24dp.png",
     "java/res/drawable-mdpi/btn_info.png",
     "java/res/drawable-mdpi/ic_arrow_back_white_24dp.png",
     "java/res/drawable-mdpi/ic_check_googblue_24dp.png",
+    "java/res/drawable-mdpi/ic_drag_handle_grey600_24dp.png",
     "java/res/drawable-mdpi/ic_more_vert_24dp_on_dark_bg.png",
     "java/res/drawable-mdpi/ic_more_vert_24dp_on_light_bg.png",
     "java/res/drawable-xhdpi/btn_delete_24dp.png",
     "java/res/drawable-xhdpi/btn_info.png",
     "java/res/drawable-xhdpi/ic_arrow_back_white_24dp.png",
     "java/res/drawable-xhdpi/ic_check_googblue_24dp.png",
+    "java/res/drawable-xhdpi/ic_drag_handle_grey600_24dp.png",
     "java/res/drawable-xhdpi/ic_more_vert_24dp_on_dark_bg.png",
     "java/res/drawable-xhdpi/ic_more_vert_24dp_on_light_bg.png",
     "java/res/drawable-xxhdpi/btn_delete_24dp.png",
     "java/res/drawable-xxhdpi/btn_info.png",
     "java/res/drawable-xxhdpi/ic_arrow_back_white_24dp.png",
     "java/res/drawable-xxhdpi/ic_check_googblue_24dp.png",
+    "java/res/drawable-xxhdpi/ic_drag_handle_grey600_24dp.png",
     "java/res/drawable-xxhdpi/ic_more_vert_24dp_on_dark_bg.png",
     "java/res/drawable-xxhdpi/ic_more_vert_24dp_on_light_bg.png",
     "java/res/drawable-xxxhdpi/btn_delete_24dp.png",
     "java/res/drawable-xxxhdpi/btn_info.png",
     "java/res/drawable-xxxhdpi/ic_arrow_back_white_24dp.png",
     "java/res/drawable-xxxhdpi/ic_check_googblue_24dp.png",
+    "java/res/drawable-xxxhdpi/ic_drag_handle_grey600_24dp.png",
     "java/res/drawable-xxxhdpi/ic_more_vert_24dp_on_dark_bg.png",
     "java/res/drawable-xxxhdpi/ic_more_vert_24dp_on_light_bg.png",
     "java/res/drawable/async_image_view_unavailable.xml",
diff --git a/chrome/android/java/res/drawable-hdpi/ic_drag_handle_grey600_24dp.png b/components/browser_ui/widget/android/java/res/drawable-hdpi/ic_drag_handle_grey600_24dp.png
similarity index 100%
rename from chrome/android/java/res/drawable-hdpi/ic_drag_handle_grey600_24dp.png
rename to components/browser_ui/widget/android/java/res/drawable-hdpi/ic_drag_handle_grey600_24dp.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/ic_drag_handle_grey600_24dp.png b/components/browser_ui/widget/android/java/res/drawable-mdpi/ic_drag_handle_grey600_24dp.png
similarity index 100%
rename from chrome/android/java/res/drawable-mdpi/ic_drag_handle_grey600_24dp.png
rename to components/browser_ui/widget/android/java/res/drawable-mdpi/ic_drag_handle_grey600_24dp.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/ic_drag_handle_grey600_24dp.png b/components/browser_ui/widget/android/java/res/drawable-xhdpi/ic_drag_handle_grey600_24dp.png
similarity index 100%
rename from chrome/android/java/res/drawable-xhdpi/ic_drag_handle_grey600_24dp.png
rename to components/browser_ui/widget/android/java/res/drawable-xhdpi/ic_drag_handle_grey600_24dp.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/ic_drag_handle_grey600_24dp.png b/components/browser_ui/widget/android/java/res/drawable-xxhdpi/ic_drag_handle_grey600_24dp.png
similarity index 100%
rename from chrome/android/java/res/drawable-xxhdpi/ic_drag_handle_grey600_24dp.png
rename to components/browser_ui/widget/android/java/res/drawable-xxhdpi/ic_drag_handle_grey600_24dp.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/ic_drag_handle_grey600_24dp.png b/components/browser_ui/widget/android/java/res/drawable-xxxhdpi/ic_drag_handle_grey600_24dp.png
similarity index 100%
rename from chrome/android/java/res/drawable-xxxhdpi/ic_drag_handle_grey600_24dp.png
rename to components/browser_ui/widget/android/java/res/drawable-xxxhdpi/ic_drag_handle_grey600_24dp.png
Binary files differ
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithEditTextTest.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithEditTextTest.java
index dee1221a..99158bc 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithEditTextTest.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithEditTextTest.java
@@ -35,6 +35,7 @@
 import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.components.browser_ui.widget.test.R;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.content_public.browser.test.util.TouchCommon;
@@ -237,6 +238,7 @@
 
     @Test
     @SmallTest
+    @DisabledTest(message = "Flaky test - see: https://crbug.com/1177183")
     public void testFocusChange() {
         Assert.assertFalse(mRadioButtonWithEditText.hasFocus());
         TestThreadUtils.runOnUiThreadBlocking(() -> { mRadioButtonWithEditText.setChecked(true); });
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/textbubble/ClickableTextBubble.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/textbubble/ClickableTextBubble.java
new file mode 100644
index 0000000..485f9d1
--- /dev/null
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/textbubble/ClickableTextBubble.java
@@ -0,0 +1,43 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+package org.chromium.components.browser_ui.widget.textbubble;
+
+import android.content.Context;
+import android.view.View;
+
+import androidx.annotation.DrawableRes;
+import androidx.annotation.StringRes;
+
+import org.chromium.ui.widget.RectProvider;
+
+/**
+ * UI component that handles showing a clickable text callout bubble.
+ *
+ * <p>This has special styling specific to clickable text bubbles.
+ * // TODO(sophey): add specific styling differences once implemented.
+ */
+public class ClickableTextBubble extends TextBubble {
+    /**
+     * Constructs a {@link ClickableTextBubble} instance.
+     *
+     * @param context Context to draw resources from.
+     * @param rootView The {@link View} to use for size calculations and for display.
+     * @param stringId The id of the string resource for the text that should be shown.
+     * @param accessibilityStringId The id of the string resource of the accessibility text.
+     * @param showArrow Whether the bubble should have an arrow.
+     * @param anchorRectProvider The {@link RectProvider} used to anchor the text bubble.
+     * @param imageDrawableId The resource id of the image to show at the start of the text bubble.
+     * @param isAccessibilityEnabled Whether accessibility mode is enabled. Used to determine bubble
+     * text and dismiss UX.
+     * @param onTouchListener The callback for all touch events being dispatched to the bubble.
+     */
+    public ClickableTextBubble(Context context, View rootView, @StringRes int stringId,
+            @StringRes int accessibilityStringId, boolean showArrow,
+            RectProvider anchorRectProvider, @DrawableRes int imageDrawableId,
+            boolean isAccessibilityEnabled, View.OnTouchListener onTouchListener) {
+        super(context, rootView, stringId, accessibilityStringId, showArrow, anchorRectProvider,
+                imageDrawableId, isAccessibilityEnabled);
+        setTouchInterceptor(onTouchListener);
+    }
+}
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/textbubble/ImageTextBubble.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/textbubble/ImageTextBubble.java
deleted file mode 100644
index 9885542..0000000
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/textbubble/ImageTextBubble.java
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-package org.chromium.components.browser_ui.widget.textbubble;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import androidx.annotation.StringRes;
-
-import org.chromium.components.browser_ui.widget.R;
-import org.chromium.ui.widget.RectProvider;
-
-/**
- * UI component that handles showing a text callout bubble with a preceding image.
- */
-public class ImageTextBubble extends TextBubble {
-    /**
-     * Constructs a {@link ImageTextBubble} instance.
-     * @param context  Context to draw resources from.
-     * @param rootView The {@link View} to use for size calculations and for display.
-     * @param stringId The id of the string resource for the text that should be shown.
-     * @param accessibilityStringId The id of the string resource of the accessibility text.
-     * @param showArrow Whether the bubble should have an arrow.
-     * @param anchorRectProvider The {@link RectProvider} used to anchor the text bubble.
-     * @param imageDrawableId The resource id of the image to show at the start of the text bubble.
-     * @param isAccessibilityEnabled Whether accessibility mode is enabled. Used to determine bubble
-     *         text and dismiss UX.
-     */
-    public ImageTextBubble(Context context, View rootView, @StringRes int stringId,
-            @StringRes int accessibilityStringId, boolean showArrow,
-            RectProvider anchorRectProvider, int imageDrawableId, boolean isAccessibilityEnabled) {
-        super(context, rootView, stringId, accessibilityStringId, showArrow, anchorRectProvider,
-                isAccessibilityEnabled);
-
-        ((ImageView) mContentView.findViewById(R.id.image)).setImageResource(imageDrawableId);
-    }
-
-    @Override
-    protected View createContentView() {
-        View view =
-                LayoutInflater.from(mContext).inflate(R.layout.textbubble_text_with_image, null);
-        setText((TextView) view.findViewById(R.id.message));
-        return view;
-    }
-}
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/textbubble/TextBubble.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/textbubble/TextBubble.java
index 600fe02..bf0853f4 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/textbubble/TextBubble.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/textbubble/TextBubble.java
@@ -6,6 +6,7 @@
 
 import android.content.Context;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -16,7 +17,10 @@
 import android.widget.PopupWindow.OnDismissListener;
 import android.widget.TextView;
 
+import androidx.annotation.DrawableRes;
+import androidx.annotation.Nullable;
 import androidx.annotation.StringRes;
+import androidx.appcompat.content.res.AppCompatResources;
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.MathUtils;
@@ -44,14 +48,17 @@
      */
     private static final Set<TextBubble> sBubbles = new HashSet<>();
 
-    protected final Context mContext;
+    private final Context mContext;
     private final Handler mHandler;
 
     /** The actual {@link PopupWindow}.  Internalized to prevent API leakage. */
     private final AnchoredPopupWindow mPopupWindow;
 
     /** The {@link Drawable} that is responsible for drawing the bubble and the arrow. */
-    private final ArrowBubbleDrawable mDrawable;
+    private final ArrowBubbleDrawable mBubbleDrawable;
+
+    /** The {@link Drawable} that precedes the text in the bubble. */
+    private final Drawable mImageDrawable;
 
     private final Runnable mDismissRunnable = new Runnable() {
         @Override
@@ -83,7 +90,7 @@
     private final boolean mIsAccessibilityEnabled;
 
     /** The content view shown in the popup window. */
-    protected View mContentView;
+    private View mContentView;
 
     /**
      * Constructs a {@link TextBubble} instance using the default arrow drawable background. Creates
@@ -169,11 +176,11 @@
             RectProvider anchorRectProvider, boolean isAccessibilityEnabled) {
         this(context, rootView, context.getString(stringId),
                 context.getString(accessibilityStringId), showArrow, anchorRectProvider,
-                isAccessibilityEnabled);
+                /*imageDrawable=*/null, isAccessibilityEnabled);
     }
 
     /**
-     * Constructs a {@link TextBubble} instance.
+     * Constructs a {@link TextBubble} instance with no preceding image.
      * @param context  Context to draw resources from.
      * @param rootView The {@link View} to use for size calculations and for display.
      * @param contentString The string for the text that should be shown.
@@ -186,13 +193,55 @@
     public TextBubble(Context context, View rootView, String contentString,
             String accessibilityString, boolean showArrow, RectProvider anchorRectProvider,
             boolean isAccessibilityEnabled) {
+        this(context, rootView, contentString, accessibilityString, showArrow, anchorRectProvider,
+                /*imageDrawable=*/null, isAccessibilityEnabled);
+    }
+
+    /**
+     * Constructs a {@link TextBubble} instance with a preceding image.
+     * @param context  Context to draw resources from.
+     * @param rootView The {@link View} to use for size calculations and for display.
+     * @param stringId The id of the string resource for the text that should be shown.
+     * @param accessibilityStringId The id of the string resource of the accessibility text.
+     * @param showArrow Whether the bubble should have an arrow.
+     * @param anchorRectProvider The {@link RectProvider} used to anchor the text bubble.
+     * @param imageDrawableId The resource id of the image to show at the start of the text bubble.
+     * @param isAccessibilityEnabled Whether accessibility mode is enabled. Used to determine bubble
+     *         text and dismiss UX.
+     */
+    public TextBubble(Context context, View rootView, @StringRes int stringId,
+            @StringRes int accessibilityStringId, boolean showArrow,
+            RectProvider anchorRectProvider, @DrawableRes int imageDrawableId,
+            boolean isAccessibilityEnabled) {
+        this(context, rootView, context.getString(stringId),
+                context.getString(accessibilityStringId), showArrow, anchorRectProvider,
+                AppCompatResources.getDrawable(context, imageDrawableId), isAccessibilityEnabled);
+    }
+
+    /**
+     * Constructs a {@link TextBubble} instance.
+     * @param context  Context to draw resources from.
+     * @param rootView The {@link View} to use for size calculations and for display.
+     * @param contentString The string for the text that should be shown.
+     * @param accessibilityString The string shown in the bubble when accessibility is enabled.
+     * @param showArrow Whether the bubble should have an arrow.
+     * @param anchorRectProvider The {@link RectProvider} used to anchor the text bubble.
+     * @param imageDrawable The image to show at the start of the text bubble, or null if there
+     *         should be no image.
+     * @param isAccessibilityEnabled Whether accessibility mode is enabled. Used to determine bubble
+     *         text and dismiss UX.
+     */
+    public TextBubble(Context context, View rootView, String contentString,
+            String accessibilityString, boolean showArrow, RectProvider anchorRectProvider,
+            @Nullable Drawable imageDrawable, boolean isAccessibilityEnabled) {
         mContext = context;
         mString = contentString;
         mAccessibilityString = accessibilityString;
+        mImageDrawable = imageDrawable;
         mIsAccessibilityEnabled = isAccessibilityEnabled;
 
-        mDrawable = new ArrowBubbleDrawable(context);
-        mDrawable.setShowArrow(showArrow);
+        mBubbleDrawable = new ArrowBubbleDrawable(context);
+        mBubbleDrawable.setShowArrow(showArrow);
 
         mContentView = createContentView();
         // On some versions of Android, the LayoutParams aren't set until after the popup window
@@ -202,7 +251,7 @@
                 new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
 
         mPopupWindow = new AnchoredPopupWindow(
-                context, rootView, mDrawable, mContentView, anchorRectProvider);
+                context, rootView, mBubbleDrawable, mContentView, anchorRectProvider);
         mPopupWindow.setMargin(
                 context.getResources().getDimensionPixelSize(R.dimen.text_bubble_margin));
         mPopupWindow.setPreferredHorizontalOrientation(
@@ -217,7 +266,7 @@
         if (mIsAccessibilityEnabled) setDismissOnTouchInteraction(true);
 
         // Set predefined styles for the TextBubble.
-        mDrawable.setBubbleColor(ApiCompatibilityUtils.getColor(
+        mBubbleDrawable.setBubbleColor(ApiCompatibilityUtils.getColor(
                 mContext.getResources(), R.color.default_control_color_active));
     }
 
@@ -324,34 +373,40 @@
     public void onPreLayoutChange(
             boolean positionBelow, int x, int y, int width, int height, Rect anchorRect) {
         int arrowXOffset = 0;
-        if (mDrawable.isShowingArrow()) {
+        if (mBubbleDrawable.isShowingArrow()) {
             arrowXOffset = anchorRect.centerX() - x;
 
             // Force the anchor to be in a reasonable spot w.r.t. the bubble (not over the corners).
-            int minArrowOffset = mDrawable.getArrowLeftSpacing();
-            int maxArrowOffset = width - mDrawable.getArrowRightSpacing();
+            int minArrowOffset = mBubbleDrawable.getArrowLeftSpacing();
+            int maxArrowOffset = width - mBubbleDrawable.getArrowRightSpacing();
             arrowXOffset = MathUtils.clamp(arrowXOffset, minArrowOffset, maxArrowOffset);
         }
 
         // TODO(dtrainor): Figure out how to move the arrow and bubble to make things look
         // better.
 
-        mDrawable.setPositionProperties(arrowXOffset, positionBelow);
+        mBubbleDrawable.setPositionProperties(arrowXOffset, positionBelow);
     }
 
     /**
      * @return The content view to show in the TextBubble.
      */
-    protected View createContentView() {
-        View view = LayoutInflater.from(mContext).inflate(R.layout.textbubble_text, null);
-        setText((TextView) view);
+    private View createContentView() {
+        if (mImageDrawable == null) {
+            View view = LayoutInflater.from(mContext).inflate(R.layout.textbubble_text, null);
+            setText((TextView) view);
+            return view;
+        }
+        View view =
+                LayoutInflater.from(mContext).inflate(R.layout.textbubble_text_with_image, null);
+        setText(view.findViewById(R.id.message));
         return view;
     }
 
     /**
      * @param view The {@link TextView} to set text on.
      */
-    protected void setText(TextView view) {
+    private void setText(TextView view) {
         view.setText(mIsAccessibilityEnabled ? mAccessibilityString : mString);
     }
 }
diff --git a/components/content_settings/core/common/content_settings.cc b/components/content_settings/core/common/content_settings.cc
index b76c341..6267194 100644
--- a/components/content_settings/core/common/content_settings.cc
+++ b/components/content_settings/core/common/content_settings.cc
@@ -32,7 +32,6 @@
     {ContentSettingsType::COOKIES, 0},
     {ContentSettingsType::IMAGES, 1},
     {ContentSettingsType::JAVASCRIPT, 2},
-    {ContentSettingsType::DEPRECATED_PLUGINS, 3},
     {ContentSettingsType::POPUPS, 4},
     {ContentSettingsType::GEOLOCATION, 5},
     {ContentSettingsType::NOTIFICATIONS, 6},
diff --git a/components/content_settings/core/common/content_settings_types.h b/components/content_settings/core/common/content_settings_types.h
index a7ea788..1c12667a 100644
--- a/components/content_settings/core/common/content_settings_types.h
+++ b/components/content_settings/core/common/content_settings_types.h
@@ -21,7 +21,6 @@
   COOKIES = 0,
   IMAGES,
   JAVASCRIPT,
-  DEPRECATED_PLUGINS,
 
   // This setting governs both popups and unwanted redirects like tab-unders and
   // framebusting.
diff --git a/components/federated_learning/sim_hash_unittest.cc b/components/federated_learning/sim_hash_unittest.cc
index 14bcb924..82e12f7c 100644
--- a/components/federated_learning/sim_hash_unittest.cc
+++ b/components/federated_learning/sim_hash_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "components/federated_learning/sim_hash.h"
 
+#include <cmath>
+
 #include "base/check_op.h"
 #include "base/hash/legacy_hash.h"
 #include "base/rand_util.h"
diff --git a/components/history/core/browser/url_database.cc b/components/history/core/browser/url_database.cc
index b97d796..a565494 100644
--- a/components/history/core/browser/url_database.cc
+++ b/components/history/core/browser/url_database.cc
@@ -419,7 +419,7 @@
     query_parser::MatchingAlgorithm algorithm,
     URLRows* results) {
   query_parser::QueryNodeVector query_nodes;
-  query_parser_.ParseQueryNodes(query, algorithm, &query_nodes);
+  query_parser::QueryParser::ParseQueryNodes(query, algorithm, &query_nodes);
 
   results->clear();
   sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
@@ -428,19 +428,19 @@
   while (statement.Step()) {
     query_parser::QueryWordVector query_words;
     base::string16 url = base::i18n::ToLower(statement.ColumnString16(1));
-    query_parser_.ExtractQueryWords(url, &query_words);
+    query_parser::QueryParser::ExtractQueryWords(url, &query_words);
     GURL gurl(url);
     if (gurl.is_valid()) {
       // Decode punycode to match IDN.
       base::string16 ascii = base::ASCIIToUTF16(gurl.host());
       base::string16 utf = url_formatter::IDNToUnicode(gurl.host());
       if (ascii != utf)
-        query_parser_.ExtractQueryWords(utf, &query_words);
+        query_parser::QueryParser::ExtractQueryWords(utf, &query_words);
     }
     base::string16 title = base::i18n::ToLower(statement.ColumnString16(2));
-    query_parser_.ExtractQueryWords(title, &query_words);
+    query_parser::QueryParser::ExtractQueryWords(title, &query_words);
 
-    if (query_parser_.DoesQueryMatch(query_words, query_nodes)) {
+    if (query_parser::QueryParser::DoesQueryMatch(query_words, query_nodes)) {
       URLResult info;
       FillURLRow(statement, &info);
       if (info.url().is_valid())
diff --git a/components/history/core/browser/url_database.h b/components/history/core/browser/url_database.h
index 29cab2c..8018ad6 100644
--- a/components/history/core/browser/url_database.h
+++ b/components/history/core/browser/url_database.h
@@ -317,8 +317,6 @@
   // have keyword search terms.
   bool has_keyword_search_terms_;
 
-  query_parser::QueryParser query_parser_;
-
   DISALLOW_COPY_AND_ASSIGN(URLDatabase);
 };
 
diff --git a/components/infobars/core/BUILD.gn b/components/infobars/core/BUILD.gn
index ecc1892..390c7e2 100644
--- a/components/infobars/core/BUILD.gn
+++ b/components/infobars/core/BUILD.gn
@@ -25,10 +25,7 @@
     "simple_alert_infobar_delegate.h",
   ]
 
-  public_deps = [
-    ":feature_flags",
-    "//skia",
-  ]
+  public_deps = [ "//skia" ]
 
   deps = [
     "//base",
@@ -44,15 +41,6 @@
   }
 }
 
-source_set("feature_flags") {
-  sources = [
-    "infobar_feature.cc",
-    "infobar_feature.h",
-  ]
-
-  public_deps = [ "//base" ]
-}
-
 if (is_android) {
   java_cpp_enum("infobar_generated_enums") {
     sources = [ "infobar_delegate.h" ]
diff --git a/components/infobars/core/infobar_feature.cc b/components/infobars/core/infobar_feature.cc
deleted file mode 100644
index 0553d65..0000000
--- a/components/infobars/core/infobar_feature.cc
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/infobars/core/infobar_feature.h"
-
-const base::Feature kIOSInfobarUIReboot{"InfobarUIReboot",
-                                        base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/components/infobars/core/infobar_feature.h b/components/infobars/core/infobar_feature.h
deleted file mode 100644
index 200b542..0000000
--- a/components/infobars/core/infobar_feature.h
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_INFOBARS_CORE_INFOBAR_FEATURE_H_
-#define COMPONENTS_INFOBARS_CORE_INFOBAR_FEATURE_H_
-
-#include "base/feature_list.h"
-
-// Feature to choose whether to use the new Messages Infobar design on iOS, or
-// the legacy one.
-extern const base::Feature kIOSInfobarUIReboot;
-
-#endif  // COMPONENTS_INFOBARS_CORE_INFOBAR_FEATURE_H_
diff --git a/components/metrics/stability_metrics_helper.cc b/components/metrics/stability_metrics_helper.cc
index ee65a24..0f01929b 100644
--- a/components/metrics/stability_metrics_helper.cc
+++ b/components/metrics/stability_metrics_helper.cc
@@ -340,6 +340,16 @@
     StabilityEventType stability_event_type) {
   UMA_STABILITY_HISTOGRAM_ENUMERATION("Stability.Counts2",
                                       stability_event_type);
+  // TODO(crbug.com/1176977): Remove temporary debugging histograms below.
+  // Like UmaHistogramSparse(), but with kUmaStabilityHistogramFlag.
+  base::SparseHistogram::FactoryGet(
+      "Stability.Experimental.Counts2",
+      base::HistogramBase::kUmaStabilityHistogramFlag)
+      ->Add(static_cast<int>(stability_event_type));
+  if (stability_event_type == StabilityEventType::kBrowserCrash) {
+    UMA_STABILITY_HISTOGRAM_BOOLEAN("Stability.Experimental.BrowserCrash",
+                                    true);
+  }
 }
 
 }  // namespace metrics
diff --git a/components/openscreen_platform/udp_socket.cc b/components/openscreen_platform/udp_socket.cc
index 01aadb7..ab9aac5 100644
--- a/components/openscreen_platform/udp_socket.cc
+++ b/components/openscreen_platform/udp_socket.cc
@@ -176,7 +176,6 @@
                                  net::ErrorToString(result)));
     return;
   }
-  client_->OnBound(this);
 
   // This is an approximate value for number of packets, and may need to be
   // adjusted when we have real world data.
@@ -190,6 +189,7 @@
       listener_.Bind(std::move(pending_listener_));
     }
   }
+  client_->OnBound(this);
 }
 
 void UdpSocket::JoinGroupCallback(int32_t result) {
diff --git a/components/optimization_guide/core/optimization_guide_util.cc b/components/optimization_guide/core/optimization_guide_util.cc
index 744319b..45a1cd8 100644
--- a/components/optimization_guide/core/optimization_guide_util.cc
+++ b/components/optimization_guide/core/optimization_guide_util.cc
@@ -18,12 +18,14 @@
 std::string GetStringNameForOptimizationTarget(
     optimization_guide::proto::OptimizationTarget optimization_target) {
   switch (optimization_target) {
-    case optimization_guide::proto::OPTIMIZATION_TARGET_UNKNOWN:
+    case proto::OPTIMIZATION_TARGET_UNKNOWN:
       return "Unknown";
-    case optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD:
+    case proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD:
       return "PainfulPageLoad";
-    case optimization_guide::proto::OPTIMIZATION_TARGET_LANGUAGE_DETECTION:
+    case proto::OPTIMIZATION_TARGET_LANGUAGE_DETECTION:
       return "LanguageDetection";
+    case proto::OPTIMIZATION_TARGET_PAGE_TOPICS:
+      return "PageTopics";
   }
   NOTREACHED();
   return std::string();
diff --git a/components/optimization_guide/proto/models.proto b/components/optimization_guide/proto/models.proto
index 323dfe3b..aff071e 100644
--- a/components/optimization_guide/proto/models.proto
+++ b/components/optimization_guide/proto/models.proto
@@ -228,6 +228,8 @@
   OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD = 1;
   // Target for supplying the language detection model via the model downloader.
   OPTIMIZATION_TARGET_LANGUAGE_DETECTION = 2;
+  // Target for determining topics present on a page.
+  OPTIMIZATION_TARGET_PAGE_TOPICS = 3;
 }
 
 // The features that only the client can compute during prediction and also
diff --git a/components/page_info/page_info.cc b/components/page_info/page_info.cc
index 6ca97ea..ecb23f3 100644
--- a/components/page_info/page_info.cc
+++ b/components/page_info/page_info.cc
@@ -841,8 +841,7 @@
           subject_name));
     }
 
-    if (visible_security_state.connection_used_legacy_tls &&
-        !visible_security_state.should_suppress_legacy_tls_warning) {
+    if (visible_security_state.connection_used_legacy_tls) {
       site_connection_status_ = SITE_CONNECTION_STATUS_LEGACY_TLS;
     }
 
diff --git a/components/query_parser/query_parser.cc b/components/query_parser/query_parser.cc
index a84d543a..f151472 100644
--- a/components/query_parser/query_parser.cc
+++ b/components/query_parser/query_parser.cc
@@ -319,8 +319,6 @@
   return MatchesAll(words, &first_word, &last_word);
 }
 
-QueryParser::QueryParser() {}
-
 // static
 bool QueryParser::IsWordLongEnoughForPrefixSearch(
     const base::string16& word, MatchingAlgorithm matching_algorithm) {
@@ -337,6 +335,7 @@
   return word.size() >= minimum_length;
 }
 
+// static
 int QueryParser::ParseQuery(const base::string16& query,
                             MatchingAlgorithm matching_algorithm,
                             base::string16* sqlite_query) {
@@ -346,6 +345,7 @@
   return root.AppendToSQLiteQuery(sqlite_query);
 }
 
+// static
 void QueryParser::ParseQueryWords(const base::string16& query,
                                   MatchingAlgorithm matching_algorithm,
                                   std::vector<base::string16>* words) {
@@ -355,6 +355,7 @@
   root.AppendWords(words);
 }
 
+// static
 void QueryParser::ParseQueryNodes(const base::string16& query,
                                   MatchingAlgorithm matching_algorithm,
                                   QueryNodeVector* nodes) {
@@ -363,6 +364,7 @@
     nodes->swap(*root.children());
 }
 
+// static
 bool QueryParser::DoesQueryMatch(const base::string16& text,
                                  const QueryNodeVector& query_nodes,
                                  Snippet::MatchPositions* match_positions) {
@@ -394,6 +396,7 @@
   return true;
 }
 
+// static
 bool QueryParser::DoesQueryMatch(const QueryWordVector& query_words,
                                  const QueryNodeVector& query_nodes) {
   if (query_nodes.empty() || query_words.empty())
@@ -406,6 +409,7 @@
   return true;
 }
 
+// static
 bool QueryParser::ParseQueryImpl(const base::string16& query,
                                  MatchingAlgorithm matching_algorithm,
                                  QueryNodeList* root) {
@@ -451,6 +455,7 @@
   return true;
 }
 
+// static
 void QueryParser::ExtractQueryWords(const base::string16& text,
                                     QueryWordVector* words) {
   base::i18n::BreakIterator iter(text, base::i18n::BreakIterator::BREAK_WORD);
diff --git a/components/query_parser/query_parser.h b/components/query_parser/query_parser.h
index a48973c..9db7a53 100644
--- a/components/query_parser/query_parser.h
+++ b/components/query_parser/query_parser.h
@@ -20,7 +20,7 @@
 
 // Used by HasMatchIn.
 struct QueryWord {
-  // The work to match against.
+  // The word to match against.
   base::string16 word;
 
   // The starting position of the word in the original text.
@@ -75,7 +75,8 @@
 // normalized queries that can be passed to the SQLite backend.
 class QueryParser {
  public:
-  QueryParser();
+  QueryParser() = delete;
+  ~QueryParser() = delete;
 
   // For CJK ideographs and Korean Hangul, even a single character
   // can be useful in prefix matching, but that may give us too many
@@ -90,40 +91,40 @@
 
   // Parse a query into a SQLite query. The resulting query is placed in
   // |sqlite_query| and the number of words is returned.
-  int ParseQuery(const base::string16& query,
-                 MatchingAlgorithm matching_algorithm,
-                 base::string16* sqlite_query);
+  static int ParseQuery(const base::string16& query,
+                        MatchingAlgorithm matching_algorithm,
+                        base::string16* sqlite_query);
 
   // Parses |query|, returning the words that make up it. Any words in quotes
   // are put in |words| without the quotes. For example, the query text
   // "foo bar" results in two entries being added to words, one for foo and one
   // for bar.
-  void ParseQueryWords(const base::string16& query,
-                       MatchingAlgorithm matching_algorithm,
-                       std::vector<base::string16>* words);
+  static void ParseQueryWords(const base::string16& query,
+                              MatchingAlgorithm matching_algorithm,
+                              std::vector<base::string16>* words);
 
   // Parses |query|, returning the nodes that constitute the valid words in the
   // query. This is intended for later usage with DoesQueryMatch. Ownership of
   // the nodes passes to the caller.
-  void ParseQueryNodes(const base::string16& query,
-                       MatchingAlgorithm matching_algorithm,
-                       QueryNodeVector* nodes);
+  static void ParseQueryNodes(const base::string16& query,
+                              MatchingAlgorithm matching_algorithm,
+                              QueryNodeVector* nodes);
 
   // Returns true if the string text matches the query nodes created by a call
   // to ParseQuery. If the query does match, each of the matching positions in
   // the text is added to |match_positions|.
-  bool DoesQueryMatch(const base::string16& text,
-                      const QueryNodeVector& nodes,
-                      Snippet::MatchPositions* match_positions);
+  static bool DoesQueryMatch(const base::string16& text,
+                             const QueryNodeVector& nodes,
+                             Snippet::MatchPositions* match_positions);
 
   // Returns true if all of the |words| match the query |nodes| created by a
   // call to ParseQuery.
-  bool DoesQueryMatch(const QueryWordVector& words,
-                      const QueryNodeVector& nodes);
+  static bool DoesQueryMatch(const QueryWordVector& words,
+                             const QueryNodeVector& nodes);
 
   // Extracts the words from |text|, placing each word into |words|.
-  void ExtractQueryWords(const base::string16& text,
-                         QueryWordVector* words);
+  static void ExtractQueryWords(const base::string16& text,
+                                QueryWordVector* words);
 
   // Sorts the match positions in |matches| by their first index, then
   // coalesces any match positions that intersect each other.
@@ -132,9 +133,9 @@
  private:
   // Does the work of parsing |query|; creates nodes in |root| as appropriate.
   // This is invoked from both of the ParseQuery methods.
-  bool ParseQueryImpl(const base::string16& query,
-                      MatchingAlgorithm matching_algorithm,
-                      QueryNodeList* root);
+  static bool ParseQueryImpl(const base::string16& query,
+                             MatchingAlgorithm matching_algorithm,
+                             QueryNodeList* root);
 
   DISALLOW_COPY_AND_ASSIGN(QueryParser);
 };
diff --git a/components/query_parser/query_parser_fuzzer.cc b/components/query_parser/query_parser_fuzzer.cc
index d1787f7b..1279e26 100644
--- a/components/query_parser/query_parser_fuzzer.cc
+++ b/components/query_parser/query_parser_fuzzer.cc
@@ -29,9 +29,8 @@
   const base::string16 query16 = base::UTF8ToUTF16(
       data_provider.ConsumeBytesAsString(data_provider.remaining_bytes()));
 
-  query_parser::QueryParser parser;
   std::vector<base::string16> words;
-  parser.ParseQueryWords(query16, matching_alg, &words);
+  query_parser::QueryParser::ParseQueryWords(query16, matching_alg, &words);
 
   return 0;
 }
diff --git a/components/query_parser/query_parser_unittest.cc b/components/query_parser/query_parser_unittest.cc
index b64e2b5af..dd90a31 100644
--- a/components/query_parser/query_parser_unittest.cc
+++ b/components/query_parser/query_parser_unittest.cc
@@ -19,18 +19,14 @@
   };
 
   std::string QueryToString(const std::string& query);
-
- protected:
-  QueryParser query_parser_;
 };
 
 // Test helper: Convert a user query string in 8-bit (for hardcoding
 // convenience) to a SQLite query string.
 std::string QueryParserTest::QueryToString(const std::string& query) {
   base::string16 sqlite_query;
-  query_parser_.ParseQuery(base::UTF8ToUTF16(query),
-                           MatchingAlgorithm::DEFAULT,
-                           &sqlite_query);
+  QueryParser::ParseQuery(base::UTF8ToUTF16(query), MatchingAlgorithm::DEFAULT,
+                          &sqlite_query);
   return base::UTF16ToUTF8(sqlite_query);
 }
 
@@ -86,10 +82,10 @@
 
   for (size_t i = 0; i < base::size(data); ++i) {
     base::string16 query_string;
-    EXPECT_EQ(data[i].expected_word_count,
-              query_parser_.ParseQuery(base::UTF8ToUTF16(data[i].input),
-                                       MatchingAlgorithm::DEFAULT,
-                                       &query_string));
+    EXPECT_EQ(
+        data[i].expected_word_count,
+        QueryParser::ParseQuery(base::UTF8ToUTF16(data[i].input),
+                                MatchingAlgorithm::DEFAULT, &query_string));
   }
 }
 
@@ -121,14 +117,13 @@
     { "foo blah",      "\"foo bar blah\"", true,  1, 4, 9, 13 },
   };
   for (size_t i = 0; i < base::size(data); ++i) {
-    QueryParser parser;
     query_parser::QueryNodeVector query_nodes;
-    parser.ParseQueryNodes(base::UTF8ToUTF16(data[i].query),
-                           MatchingAlgorithm::DEFAULT, &query_nodes);
+    QueryParser::ParseQueryNodes(base::UTF8ToUTF16(data[i].query),
+                                 MatchingAlgorithm::DEFAULT, &query_nodes);
     Snippet::MatchPositions match_positions;
     ASSERT_EQ(data[i].matches,
-              parser.DoesQueryMatch(base::UTF8ToUTF16(data[i].text),
-                                    query_nodes, &match_positions));
+              QueryParser::DoesQueryMatch(base::UTF8ToUTF16(data[i].text),
+                                          query_nodes, &match_positions));
     size_t offset = 0;
     if (data[i].m1_start != 0 || data[i].m1_end != 0) {
       ASSERT_TRUE(match_positions.size() >= 1);
@@ -159,10 +154,8 @@
   };
   for (size_t i = 0; i < base::size(data); ++i) {
     std::vector<base::string16> results;
-    QueryParser parser;
-    parser.ParseQueryWords(base::UTF8ToUTF16(data[i].text),
-                           MatchingAlgorithm::DEFAULT,
-                           &results);
+    QueryParser::ParseQueryWords(base::UTF8ToUTF16(data[i].text),
+                                 MatchingAlgorithm::DEFAULT, &results);
     ASSERT_EQ(data[i].word_count, results.size());
     EXPECT_EQ(data[i].w1, base::UTF16ToUTF8(results[0]));
     if (results.size() == 2)
diff --git a/components/security_state/core/security_state.cc b/components/security_state/core/security_state.cc
index 63751af..12c83c08 100644
--- a/components/security_state/core/security_state.cc
+++ b/components/security_state/core/security_state.cc
@@ -166,8 +166,7 @@
   // Downgrade the security level for pages loaded over legacy TLS versions.
   if (base::FeatureList::IsEnabled(
           security_state::features::kLegacyTLSWarnings) &&
-      visible_security_state.connection_used_legacy_tls &&
-      !visible_security_state.should_suppress_legacy_tls_warning) {
+      visible_security_state.connection_used_legacy_tls) {
     return WARNING;
   }
 
@@ -246,7 +245,6 @@
       is_devtools(false),
       is_reader_mode(false),
       connection_used_legacy_tls(false),
-      should_suppress_legacy_tls_warning(false),
       should_treat_displayed_mixed_forms_as_secure(false) {}
 
 VisibleSecurityState::VisibleSecurityState(const VisibleSecurityState& other) =
@@ -282,14 +280,13 @@
 
 bool GetLegacyTLSWarningStatus(
     const VisibleSecurityState& visible_security_state) {
-  return visible_security_state.connection_used_legacy_tls &&
-         !visible_security_state.should_suppress_legacy_tls_warning;
+  return visible_security_state.connection_used_legacy_tls;
 }
 
 std::string GetLegacyTLSHistogramName(
     const std::string& prefix,
     const VisibleSecurityState& visible_security_state) {
-  if (GetLegacyTLSWarningStatus(visible_security_state)) {
+  if (visible_security_state.connection_used_legacy_tls) {
     return prefix + "." + "LegacyTLS_Triggered";
   } else {
     return prefix + "." + "LegacyTLS_NotTriggered";
diff --git a/components/security_state/core/security_state.h b/components/security_state/core/security_state.h
index 10e52674..d00cdef 100644
--- a/components/security_state/core/security_state.h
+++ b/components/security_state/core/security_state.h
@@ -201,9 +201,6 @@
   bool is_reader_mode;
   // True if the page was loaded over a legacy TLS version.
   bool connection_used_legacy_tls;
-  // True if the page should be excluded from a UI treatment for legacy TLS
-  // (used for control group in an experimental UI rollout).
-  bool should_suppress_legacy_tls_warning;
   // True if mixed forms should be treated as secure from the visible security
   // state perspective (for example, if a different warning is being shown for
   // them).
@@ -252,8 +249,7 @@
                                       SafetyTipStatus safety_tip_status);
 
 // Returns whether the given VisibleSecurityState would trigger a legacy TLS
-// warning (i.e., uses legacy TLS and isn't in the control group), if the user
-// were in the appropriate field trial.
+// warning.
 bool GetLegacyTLSWarningStatus(
     const VisibleSecurityState& visible_security_state);
 
diff --git a/components/security_state/core/security_state_unittest.cc b/components/security_state/core/security_state_unittest.cc
index aa6d3a5..b2b832b 100644
--- a/components/security_state/core/security_state_unittest.cc
+++ b/components/security_state/core/security_state_unittest.cc
@@ -388,19 +388,14 @@
 TEST(SecurityStateTest, LegacyTLSWarningStatus) {
   const struct {
     bool connection_used_legacy_tls;
-    bool should_suppress_legacy_tls_warning;
     bool expected_legacy_tls_warning_status;
   } kTestCases[] = {
-      {true, false, true},
-      {true, true, false},
-      {false, false, false},
-      {false, true, false},
+      {true, true},
+      {false, false},
   };
   for (auto testcase : kTestCases) {
     auto state = VisibleSecurityState();
     state.connection_used_legacy_tls = testcase.connection_used_legacy_tls;
-    state.should_suppress_legacy_tls_warning =
-        testcase.should_suppress_legacy_tls_warning;
     EXPECT_EQ(testcase.expected_legacy_tls_warning_status,
               GetLegacyTLSWarningStatus(state));
   }
diff --git a/components/viz/README.md b/components/viz/README.md
index 2d3469e..61e8742 100644
--- a/components/viz/README.md
+++ b/components/viz/README.md
@@ -160,6 +160,27 @@
 | viz/service/surfaces/         |
 | viz/service/transitions/      |
 
+#### service/frame_sinks/video_capture
+**FrameSinkVideoCaptureImpl**: This component implements the [Mojo
+interfaces](../../services/viz/privileged/mojom/compositing/frame_sink_video_capture.mojom)
+to capture frames that are sent to the compositing service, producing a stream
+of video frames. It provides a capture pipeline where asynchronous GPU readback
+is executed (via [CopyOutputRequests](common/frame_sinks/README.md), and then
+CopyOutputResults are applied to a pool of shared memory buffers backing video
+frames that are sent to privileged consumers.
+
+A capturer instance is created via [FrameSinkManager's
+CreateVideoCapturer()](../../services/viz/privileged/mojom/compositing/frame_sink_manager.mojom)
+method.
+
+| Can depend on:                |
+|:------------------------------|
+| media/base/*                  |
+| media/capture/*               |
+| media/mojo/*                  |
+| viz/common/*                  |
+| viz/service/frame_sinks/*     |
+
 #### service/gl
 **GL**: This component implements the Mojo interfaces for allocating (and
 deallocating) gpu memory buffers, setting up a channel for the command buffer,
diff --git a/components/viz/common/frame_sinks/README.md b/components/viz/common/frame_sinks/README.md
new file mode 100644
index 0000000..6a336ec
--- /dev/null
+++ b/components/viz/common/frame_sinks/README.md
@@ -0,0 +1,131 @@
+
+# [Supplemental Documentation: CopyOutputRequests](https://chromium.googlesource.com/chromium/src.git/+/refs/heads/master/components/viz/common/frame_sinks/README.md)
+
+The [CopyOutputRequest](./copy_output_request.h) and
+[CopyOutputResult](./copy_output_result.h) headers include rather detailed usage
+information. However, that low-level description of a rather complex jumble of
+"knobs" alone is insufficient. Developers need to understand the context
+surrounding a CopyOutputRequest's use cases. The following information provides
+design rationale and explains the intended usage and behaviors of
+CopyOutputRequests.
+
+## Goals
+
+ * Minimize cost of making copies (GPU/CPU/memory utilization) by providing
+   features that reduce the volume of data being processed and copied.
+
+ * Incorporate high-quality scaling of the source.
+
+ * Provide image format conversion within the GPU (currently, RGBA and
+   I420/YUV).
+
+ * Zero-copy interface for GPU readback and result access.
+
+ * Support for [FrameSink Video
+   Capture](../../service/frame_sinks/video_capture/) in VIZ.
+
+The following diagram depicts an old, "big-stick" CopyOutputRequest processing
+path, as well as the new, current implementation for comparison:
+
+![conceptual diagram](conceptual_diagram.png)
+
+## Controls and Behaviors
+
+With the scaling and image format conversion steps integrated into the
+CopyOutputRequest processing, a client would provide the following request
+properties:
+
+ * A source selection region is made, in terms of the Surface's (or Layer's)
+   coordinate system, using the `set_area()` method. The source selection
+   defaults to the entire surface of (0,0)x[surfacewidth, surfaceheight], but
+   it may also be set to any subregion within the surface.
+
+ * A result scaling ratio that defines the scaling in both the horizontal and
+   vertical directions, in terms of "from S source pixels to T result pixels."
+   By default, there is no scaling (the scaling ratio is 1:1). The scale ratios
+   are specified to the `SetScaleRatio()` method as pairs of integers (S
+   width-by-height and T width-by-height). **Design note:** Floating-point
+   values were explicitly not chosen to represent the scale ratio because the
+   "patching" of damaged regions demands that request parameters are not
+   "fuzzed" by insufficient precision or rounding error effects (users would see
+   visible artifacts).
+
+ * The result selection (in terms of the scaled output space) is specified using
+   the `set_result_selection()` method. The valid region of the result selection
+   is the rect (0,0)x[⌈source_selwidth * scalex⌉, ⌈source_selheight * scaley⌉].
+   Note that all coordinates are constrained to be integer values, to avoid
+   introducing alignment, rounding or other "fuzz" issues.
+
+ * Result format: An RGBA-interleaved bitmap (SkBitmap) or I420 Y+U+V image
+   planes.
+
+For efficient video capture, the above are used as follows: An issuer of
+CopyOutputRequests "locks into" a target area within the Surface (usually the
+whole Surface) and "locks into" a scale factor (the ratio between target area
+and video frame size); but then varies the result selection of pixels that is
+going to be produced for each successive video frame. For the first video frame,
+all of the output must be produce (result selection in the request is set to the
+entire valid region in the output space). Then, for successive video frames,
+only the output pixels that are affected by changes in the source Surface need
+to be produced. In other words, the result selection specifies the region of
+pixels in the prior video frame that needs to be "patched" to produce a whole
+up-to-date video frame.
+
+## "Patching" CopyOutputRequests Example (e.g., for efficient video)
+
+It is helpful to walk through an example sequence of CopyOutputRequests when
+executing video capture, to better understand how everything comes together. In
+this example, let's assume a source Surface is 800x450 in size, and the output
+(for a video frame) is of size 640x360. Then, for the very first frame, all of
+the source content needs to be copied+scaled and placed into the first video
+frame. To do this, the source region in the request would be set to the rect
+(0,0)x[800,450], the result selection would be set to the rect (0,0)x[640,360],
+and the scaling ratio would be 800:640 (this ratio is automatically reduced to
+the equivalent 5:4 internally).
+
+Then, some time goes by and the source Surface content changes (a.k.a. "takes
+damage") in the rect (77,77)x[401,200]. This will cause the video capture
+control logic to trigger a "patching" CopyOutputRequest, in order to efficiently
+acquire just the changed pixels. The source damage rect maps to the result rect
+(61.6,61.6)x[320.8,160] in the output space of the video frame. However, since
+partial pixels cannot be generated, this result selection rect must be expanded
+to whole pixels in all directions: (61,61)x[322,161]. Thus, the second
+CopyOutputRequest will have the same source selection rect as the first and the
+same scaling ratio as the first, but with the result selection set to
+(61,61)x[322,161]. When the CopyOutputResult is produced, its pixels will be
+applied as a patch on top of the prior video frame, in order to produce the next
+video frame.
+
+Note that it is important to keep the source selection and scale ratio the same
+in all "patching" CopyOutputRequests. If, instead, the source selection is
+changed to Surface's damage rect (i.e., **not** using `set_result_selection()`),
+the resulting scaled output pixels (the patch) wouldn't line up exactly with the
+pixel boundaries in the prior video frame:
+
+![Patching Source Changes into a Video Frame](patching_region.png)
+
+Finally, it's worth noting that whenever the source selection (offset or size)
+changes, or the scale ratio changes; a full "refresh CopyOutputRequest"
+consisting of all the source→output pixels must be made before any successive
+"patching only" requests can be made.
+
+Further background: [Design Doc: Dynamic Screen Capture in Chromium](https://docs.google.com/document/d/1YNYaP22fepgP_MXrFXytbo0LOyx29mdCKAsQ2TZAckg/edit?usp=sharing)
+
+*NOTE:* The implementation of CopyOutputRequests is migrating to
+[SkiaRenderer](../../service/display/skia_renderer.h) on all platforms (as of
+M90 this is complete for Windows, Linux and Android).  This
+[implementation](../../service/display_embedder/skia_output_surface_impl_on_gpu.cc)
+computes the scale ratio differently from the previous implementation in
+`GLRenderer`, which may result in a different alignment of the patch.
+
+The `SkiaRenderer` API takes an integer `source_rect` to sample from and integer
+destination size for the output. The ratio between that `source_rect` and
+destination size might not be exactly the same as the scale factor specified by
+the CopyOutputRequest due to integer rounding.
+
+In the example above, the `SkiaRenderer` copies a source region of
+(76,76)x[403,202] to patch a result rect of (61,61)x[322,161], resulting in an
+effective scale of (0.799,0.797) instead of (0.8,0.8).
+
+In practice this does not seem to impact quality significantly; see 
+[crbug.com/1055939](https://crbug.com/1055939).
diff --git a/components/viz/common/frame_sinks/conceptual_diagram.png b/components/viz/common/frame_sinks/conceptual_diagram.png
new file mode 100644
index 0000000..e18b37c
--- /dev/null
+++ b/components/viz/common/frame_sinks/conceptual_diagram.png
Binary files differ
diff --git a/components/viz/common/frame_sinks/patching_region.png b/components/viz/common/frame_sinks/patching_region.png
new file mode 100644
index 0000000..75a5b9c
--- /dev/null
+++ b/components/viz/common/frame_sinks/patching_region.png
Binary files differ
diff --git a/components/viz/service/display/display_resource_provider.cc b/components/viz/service/display/display_resource_provider.cc
index 454e5c3..945325716 100644
--- a/components/viz/service/display/display_resource_provider.cc
+++ b/components/viz/service/display/display_resource_provider.cc
@@ -51,31 +51,6 @@
   gpu::ScopedAllowScheduleGpuTask allow_gpu_;
 };
 
-class ScopedSetActiveTexture {
- public:
-  ScopedSetActiveTexture(GLES2Interface* gl, GLenum unit)
-      : gl_(gl), unit_(unit) {
-#if DCHECK_IS_ON()
-    GLint active_unit = 0;
-    gl->GetIntegerv(GL_ACTIVE_TEXTURE, &active_unit);
-    DCHECK_EQ(GL_TEXTURE0, active_unit);
-#endif
-
-    if (unit_ != GL_TEXTURE0)
-      gl_->ActiveTexture(unit_);
-  }
-
-  ~ScopedSetActiveTexture() {
-    // Active unit being GL_TEXTURE0 is effectively the ground state.
-    if (unit_ != GL_TEXTURE0)
-      gl_->ActiveTexture(GL_TEXTURE0);
-  }
-
- private:
-  GLES2Interface* gl_;
-  GLenum unit_;
-};
-
 DisplayResourceProvider::DisplayResourceProvider(
     Mode mode,
     ContextProvider* compositor_context_provider,
@@ -237,10 +212,6 @@
   return GetResource(id)->transferable.is_software;
 }
 
-GLenum DisplayResourceProvider::GetResourceTextureTarget(ResourceId id) {
-  return GetResource(id)->transferable.mailbox_holder.texture_target;
-}
-
 gfx::BufferFormat DisplayResourceProvider::GetBufferFormat(ResourceId id) {
   return BufferFormat(GetResourceFormat(id));
 }
@@ -255,21 +226,6 @@
   return resource->transferable.color_space;
 }
 
-void DisplayResourceProvider::WaitSyncToken(ResourceId id) {
-  ChildResource* resource = TryGetResource(id);
-  // TODO(ericrk): We should never fail TryGetResource, but we appear to
-  // be doing so on Android in rare cases. Handle this gracefully until a
-  // better solution can be found. https://crbug.com/811858
-  if (!resource)
-    return;
-  WaitSyncTokenInternal(resource);
-
-#if defined(OS_ANDROID)
-  // Now that the resource is synced, we may send it a promotion hint.
-  InitializePromotionHintRequest(id);
-#endif
-}
-
 int DisplayResourceProvider::CreateChild(ReturnCallback return_callback) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
@@ -390,18 +346,6 @@
   return &it->second;
 }
 
-void DisplayResourceProvider::PopulateSkBitmapWithResource(
-    SkBitmap* sk_bitmap,
-    const ChildResource* resource) {
-  DCHECK(IsBitmapFormatSupported(resource->transferable.format));
-  SkImageInfo info =
-      SkImageInfo::MakeN32Premul(resource->transferable.size.width(),
-                                 resource->transferable.size.height());
-  bool pixels_installed = sk_bitmap->installPixels(
-      info, resource->shared_bitmap->pixels(), info.minRowBytes());
-  DCHECK(pixels_installed);
-}
-
 void DisplayResourceProvider::DeleteResourceInternal(ResourceMap::iterator it) {
   TRACE_EVENT0("viz", "DisplayResourceProvider::DeleteResourceInternal");
   ChildResource* resource = &it->second;
@@ -415,20 +359,6 @@
   resources_.erase(it);
 }
 
-void DisplayResourceProvider::WaitSyncTokenInternal(ChildResource* resource) {
-  DCHECK(resource);
-  if (!resource->ShouldWaitSyncToken())
-    return;
-  GLES2Interface* gl = ContextGL();
-  DCHECK(gl);
-  // In the case of context lost, this sync token may be empty (see comment in
-  // the UpdateSyncToken() function). The WaitSyncTokenCHROMIUM() function
-  // handles empty sync tokens properly so just wait anyways and update the
-  // state the synchronized.
-  gl->WaitSyncTokenCHROMIUM(resource->sync_token().GetConstData());
-  resource->SetSynchronized();
-}
-
 GLES2Interface* DisplayResourceProvider::ContextGL() const {
   ContextProvider* context_provider = compositor_context_provider_;
   return context_provider ? context_provider->ContextGL() : nullptr;
@@ -550,34 +480,6 @@
   }
 }
 
-GLenum DisplayResourceProvider::BindForSampling(ResourceId resource_id,
-                                                GLenum unit,
-                                                GLenum filter) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  GLES2Interface* gl = ContextGL();
-  auto it = resources_.find(resource_id);
-  // TODO(ericrk): We should never fail to find resource_id, but we appear to
-  // be doing so on Android in rare cases. Handle this gracefully until a
-  // better solution can be found. https://crbug.com/811858
-  if (it == resources_.end())
-    return GL_TEXTURE_2D;
-
-  ChildResource* resource = &it->second;
-  DCHECK(resource->lock_for_read_count);
-
-  ScopedSetActiveTexture scoped_active_tex(gl, unit);
-  GLenum target = resource->transferable.mailbox_holder.texture_target;
-  gl->BindTexture(target, resource->gl_id);
-
-  // Texture parameters can be modified by concurrent reads so reset them
-  // before binding the texture. See https://crbug.com/1092080.
-  gl->TexParameteri(target, GL_TEXTURE_MIN_FILTER, filter);
-  gl->TexParameteri(target, GL_TEXTURE_MAG_FILTER, filter);
-  resource->filter = filter;
-
-  return target;
-}
-
 bool DisplayResourceProvider::ReadLockFenceHasPassed(
     const ChildResource* resource) {
   return !resource->read_lock_fence || resource->read_lock_fence->HasPassed();
@@ -807,122 +709,6 @@
   }
 }
 
-DisplayResourceProvider::ScopedReadLockGL::ScopedReadLockGL(
-    DisplayResourceProvider* resource_provider,
-    ResourceId resource_id)
-    : resource_provider_(resource_provider), resource_id_(resource_id) {
-  const ChildResource* resource =
-      resource_provider->LockForRead(resource_id, false /* overlay_only */);
-  // TODO(ericrk): We should never fail LockForRead, but we appear to be
-  // doing so on Android in rare cases. Handle this gracefully until a better
-  // solution can be found. https://crbug.com/811858
-  if (!resource)
-    return;
-
-  texture_id_ = resource->gl_id;
-  target_ = resource->transferable.mailbox_holder.texture_target;
-  size_ = resource->transferable.size;
-  color_space_ = resource->transferable.color_space;
-}
-
-DisplayResourceProvider::ScopedReadLockGL::~ScopedReadLockGL() {
-  resource_provider_->UnlockForRead(resource_id_, false /* overlay_only */);
-}
-
-DisplayResourceProvider::ScopedOverlayLockGL::ScopedOverlayLockGL(
-    DisplayResourceProvider* resource_provider,
-    ResourceId resource_id)
-    : resource_provider_(resource_provider), resource_id_(resource_id) {
-  const ChildResource* resource =
-      resource_provider->LockForRead(resource_id, true /* overlay_only */);
-  if (!resource)
-    return;
-
-  texture_id_ = resource->gl_id;
-}
-
-DisplayResourceProvider::ScopedOverlayLockGL::~ScopedOverlayLockGL() {
-  resource_provider_->UnlockForRead(resource_id_, true /* overlay_only */);
-}
-
-DisplayResourceProvider::ScopedSamplerGL::ScopedSamplerGL(
-    DisplayResourceProvider* resource_provider,
-    ResourceId resource_id,
-    GLenum filter)
-    : resource_lock_(resource_provider, resource_id),
-      unit_(GL_TEXTURE0),
-      target_(resource_provider->BindForSampling(resource_id, unit_, filter)) {}
-
-DisplayResourceProvider::ScopedSamplerGL::ScopedSamplerGL(
-    DisplayResourceProvider* resource_provider,
-    ResourceId resource_id,
-    GLenum unit,
-    GLenum filter)
-    : resource_lock_(resource_provider, resource_id),
-      unit_(unit),
-      target_(resource_provider->BindForSampling(resource_id, unit_, filter)) {}
-
-DisplayResourceProvider::ScopedSamplerGL::~ScopedSamplerGL() = default;
-
-DisplayResourceProvider::ScopedReadLockSkImage::ScopedReadLockSkImage(
-    DisplayResourceProvider* resource_provider,
-    ResourceId resource_id,
-    SkAlphaType alpha_type,
-    GrSurfaceOrigin origin)
-    : resource_provider_(resource_provider), resource_id_(resource_id) {
-  const ChildResource* resource =
-      resource_provider->LockForRead(resource_id, false /* overlay_only */);
-  DCHECK(resource);
-
-  // Use cached SkImage if possible.
-  auto it = resource_provider_->resource_sk_images_.find(resource_id);
-  if (it != resource_provider_->resource_sk_images_.end()) {
-    sk_image_ = it->second;
-    return;
-  }
-
-  if (resource->is_gpu_resource_type()) {
-    DCHECK(resource->gl_id);
-    GrGLTextureInfo texture_info;
-    texture_info.fID = resource->gl_id;
-    texture_info.fTarget = resource->transferable.mailbox_holder.texture_target;
-    texture_info.fFormat = TextureStorageFormat(resource->transferable.format);
-    GrBackendTexture backend_texture(resource->transferable.size.width(),
-                                     resource->transferable.size.height(),
-                                     GrMipMapped::kNo, texture_info);
-    sk_image_ = SkImage::MakeFromTexture(
-        resource_provider->compositor_context_provider_->GrContext(),
-        backend_texture, origin,
-        ResourceFormatToClosestSkColorType(!resource_provider->IsSoftware(),
-                                           resource->transferable.format),
-        alpha_type, resource->transferable.color_space.ToSkColorSpace());
-    return;
-  }
-
-  if (!resource->shared_bitmap) {
-    // If a CompositorFrameSink is destroyed, it destroys all SharedBitmapIds
-    // that it registered. In this case, a CompositorFrame can be drawn with
-    // SharedBitmapIds that are not known in the viz service. As well, a
-    // misbehaved client can use SharedBitampIds that it did not report to
-    // the service. Then the |shared_bitmap| will be null, and this read lock
-    // will not be valid. Software-compositing users of this read lock must
-    // check for valid() to deal with this scenario.
-    sk_image_ = nullptr;
-    return;
-  }
-
-  DCHECK(origin == kTopLeft_GrSurfaceOrigin);
-  SkBitmap sk_bitmap;
-  resource_provider->PopulateSkBitmapWithResource(&sk_bitmap, resource);
-  sk_bitmap.setImmutable();
-  sk_image_ = SkImage::MakeFromBitmap(sk_bitmap);
-  resource_provider_->resource_sk_images_[resource_id] = sk_image_;
-}
-
-DisplayResourceProvider::ScopedReadLockSkImage::~ScopedReadLockSkImage() {
-  resource_provider_->UnlockForRead(resource_id_, false /* overlay_only */);
-}
-
 DisplayResourceProvider::ScopedReadLockSharedImage::ScopedReadLockSharedImage(
     DisplayResourceProvider* resource_provider,
     ResourceId resource_id)
@@ -972,89 +758,6 @@
   resource_ = nullptr;
 }
 
-DisplayResourceProvider::LockSetForExternalUse::LockSetForExternalUse(
-    DisplayResourceProvider* resource_provider,
-    ExternalUseClient* client)
-    : resource_provider_(resource_provider) {
-  DCHECK(!resource_provider_->external_use_client_);
-  resource_provider_->external_use_client_ = client;
-}
-
-DisplayResourceProvider::LockSetForExternalUse::~LockSetForExternalUse() {
-  DCHECK(resources_.empty());
-}
-
-ExternalUseClient::ImageContext*
-DisplayResourceProvider::LockSetForExternalUse::LockResource(
-    ResourceId id,
-    bool maybe_concurrent_reads,
-    bool is_video_plane,
-    const gfx::ColorSpace& color_space) {
-  auto it = resource_provider_->resources_.find(id);
-  DCHECK(it != resource_provider_->resources_.end());
-
-  ChildResource& resource = it->second;
-  DCHECK(resource.is_gpu_resource_type());
-
-  if (!resource.locked_for_external_use) {
-    DCHECK(!base::Contains(resources_, std::make_pair(id, &resource)));
-    resources_.emplace_back(id, &resource);
-
-    if (!resource.image_context) {
-      sk_sp<SkColorSpace> image_color_space;
-      if (!is_video_plane) {
-        // HDR video color conversion is handled externally in SkiaRenderer
-        // using a special color filter and |color_space| is set to destination
-        // color space so that Skia doesn't perform implicit color conversion.
-        image_color_space =
-            color_space.IsValid()
-                ? color_space.ToSkColorSpace()
-                : resource.transferable.color_space.ToSkColorSpace();
-      }
-      resource.image_context =
-          resource_provider_->external_use_client_->CreateImageContext(
-              resource.transferable.mailbox_holder, resource.transferable.size,
-              resource.transferable.format, maybe_concurrent_reads,
-              resource.transferable.ycbcr_info, std::move(image_color_space));
-    }
-    resource.locked_for_external_use = true;
-
-    if (resource.transferable.read_lock_fences_enabled) {
-      if (resource_provider_->current_read_lock_fence_.get())
-        resource_provider_->current_read_lock_fence_->Set();
-      resource.read_lock_fence = resource_provider_->current_read_lock_fence_;
-    }
-  }
-
-  DCHECK(base::Contains(resources_, std::make_pair(id, &resource)));
-  return resource.image_context.get();
-}
-
-void DisplayResourceProvider::LockSetForExternalUse::UnlockResources(
-    const gpu::SyncToken& sync_token) {
-  DCHECK(sync_token.verified_flush());
-  for (const auto& pair : resources_) {
-    auto id = pair.first;
-    auto* resource = pair.second;
-    DCHECK(resource->locked_for_external_use);
-
-    // TODO(penghuang): support software resource.
-    DCHECK(resource->is_gpu_resource_type());
-
-    // Update the resource sync token to |sync_token|. When the next frame is
-    // being composited, the DeclareUsedResourcesFromChild() will be called with
-    // resources belong to every child for the next frame. If the resource is
-    // not used by the next frame, the resource will be returned to a child
-    // which owns it with the |sync_token|. The child is responsible for issuing
-    // a WaitSyncToken GL command with the |sync_token| before reusing it.
-    resource->UpdateSyncToken(sync_token);
-    resource->locked_for_external_use = false;
-
-    resource_provider_->TryReleaseResource(id, resource);
-  }
-  resources_.clear();
-}
-
 DisplayResourceProvider::SynchronousFence::SynchronousFence(
     gpu::gles2::GLES2Interface* gl)
     : gl_(gl), has_synchronized_(true) {}
diff --git a/components/viz/service/display/display_resource_provider.h b/components/viz/service/display/display_resource_provider.h
index f5d6b08..3dfbf00 100644
--- a/components/viz/service/display/display_resource_provider.h
+++ b/components/viz/service/display/display_resource_provider.h
@@ -99,7 +99,6 @@
   bool DoAnyResourcesWantPromotionHints() const;
 
   bool IsResourceSoftwareBacked(ResourceId id);
-  GLenum GetResourceTextureTarget(ResourceId id);
   // Return the format of the underlying buffer that can be used for scanout.
   gfx::BufferFormat GetBufferFormat(ResourceId id);
   ResourceFormat GetResourceFormat(ResourceId id);
@@ -107,105 +106,14 @@
   // Indicates if this resource may be used for a hardware overlay plane.
   bool IsOverlayCandidate(ResourceId id);
 
-  void WaitSyncToken(ResourceId id);
-
   // Checks whether a resource is in use.
   bool InUse(ResourceId id);
 
   // The following lock classes are part of the DisplayResourceProvider API and
   // are needed to read the resource contents. The user must ensure that they
   // only use GL locks on GL resources, etc, and this is enforced by assertions.
-  class VIZ_SERVICE_EXPORT ScopedReadLockGL {
-   public:
-    ScopedReadLockGL(DisplayResourceProvider* resource_provider,
-                     ResourceId resource_id);
-    ~ScopedReadLockGL();
 
-    ScopedReadLockGL(const ScopedReadLockGL&) = delete;
-    ScopedReadLockGL& operator=(const ScopedReadLockGL&) = delete;
-
-    GLuint texture_id() const { return texture_id_; }
-    GLenum target() const { return target_; }
-    const gfx::Size& size() const { return size_; }
-    const gfx::ColorSpace& color_space() const { return color_space_; }
-
-   private:
-    DisplayResourceProvider* const resource_provider_;
-    const ResourceId resource_id_;
-
-    GLuint texture_id_ = 0;
-    GLenum target_ = GL_TEXTURE_2D;
-    gfx::Size size_;
-    gfx::ColorSpace color_space_;
-  };
-
-  class VIZ_SERVICE_EXPORT ScopedOverlayLockGL {
-   public:
-    ScopedOverlayLockGL(DisplayResourceProvider* resource_provider,
-                        ResourceId resource_id);
-    ~ScopedOverlayLockGL();
-
-    ScopedOverlayLockGL(const ScopedOverlayLockGL&) = delete;
-    ScopedOverlayLockGL& operator=(const ScopedOverlayLockGL&) = delete;
-
-    GLuint texture_id() const { return texture_id_; }
-
-   private:
-    DisplayResourceProvider* const resource_provider_;
-    const ResourceId resource_id_;
-    GLuint texture_id_ = 0;
-  };
-
-  class VIZ_SERVICE_EXPORT ScopedSamplerGL {
-   public:
-    ScopedSamplerGL(DisplayResourceProvider* resource_provider,
-                    ResourceId resource_id,
-                    GLenum filter);
-    ScopedSamplerGL(DisplayResourceProvider* resource_provider,
-                    ResourceId resource_id,
-                    GLenum unit,
-                    GLenum filter);
-    ~ScopedSamplerGL();
-
-    ScopedSamplerGL(const ScopedSamplerGL&) = delete;
-    ScopedSamplerGL& operator=(const ScopedSamplerGL&) = delete;
-
-    GLuint texture_id() const { return resource_lock_.texture_id(); }
-    GLenum target() const { return target_; }
-    const gfx::ColorSpace& color_space() const {
-      return resource_lock_.color_space();
-    }
-
-   private:
-    const ScopedReadLockGL resource_lock_;
-    const GLenum unit_;
-    const GLenum target_;
-  };
-
-  class VIZ_SERVICE_EXPORT ScopedReadLockSkImage {
-   public:
-    ScopedReadLockSkImage(DisplayResourceProvider* resource_provider,
-                          ResourceId resource_id,
-                          SkAlphaType alpha_type = kPremul_SkAlphaType,
-                          GrSurfaceOrigin origin = kTopLeft_GrSurfaceOrigin);
-    ~ScopedReadLockSkImage();
-
-    ScopedReadLockSkImage(const ScopedReadLockSkImage&) = delete;
-    ScopedReadLockSkImage& operator=(const ScopedReadLockSkImage& other) =
-        delete;
-
-    const SkImage* sk_image() const { return sk_image_.get(); }
-    sk_sp<SkImage> TakeSkImage() { return std::move(sk_image_); }
-
-    bool valid() const { return !!sk_image_; }
-
-   private:
-    DisplayResourceProvider* const resource_provider_;
-    const ResourceId resource_id_;
-    sk_sp<SkImage> sk_image_;
-  };
-
- private:
+ protected:
   // Forward declared for LockSetForExternalUse below.
   struct ChildResource;
 
@@ -238,42 +146,6 @@
     ChildResource* resource_ = nullptr;
   };
 
-  // Maintains set of resources locked for external use by SkiaRenderer.
-  class VIZ_SERVICE_EXPORT LockSetForExternalUse {
-   public:
-    // There should be at most one instance of this class per
-    // |resource_provider|. Both |resource_provider| and |client| outlive this
-    // class.
-    LockSetForExternalUse(DisplayResourceProvider* resource_provider,
-                          ExternalUseClient* client);
-    ~LockSetForExternalUse();
-
-    LockSetForExternalUse(const LockSetForExternalUse&) = delete;
-    LockSetForExternalUse& operator=(const LockSetForExternalUse& other) =
-        delete;
-
-    // Lock a resource for external use. The return value was created by
-    // |client| at some point in the past. The SkImage color space will be set
-    // to |color_space| if valid, otherwise it will be set to the resource's
-    // color space. If |is_video_plane| is true, the image color space will be
-    // set to nullptr (to avoid LOG spam).
-    ExternalUseClient::ImageContext* LockResource(
-        ResourceId resource_id,
-        bool maybe_concurrent_reads,
-        bool is_video_plane,
-        const gfx::ColorSpace& color_space = gfx::ColorSpace());
-
-    // Unlock all locked resources with a |sync_token|.  The |sync_token| should
-    // be waited on before reusing the resource's backing to ensure that any
-    // external use of it is completed. This |sync_token| should have been
-    // verified.  All resources must be unlocked before destroying this class.
-    void UnlockResources(const gpu::SyncToken& sync_token);
-
-   private:
-    DisplayResourceProvider* const resource_provider_;
-    std::vector<std::pair<ResourceId, ChildResource*>> resources_;
-  };
-
   // All resources that are returned to children while an instance of this
   // class exists will be stored and returned when the instance is destroyed.
   class VIZ_SERVICE_EXPORT ScopedBatchReturnResources {
@@ -355,14 +227,6 @@
   void SetAllowAccessToGPUThread(bool allow);
 
  protected:
-  // TODO(cblume, crbug.com/900973): |enable_shared_images| is a temporary
-  // solution that unblocks us until SharedImages are threadsafe in WebView.
-  DisplayResourceProvider(Mode mode,
-                          ContextProvider* compositor_context_provider,
-                          SharedBitmapManager* shared_bitmap_manager,
-                          bool enable_shared_images = true);
-
- private:
   friend class ScopedAllowGpuAccessForDisplayResourceProvider;
   enum DeleteStyle {
     NORMAL,
@@ -506,6 +370,13 @@
   using ChildMap = std::unordered_map<int, Child>;
   using ResourceMap = std::unordered_map<ResourceId, ChildResource>;
 
+  // TODO(cblume, crbug.com/900973): |enable_shared_images| is a temporary
+  // solution that unblocks us until SharedImages are threadsafe in WebView.
+  DisplayResourceProvider(Mode mode,
+                          ContextProvider* compositor_context_provider,
+                          SharedBitmapManager* shared_bitmap_manager,
+                          bool enable_shared_images = true);
+
   ChildResource* GetResource(ResourceId id);
 
   // TODO(ericrk): TryGetResource is part of a temporary workaround for cases
@@ -513,13 +384,8 @@
   // return nullptr if a resource is not found. https://crbug.com/811858
   ChildResource* TryGetResource(ResourceId id);
 
-  void PopulateSkBitmapWithResource(SkBitmap* sk_bitmap,
-                                    const ChildResource* resource);
-
   void DeleteResourceInternal(ResourceMap::iterator it);
 
-  void WaitSyncTokenInternal(ChildResource* resource);
-
   // Returns null if we do not have a ContextProvider.
   gpu::gles2::GLES2Interface* ContextGL() const;
 
@@ -530,7 +396,6 @@
   // Binds the given GL resource to a texture target for sampling using the
   // specified filter for both minification and magnification. Returns the
   // texture target used. The resource must be locked for reading.
-  GLenum BindForSampling(ResourceId resource_id, GLenum unit, GLenum filter);
   bool ReadLockFenceHasPassed(const ChildResource* resource);
 #if defined(OS_ANDROID)
   void DeletePromotionHint(ResourceMap::iterator it);
diff --git a/components/viz/service/display/display_resource_provider_gl.cc b/components/viz/service/display/display_resource_provider_gl.cc
index d756fe0..940ab94 100644
--- a/components/viz/service/display/display_resource_provider_gl.cc
+++ b/components/viz/service/display/display_resource_provider_gl.cc
@@ -4,7 +4,41 @@
 
 #include "components/viz/service/display/display_resource_provider_gl.h"
 
+#include "base/dcheck_is_on.h"
+#include "build/build_config.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+
+using gpu::gles2::GLES2Interface;
+
 namespace viz {
+namespace {
+
+class ScopedSetActiveTexture {
+ public:
+  ScopedSetActiveTexture(GLES2Interface* gl, GLenum unit)
+      : gl_(gl), unit_(unit) {
+#if DCHECK_IS_ON()
+    GLint active_unit = 0;
+    gl->GetIntegerv(GL_ACTIVE_TEXTURE, &active_unit);
+    DCHECK_EQ(GL_TEXTURE0, active_unit);
+#endif
+
+    if (unit_ != GL_TEXTURE0)
+      gl_->ActiveTexture(unit_);
+  }
+
+  ~ScopedSetActiveTexture() {
+    // Active unit being GL_TEXTURE0 is effectively the ground state.
+    if (unit_ != GL_TEXTURE0)
+      gl_->ActiveTexture(GL_TEXTURE0);
+  }
+
+ private:
+  GLES2Interface* gl_;
+  GLenum unit_;
+};
+
+}  // namespace
 
 DisplayResourceProviderGL::DisplayResourceProviderGL(
     ContextProvider* compositor_context_provider,
@@ -15,4 +49,122 @@
                               shared_bitmap_manager,
                               enable_shared_images) {}
 
+GLenum DisplayResourceProviderGL::GetResourceTextureTarget(ResourceId id) {
+  return GetResource(id)->transferable.mailbox_holder.texture_target;
+}
+
+void DisplayResourceProviderGL::WaitSyncToken(ResourceId id) {
+  ChildResource* resource = TryGetResource(id);
+  // TODO(ericrk): We should never fail TryGetResource, but we appear to
+  // be doing so on Android in rare cases. Handle this gracefully until a
+  // better solution can be found. https://crbug.com/811858
+  if (!resource)
+    return;
+  WaitSyncTokenInternal(resource);
+
+#if defined(OS_ANDROID)
+  // Now that the resource is synced, we may send it a promotion hint.
+  InitializePromotionHintRequest(id);
+#endif
+}
+
+GLenum DisplayResourceProviderGL::BindForSampling(ResourceId resource_id,
+                                                  GLenum unit,
+                                                  GLenum filter) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  GLES2Interface* gl = ContextGL();
+  auto it = resources_.find(resource_id);
+  // TODO(ericrk): We should never fail to find resource_id, but we appear to
+  // be doing so on Android in rare cases. Handle this gracefully until a
+  // better solution can be found. https://crbug.com/811858
+  if (it == resources_.end())
+    return GL_TEXTURE_2D;
+
+  ChildResource* resource = &it->second;
+  DCHECK(resource->lock_for_read_count);
+
+  ScopedSetActiveTexture scoped_active_tex(gl, unit);
+  GLenum target = resource->transferable.mailbox_holder.texture_target;
+  gl->BindTexture(target, resource->gl_id);
+
+  // Texture parameters can be modified by concurrent reads so reset them
+  // before binding the texture. See https://crbug.com/1092080.
+  gl->TexParameteri(target, GL_TEXTURE_MIN_FILTER, filter);
+  gl->TexParameteri(target, GL_TEXTURE_MAG_FILTER, filter);
+  resource->filter = filter;
+
+  return target;
+}
+
+void DisplayResourceProviderGL::WaitSyncTokenInternal(ChildResource* resource) {
+  DCHECK(resource);
+  if (!resource->ShouldWaitSyncToken())
+    return;
+  GLES2Interface* gl = ContextGL();
+  DCHECK(gl);
+  // In the case of context lost, this sync token may be empty (see comment in
+  // the UpdateSyncToken() function). The WaitSyncTokenCHROMIUM() function
+  // handles empty sync tokens properly so just wait anyways and update the
+  // state the synchronized.
+  gl->WaitSyncTokenCHROMIUM(resource->sync_token().GetConstData());
+  resource->SetSynchronized();
+}
+
+DisplayResourceProviderGL::ScopedReadLockGL::ScopedReadLockGL(
+    DisplayResourceProviderGL* resource_provider,
+    ResourceId resource_id)
+    : resource_provider_(resource_provider), resource_id_(resource_id) {
+  const ChildResource* resource =
+      resource_provider->LockForRead(resource_id, false /* overlay_only */);
+  // TODO(ericrk): We should never fail LockForRead, but we appear to be
+  // doing so on Android in rare cases. Handle this gracefully until a better
+  // solution can be found. https://crbug.com/811858
+  if (!resource)
+    return;
+
+  texture_id_ = resource->gl_id;
+  target_ = resource->transferable.mailbox_holder.texture_target;
+  size_ = resource->transferable.size;
+  color_space_ = resource->transferable.color_space;
+}
+
+DisplayResourceProviderGL::ScopedReadLockGL::~ScopedReadLockGL() {
+  resource_provider_->UnlockForRead(resource_id_, false /* overlay_only */);
+}
+
+DisplayResourceProviderGL::ScopedSamplerGL::ScopedSamplerGL(
+    DisplayResourceProviderGL* resource_provider,
+    ResourceId resource_id,
+    GLenum filter)
+    : resource_lock_(resource_provider, resource_id),
+      unit_(GL_TEXTURE0),
+      target_(resource_provider->BindForSampling(resource_id, unit_, filter)) {}
+
+DisplayResourceProviderGL::ScopedSamplerGL::ScopedSamplerGL(
+    DisplayResourceProviderGL* resource_provider,
+    ResourceId resource_id,
+    GLenum unit,
+    GLenum filter)
+    : resource_lock_(resource_provider, resource_id),
+      unit_(unit),
+      target_(resource_provider->BindForSampling(resource_id, unit_, filter)) {}
+
+DisplayResourceProviderGL::ScopedSamplerGL::~ScopedSamplerGL() = default;
+
+DisplayResourceProviderGL::ScopedOverlayLockGL::ScopedOverlayLockGL(
+    DisplayResourceProviderGL* resource_provider,
+    ResourceId resource_id)
+    : resource_provider_(resource_provider), resource_id_(resource_id) {
+  const ChildResource* resource =
+      resource_provider->LockForRead(resource_id, true /* overlay_only */);
+  if (!resource)
+    return;
+
+  texture_id_ = resource->gl_id;
+}
+
+DisplayResourceProviderGL::ScopedOverlayLockGL::~ScopedOverlayLockGL() {
+  resource_provider_->UnlockForRead(resource_id_, true /* overlay_only */);
+}
+
 }  // namespace viz
diff --git a/components/viz/service/display/display_resource_provider_gl.h b/components/viz/service/display/display_resource_provider_gl.h
index 0004ba9..8ae419a 100644
--- a/components/viz/service/display/display_resource_provider_gl.h
+++ b/components/viz/service/display/display_resource_provider_gl.h
@@ -17,6 +17,83 @@
   DisplayResourceProviderGL(ContextProvider* compositor_context_provider,
                             SharedBitmapManager* shared_bitmap_manager,
                             bool enable_shared_images = true);
+
+  GLenum GetResourceTextureTarget(ResourceId id);
+  void WaitSyncToken(ResourceId id);
+
+  // The following lock classes are part of the DisplayResourceProvider API and
+  // are needed to read the resource contents. The user must ensure that they
+  // only use GL locks on GL resources, etc, and this is enforced by assertions.
+  class VIZ_SERVICE_EXPORT ScopedReadLockGL {
+   public:
+    ScopedReadLockGL(DisplayResourceProviderGL* resource_provider,
+                     ResourceId resource_id);
+    ~ScopedReadLockGL();
+
+    ScopedReadLockGL(const ScopedReadLockGL&) = delete;
+    ScopedReadLockGL& operator=(const ScopedReadLockGL&) = delete;
+
+    GLuint texture_id() const { return texture_id_; }
+    GLenum target() const { return target_; }
+    const gfx::Size& size() const { return size_; }
+    const gfx::ColorSpace& color_space() const { return color_space_; }
+
+   private:
+    DisplayResourceProviderGL* const resource_provider_;
+    const ResourceId resource_id_;
+
+    GLuint texture_id_ = 0;
+    GLenum target_ = GL_TEXTURE_2D;
+    gfx::Size size_;
+    gfx::ColorSpace color_space_;
+  };
+
+  class VIZ_SERVICE_EXPORT ScopedSamplerGL {
+   public:
+    ScopedSamplerGL(DisplayResourceProviderGL* resource_provider,
+                    ResourceId resource_id,
+                    GLenum filter);
+    ScopedSamplerGL(DisplayResourceProviderGL* resource_provider,
+                    ResourceId resource_id,
+                    GLenum unit,
+                    GLenum filter);
+    ~ScopedSamplerGL();
+
+    ScopedSamplerGL(const ScopedSamplerGL&) = delete;
+    ScopedSamplerGL& operator=(const ScopedSamplerGL&) = delete;
+
+    GLuint texture_id() const { return resource_lock_.texture_id(); }
+    GLenum target() const { return target_; }
+    const gfx::ColorSpace& color_space() const {
+      return resource_lock_.color_space();
+    }
+
+   private:
+    const ScopedReadLockGL resource_lock_;
+    const GLenum unit_;
+    const GLenum target_;
+  };
+
+  class VIZ_SERVICE_EXPORT ScopedOverlayLockGL {
+   public:
+    ScopedOverlayLockGL(DisplayResourceProviderGL* resource_provider,
+                        ResourceId resource_id);
+    ~ScopedOverlayLockGL();
+
+    ScopedOverlayLockGL(const ScopedOverlayLockGL&) = delete;
+    ScopedOverlayLockGL& operator=(const ScopedOverlayLockGL&) = delete;
+
+    GLuint texture_id() const { return texture_id_; }
+
+   private:
+    DisplayResourceProviderGL* const resource_provider_;
+    const ResourceId resource_id_;
+    GLuint texture_id_ = 0;
+  };
+
+ private:
+  GLenum BindForSampling(ResourceId resource_id, GLenum unit, GLenum filter);
+  void WaitSyncTokenInternal(ChildResource* resource);
 };
 
 }  // namespace viz
diff --git a/components/viz/service/display/display_resource_provider_skia.cc b/components/viz/service/display/display_resource_provider_skia.cc
index 2528cad..0355708e 100644
--- a/components/viz/service/display/display_resource_provider_skia.cc
+++ b/components/viz/service/display/display_resource_provider_skia.cc
@@ -4,6 +4,8 @@
 
 #include "components/viz/service/display/display_resource_provider_skia.h"
 
+#include <utility>
+
 namespace viz {
 
 DisplayResourceProviderSkia::DisplayResourceProviderSkia(
@@ -13,4 +15,87 @@
                               shared_bitmap_manager,
                               /*enable_shared_images=*/true) {}
 
+DisplayResourceProviderSkia::LockSetForExternalUse::LockSetForExternalUse(
+    DisplayResourceProviderSkia* resource_provider,
+    ExternalUseClient* client)
+    : resource_provider_(resource_provider) {
+  DCHECK(!resource_provider_->external_use_client_);
+  resource_provider_->external_use_client_ = client;
+}
+
+DisplayResourceProviderSkia::LockSetForExternalUse::~LockSetForExternalUse() {
+  DCHECK(resources_.empty());
+}
+
+ExternalUseClient::ImageContext*
+DisplayResourceProviderSkia::LockSetForExternalUse::LockResource(
+    ResourceId id,
+    bool maybe_concurrent_reads,
+    bool is_video_plane,
+    const gfx::ColorSpace& color_space) {
+  auto it = resource_provider_->resources_.find(id);
+  DCHECK(it != resource_provider_->resources_.end());
+
+  ChildResource& resource = it->second;
+  DCHECK(resource.is_gpu_resource_type());
+
+  if (!resource.locked_for_external_use) {
+    DCHECK(!base::Contains(resources_, std::make_pair(id, &resource)));
+    resources_.emplace_back(id, &resource);
+
+    if (!resource.image_context) {
+      sk_sp<SkColorSpace> image_color_space;
+      if (!is_video_plane) {
+        // HDR video color conversion is handled externally in SkiaRenderer
+        // using a special color filter and |color_space| is set to destination
+        // color space so that Skia doesn't perform implicit color conversion.
+        image_color_space =
+            color_space.IsValid()
+                ? color_space.ToSkColorSpace()
+                : resource.transferable.color_space.ToSkColorSpace();
+      }
+      resource.image_context =
+          resource_provider_->external_use_client_->CreateImageContext(
+              resource.transferable.mailbox_holder, resource.transferable.size,
+              resource.transferable.format, maybe_concurrent_reads,
+              resource.transferable.ycbcr_info, std::move(image_color_space));
+    }
+    resource.locked_for_external_use = true;
+
+    if (resource.transferable.read_lock_fences_enabled) {
+      if (resource_provider_->current_read_lock_fence_.get())
+        resource_provider_->current_read_lock_fence_->Set();
+      resource.read_lock_fence = resource_provider_->current_read_lock_fence_;
+    }
+  }
+
+  DCHECK(base::Contains(resources_, std::make_pair(id, &resource)));
+  return resource.image_context.get();
+}
+
+void DisplayResourceProviderSkia::LockSetForExternalUse::UnlockResources(
+    const gpu::SyncToken& sync_token) {
+  DCHECK(sync_token.verified_flush());
+  for (const auto& pair : resources_) {
+    auto id = pair.first;
+    auto* resource = pair.second;
+    DCHECK(resource->locked_for_external_use);
+
+    // TODO(penghuang): support software resource.
+    DCHECK(resource->is_gpu_resource_type());
+
+    // Update the resource sync token to |sync_token|. When the next frame is
+    // being composited, the DeclareUsedResourcesFromChild() will be called with
+    // resources belong to every child for the next frame. If the resource is
+    // not used by the next frame, the resource will be returned to a child
+    // which owns it with the |sync_token|. The child is responsible for issuing
+    // a WaitSyncToken GL command with the |sync_token| before reusing it.
+    resource->UpdateSyncToken(sync_token);
+    resource->locked_for_external_use = false;
+
+    resource_provider_->TryReleaseResource(id, resource);
+  }
+  resources_.clear();
+}
+
 }  // namespace viz
diff --git a/components/viz/service/display/display_resource_provider_skia.h b/components/viz/service/display/display_resource_provider_skia.h
index 6e799996..51304ed 100644
--- a/components/viz/service/display/display_resource_provider_skia.h
+++ b/components/viz/service/display/display_resource_provider_skia.h
@@ -5,6 +5,9 @@
 #ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_DISPLAY_RESOURCE_PROVIDER_SKIA_H_
 #define COMPONENTS_VIZ_SERVICE_DISPLAY_DISPLAY_RESOURCE_PROVIDER_SKIA_H_
 
+#include <utility>
+#include <vector>
+
 #include "components/viz/service/display/display_resource_provider.h"
 #include "components/viz/service/viz_service_export.h"
 
@@ -16,6 +19,42 @@
  public:
   explicit DisplayResourceProviderSkia(
       SharedBitmapManager* shared_bitmap_manager);
+
+  // Maintains set of resources locked for external use by SkiaRenderer.
+  class VIZ_SERVICE_EXPORT LockSetForExternalUse {
+   public:
+    // There should be at most one instance of this class per
+    // |resource_provider|. Both |resource_provider| and |client| outlive this
+    // class.
+    LockSetForExternalUse(DisplayResourceProviderSkia* resource_provider,
+                          ExternalUseClient* client);
+    ~LockSetForExternalUse();
+
+    LockSetForExternalUse(const LockSetForExternalUse&) = delete;
+    LockSetForExternalUse& operator=(const LockSetForExternalUse& other) =
+        delete;
+
+    // Lock a resource for external use. The return value was created by
+    // |client| at some point in the past. The SkImage color space will be set
+    // to |color_space| if valid, otherwise it will be set to the resource's
+    // color space. If |is_video_plane| is true, the image color space will be
+    // set to nullptr (to avoid LOG spam).
+    ExternalUseClient::ImageContext* LockResource(
+        ResourceId resource_id,
+        bool maybe_concurrent_reads,
+        bool is_video_plane,
+        const gfx::ColorSpace& color_space = gfx::ColorSpace());
+
+    // Unlock all locked resources with a |sync_token|.  The |sync_token| should
+    // be waited on before reusing the resource's backing to ensure that any
+    // external use of it is completed. This |sync_token| should have been
+    // verified.  All resources must be unlocked before destroying this class.
+    void UnlockResources(const gpu::SyncToken& sync_token);
+
+   private:
+    DisplayResourceProviderSkia* const resource_provider_;
+    std::vector<std::pair<ResourceId, ChildResource*>> resources_;
+  };
 };
 
 }  // namespace viz
diff --git a/components/viz/service/display/display_resource_provider_software.cc b/components/viz/service/display/display_resource_provider_software.cc
index 01147557..000e89b 100644
--- a/components/viz/service/display/display_resource_provider_software.cc
+++ b/components/viz/service/display/display_resource_provider_software.cc
@@ -4,6 +4,8 @@
 
 #include "components/viz/service/display/display_resource_provider_software.h"
 
+#include "components/viz/common/resources/resource_format_utils.h"
+
 namespace viz {
 
 DisplayResourceProviderSoftware::DisplayResourceProviderSoftware(
@@ -13,4 +15,59 @@
                               shared_bitmap_manager,
                               /*enable_shared_images=*/true) {}
 
+void DisplayResourceProviderSoftware::PopulateSkBitmapWithResource(
+    SkBitmap* sk_bitmap,
+    const ChildResource* resource) {
+  DCHECK(IsBitmapFormatSupported(resource->transferable.format));
+  SkImageInfo info =
+      SkImageInfo::MakeN32Premul(resource->transferable.size.width(),
+                                 resource->transferable.size.height());
+  bool pixels_installed = sk_bitmap->installPixels(
+      info, resource->shared_bitmap->pixels(), info.minRowBytes());
+  DCHECK(pixels_installed);
+}
+
+DisplayResourceProviderSoftware::ScopedReadLockSkImage::ScopedReadLockSkImage(
+    DisplayResourceProviderSoftware* resource_provider,
+    ResourceId resource_id,
+    SkAlphaType alpha_type,
+    GrSurfaceOrigin origin)
+    : resource_provider_(resource_provider), resource_id_(resource_id) {
+  const ChildResource* resource =
+      resource_provider->LockForRead(resource_id, false /* overlay_only */);
+  DCHECK(resource);
+  DCHECK(!resource->is_gpu_resource_type());
+
+  // Use cached SkImage if possible.
+  auto it = resource_provider_->resource_sk_images_.find(resource_id);
+  if (it != resource_provider_->resource_sk_images_.end()) {
+    sk_image_ = it->second;
+    return;
+  }
+
+  if (!resource->shared_bitmap) {
+    // If a CompositorFrameSink is destroyed, it destroys all SharedBitmapIds
+    // that it registered. In this case, a CompositorFrame can be drawn with
+    // SharedBitmapIds that are not known in the viz service. As well, a
+    // misbehaved client can use SharedBitampIds that it did not report to
+    // the service. Then the |shared_bitmap| will be null, and this read lock
+    // will not be valid. Software-compositing users of this read lock must
+    // check for valid() to deal with this scenario.
+    sk_image_ = nullptr;
+    return;
+  }
+
+  DCHECK(origin == kTopLeft_GrSurfaceOrigin);
+  SkBitmap sk_bitmap;
+  resource_provider->PopulateSkBitmapWithResource(&sk_bitmap, resource);
+  sk_bitmap.setImmutable();
+  sk_image_ = SkImage::MakeFromBitmap(sk_bitmap);
+  resource_provider_->resource_sk_images_[resource_id] = sk_image_;
+}
+
+DisplayResourceProviderSoftware::ScopedReadLockSkImage::
+    ~ScopedReadLockSkImage() {
+  resource_provider_->UnlockForRead(resource_id_, false /* overlay_only */);
+}
+
 }  // namespace viz
diff --git a/components/viz/service/display/display_resource_provider_software.h b/components/viz/service/display/display_resource_provider_software.h
index eb1ce88..98792ed 100644
--- a/components/viz/service/display/display_resource_provider_software.h
+++ b/components/viz/service/display/display_resource_provider_software.h
@@ -5,6 +5,8 @@
 #ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_DISPLAY_RESOURCE_PROVIDER_SOFTWARE_H_
 #define COMPONENTS_VIZ_SERVICE_DISPLAY_DISPLAY_RESOURCE_PROVIDER_SOFTWARE_H_
 
+#include <utility>
+
 #include "components/viz/service/display/display_resource_provider.h"
 #include "components/viz/service/viz_service_export.h"
 
@@ -16,6 +18,33 @@
  public:
   explicit DisplayResourceProviderSoftware(
       SharedBitmapManager* shared_bitmap_manager);
+
+  class VIZ_SERVICE_EXPORT ScopedReadLockSkImage {
+   public:
+    ScopedReadLockSkImage(DisplayResourceProviderSoftware* resource_provider,
+                          ResourceId resource_id,
+                          SkAlphaType alpha_type = kPremul_SkAlphaType,
+                          GrSurfaceOrigin origin = kTopLeft_GrSurfaceOrigin);
+    ~ScopedReadLockSkImage();
+
+    ScopedReadLockSkImage(const ScopedReadLockSkImage&) = delete;
+    ScopedReadLockSkImage& operator=(const ScopedReadLockSkImage& other) =
+        delete;
+
+    const SkImage* sk_image() const { return sk_image_.get(); }
+    sk_sp<SkImage> TakeSkImage() { return std::move(sk_image_); }
+
+    bool valid() const { return !!sk_image_; }
+
+   private:
+    DisplayResourceProviderSoftware* const resource_provider_;
+    const ResourceId resource_id_;
+    sk_sp<SkImage> sk_image_;
+  };
+
+ private:
+  void PopulateSkBitmapWithResource(SkBitmap* sk_bitmap,
+                                    const ChildResource* resource);
 };
 
 }  // namespace viz
diff --git a/components/viz/service/display/gl_renderer.cc b/components/viz/service/display/gl_renderer.cc
index ca43fd2..fab5f2f4 100644
--- a/components/viz/service/display/gl_renderer.cc
+++ b/components/viz/service/display/gl_renderer.cc
@@ -328,11 +328,12 @@
   sk_sp<SkImage> filter_image;
 
   // The original contents, bound for sampling.
-  std::unique_ptr<DisplayResourceProvider::ScopedSamplerGL>
+  std::unique_ptr<DisplayResourceProviderGL::ScopedSamplerGL>
       bypass_quad_resource_lock;
 
   // A mask to be applied when drawing the RPDQ.
-  std::unique_ptr<DisplayResourceProvider::ScopedSamplerGL> mask_resource_lock;
+  std::unique_ptr<DisplayResourceProviderGL::ScopedSamplerGL>
+      mask_resource_lock;
 
   // Whether a color matrix needs to be applied by the shaders when drawing
   // the RPDQ.
@@ -420,7 +421,7 @@
     const RendererSettings* settings,
     const DebugRendererSettings* debug_settings,
     OutputSurface* output_surface,
-    DisplayResourceProvider* resource_provider,
+    DisplayResourceProviderGL* resource_provider,
     OverlayProcessorInterface* overlay_processor,
     scoped_refptr<base::SingleThreadTaskRunner> current_task_runner)
     : DirectRenderer(settings,
@@ -589,16 +590,16 @@
     read_lock_fence = sync_queries_.StartNewFrame();
   } else {
     read_lock_fence =
-        base::MakeRefCounted<DisplayResourceProvider::SynchronousFence>(gl_);
+        base::MakeRefCounted<DisplayResourceProviderGL::SynchronousFence>(gl_);
   }
-  resource_provider_->SetReadLockFence(read_lock_fence.get());
+  resource_provider()->SetReadLockFence(read_lock_fence.get());
 
   // Insert WaitSyncTokenCHROMIUM on quad resources prior to drawing the frame,
   // so that drawing can proceed without GL context switching interruptions.
   for (const auto& pass : *current_frame()->render_passes_in_draw_order) {
     for (auto* quad : pass->quad_list) {
       for (ResourceId resource_id : quad->resources)
-        resource_provider_->WaitSyncToken(resource_id);
+        resource_provider()->WaitSyncToken(resource_id);
     }
   }
 
@@ -1256,7 +1257,7 @@
   // BUG=skia:3868, Skia currently doesn't support texture rectangle inputs.
   // See also the DCHECKs about GL_TEXTURE_2D in DrawRenderPassQuad.
   GLenum target =
-      resource_provider_->GetResourceTextureTarget(tile_quad->resource_id());
+      resource_provider()->GetResourceTextureTarget(tile_quad->resource_id());
   if (target != GL_TEXTURE_2D)
     return nullptr;
 
@@ -1554,9 +1555,9 @@
               quad->filters_scale, std::move(filter), &offset, &subset,
               quad->filters_origin, true);
         } else {
-          DisplayResourceProvider::ScopedReadLockGL
+          DisplayResourceProviderGL::ScopedReadLockGL
               prefilter_bypass_quad_texture_lock(
-                  resource_provider_, params->bypass_quad_texture.resource_id);
+                  resource_provider(), params->bypass_quad_texture.resource_id);
           params->contents_and_bypass_color_space =
               prefilter_bypass_quad_texture_lock.color_space();
           sk_sp<SkImage> src_image =
@@ -1588,10 +1589,11 @@
 void GLRenderer::UpdateRPDQTexturesForSampling(
     DrawRenderPassDrawQuadParams* params) {
   if (params->quad->mask_resource_id()) {
-    params->mask_resource_lock.reset(
-        new DisplayResourceProvider::ScopedSamplerGL(
-            resource_provider_, params->quad->mask_resource_id(), GL_TEXTURE1,
-            GL_LINEAR));
+    params->mask_resource_lock =
+        std::make_unique<DisplayResourceProviderGL::ScopedSamplerGL>(
+
+            resource_provider(), params->quad->mask_resource_id(), GL_TEXTURE1,
+            GL_LINEAR);
   }
 
   if (params->filter_image) {
@@ -1613,8 +1615,8 @@
     params->source_needs_flip = params->flip_texture;
   } else {
     params->bypass_quad_resource_lock =
-        std::make_unique<DisplayResourceProvider::ScopedSamplerGL>(
-            resource_provider_, params->bypass_quad_texture.resource_id,
+        std::make_unique<DisplayResourceProviderGL::ScopedSamplerGL>(
+            resource_provider(), params->bypass_quad_texture.resource_id,
             GL_LINEAR);
     DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D),
               params->bypass_quad_resource_lock->target());
@@ -2340,8 +2342,8 @@
   float edge[24];
   SetupQuadForClippingAndAntialiasing(device_transform, quad, &aa_quad,
                                       clip_region, &local_quad, edge);
-  DisplayResourceProvider::ScopedSamplerGL quad_resource_lock(
-      resource_provider_, resource_id,
+  DisplayResourceProviderGL::ScopedSamplerGL quad_resource_lock(
+      resource_provider(), resource_id,
       quad->nearest_neighbor ? GL_NEAREST : GL_LINEAR);
   SamplerType sampler =
       SamplerTypeFromTextureTarget(quad_resource_lock.target());
@@ -2420,8 +2422,8 @@
                       ? GL_LINEAR
                       : GL_NEAREST;
 
-  DisplayResourceProvider::ScopedSamplerGL quad_resource_lock(
-      resource_provider_, resource_id, filter);
+  DisplayResourceProviderGL::ScopedSamplerGL quad_resource_lock(
+      resource_provider(), resource_id, filter);
   SamplerType sampler =
       SamplerTypeFromTextureTarget(quad_resource_lock.target());
 
@@ -2558,10 +2560,10 @@
           ? UV_TEXTURE_MODE_UV
           : UV_TEXTURE_MODE_U_V;
 
-  DisplayResourceProvider::ScopedSamplerGL y_plane_lock(
-      resource_provider_, quad->y_plane_resource_id(), GL_TEXTURE1, GL_LINEAR);
-  DisplayResourceProvider::ScopedSamplerGL u_plane_lock(
-      resource_provider_, quad->u_plane_resource_id(), GL_TEXTURE2, GL_LINEAR);
+  DisplayResourceProviderGL::ScopedSamplerGL y_plane_lock(
+      resource_provider(), quad->y_plane_resource_id(), GL_TEXTURE1, GL_LINEAR);
+  DisplayResourceProviderGL::ScopedSamplerGL u_plane_lock(
+      resource_provider(), quad->u_plane_resource_id(), GL_TEXTURE2, GL_LINEAR);
   DCHECK_EQ(y_plane_lock.target(), u_plane_lock.target());
   DCHECK_EQ(y_plane_lock.color_space(), u_plane_lock.color_space());
 
@@ -2589,26 +2591,27 @@
   const bool supports_dc_layers =
       output_surface_->capabilities().supports_dc_layers;
   if (supports_dc_layers && !src_color_space.IsHDR() &&
-      resource_provider_->IsOverlayCandidate(quad->y_plane_resource_id())) {
-    DCHECK(resource_provider_->IsOverlayCandidate(quad->u_plane_resource_id()));
+      resource_provider()->IsOverlayCandidate(quad->y_plane_resource_id())) {
+    DCHECK(
+        resource_provider()->IsOverlayCandidate(quad->u_plane_resource_id()));
     dst_color_space = gfx::ColorSpace::CreateSRGB();
   }
 #endif
 
   // TODO(jbauman): Use base::Optional when available.
-  std::unique_ptr<DisplayResourceProvider::ScopedSamplerGL> v_plane_lock;
+  std::unique_ptr<DisplayResourceProviderGL::ScopedSamplerGL> v_plane_lock;
   if (uv_texture_mode == UV_TEXTURE_MODE_U_V) {
-    v_plane_lock.reset(new DisplayResourceProvider::ScopedSamplerGL(
-        resource_provider_, quad->v_plane_resource_id(), GL_TEXTURE3,
-        GL_LINEAR));
+    v_plane_lock = std::make_unique<DisplayResourceProviderGL::ScopedSamplerGL>(
+        resource_provider(), quad->v_plane_resource_id(), GL_TEXTURE3,
+        GL_LINEAR);
     DCHECK_EQ(y_plane_lock.target(), v_plane_lock->target());
     DCHECK_EQ(y_plane_lock.color_space(), v_plane_lock->color_space());
   }
-  std::unique_ptr<DisplayResourceProvider::ScopedSamplerGL> a_plane_lock;
+  std::unique_ptr<DisplayResourceProviderGL::ScopedSamplerGL> a_plane_lock;
   if (alpha_texture_mode == YUV_HAS_ALPHA_TEXTURE) {
-    a_plane_lock.reset(new DisplayResourceProvider::ScopedSamplerGL(
-        resource_provider_, quad->a_plane_resource_id(), GL_TEXTURE4,
-        GL_LINEAR));
+    a_plane_lock = std::make_unique<DisplayResourceProviderGL::ScopedSamplerGL>(
+        resource_provider(), quad->a_plane_resource_id(), GL_TEXTURE4,
+        GL_LINEAR);
     DCHECK_EQ(y_plane_lock.target(), a_plane_lock->target());
   }
 
@@ -2751,8 +2754,8 @@
       gl_, &highp_threshold_cache_, settings_->highp_threshold_min,
       quad->shared_quad_state->visible_quad_layer_rect.size());
 
-  DisplayResourceProvider::ScopedReadLockGL lock(resource_provider_,
-                                                 quad->resource_id());
+  DisplayResourceProviderGL::ScopedReadLockGL lock(resource_provider(),
+                                                   quad->resource_id());
 
   SetUseProgram(ProgramKey::VideoStream(tex_coord_precision,
                                         ShouldApplyRoundedCorner(quad)),
@@ -2822,8 +2825,8 @@
   SetBlendEnabled(draw_cache_.needs_blending);
 
   // Assume the current active textures is 0.
-  DisplayResourceProvider::ScopedSamplerGL locked_quad(
-      resource_provider_, draw_cache_.resource_id,
+  DisplayResourceProviderGL::ScopedSamplerGL locked_quad(
+      resource_provider(), draw_cache_.resource_id,
       draw_cache_.nearest_neighbor ? GL_NEAREST : GL_LINEAR);
 
   // Bind the program to the GL state.
@@ -2918,8 +2921,8 @@
     FlushTextureQuadCache(SHARED_BINDING);
   }
 
-  DisplayResourceProvider::ScopedReadLockGL lock(resource_provider_,
-                                                 quad->resource_id());
+  DisplayResourceProviderGL::ScopedReadLockGL lock(resource_provider(),
+                                                   quad->resource_id());
   // ScopedReadLockGL contains the correct texture size, even when
   // quad->resource_size_in_pixels() is empty.
   const gfx::Size texture_size = lock.size();
@@ -3493,8 +3496,8 @@
     // that once a swap buffer has completed we can remove the oldest buffers
     // from the queue, but only once we've swapped another frame afterward.
     if (swapping_overlay_resources_.size() > 1) {
-      DisplayResourceProvider::ScopedBatchReturnResources returner(
-          resource_provider_);
+      DisplayResourceProviderGL::ScopedBatchReturnResources returner(
+          resource_provider());
       swapping_overlay_resources_.pop_front();
     }
     // If |displayed_overlay_textures_| has a non-empty member that means we're
@@ -3511,8 +3514,8 @@
 void GLRenderer::DidReceiveTextureInUseResponses(
     const gpu::TextureInUseResponses& responses) {
   DCHECK(settings_->release_overlay_resources_after_gpu_query);
-  DisplayResourceProvider::ScopedBatchReturnResources returner(
-      resource_provider_);
+  DisplayResourceProviderGL::ScopedBatchReturnResources returner(
+      resource_provider());
   for (const gpu::TextureInUseResponse& response : responses) {
     if (response.in_use)
       continue;
@@ -3520,7 +3523,8 @@
     // Returned texture ids may be for resources from clients of the
     // display compositor, in |swapped_and_acked_overlay_resources_|. In that
     // case we remove the lock from the map, allowing them to be returned to the
-    // client if the resource has been deleted from the DisplayResourceProvider.
+    // client if the resource has been deleted from the
+    // DisplayResourceProviderGL.
     if (swapped_and_acked_overlay_resources_.erase(response.texture))
       continue;
     // If not, then they would be a RenderPass copy texture, which is held in
@@ -3834,8 +3838,8 @@
     unsigned texture_id = 0;
     if (contents_resource_id) {
       pending_overlay_resources_.push_back(
-          std::make_unique<DisplayResourceProvider::ScopedOverlayLockGL>(
-              resource_provider_, contents_resource_id));
+          std::make_unique<DisplayResourceProviderGL::ScopedOverlayLockGL>(
+              resource_provider(), contents_resource_id));
       texture_id = pending_overlay_resources_.back()->texture_id();
     }
     GLfloat contents_rect[4] = {
@@ -3891,8 +3895,8 @@
       if (resource_id == kInvalidResourceId)
         break;
       pending_overlay_resources_.push_back(
-          std::make_unique<DisplayResourceProvider::ScopedOverlayLockGL>(
-              resource_provider_, resource_id));
+          std::make_unique<DisplayResourceProviderGL::ScopedOverlayLockGL>(
+              resource_provider(), resource_id));
       texture_ids[i] = pending_overlay_resources_.back()->texture_id();
     }
     DCHECK(texture_ids[0]);
@@ -3930,8 +3934,8 @@
   OverlayCandidateList& overlays = current_frame()->overlay_list;
   for (const auto& overlay_candidate : overlays) {
     pending_overlay_resources_.push_back(
-        std::make_unique<DisplayResourceProvider::ScopedOverlayLockGL>(
-            resource_provider_, overlay_candidate.resource_id));
+        std::make_unique<DisplayResourceProviderGL::ScopedOverlayLockGL>(
+            resource_provider(), overlay_candidate.resource_id));
     unsigned texture_id = pending_overlay_resources_.back()->texture_id();
 
     context_support_->ScheduleOverlayPlane(
diff --git a/components/viz/service/display/gl_renderer.h b/components/viz/service/display/gl_renderer.h
index 138392e..60eb61f 100644
--- a/components/viz/service/display/gl_renderer.h
+++ b/components/viz/service/display/gl_renderer.h
@@ -25,6 +25,7 @@
 #include "components/viz/common/quads/tile_draw_quad.h"
 #include "components/viz/common/quads/yuv_video_draw_quad.h"
 #include "components/viz/service/display/direct_renderer.h"
+#include "components/viz/service/display/display_resource_provider_gl.h"
 #include "components/viz/service/display/gl_renderer_copier.h"
 #include "components/viz/service/display/gl_renderer_draw_cache.h"
 #include "components/viz/service/display/program_binding.h"
@@ -75,7 +76,7 @@
   GLRenderer(const RendererSettings* settings,
              const DebugRendererSettings* debug_settings,
              OutputSurface* output_surface,
-             DisplayResourceProvider* resource_provider,
+             DisplayResourceProviderGL* resource_provider,
              OverlayProcessorInterface* overlay_processor,
              scoped_refptr<base::SingleThreadTaskRunner> current_task_runner);
   ~GLRenderer() override;
@@ -158,7 +159,7 @@
   friend class GLRendererTest;
 
   using OverlayResourceLock =
-      std::unique_ptr<DisplayResourceProvider::ScopedOverlayLockGL>;
+      std::unique_ptr<DisplayResourceProviderGL::ScopedOverlayLockGL>;
   using OverlayResourceLockList = std::vector<OverlayResourceLock>;
 
   // If a RenderPass is used as an overlay, we render the RenderPass with any
@@ -376,6 +377,10 @@
   // the glClear function call.
   bool CanUseFastSolidColorDraw(const SolidColorDrawQuad* quad) const;
 
+  DisplayResourceProviderGL* resource_provider() {
+    return static_cast<DisplayResourceProviderGL*>(resource_provider_);
+  }
+
   // A map from RenderPass id to the texture used to draw the RenderPass from.
   base::flat_map<AggregatedRenderPassId, ScopedRenderPassTexture>
       render_pass_textures_;
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc
index e3574b09..f6f0ff1 100644
--- a/components/viz/service/display/skia_renderer.cc
+++ b/components/viz/service/display/skia_renderer.cc
@@ -106,7 +106,7 @@
   points[3] = gfx::PointFToSkPoint(draw_region.p4());
 }
 
-bool IsTextureResource(DisplayResourceProvider* resource_provider,
+bool IsTextureResource(DisplayResourceProviderSkia* resource_provider,
                        ResourceId resource_id) {
   return !resource_provider->IsResourceSoftwareBacked(resource_id);
 }
@@ -585,7 +585,7 @@
     bool use_target_color_space) {
   if (!resource_id)
     return;
-  auto* resource_provider = skia_renderer->resource_provider_;
+  auto* resource_provider = skia_renderer->resource_provider();
   DCHECK(IsTextureResource(resource_provider, resource_id));
 
   gfx::ColorSpace color_space;
@@ -614,14 +614,14 @@
   ScopedYUVSkImageBuilder(SkiaRenderer* skia_renderer,
                           const YUVVideoDrawQuad* quad,
                           sk_sp<SkColorSpace> dst_color_space) {
-    DCHECK(IsTextureResource(skia_renderer->resource_provider_,
+    DCHECK(IsTextureResource(skia_renderer->resource_provider(),
                              quad->y_plane_resource_id()));
-    DCHECK(IsTextureResource(skia_renderer->resource_provider_,
+    DCHECK(IsTextureResource(skia_renderer->resource_provider(),
                              quad->u_plane_resource_id()));
-    DCHECK(IsTextureResource(skia_renderer->resource_provider_,
+    DCHECK(IsTextureResource(skia_renderer->resource_provider(),
                              quad->v_plane_resource_id()));
     DCHECK(quad->a_plane_resource_id() == kInvalidResourceId ||
-           IsTextureResource(skia_renderer->resource_provider_,
+           IsTextureResource(skia_renderer->resource_provider(),
                              quad->a_plane_resource_id()));
 
     // The image is always either NV12 or I420, possibly with a separate alpha
@@ -676,7 +676,6 @@
   const SkImage* sk_image() const { return sk_image_.get(); }
 
  private:
-  std::unique_ptr<DisplayResourceProvider::ScopedReadLockSkImage> lock_;
   sk_sp<SkImage> sk_image_;
 
   DISALLOW_COPY_AND_ASSIGN(ScopedYUVSkImageBuilder);
@@ -707,7 +706,7 @@
 SkiaRenderer::SkiaRenderer(const RendererSettings* settings,
                            const DebugRendererSettings* debug_settings,
                            OutputSurface* output_surface,
-                           DisplayResourceProvider* resource_provider,
+                           DisplayResourceProviderSkia* resource_provider,
                            OverlayProcessorInterface* overlay_processor,
                            SkiaOutputSurface* skia_output_surface)
     : DirectRenderer(settings,
@@ -720,7 +719,8 @@
   lock_set_for_external_use_.emplace(resource_provider, skia_output_surface_);
 
   current_frame_resource_fence_ = base::MakeRefCounted<FrameResourceFence>();
-  resource_provider_->SetReadLockFence(current_frame_resource_fence_.get());
+  this->resource_provider()->SetReadLockFence(
+      current_frame_resource_fence_.get());
 }
 
 SkiaRenderer::~SkiaRenderer() = default;
@@ -738,7 +738,7 @@
   for (const auto& pass : *current_frame()->render_passes_in_draw_order) {
     for (auto* quad : pass->quad_list) {
       for (ResourceId resource_id : quad->resources)
-        resource_provider_->InitializePromotionHintRequest(resource_id);
+        resource_provider()->InitializePromotionHintRequest(resource_id);
     }
   }
 #endif
@@ -1863,7 +1863,7 @@
                                    const DrawRPDQParams* rpdq_params,
                                    DrawQuadParams* params) {
   const gfx::ColorSpace& src_color_space =
-      resource_provider_->GetColorSpace(quad->resource_id());
+      resource_provider()->GetColorSpace(quad->resource_id());
   const bool needs_color_conversion_filter =
       quad->is_video_frame && src_color_space.IsHDR();
 
@@ -2012,9 +2012,9 @@
                                     const DrawRPDQParams* rpdq_params,
                                     DrawQuadParams* params) {
   DCHECK(!MustFlushBatchedQuads(quad, rpdq_params, *params));
-  // |resource_provider_| can be NULL in resourceless software draws, which
+  // |resource_provider()| can be NULL in resourceless software draws, which
   // should never produce tile quads in the first place.
-  DCHECK(resource_provider_);
+  DCHECK(resource_provider());
   ScopedSkImageBuilder builder(
       this, quad->resource_id(), /*maybe_concurrent_reads=*/false,
       quad->is_premultiplied ? kPremul_SkAlphaType : kUnpremul_SkAlphaType);
@@ -2079,13 +2079,14 @@
   const bool supports_dc_layers =
       output_surface_->capabilities().supports_dc_layers;
   if (supports_dc_layers && !src_color_space.IsHDR() &&
-      resource_provider_->IsOverlayCandidate(quad->y_plane_resource_id())) {
-    DCHECK(resource_provider_->IsOverlayCandidate(quad->u_plane_resource_id()));
+      resource_provider()->IsOverlayCandidate(quad->y_plane_resource_id())) {
+    DCHECK(
+        resource_provider()->IsOverlayCandidate(quad->u_plane_resource_id()));
     dst_color_space = gfx::ColorSpace::CreateSRGB();
   }
 #endif
 
-  DCHECK(resource_provider_);
+  DCHECK(resource_provider());
   // Pass in |frame_color_space| here instead of |dst_color_space| so the color
   // space transform going from SkImage to SkSurface is identity. The
   // SkColorFilter already handles color space conversion so this avoids
@@ -2143,7 +2144,7 @@
   // to OverlayProcessor as well.
   for (auto& overlay : current_frame()->overlay_list) {
     // Resources will be unlocked after the next SwapBuffers() is completed.
-    locks.emplace_back(resource_provider_, overlay.resource_id);
+    locks.emplace_back(resource_provider(), overlay.resource_id);
     auto& lock = locks.back();
 
     // Sync tokens ensure the texture to be overlaid is available before
@@ -2162,7 +2163,7 @@
         break;
 
       // Resources will be unlocked after the next SwapBuffers() is completed.
-      locks.emplace_back(resource_provider_, resource_id);
+      locks.emplace_back(resource_provider(), resource_id);
       auto& lock = locks.back();
 
       // Sync tokens ensure the texture to be overlaid is available before
@@ -2186,7 +2187,7 @@
 
     // TODO(https://crbug.com/894929): Track IOSurface in-use instead of just
     // unlocking after the next SwapBuffers is completed.
-    locks.emplace_back(resource_provider_,
+    locks.emplace_back(resource_provider(),
                        ca_layer_overlay.contents_resource_id);
     auto& lock = locks.back();
 
@@ -2211,7 +2212,7 @@
   // Only Wayland uses this code path.
   for (auto& overlay : current_frame()->overlay_list) {
     // Resources will be unlocked after the next SwapBuffers() is completed.
-    locks.emplace_back(resource_provider_, overlay.resource_id);
+    locks.emplace_back(resource_provider(), overlay.resource_id);
     auto& lock = locks.back();
 
     // Sync tokens ensure the texture to be overlaid is available before
@@ -2234,7 +2235,7 @@
     on_finished_callback = base::BindOnce(
         &FrameResourceFence::Signal, std::move(current_frame_resource_fence_));
     current_frame_resource_fence_ = base::MakeRefCounted<FrameResourceFence>();
-    resource_provider_->SetReadLockFence(current_frame_resource_fence_.get());
+    resource_provider()->SetReadLockFence(current_frame_resource_fence_.get());
   }
 
   skia_output_surface_->ScheduleOverlays(
@@ -2597,7 +2598,7 @@
     on_finished_callback = base::BindOnce(
         &FrameResourceFence::Signal, std::move(current_frame_resource_fence_));
     current_frame_resource_fence_ = base::MakeRefCounted<FrameResourceFence>();
-    resource_provider_->SetReadLockFence(current_frame_resource_fence_.get());
+    resource_provider()->SetReadLockFence(current_frame_resource_fence_.get());
   }
   skia_output_surface_->EndPaint(std::move(on_finished_callback));
 
@@ -2897,20 +2898,20 @@
 
 #if defined(OS_APPLE)
 bool SkiaRenderer::ScopedReadLockComparator::operator()(
-    const DisplayResourceProvider::ScopedReadLockSharedImage& lhs,
-    const DisplayResourceProvider::ScopedReadLockSharedImage& rhs) const {
+    const DisplayResourceProviderSkia::ScopedReadLockSharedImage& lhs,
+    const DisplayResourceProviderSkia::ScopedReadLockSharedImage& rhs) const {
   return lhs.mailbox() < rhs.mailbox();
 }
 
 bool SkiaRenderer::ScopedReadLockComparator::operator()(
-    const DisplayResourceProvider::ScopedReadLockSharedImage& lhs,
+    const DisplayResourceProviderSkia::ScopedReadLockSharedImage& lhs,
     const gpu::Mailbox& rhs) const {
   return lhs.mailbox() < rhs;
 }
 
 bool SkiaRenderer::ScopedReadLockComparator::operator()(
     const gpu::Mailbox& lhs,
-    const DisplayResourceProvider::ScopedReadLockSharedImage& rhs) const {
+    const DisplayResourceProviderSkia::ScopedReadLockSharedImage& rhs) const {
   return lhs < rhs.mailbox();
 }
 #endif  // defined(OS_APPLE)
diff --git a/components/viz/service/display/skia_renderer.h b/components/viz/service/display/skia_renderer.h
index 486d4836..2dbe305 100644
--- a/components/viz/service/display/skia_renderer.h
+++ b/components/viz/service/display/skia_renderer.h
@@ -15,6 +15,7 @@
 #include "build/build_config.h"
 #include "cc/cc_export.h"
 #include "components/viz/service/display/direct_renderer.h"
+#include "components/viz/service/display/display_resource_provider_skia.h"
 #include "components/viz/service/display/sync_query_collection.h"
 #include "components/viz/service/viz_service_export.h"
 #include "ui/latency/latency_info.h"
@@ -43,7 +44,7 @@
   SkiaRenderer(const RendererSettings* settings,
                const DebugRendererSettings* debug_settings,
                OutputSurface* output_surface,
-               DisplayResourceProvider* resource_provider,
+               DisplayResourceProviderSkia* resource_provider,
                OverlayProcessorInterface* overlay_processor,
                SkiaOutputSurface* skia_output_surface);
   ~SkiaRenderer() override;
@@ -250,6 +251,10 @@
   void PrepareRenderPassOverlay(CALayerOverlay* overlay);
 #endif
 
+  DisplayResourceProviderSkia* resource_provider() {
+    return static_cast<DisplayResourceProviderSkia*>(resource_provider_);
+  }
+
   // A map from RenderPass id to the texture used to draw the RenderPass from.
   struct RenderPassBacking {
     gfx::Size size;
@@ -302,17 +307,17 @@
   // the compositor thread. And the sync token will be released when the DDL
   // for the current frame is replayed on the GPU thread.
   // It is only used with DDL.
-  base::Optional<DisplayResourceProvider::LockSetForExternalUse>
+  base::Optional<DisplayResourceProviderSkia::LockSetForExternalUse>
       lock_set_for_external_use_;
 
   // Locks for overlays are pending for swapbuffers.
   base::circular_deque<
-      std::vector<DisplayResourceProvider::ScopedReadLockSharedImage>>
+      std::vector<DisplayResourceProviderSkia::ScopedReadLockSharedImage>>
       pending_overlay_locks_;
 
   // Locks for overlays have been committed. |pending_overlay_locks_| will
   // be moved to |committed_overlay_locks_| after SwapBuffers() completed.
-  std::vector<DisplayResourceProvider::ScopedReadLockSharedImage>
+  std::vector<DisplayResourceProviderSkia::ScopedReadLockSharedImage>
       committed_overlay_locks_;
 
 #if defined(OS_APPLE)
@@ -320,18 +325,20 @@
    public:
     using is_transparent = void;
     bool operator()(
-        const DisplayResourceProvider::ScopedReadLockSharedImage& lhs,
-        const DisplayResourceProvider::ScopedReadLockSharedImage& rhs) const;
+        const DisplayResourceProviderSkia::ScopedReadLockSharedImage& lhs,
+        const DisplayResourceProviderSkia::ScopedReadLockSharedImage& rhs)
+        const;
     bool operator()(
-        const DisplayResourceProvider::ScopedReadLockSharedImage& lhs,
+        const DisplayResourceProviderSkia::ScopedReadLockSharedImage& lhs,
         const gpu::Mailbox& rhs) const;
     bool operator()(
         const gpu::Mailbox& lhs,
-        const DisplayResourceProvider::ScopedReadLockSharedImage& rhs) const;
+        const DisplayResourceProviderSkia::ScopedReadLockSharedImage& rhs)
+        const;
   };
   // a set for locks of overlays which are waiting for releasing.
   // The set is using lock.mailbox() as the unique key.
-  base::flat_set<DisplayResourceProvider::ScopedReadLockSharedImage,
+  base::flat_set<DisplayResourceProviderSkia::ScopedReadLockSharedImage,
                  ScopedReadLockComparator>
       awaiting_release_overlay_locks_;
 #endif  // defined(OS_APPLE)
diff --git a/components/viz/service/display/software_renderer.cc b/components/viz/service/display/software_renderer.cc
index 96706fda..8512eca 100644
--- a/components/viz/service/display/software_renderer.cc
+++ b/components/viz/service/display/software_renderer.cc
@@ -76,11 +76,12 @@
 
 }  // namespace
 
-SoftwareRenderer::SoftwareRenderer(const RendererSettings* settings,
-                                   const DebugRendererSettings* debug_settings,
-                                   OutputSurface* output_surface,
-                                   DisplayResourceProvider* resource_provider,
-                                   OverlayProcessorInterface* overlay_processor)
+SoftwareRenderer::SoftwareRenderer(
+    const RendererSettings* settings,
+    const DebugRendererSettings* debug_settings,
+    OutputSurface* output_surface,
+    DisplayResourceProviderSoftware* resource_provider,
+    OverlayProcessorInterface* overlay_processor)
     : DirectRenderer(settings,
                      debug_settings,
                      output_surface,
@@ -240,8 +241,8 @@
   }
 }
 
-bool SoftwareRenderer::IsSoftwareResource(ResourceId resource_id) const {
-  return resource_provider_->IsResourceSoftwareBacked(resource_id);
+bool SoftwareRenderer::IsSoftwareResource(ResourceId resource_id) {
+  return resource_provider()->IsResourceSoftwareBacked(resource_id);
 }
 
 void SoftwareRenderer::DoDrawQuad(const DrawQuad* quad,
@@ -438,8 +439,8 @@
   }
 
   // TODO(skaslev): Add support for non-premultiplied alpha.
-  DisplayResourceProvider::ScopedReadLockSkImage lock(resource_provider_,
-                                                      quad->resource_id());
+  DisplayResourceProviderSoftware::ScopedReadLockSkImage lock(
+      resource_provider(), quad->resource_id());
   if (!lock.valid())
     return;
   const SkImage* image = lock.sk_image();
@@ -483,8 +484,8 @@
   DCHECK(resource_provider_);
   DCHECK(IsSoftwareResource(quad->resource_id()));
 
-  DisplayResourceProvider::ScopedReadLockSkImage lock(resource_provider_,
-                                                      quad->resource_id());
+  DisplayResourceProviderSoftware::ScopedReadLockSkImage lock(
+      resource_provider(), quad->resource_id());
   if (!lock.valid())
     return;
 
@@ -556,8 +557,8 @@
   }
 
   if (quad->mask_resource_id()) {
-    DisplayResourceProvider::ScopedReadLockSkImage mask_lock(
-        resource_provider_, quad->mask_resource_id());
+    DisplayResourceProviderSoftware::ScopedReadLockSkImage mask_lock(
+        resource_provider(), quad->mask_resource_id());
     if (!mask_lock.valid())
       return;
 
diff --git a/components/viz/service/display/software_renderer.h b/components/viz/service/display/software_renderer.h
index 1d34c54..d879a3b3 100644
--- a/components/viz/service/display/software_renderer.h
+++ b/components/viz/service/display/software_renderer.h
@@ -12,12 +12,12 @@
 #include "build/build_config.h"
 #include "components/viz/common/quads/aggregated_render_pass.h"
 #include "components/viz/service/display/direct_renderer.h"
+#include "components/viz/service/display/display_resource_provider_software.h"
 #include "components/viz/service/viz_service_export.h"
 #include "ui/latency/latency_info.h"
 
 namespace viz {
 class DebugBorderDrawQuad;
-class DisplayResourceProvider;
 class OutputSurface;
 class PictureDrawQuad;
 class AggregatedRenderPassDrawQuad;
@@ -31,7 +31,7 @@
   SoftwareRenderer(const RendererSettings* settings,
                    const DebugRendererSettings* debug_settings,
                    OutputSurface* output_surface,
-                   DisplayResourceProvider* resource_provider,
+                   DisplayResourceProviderSoftware* resource_provider,
                    OverlayProcessorInterface* overlay_processor);
 
   ~SoftwareRenderer() override;
@@ -77,7 +77,7 @@
   void ClearFramebuffer();
   void SetClipRect(const gfx::Rect& rect);
   void SetClipRRect(const gfx::RRectF& rrect);
-  bool IsSoftwareResource(ResourceId resource_id) const;
+  bool IsSoftwareResource(ResourceId resource_id);
 
   void DrawDebugBorderQuad(const DebugBorderDrawQuad* quad);
   void DrawPictureQuad(const PictureDrawQuad* quad);
@@ -108,6 +108,10 @@
       const AggregatedRenderPassDrawQuad* quad,
       SkTileMode content_tile_mode) const;
 
+  DisplayResourceProviderSoftware* resource_provider() {
+    return static_cast<DisplayResourceProviderSoftware*>(resource_provider_);
+  }
+
   // A map from RenderPass id to the bitmap used to draw the RenderPass from.
   base::flat_map<AggregatedRenderPassId, SkBitmap> render_pass_bitmaps_;
 
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
index 8d8f46d9..64d49b1 100644
--- a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
+++ b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
@@ -149,14 +149,28 @@
     UpdateNeedsBeginFramesInternal();
 
   // Let the animation manager process any new directives on the surface.
-  surface_animation_manager_.ProcessTransitionDirectives(
-      last_frame_time_,
-      surface->GetActiveFrame().metadata.transition_directives,
-      surface->GetSurfaceSavedFrameStorage());
+  // TODO(vmpstr): Figure out if `last_frame_time_` is correct here.
+  // SurfaceAcitvation may have happened some time after we sent the last
+  // BeginFrame to the client (which is when the frame time is updated). We may
+  // need to keep track of a separate time that updates when OnBeginFrame
+  // happens whether or not it is sent to the client.
+  bool started_animation =
+      surface_animation_manager_.ProcessTransitionDirectives(
+          last_frame_time_,
+          surface->GetActiveFrame().metadata.transition_directives,
+          surface->GetSurfaceSavedFrameStorage());
+  // If processing the new directives caused us to start an animation, then
+  // interpoate the frame immediately. This is needed since if we wait until the
+  // next BeginFrame to do the first interpolation, then we maybe have already
+  // drawn this destination frame.
+  if (started_animation)
+    surface_animation_manager_.InterpolateFrame(surface);
 
   // The above call can cause us to start an animation, meaning we need begin
   // frames. If that's the case, make sure to update the begin frame
   // observation.
+  // TODO(vmpstr): Note that if we need to produce an interpolation to the
+  // latest frame, then this is where we would do that.
   if (surface_animation_manager_.NeedsBeginFrame())
     UpdateNeedsBeginFramesInternal();
 
@@ -728,10 +742,19 @@
   if (surface_animation_manager_.NeedsBeginFrame()) {
     surface_animation_manager_.NotifyFrameAdvanced(args.frame_time);
 
-    // If notifying causes us to stop needing frames, then update needs begin
-    // frames, in case we no longer are interested in receiving begin frames.
-    if (!surface_animation_manager_.NeedsBeginFrame())
+    // Interpolate the frame here, since it is a reliable spot during the
+    // animation.
+    if (surface_animation_manager_.NeedsBeginFrame()) {
+      if (last_activated_surface_id_.is_valid()) {
+        auto* surface =
+            surface_manager_->GetSurfaceForId(last_activated_surface_id_);
+        surface_animation_manager_.InterpolateFrame(surface);
+      }
+    } else {
+      // If notifying causes us to stop needing frames, then update needs begin
+      // frames, in case we no longer are interested in receiving begin frames.
       UpdateNeedsBeginFramesInternal();
+    }
   }
 }
 
diff --git a/components/viz/service/surfaces/surface_saved_frame.cc b/components/viz/service/surfaces/surface_saved_frame.cc
index 2b3d7c2..7b1a6a4 100644
--- a/components/viz/service/surfaces/surface_saved_frame.cc
+++ b/components/viz/service/surfaces/surface_saved_frame.cc
@@ -6,7 +6,6 @@
 
 #include <utility>
 
-#include "base/bind.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/viz/common/frame_sinks/copy_output_request.h"
 #include "components/viz/common/quads/compositor_frame_transition_directive.h"
@@ -21,17 +20,12 @@
   DCHECK_EQ(directive.type(), CompositorFrameTransitionDirective::Type::kSave);
 }
 
-SurfaceSavedFrame::~SurfaceSavedFrame() {
-  if (texture_release_callback_) {
-    texture_release_callback_->Run(texture_result_.sync_token,
-                                   /*is_lost=*/false);
-  }
-}
+SurfaceSavedFrame::~SurfaceSavedFrame() = default;
 
 bool SurfaceSavedFrame::IsValid() const {
   // TODO(crbug.com/1174129): This needs to be updated with software copies as
   // well.
-  return !texture_result_.mailbox.IsZero();
+  return HasTextureResult();
 }
 
 void SurfaceSavedFrame::RequestCopyOfOutput(Surface* surface) {
@@ -55,13 +49,55 @@
   if (!result->GetTextureResult())
     return;
 
-  texture_result_ = *result->GetTextureResult();
-  texture_release_callback_ = result->TakeTextureOwnership();
+  auto copy_output_texture = *result->GetTextureResult();
+  texture_result_.mailbox = copy_output_texture.mailbox;
+  texture_result_.sync_token = copy_output_texture.sync_token;
+  texture_result_.size = result->size();
+  texture_result_.release_callback = result->TakeTextureOwnership();
 }
 
-void SurfaceSavedFrame::CompleteSavedFrameForTesting() {
+bool SurfaceSavedFrame::HasTextureResult() const {
+  return texture_result_.release_callback && !texture_result_.mailbox.IsZero();
+}
+
+SurfaceSavedFrame::TextureResult SurfaceSavedFrame::TakeTextureResult() {
+  DCHECK(HasTextureResult());
+  // Note that the TextureResult move constructor resets sufficient state in the
+  // member so that HasTextureResult() returns false afterwards, effectively
+  // clearing the member variable.
+  return std::move(texture_result_);
+}
+
+void SurfaceSavedFrame::CompleteSavedFrameForTesting(
+    base::OnceCallback<void(const gpu::SyncToken&, bool)> release_callback) {
   texture_result_.mailbox = gpu::Mailbox::GenerateForSharedImage();
+  texture_result_.release_callback =
+      SingleReleaseCallback::Create(std::move(release_callback));
   DCHECK(IsValid());
 }
 
+SurfaceSavedFrame::TextureResult::TextureResult() = default;
+SurfaceSavedFrame::TextureResult::TextureResult(TextureResult&& other) {
+  *this = std::move(other);
+}
+
+SurfaceSavedFrame::TextureResult::~TextureResult() {
+  if (release_callback)
+    release_callback->Run(sync_token, /*is_lost=*/false);
+}
+
+SurfaceSavedFrame::TextureResult& SurfaceSavedFrame::TextureResult::operator=(
+    TextureResult&& other) {
+  mailbox = std::move(other.mailbox);
+  other.mailbox = gpu::Mailbox();
+
+  sync_token = std::move(other.sync_token);
+  other.sync_token = gpu::SyncToken();
+
+  size = std::move(other.size);
+
+  release_callback = std::move(other.release_callback);
+  return *this;
+}
+
 }  // namespace viz
diff --git a/components/viz/service/surfaces/surface_saved_frame.h b/components/viz/service/surfaces/surface_saved_frame.h
index 8967f1f..4ee9c4a0 100644
--- a/components/viz/service/surfaces/surface_saved_frame.h
+++ b/components/viz/service/surfaces/surface_saved_frame.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "base/compiler_specific.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
 #include "components/viz/common/frame_sinks/copy_output_result.h"
@@ -20,6 +21,19 @@
 
 class VIZ_SERVICE_EXPORT SurfaceSavedFrame {
  public:
+  struct TextureResult {
+    TextureResult();
+    TextureResult(TextureResult&& other);
+    ~TextureResult();
+
+    TextureResult& operator=(TextureResult&& other);
+
+    gpu::Mailbox mailbox;
+    gpu::SyncToken sync_token;
+    gfx::Size size;
+    std::unique_ptr<SingleReleaseCallback> release_callback;
+  };
+
   explicit SurfaceSavedFrame(
       const CompositorFrameTransitionDirective& directive);
   ~SurfaceSavedFrame();
@@ -27,23 +41,27 @@
   // Returns true iff the frame is valid and complete.
   bool IsValid() const;
 
-  // Returns the animation duration from the saved directive.
-  base::TimeDelta animation_duration() const { return directive_.duration(); }
+  CompositorFrameTransitionDirective directive() const { return directive_; }
 
   // Appends copy output requests to the needed render passes in the active
   // frame.
   void RequestCopyOfOutput(Surface* surface);
 
+  // Takes the root texture result.
+  // TODO(crbug.com/1174141): We need to support more than just the root result.
+  bool HasTextureResult() const;
+  TextureResult TakeTextureResult() WARN_UNUSED_RESULT;
+
   // For testing functionality that ensures that we have a valid frame.
-  void CompleteSavedFrameForTesting();
+  void CompleteSavedFrameForTesting(
+      base::OnceCallback<void(const gpu::SyncToken&, bool)> release_callback);
 
  private:
   void NotifyCopyOfOutputComplete(std::unique_ptr<CopyOutputResult> result);
 
   CompositorFrameTransitionDirective directive_;
 
-  CopyOutputResult::TextureResult texture_result_;
-  std::unique_ptr<SingleReleaseCallback> texture_release_callback_;
+  TextureResult texture_result_;
 
   base::WeakPtrFactory<SurfaceSavedFrame> weak_factory_{this};
 };
diff --git a/components/viz/service/surfaces/surface_saved_frame_storage.cc b/components/viz/service/surfaces/surface_saved_frame_storage.cc
index 48b5939e..4106564 100644
--- a/components/viz/service/surfaces/surface_saved_frame_storage.cc
+++ b/components/viz/service/surfaces/surface_saved_frame_storage.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <utility>
 
+#include "base/bind.h"
 #include "base/cancelable_callback.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/viz/service/surfaces/surface.h"
@@ -62,4 +63,17 @@
   saved_frame_.reset();
 }
 
+void SurfaceSavedFrameStorage::ExpireForTesting() {
+  // Only do any work if we have an expiry closure.
+  if (!expiry_closure_.IsCancelled())
+    ExpireSavedFrame();
+}
+
+void SurfaceSavedFrameStorage::CompleteForTesting() {
+  if (saved_frame_) {
+    saved_frame_->CompleteSavedFrameForTesting(  // IN-TEST
+        base::BindOnce([](const gpu::SyncToken&, bool) {}));
+  }
+}
+
 }  // namespace viz
diff --git a/components/viz/service/surfaces/surface_saved_frame_storage.h b/components/viz/service/surfaces/surface_saved_frame_storage.h
index c901487..4a673a7 100644
--- a/components/viz/service/surfaces/surface_saved_frame_storage.h
+++ b/components/viz/service/surfaces/surface_saved_frame_storage.h
@@ -39,15 +39,8 @@
   std::unique_ptr<SurfaceSavedFrame> TakeSavedFrame();
 
   // For testing functionality.
-  void ExpireForTesting() {
-    // Only do any work if we have an expiry closure.
-    if (!expiry_closure_.IsCancelled())
-      ExpireSavedFrame();
-  }
-  void CompleteForTesting() {
-    if (saved_frame_)
-      saved_frame_->CompleteSavedFrameForTesting();
-  }
+  void ExpireForTesting();
+  void CompleteForTesting();
 
  private:
   // This expires the saved frame, if any.
diff --git a/components/viz/service/transitions/surface_animation_manager.cc b/components/viz/service/transitions/surface_animation_manager.cc
index ad02383..45ef64b 100644
--- a/components/viz/service/transitions/surface_animation_manager.cc
+++ b/components/viz/service/transitions/surface_animation_manager.cc
@@ -4,6 +4,7 @@
 
 #include "components/viz/service/transitions/surface_animation_manager.h"
 
+#include <utility>
 #include <vector>
 
 #include "base/time/time.h"
@@ -13,12 +14,13 @@
 SurfaceAnimationManager::SurfaceAnimationManager() = default;
 SurfaceAnimationManager::~SurfaceAnimationManager() = default;
 
-void SurfaceAnimationManager::ProcessTransitionDirectives(
+bool SurfaceAnimationManager::ProcessTransitionDirectives(
     base::TimeTicks last_frame_time,
     const std::vector<CompositorFrameTransitionDirective>& directives,
     SurfaceSavedFrameStorage* storage) {
   DCHECK_GE(last_frame_time, current_time_);
   current_time_ = last_frame_time;
+  bool started_animation = false;
   for (auto& directive : directives) {
     // Don't process directives with sequence ids smaller than or equal to the
     // last seen one. It is possible that we call this with the same frame
@@ -33,10 +35,11 @@
         ProcessSaveDirective(directive, storage);
         break;
       case CompositorFrameTransitionDirective::Type::kAnimate:
-        ProcessAnimateDirective(directive, storage);
+        started_animation |= ProcessAnimateDirective(directive, storage);
         break;
     }
   }
+  return started_animation;
 }
 
 void SurfaceAnimationManager::ProcessSaveDirective(
@@ -48,20 +51,29 @@
   storage->ProcessSaveDirective(directive);
 }
 
-void SurfaceAnimationManager::ProcessAnimateDirective(
+bool SurfaceAnimationManager::ProcessAnimateDirective(
     const CompositorFrameTransitionDirective& directive,
     SurfaceSavedFrameStorage* storage) {
   // We can only begin an animate if we are currently idle.
   if (state_ != State::kIdle)
-    return;
+    return false;
 
-  saved_frame_ = storage->TakeSavedFrame();
+  // Make sure we don't actually have anything saved as a texture.
+  DCHECK(!saved_root_texture_.has_value());
+
+  auto saved_frame = storage->TakeSavedFrame();
   // We can't animate if we don't have a saved frame.
-  if (!saved_frame_)
-    return;
+  if (!saved_frame || !saved_frame->IsValid())
+    return false;
+
+  // Convert the texture result into a transferable resource.
+  saved_directive_ = saved_frame->directive();
+  saved_root_texture_.emplace(
+      transferable_resource_tracker_.ImportResource(std::move(saved_frame)));
 
   state_ = State::kAnimating;
   started_time_ = current_time_;
+  return true;
 }
 
 bool SurfaceAnimationManager::NeedsBeginFrame() const {
@@ -88,29 +100,39 @@
 
 void SurfaceAnimationManager::FinishAnimationIfNeeded() {
   DCHECK_EQ(state_, State::kAnimating);
-  DCHECK(saved_frame_);
-  if (current_time_ >= started_time_ + saved_frame_->animation_duration())
+  DCHECK(saved_root_texture_.has_value());
+  if (current_time_ >= started_time_ + saved_directive_.duration())
     state_ = State::kDone;
 }
 
 void SurfaceAnimationManager::FinalizeAndDisposeOfState() {
   DCHECK_EQ(state_, State::kDone);
+  DCHECK(saved_root_texture_.has_value());
+  // Set state to idle.
   state_ = State::kIdle;
-  saved_frame_.reset();
+
+  // Ensure to return the texture / unref it.
+  transferable_resource_tracker_.UnrefResource(saved_root_texture_->id);
+  saved_root_texture_.reset();
+
+  // Other resets so that we don't accidentally access stale values.
+  saved_directive_ = CompositorFrameTransitionDirective();
   started_time_ = base::TimeTicks();
 }
 
 double SurfaceAnimationManager::CalculateAnimationProgress() const {
   DCHECK(state_ == State::kAnimating || state_ == State::kDone);
-  DCHECK(saved_frame_);
   if (state_ == State::kDone)
     return 1.;
 
-  double result =
-      (current_time_ - started_time_) / saved_frame_->animation_duration();
+  double result = (current_time_ - started_time_) / saved_directive_.duration();
   DCHECK_GE(result, 0.);
   DCHECK_LE(result, 1.);
   return result;
 }
 
+void SurfaceAnimationManager::InterpolateFrame(Surface* surface) {
+  // TODO(vmpstr): Do the interpolation and saving things back to surface.
+}
+
 }  // namespace viz
diff --git a/components/viz/service/transitions/surface_animation_manager.h b/components/viz/service/transitions/surface_animation_manager.h
index 114751e..40cc0223 100644
--- a/components/viz/service/transitions/surface_animation_manager.h
+++ b/components/viz/service/transitions/surface_animation_manager.h
@@ -5,18 +5,26 @@
 #ifndef COMPONENTS_VIZ_SERVICE_TRANSITIONS_SURFACE_ANIMATION_MANAGER_H_
 #define COMPONENTS_VIZ_SERVICE_TRANSITIONS_SURFACE_ANIMATION_MANAGER_H_
 
+#include <limits>
 #include <memory>
 #include <vector>
 
 #include "base/time/time.h"
 #include "components/viz/common/quads/compositor_frame_transition_directive.h"
+#include "components/viz/common/resources/resource_id.h"
+#include "components/viz/service/transitions/transferable_resource_tracker.h"
 #include "components/viz/service/viz_service_export.h"
 
 namespace viz {
 
-class SurfaceSavedFrame;
+class Surface;
 class SurfaceSavedFrameStorage;
 
+// The resource ids generated by SurfaceAnimationManager are restricted to the
+// last 3000 numbers of the ResourceId range.
+constexpr ResourceId kMinSurfaceAnimationResourceId =
+    std::numeric_limits<ResourceId>::max() - 3000u;
+
 // This class is responsible for processing CompositorFrameTransitionDirectives,
 // and keeping track of the animation state.
 // TODO(vmpstr): This class should also be responsible for interpolating frames
@@ -32,7 +40,10 @@
   // with same sequence ids will have no effect.
   // Uses `storage` for saving or retrieving animation parameters and saved
   // frames.
-  void ProcessTransitionDirectives(
+  // Returns true if this call caused an animation to begin. This is a signal
+  // that we need to interpolate the current active frame, even if we would
+  // normally not do so in the middle of the animation.
+  bool ProcessTransitionDirectives(
       base::TimeTicks last_frame_time,
       const std::vector<CompositorFrameTransitionDirective>& directives,
       SurfaceSavedFrameStorage* storage);
@@ -44,14 +55,16 @@
   // Notify when a begin frame happens and a frame is advanced.
   void NotifyFrameAdvanced(base::TimeTicks new_time);
 
-  // TODO(vmpstr): Add frame interpolation functionality that uses some
-  // interpolation curves.
+  // Interpolates from the saved frame to the current active frame on the
+  // surface, storing the result back on the surface.
+  void InterpolateFrame(Surface* surface);
 
  private:
   // Helpers to process specific directives.
   void ProcessSaveDirective(const CompositorFrameTransitionDirective& directive,
                             SurfaceSavedFrameStorage* storage);
-  void ProcessAnimateDirective(
+  // Returns true if the animation has started.
+  bool ProcessAnimateDirective(
       const CompositorFrameTransitionDirective& directive,
       SurfaceSavedFrameStorage* storage);
 
@@ -76,7 +89,11 @@
   base::TimeTicks started_time_;
   base::TimeTicks current_time_;
 
-  std::unique_ptr<SurfaceSavedFrame> saved_frame_;
+  TransferableResourceTracker transferable_resource_tracker_{
+      kMinSurfaceAnimationResourceId};
+
+  base::Optional<TransferableResource> saved_root_texture_;
+  CompositorFrameTransitionDirective saved_directive_;
 };
 
 }  // namespace viz
diff --git a/components/viz/service/transitions/transferable_resource_tracker.cc b/components/viz/service/transitions/transferable_resource_tracker.cc
index 38207f79..0523b33 100644
--- a/components/viz/service/transitions/transferable_resource_tracker.cc
+++ b/components/viz/service/transitions/transferable_resource_tracker.cc
@@ -25,20 +25,29 @@
 
 TransferableResourceTracker::~TransferableResourceTracker() = default;
 
-TransferableResource TransferableResourceTracker::AddMailboxResource(
-    const gpu::Mailbox& mailbox,
-    const gpu::SyncToken& sync_token,
-    const gfx::Size& size,
-    std::unique_ptr<SingleReleaseCallback> release_callback) {
-  TransferableResource result = TransferableResource::MakeGL(
-      mailbox, GL_LINEAR, GL_TEXTURE_2D, sync_token, size,
-      /*is_overlay_candidate=*/false);
+TransferableResource TransferableResourceTracker::ImportResource(
+    std::unique_ptr<SurfaceSavedFrame> saved_frame) {
+  DCHECK(saved_frame);
+  DCHECK(saved_frame->IsValid());
+  if (saved_frame->HasTextureResult())
+    return ImportTextureResult(saved_frame->TakeTextureResult());
+
+  NOTREACHED();
+  return TransferableResource();
+}
+
+TransferableResource TransferableResourceTracker::ImportTextureResult(
+    SurfaceSavedFrame::TextureResult texture) {
+  TransferableResource result =
+      TransferableResource::MakeGL(texture.mailbox, GL_LINEAR, GL_TEXTURE_2D,
+                                   texture.sync_token, texture.size,
+                                   /*is_overlay_candidate=*/false);
   result.id = GetNextAvailableResourceId();
 
   DCHECK(!base::Contains(managed_resources_, result.id));
   managed_resources_.emplace(
       result.id,
-      TransferableResourceHolder(result, std::move(release_callback)));
+      TransferableResourceHolder(result, std::move(texture.release_callback)));
   return result;
 }
 
diff --git a/components/viz/service/transitions/transferable_resource_tracker.h b/components/viz/service/transitions/transferable_resource_tracker.h
index 3cd8ae0..1fd874e 100644
--- a/components/viz/service/transitions/transferable_resource_tracker.h
+++ b/components/viz/service/transitions/transferable_resource_tracker.h
@@ -11,6 +11,7 @@
 #include "components/viz/common/resources/resource_id.h"
 #include "components/viz/common/resources/single_release_callback.h"
 #include "components/viz/common/resources/transferable_resource.h"
+#include "components/viz/service/surfaces/surface_saved_frame.h"
 #include "components/viz/service/viz_service_export.h"
 #include "gpu/command_buffer/common/mailbox.h"
 #include "gpu/command_buffer/common/sync_token.h"
@@ -28,11 +29,8 @@
   TransferableResourceTracker& operator=(const TransferableResourceTracker&) =
       delete;
 
-  TransferableResource AddMailboxResource(
-      const gpu::Mailbox& mailbox,
-      const gpu::SyncToken& sync_token,
-      const gfx::Size& size,
-      std::unique_ptr<SingleReleaseCallback> release_callback);
+  TransferableResource ImportResource(
+      std::unique_ptr<SurfaceSavedFrame> saved_frame);
 
   void RefResource(ResourceId id);
   void UnrefResource(ResourceId id);
@@ -40,6 +38,9 @@
  private:
   ResourceId GetNextAvailableResourceId();
 
+  TransferableResource ImportTextureResult(
+      SurfaceSavedFrame::TextureResult result);
+
   const ResourceId starting_id_;
   ResourceId next_id_;
 
diff --git a/components/viz/service/transitions/transferable_resource_tracker_unittest.cc b/components/viz/service/transitions/transferable_resource_tracker_unittest.cc
index d539be59..7e0b8df 100644
--- a/components/viz/service/transitions/transferable_resource_tracker_unittest.cc
+++ b/components/viz/service/transitions/transferable_resource_tracker_unittest.cc
@@ -3,23 +3,38 @@
 // found in the LICENSE file.
 
 #include <limits>
+#include <memory>
+#include <utility>
 
 #include "base/test/bind.h"
+#include "components/viz/common/quads/compositor_frame_transition_directive.h"
+#include "components/viz/service/surfaces/surface_saved_frame.h"
 #include "components/viz/service/transitions/transferable_resource_tracker.h"
 #include "gpu/command_buffer/common/mailbox.h"
 #include "gpu/command_buffer/common/sync_token.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace viz {
+namespace {
+
+std::unique_ptr<SurfaceSavedFrame> CreateFrameWithResult(
+    base::OnceCallback<void(const gpu::SyncToken&, bool)> callback) {
+  CompositorFrameTransitionDirective directive(
+      1, CompositorFrameTransitionDirective::Type::kSave);
+  auto frame = std::make_unique<SurfaceSavedFrame>(directive);
+  frame->CompleteSavedFrameForTesting(std::move(callback));
+  return frame;
+}
+
+}  // namespace
 
 TEST(TransferableResourceTrackerTest, IdInRange) {
   ResourceId starting_id = 12345u;
   TransferableResourceTracker tracker(starting_id);
 
   bool resource1_released = false;
-  auto resource1 = tracker.AddMailboxResource(
-      gpu::Mailbox::GenerateForSharedImage(), gpu::SyncToken(), gfx::Size(1, 2),
-      SingleReleaseCallback::Create(base::BindLambdaForTesting(
+  auto resource1 =
+      tracker.ImportResource(CreateFrameWithResult(base::BindLambdaForTesting(
           [&resource1_released](const gpu::SyncToken&, bool) {
             ASSERT_FALSE(resource1_released);
             resource1_released = true;
@@ -28,9 +43,8 @@
   EXPECT_GE(resource1.id, starting_id);
 
   bool resource2_released = false;
-  auto resource2 = tracker.AddMailboxResource(
-      gpu::Mailbox::GenerateForSharedImage(), gpu::SyncToken(), gfx::Size(1, 2),
-      SingleReleaseCallback::Create(base::BindLambdaForTesting(
+  auto resource2 =
+      tracker.ImportResource(CreateFrameWithResult(base::BindLambdaForTesting(
           [&resource2_released](const gpu::SyncToken&, bool) {
             ASSERT_FALSE(resource2_released);
             resource2_released = true;
@@ -55,10 +69,8 @@
   ResourceId last_id = 0;
   for (int i = 0; i < 10; ++i) {
     bool resource_released = false;
-    auto resource = tracker.AddMailboxResource(
-        gpu::Mailbox::GenerateForSharedImage(), gpu::SyncToken(),
-        gfx::Size(1, 2),
-        SingleReleaseCallback::Create(base::BindLambdaForTesting(
+    auto resource =
+        tracker.ImportResource(CreateFrameWithResult(base::BindLambdaForTesting(
             [&resource_released](const gpu::SyncToken&, bool) {
               ASSERT_FALSE(resource_released);
               resource_released = true;
@@ -76,19 +88,15 @@
   ResourceId starting_id = std::numeric_limits<ResourceId>::max() - 3u;
   TransferableResourceTracker tracker(starting_id);
 
-  auto reserved_resource = tracker.AddMailboxResource(
-      gpu::Mailbox::GenerateForSharedImage(), gpu::SyncToken(), gfx::Size(1, 2),
-      SingleReleaseCallback::Create(
-          base::BindOnce([](const gpu::SyncToken&, bool) {})));
+  auto reserved_resource = tracker.ImportResource(CreateFrameWithResult(
+      base::BindOnce([](const gpu::SyncToken&, bool) {})));
   EXPECT_GE(reserved_resource.id, starting_id);
 
   ResourceId last_id = 0;
   for (int i = 0; i < 10; ++i) {
     bool resource_released = false;
-    auto resource = tracker.AddMailboxResource(
-        gpu::Mailbox::GenerateForSharedImage(), gpu::SyncToken(),
-        gfx::Size(1, 2),
-        SingleReleaseCallback::Create(base::BindLambdaForTesting(
+    auto resource =
+        tracker.ImportResource(CreateFrameWithResult(base::BindLambdaForTesting(
             [&resource_released](const gpu::SyncToken&, bool) {
               ASSERT_FALSE(resource_released);
               resource_released = true;
diff --git a/components/webapps/browser/android/android_webapps_strings.grd b/components/webapps/browser/android/android_webapps_strings.grd
index 3c482955..db251bb06 100644
--- a/components/webapps/browser/android/android_webapps_strings.grd
+++ b/components/webapps/browser/android/android_webapps_strings.grd
@@ -184,6 +184,9 @@
       <message name="IDS_MENU_ADD_TO_HOMESCREEN_INSTALL" desc="Menu item for adding a shortcut to the Home screen. [CHAR-LIMIT=27]">
         Install app
       </message>
+      <message name="IDS_ADDED_TO_HOMESCREEN" desc="Text in a toast indicating that the website with the specified name was added to the user's Home screen.">
+        <ph name="NAME">%1$s<ex>Google</ex></ph> was added to your Home screen
+      </message>
     </messages>
   </release>
 </grit>
diff --git a/components/webapps/browser/android/java/src/org/chromium/components/webapps/WebappsUtils.java b/components/webapps/browser/android/java/src/org/chromium/components/webapps/WebappsUtils.java
index b687554..624d959 100644
--- a/components/webapps/browser/android/java/src/org/chromium/components/webapps/WebappsUtils.java
+++ b/components/webapps/browser/android/java/src/org/chromium/components/webapps/WebappsUtils.java
@@ -6,17 +6,23 @@
 
 import android.annotation.SuppressLint;
 import android.annotation.TargetApi;
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.content.pm.ShortcutInfo;
 import android.content.pm.ShortcutManager;
 import android.graphics.Bitmap;
+import android.graphics.drawable.Icon;
 import android.os.Build;
 
 import org.chromium.base.ContextUtils;
+import org.chromium.base.Log;
 import org.chromium.base.StrictModeContext;
+import org.chromium.base.ThreadUtils;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.components.webapk.lib.client.WebApkValidator;
+import org.chromium.ui.widget.Toast;
 
 import java.util.List;
 
@@ -24,6 +30,8 @@
  * Contains utilities for Web Apps and homescreen shortcuts.
  */
 public class WebappsUtils {
+    private static final String TAG = "WebappsUtils";
+
     private static final String INSTALL_SHORTCUT = "com.android.launcher.action.INSTALL_SHORTCUT";
 
     // True when Android O's ShortcutManager.requestPinShortcut() is supported.
@@ -48,6 +56,70 @@
     }
 
     /**
+     * Request Android to add a shortcut to the home screen.
+     * @param id The generated GUID of the shortcut.
+     * @param title Title of the shortcut.
+     * @param icon Image that represents the shortcut.
+     * @param isIconAdaptive Whether to create an Android Adaptive icon.
+     * @param shortcutIntent Intent to fire when the shortcut is activated.
+     */
+    public static void addShortcutToHomescreen(
+            String id, String title, Bitmap icon, boolean isIconAdaptive, Intent shortcutIntent) {
+        if (isRequestPinShortcutSupported()) {
+            addShortcutWithShortcutManager(id, title, icon, isIconAdaptive, shortcutIntent);
+            return;
+        }
+
+        Intent intent = createAddToHomeIntent(title, icon, shortcutIntent);
+        ContextUtils.getApplicationContext().sendBroadcast(intent);
+        showAddedToHomescreenToast(title);
+    }
+
+    @TargetApi(Build.VERSION_CODES.O)
+    public static void addShortcutWithShortcutManager(
+            String id, String title, Bitmap bitmap, boolean isMaskableIcon, Intent shortcutIntent) {
+        Context context = ContextUtils.getApplicationContext();
+
+        if (bitmap == null) {
+            Log.e(TAG, "Failed to find an icon for " + title + ", not adding.");
+            return;
+        }
+        Icon icon = isMaskableIcon ? Icon.createWithAdaptiveBitmap(bitmap)
+                                   : Icon.createWithBitmap(bitmap);
+
+        ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(context, id)
+                                            .setShortLabel(title)
+                                            .setLongLabel(title)
+                                            .setIcon(icon)
+                                            .setIntent(shortcutIntent)
+                                            .build();
+        try {
+            ShortcutManager shortcutManager = context.getSystemService(ShortcutManager.class);
+            shortcutManager.requestPinShortcut(shortcutInfo, null);
+        } catch (IllegalStateException e) {
+            Log.d(TAG,
+                    "Could not create pinned shortcut: device is locked, or "
+                            + "activity is backgrounded.");
+        }
+    }
+
+    /**
+     * Show toast to alert user that the shortcut was added to the home screen.
+     */
+    private static void showAddedToHomescreenToast(final String title) {
+        Context applicationContext = ContextUtils.getApplicationContext();
+        String toastText = applicationContext.getString(R.string.added_to_homescreen, title);
+        showToast(toastText);
+    }
+
+    public static void showToast(String text) {
+        assert ThreadUtils.runningOnUiThread();
+        Toast toast =
+                Toast.makeText(ContextUtils.getApplicationContext(), text, Toast.LENGTH_SHORT);
+        toast.show();
+    }
+
+    /**
      * Utility method to check if a shortcut can be added to the home screen.
      * @return if a shortcut can be added to the home screen under the current profile.
      */
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index d099977..8b76b9a 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -328,8 +328,6 @@
     "accessibility/browser_accessibility.h",
     "accessibility/browser_accessibility_manager.cc",
     "accessibility/browser_accessibility_manager.h",
-    "accessibility/browser_accessibility_position.cc",
-    "accessibility/browser_accessibility_position.h",
     "accessibility/browser_accessibility_state_impl.cc",
     "accessibility/browser_accessibility_state_impl.h",
     "accessibility/one_shot_accessibility_tree_search.cc",
diff --git a/content/browser/accessibility/accessibility_action_browsertest.cc b/content/browser/accessibility/accessibility_action_browsertest.cc
index 516730716..2bc3064b6 100644
--- a/content/browser/accessibility/accessibility_action_browsertest.cc
+++ b/content/browser/accessibility/accessibility_action_browsertest.cc
@@ -22,6 +22,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/accessibility/accessibility_switches.h"
 #include "ui/accessibility/ax_enums.mojom.h"
+#include "ui/accessibility/ax_node_position.h"
 #include "ui/gfx/codec/png_codec.h"
 #include "url/gurl.h"
 
@@ -463,10 +464,11 @@
   // We should do it with accessibility flags instead. http://crbug.com/672205
 #if !defined(OS_ANDROID)
   // Check that it really does contain two lines.
-  auto start_pos =
+  BrowserAccessibility::AXPosition start_position =
       target->CreatePositionAt(0, ax::mojom::TextAffinity::kDownstream);
-  auto end_of_line_1 = start_pos->CreateNextLineEndPosition(
-      ui::AXBoundaryBehavior::CrossBoundary);
+  BrowserAccessibility::AXPosition end_of_line_1 =
+      start_position->CreateNextLineEndPosition(
+          ui::AXBoundaryBehavior::CrossBoundary);
   EXPECT_EQ(5, end_of_line_1->text_offset());
 #endif
 }
@@ -496,10 +498,11 @@
   // We should do it with accessibility flags instead. http://crbug.com/672205
 #if !defined(OS_ANDROID)
   // Check that it really does contain two lines.
-  auto start_pos =
+  BrowserAccessibility::AXPosition start_position =
       target->CreatePositionAt(0, ax::mojom::TextAffinity::kDownstream);
-  auto end_of_line_1 = start_pos->CreateNextLineEndPosition(
-      ui::AXBoundaryBehavior::CrossBoundary);
+  BrowserAccessibility::AXPosition end_of_line_1 =
+      start_position->CreateNextLineEndPosition(
+          ui::AXBoundaryBehavior::CrossBoundary);
   EXPECT_EQ(5, end_of_line_1->text_offset());
 #endif
 }
diff --git a/content/browser/accessibility/accessibility_tree_formatter_mac.h b/content/browser/accessibility/accessibility_tree_formatter_mac.h
index c3f5b06..c851a27 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_mac.h
+++ b/content/browser/accessibility/accessibility_tree_formatter_mac.h
@@ -20,7 +20,7 @@
 class CONTENT_EXPORT AccessibilityTreeFormatterMac
     : public ui::AXTreeFormatterBase {
  public:
-  explicit AccessibilityTreeFormatterMac();
+  AccessibilityTreeFormatterMac();
   ~AccessibilityTreeFormatterMac() override;
 
   base::Value BuildTree(ui::AXPlatformNodeDelegate* root) const override;
@@ -63,9 +63,11 @@
   base::Value PopulateRect(NSRect) const;
   base::Value PopulateRange(NSRange) const;
   base::Value PopulateTextPosition(
-      BrowserAccessibilityPosition::AXPositionInstance::pointer,
-      const a11y::LineIndexer*) const;
-  base::Value PopulateTextMarkerRange(id, const a11y::LineIndexer*) const;
+      const BrowserAccessibility::AXPosition& position,
+      const a11y::LineIndexer* line_indexer) const;
+  base::Value PopulateTextMarkerRange(
+      id marker_range,
+      const a11y::LineIndexer* line_indexer) const;
   base::Value PopulateObject(id, const a11y::LineIndexer* line_indexer) const;
   base::Value PopulateArray(NSArray*,
                             const a11y::LineIndexer* line_indexer) const;
diff --git a/content/browser/accessibility/accessibility_tree_formatter_mac.mm b/content/browser/accessibility/accessibility_tree_formatter_mac.mm
index fa612b1..f0824ad 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_mac.mm
+++ b/content/browser/accessibility/accessibility_tree_formatter_mac.mm
@@ -55,9 +55,9 @@
 
 }  // namespace
 
-AccessibilityTreeFormatterMac::AccessibilityTreeFormatterMac() {}
+AccessibilityTreeFormatterMac::AccessibilityTreeFormatterMac() = default;
 
-AccessibilityTreeFormatterMac::~AccessibilityTreeFormatterMac() {}
+AccessibilityTreeFormatterMac::~AccessibilityTreeFormatterMac() = default;
 
 void AccessibilityTreeFormatterMac::AddDefaultFilters(
     std::vector<AXPropertyFilter>* property_filters) {
@@ -241,7 +241,7 @@
 
   // AXTextMarker
   if (content::IsAXTextMarker(value)) {
-    return PopulateTextPosition(content::AXTextMarkerToPosition(value).get(),
+    return PopulateTextPosition(content::AXTextMarkerToAXPosition(value),
                                 line_indexer);
   }
 
@@ -328,13 +328,15 @@
 }
 
 base::Value AccessibilityTreeFormatterMac::PopulateTextPosition(
-    BrowserAccessibilityPosition::AXPositionInstance::pointer position,
+    const BrowserAccessibility::AXPosition& position,
     const LineIndexer* line_indexer) const {
-  if (position->IsNullPosition()) {
+  if (position->IsNullPosition())
     return base::Value(kNULLValue);
-  }
 
-  BrowserAccessibility* anchor = position->GetAnchor();
+  auto* manager = BrowserAccessibilityManager::FromID(position->tree_id());
+  DCHECK(manager) << "A non-null position should have an associated AX tree.";
+  BrowserAccessibility* anchor = manager->GetFromID(position->anchor_id());
+  DCHECK(anchor) << "A non-null position should have a non-null anchor node.";
   BrowserAccessibilityCocoa* cocoa_anchor = ToBrowserAccessibilityCocoa(anchor);
 
   std::string affinity;
@@ -361,16 +363,18 @@
 }
 
 base::Value AccessibilityTreeFormatterMac::PopulateTextMarkerRange(
-    id object,
+    id marker_range,
     const LineIndexer* line_indexer) const {
-  auto range = content::AXTextMarkerRangeToRange(object);
-  if (range.IsNull()) {
+  BrowserAccessibility::AXRange ax_range =
+      content::AXTextMarkerRangeToAXRange(marker_range);
+  if (ax_range.IsNull())
     return base::Value(kNULLValue);
-  }
 
   base::Value dict(base::Value::Type::DICTIONARY);
-  dict.SetPath("anchor", PopulateTextPosition(range.anchor(), line_indexer));
-  dict.SetPath("focus", PopulateTextPosition(range.focus(), line_indexer));
+  dict.SetPath("anchor",
+               PopulateTextPosition(ax_range.anchor()->Clone(), line_indexer));
+  dict.SetPath("focus",
+               PopulateTextPosition(ax_range.focus()->Clone(), line_indexer));
   return dict;
 }
 
diff --git a/content/browser/accessibility/accessibility_tree_formatter_utils_mac.mm b/content/browser/accessibility/accessibility_tree_formatter_utils_mac.mm
index 955a1c8..88f339d 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_utils_mac.mm
+++ b/content/browser/accessibility/accessibility_tree_formatter_utils_mac.mm
@@ -379,36 +379,39 @@
   if (!IsAXTextMarkerRange(*obj))
     return OptionalNSObject::NotApplicable();
 
-  BrowserAccessibilityPosition::AXRangeType range =
-      AXTextMarkerRangeToRange(*obj);
+  const BrowserAccessibility::AXRange range = AXTextMarkerRangeToAXRange(*obj);
   if (range.IsNull())
     return OptionalNSObject::Error();
 
-  BrowserAccessibilityPosition::AXPositionInstance::pointer position =
-      range.anchor();
-  const BrowserAccessibility* node = position->GetAnchor();
+  auto* manager =
+      BrowserAccessibilityManager::FromID(range.anchor()->tree_id());
+  DCHECK(manager) << "A non-null range should have an associated AX tree.";
+  const BrowserAccessibility* node =
+      manager->GetFromID(range.anchor()->anchor_id());
+  DCHECK(node) << "A non-null range should have a non-null anchor node.";
   const BrowserAccessibilityCocoa* cocoa_node =
       ToBrowserAccessibilityCocoa(node);
   return OptionalNSObject::NotNilOrError(content::AXTextMarkerFrom(
-      cocoa_node, position->text_offset(), position->affinity()));
+      cocoa_node, range.anchor()->text_offset(), range.anchor()->affinity()));
 }
 
 OptionalNSObject TextMarkerRangeGetEndMarker(const OptionalNSObject& obj) {
   if (!IsAXTextMarkerRange(*obj))
     return OptionalNSObject::NotApplicable();
 
-  BrowserAccessibilityPosition::AXRangeType range =
-      AXTextMarkerRangeToRange(*obj);
+  const BrowserAccessibility::AXRange range = AXTextMarkerRangeToAXRange(*obj);
   if (range.IsNull())
     return OptionalNSObject::Error();
 
-  BrowserAccessibilityPosition::AXPositionInstance::pointer position =
-      range.focus();
-  const BrowserAccessibility* node = position->GetAnchor();
+  auto* manager = BrowserAccessibilityManager::FromID(range.focus()->tree_id());
+  DCHECK(manager) << "A non-null range should have an associated AX tree.";
+  const BrowserAccessibility* node =
+      manager->GetFromID(range.focus()->anchor_id());
+  DCHECK(node) << "A non-null range should have a non-null focus node.";
   const BrowserAccessibilityCocoa* cocoa_node =
       ToBrowserAccessibilityCocoa(node);
   return OptionalNSObject::NotNilOrError(content::AXTextMarkerFrom(
-      cocoa_node, position->text_offset(), position->affinity()));
+      cocoa_node, range.focus()->text_offset(), range.focus()->affinity()));
 }
 
 OptionalNSObject MakePairArray(const OptionalNSObject& obj1,
diff --git a/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc b/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc
index c007254..f4fd98b6 100644
--- a/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc
+++ b/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc
@@ -260,9 +260,9 @@
                             BrowserAccessibility* browser_accessibility_start,
                             BrowserAccessibility* browser_accessibility_end,
                             const bool align_to_top) {
-    ui::AXNodePosition::AXPositionInstance start =
+    BrowserAccessibility::AXPosition start =
         browser_accessibility_start->CreateTextPositionAt(0);
-    ui::AXNodePosition::AXPositionInstance end =
+    BrowserAccessibility::AXPosition end =
         browser_accessibility_end->CreateTextPositionAt(0)
             ->CreatePositionAtEndOfAnchor();
 
diff --git a/content/browser/accessibility/ax_platform_node_win_browsertest.cc b/content/browser/accessibility/ax_platform_node_win_browsertest.cc
index 2c0c14e..f2b31cf 100644
--- a/content/browser/accessibility/ax_platform_node_win_browsertest.cc
+++ b/content/browser/accessibility/ax_platform_node_win_browsertest.cc
@@ -653,16 +653,16 @@
             *after_iframe_node->CreatePositionAt(0));
 
   // Traverse the leaves of the AXTree forwards.
-  BrowserAccessibilityPosition::AXPositionInstance tree_position =
+  BrowserAccessibility::AXPosition tree_position =
       root_node->CreatePositionAt(0)->CreateNextLeafTreePosition();
   EXPECT_TRUE(tree_position->IsTreePosition());
-  EXPECT_EQ(before_iframe_node, tree_position->GetAnchor());
+  EXPECT_EQ(before_iframe_node->node(), tree_position->GetAnchor());
   tree_position = tree_position->CreateNextLeafTreePosition();
   EXPECT_TRUE(tree_position->IsTreePosition());
-  EXPECT_EQ(inside_iframe_node, tree_position->GetAnchor());
+  EXPECT_EQ(inside_iframe_node->node(), tree_position->GetAnchor());
   tree_position = tree_position->CreateNextLeafTreePosition();
   EXPECT_TRUE(tree_position->IsTreePosition());
-  EXPECT_EQ(after_iframe_node, tree_position->GetAnchor());
+  EXPECT_EQ(after_iframe_node->node(), tree_position->GetAnchor());
   tree_position = tree_position->CreateNextLeafTreePosition();
   EXPECT_TRUE(tree_position->IsNullPosition());
 
@@ -671,13 +671,13 @@
                       ->CreatePositionAtEndOfAnchor()
                       ->AsLeafTreePosition();
   EXPECT_TRUE(tree_position->IsTreePosition());
-  EXPECT_EQ(after_iframe_node, tree_position->GetAnchor());
+  EXPECT_EQ(after_iframe_node->node(), tree_position->GetAnchor());
   tree_position = tree_position->CreatePreviousLeafTreePosition();
   EXPECT_TRUE(tree_position->IsTreePosition());
-  EXPECT_EQ(inside_iframe_node, tree_position->GetAnchor());
+  EXPECT_EQ(inside_iframe_node->node(), tree_position->GetAnchor());
   tree_position = tree_position->CreatePreviousLeafTreePosition();
   EXPECT_TRUE(tree_position->IsTreePosition());
-  EXPECT_EQ(before_iframe_node, tree_position->GetAnchor());
+  EXPECT_EQ(before_iframe_node->node(), tree_position->GetAnchor());
   tree_position = tree_position->CreatePreviousLeafTreePosition();
   EXPECT_TRUE(tree_position->IsNullPosition());
 }
diff --git a/content/browser/accessibility/browser_accessibility.cc b/content/browser/accessibility/browser_accessibility.cc
index 1356c3b..5288c3d 100644
--- a/content/browser/accessibility/browser_accessibility.cc
+++ b/content/browser/accessibility/browser_accessibility.cc
@@ -21,7 +21,6 @@
 #include "content/public/common/use_zoom_for_dsf_policy.h"
 #include "third_party/blink/public/strings/grit/blink_strings.h"
 #include "ui/accessibility/ax_enums.mojom.h"
-#include "ui/accessibility/ax_node_position.h"
 #include "ui/accessibility/ax_role_properties.h"
 #include "ui/accessibility/ax_tree_id.h"
 #include "ui/accessibility/platform/ax_unique_id.h"
@@ -88,8 +87,8 @@
 
 int GetBoundaryTextOffsetInsideBaseAnchor(
     ax::mojom::MoveDirection direction,
-    const BrowserAccessibilityPosition::AXPositionInstance& base,
-    const BrowserAccessibilityPosition::AXPositionInstance& position) {
+    const BrowserAccessibility::AXPosition& base,
+    const BrowserAccessibility::AXPosition& position) {
   if (base->GetAnchor() == position->GetAnchor())
     return position->text_offset();
 
@@ -1035,12 +1034,12 @@
   return node()->GetOrComputeLineStartOffsets();
 }
 
-BrowserAccessibilityPosition::AXPositionInstance
-BrowserAccessibility::CreatePositionAt(int offset,
-                                       ax::mojom::TextAffinity affinity) const {
+BrowserAccessibility::AXPosition BrowserAccessibility::CreatePositionAt(
+    int offset,
+    ax::mojom::TextAffinity affinity) const {
   DCHECK(manager_);
-  return BrowserAccessibilityPosition::CreateTextPosition(
-      manager_->ax_tree_id(), GetId(), offset, affinity);
+  return ui::AXNodePosition::CreateTextPosition(manager_->ax_tree_id(), GetId(),
+                                                offset, affinity);
 }
 
 // |offset| could either be a text character or a child index in case of
@@ -1049,23 +1048,18 @@
 // tree positions.
 // TODO(nektar): Remove this function once selection fixes in Blink are
 // thoroughly tested and convert to tree positions.
-BrowserAccessibilityPosition::AXPositionInstance
+BrowserAccessibility::AXPosition
 BrowserAccessibility::CreatePositionForSelectionAt(int offset) const {
-  BrowserAccessibilityPositionInstance position =
+  AXPosition position =
       CreatePositionAt(offset, ax::mojom::TextAffinity::kDownstream)
           ->AsLeafTextPosition();
   if (position->GetAnchor() &&
-      position->GetAnchor()->GetRole() == ax::mojom::Role::kInlineTextBox) {
+      position->GetRole() == ax::mojom::Role::kInlineTextBox) {
     return position->CreateParentPosition();
   }
   return position;
 }
 
-base::string16 BrowserAccessibility::GetText() const {
-  // Default to inner text for non-native accessibility implementations.
-  return GetInnerText();
-}
-
 base::string16 BrowserAccessibility::GetNameAsString16() const {
   return base::UTF8ToUTF16(GetName());
 }
@@ -1313,8 +1307,7 @@
     int offset,
     ax::mojom::MoveDirection direction,
     ax::mojom::TextAffinity affinity) const {
-  BrowserAccessibilityPositionInstance position =
-      CreatePositionAt(offset, affinity);
+  const AXPosition position = CreatePositionAt(offset, affinity);
 
   // On Windows and Linux ATK, searching for a text boundary should always stop
   // at the boundary of the current object.
@@ -1415,8 +1408,8 @@
   return selection;
 }
 
-ui::AXNodePosition::AXPositionInstance
-BrowserAccessibility::CreateTextPositionAt(int offset) const {
+BrowserAccessibility::AXPosition BrowserAccessibility::CreateTextPositionAt(
+    int offset) const {
   DCHECK(manager_);
   return ui::AXNodePosition::CreateTextPosition(
       manager_->ax_tree_id(), GetId(), offset,
@@ -1514,10 +1507,9 @@
     return !child_count ||
            (child_count == 1 && InternalGetFirstChild()->IsText());
   }
-
-  // If this object is hosting another accessibility tree, then it is certainly
-  // not a leaf.
-  return PlatformGetRootOfChildTree() ? false : node()->IsLeaf();
+  if (PlatformGetRootOfChildTree())
+    return false;  // This object is hosting another tree.
+  return node()->IsLeaf();
 }
 
 bool BrowserAccessibility::IsFocused() const {
@@ -2138,9 +2130,8 @@
 
 bool BrowserAccessibility::SetHypertextSelection(int start_offset,
                                                  int end_offset) {
-  manager()->SetSelection(
-      AXPlatformRange(CreatePositionForSelectionAt(start_offset),
-                      CreatePositionForSelectionAt(end_offset)));
+  manager()->SetSelection(AXRange(CreatePositionForSelectionAt(start_offset),
+                                  CreatePositionForSelectionAt(end_offset)));
   return true;
 }
 
@@ -2154,7 +2145,8 @@
       << "A node should not have both children and a child tree.";
 
   BrowserAccessibilityManager* child_manager =
-      BrowserAccessibilityManager::FromID(AXTreeID::FromString(child_tree_id));
+      BrowserAccessibilityManager::FromID(
+          ui::AXTreeID::FromString(child_tree_id));
   if (child_manager && child_manager->GetRoot()->PlatformGetParent() == this)
     return child_manager->GetRoot();
   return nullptr;
diff --git a/content/browser/accessibility/browser_accessibility.h b/content/browser/accessibility/browser_accessibility.h
index 635c68f..52f1801 100644
--- a/content/browser/accessibility/browser_accessibility.h
+++ b/content/browser/accessibility/browser_accessibility.h
@@ -20,7 +20,6 @@
 #include "base/strings/string16.h"
 #include "base/strings/string_split.h"
 #include "build/build_config.h"
-#include "content/browser/accessibility/browser_accessibility_position.h"
 #include "content/common/content_export.h"
 #include "third_party/blink/public/web/web_ax_enums.h"
 #include "ui/accessibility/ax_enums.mojom-forward.h"
@@ -54,6 +53,10 @@
 ////////////////////////////////////////////////////////////////////////////////
 class CONTENT_EXPORT BrowserAccessibility : public ui::AXPlatformNodeDelegate {
  public:
+  using AXPosition = ui::AXNodePosition::AXPositionInstance;
+  using SerializedPosition = ui::AXNodePosition::SerializedPosition;
+  using AXRange = ui::AXRange<AXPosition::element_type>;
+
   // Creates a platform specific BrowserAccessibility. Ownership passes to the
   // caller.
   static BrowserAccessibility* Create();
@@ -359,37 +362,25 @@
   // implement this functionality.
   std::string GetLiveRegionText() const;
 
-  // Creates a text position rooted at this object. Does not conver to a
-  // leaf text position - see CreatePositionForSelectionAt, below.
-  BrowserAccessibilityPosition::AXPositionInstance CreatePositionAt(
-      int offset,
-      ax::mojom::TextAffinity affinity =
-          ax::mojom::TextAffinity::kDownstream) const;
+  // Creates a text position rooted at this object. Does not convert to a
+  // leaf text position - see CreatePositionForSelectionAt, below. |offset|
+  // could only be a character offset, either in the object's inner text
+  // (Android and Mac), or in the object's hypertext (Linux ATK and Windows
+  // IA2).
+  AXPosition CreatePositionAt(int offset,
+                              ax::mojom::TextAffinity affinity =
+                                  ax::mojom::TextAffinity::kDownstream) const;
 
-  // |offset| could either be a text character or a child index in case of
-  // non-text objects. Converts to a leaf text position if you pass a
-  // character offset on a container node.
-  BrowserAccessibilityPosition::AXPositionInstance CreatePositionForSelectionAt(
-      int offset) const;
+  // |offset| could only be a character offset. Depending on the platform, the
+  // character offset could be either in the object's inner text (Android and
+  // Mac), or an offset in the object's hypertext (Linux ATK and Windows IA2).
+  // Converts to a leaf text position if you pass a character offset on a
+  // non-leaf node.
+  AXPosition CreatePositionForSelectionAt(int offset) const;
 
   // Gets the text offsets where new lines start.
   std::vector<int> GetLineStartOffsets() const;
 
-  gfx::NativeViewAccessible GetNativeViewAccessible() override;
-
-  // AXPosition Support
-
-  // Returns the text that is present inside this node, where the
-  // representation of text found in descendant nodes depends on the platform.
-  // For example some platforms may include descendant text while while other
-  // platforms may use a special character to represent descendant text.
-  // Prefer either GetHypertext or GetInnerText so it's clear which API is
-  // called.
-  //
-  // TODO(nektar): Move this method to AXNode when AXNodePosition and
-  // BrowserAccessibilityPosition are merged into one class.
-  virtual base::string16 GetText() const;
-
   base::string16 GetNameAsString16() const;
 
   // AXPlatformNodeDelegate.
@@ -397,9 +388,9 @@
   const ui::AXNodeData& GetData() const override;
   const ui::AXTreeData& GetTreeData() const override;
   const ui::AXTree::Selection GetUnignoredSelection() const override;
-  ui::AXNodePosition::AXPositionInstance CreateTextPositionAt(
-      int offset) const override;
+  AXPosition CreateTextPositionAt(int offset) const override;
   gfx::NativeViewAccessible GetNSWindow() override;
+  gfx::NativeViewAccessible GetNativeViewAccessible() override;
   gfx::NativeViewAccessible GetParent() override;
   int GetChildCount() const override;
   gfx::NativeViewAccessible ChildAtIndex(int index) override;
@@ -548,15 +539,6 @@
   std::string ToString() const;
 
  protected:
-  // The UIA tree formatter needs access to GetUniqueId() to identify the
-  // starting point for tree dumps.
-  friend class AccessibilityTreeFormatterUia;
-
-  using BrowserAccessibilityPositionInstance =
-      BrowserAccessibilityPosition::AXPositionInstance;
-  using AXPlatformRange =
-      ui::AXRange<BrowserAccessibilityPositionInstance::element_type>;
-
   virtual ui::TextAttributeList ComputeTextAttributes() const;
 
   // The manager of this tree of accessibility objects.
@@ -578,6 +560,10 @@
 
   std::string SubtreeToStringHelper(size_t level) override;
 
+  // The UIA tree formatter needs access to GetUniqueId() to identify the
+  // starting point for tree dumps.
+  friend class AccessibilityTreeFormatterUia;
+
  private:
   // Return the bounds after converting from this node's coordinate system
   // (which is relative to its nearest scrollable ancestor) to the coordinate
diff --git a/content/browser/accessibility/browser_accessibility_android.cc b/content/browser/accessibility/browser_accessibility_android.cc
index af59f6e2..ffb95035 100644
--- a/content/browser/accessibility/browser_accessibility_android.cc
+++ b/content/browser/accessibility/browser_accessibility_android.cc
@@ -19,6 +19,7 @@
 #include "third_party/blink/public/strings/grit/blink_strings.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/accessibility/ax_assistant_structure.h"
+#include "ui/accessibility/ax_node_position.h"
 #include "ui/accessibility/ax_role_properties.h"
 #include "ui/accessibility/platform/ax_android_constants.h"
 #include "ui/accessibility/platform/ax_unique_id.h"
@@ -1833,9 +1834,9 @@
     return 0;
   }
 
-  auto position = anchor_object->CreatePositionAt(
+  AXPosition position = anchor_object->CreatePositionAt(
       unignored_selection.anchor_offset, unignored_selection.anchor_affinity);
-  while (position->GetAnchor() && position->GetAnchor() != this)
+  while (position->GetAnchor() && position->GetAnchor() != node())
     position = position->CreateParentPosition();
 
   return !position->IsNullPosition() ? position->text_offset() : 0;
@@ -1855,9 +1856,9 @@
   if (!focus_object)
     return 0;
 
-  auto position = focus_object->CreatePositionAt(
+  AXPosition position = focus_object->CreatePositionAt(
       unignored_selection.focus_offset, unignored_selection.focus_affinity);
-  while (position->GetAnchor() && position->GetAnchor() != this)
+  while (position->GetAnchor() && position->GetAnchor() != node())
     position = position->CreateParentPosition();
 
   return !position->IsNullPosition() ? position->text_offset() : 0;
@@ -2143,7 +2144,7 @@
           suggestion_ends->push_back(suggestion_end);
         }
       }
-      start_offset += node->GetText().length();
+      start_offset += node->GetInnerText().length();
     }
 
     // Implementation of NextInTreeOrder, but walking the internal tree.
diff --git a/content/browser/accessibility/browser_accessibility_auralinux.cc b/content/browser/accessibility/browser_accessibility_auralinux.cc
index a7463066..3c4bb24ca 100644
--- a/content/browser/accessibility/browser_accessibility_auralinux.cc
+++ b/content/browser/accessibility/browser_accessibility_auralinux.cc
@@ -53,10 +53,6 @@
   return GetNode();
 }
 
-base::string16 BrowserAccessibilityAuraLinux::GetText() const {
-  return GetHypertext();
-}
-
 base::string16 BrowserAccessibilityAuraLinux::GetHypertext() const {
   return GetNode()->AXPlatformNodeAuraLinux::GetHypertext();
 }
diff --git a/content/browser/accessibility/browser_accessibility_auralinux.h b/content/browser/accessibility/browser_accessibility_auralinux.h
index 6667aaf..5dd3b5dc 100644
--- a/content/browser/accessibility/browser_accessibility_auralinux.h
+++ b/content/browser/accessibility/browser_accessibility_auralinux.h
@@ -31,7 +31,6 @@
   // BrowserAccessibility methods.
   void OnDataChanged() override;
   ui::AXPlatformNode* GetAXPlatformNode() const override;
-  base::string16 GetText() const override;
   base::string16 GetHypertext() const override;
 
   gfx::NativeViewAccessible GetNativeViewAccessible() override;
diff --git a/content/browser/accessibility/browser_accessibility_cocoa.h b/content/browser/accessibility/browser_accessibility_cocoa.h
index 33a689c..a6493f1c 100644
--- a/content/browser/accessibility/browser_accessibility_cocoa.h
+++ b/content/browser/accessibility/browser_accessibility_cocoa.h
@@ -13,6 +13,8 @@
 #include "content/browser/accessibility/browser_accessibility.h"
 #include "content/browser/accessibility/browser_accessibility_manager.h"
 #include "content/common/content_export.h"
+#include "ui/accessibility/ax_node_position.h"
+#include "ui/accessibility/ax_range.h"
 
 namespace content {
 
@@ -33,25 +35,27 @@
   base::scoped_nsprotocol<id> edit_text_marker;
 };
 
-// Returns true if the given object is AXTextMarker object.
-bool IsAXTextMarker(id);
+// Uses a system API to verify that the given object is an AXTextMarker object.
+bool IsAXTextMarker(id text_marker);
 
-// Returns true if the given object is AXTextMarkerRange object.
-bool IsAXTextMarkerRange(id);
+// Uses a system API to verify that the given object is an AXTextMarkerRange
+// object.
+bool IsAXTextMarkerRange(id marker_range);
 
-// Returns browser accessibility position for the given AXTextMarker.
-CONTENT_EXPORT BrowserAccessibilityPosition::AXPositionInstance AXTextMarkerToPosition(id);
+// Returns the AXNodePosition representing the given AXTextMarker.
+CONTENT_EXPORT BrowserAccessibility::AXPosition AXTextMarkerToAXPosition(
+    id text_marker);
 
-// Returns browser accessibility range for the given AXTextMarkerRange.
-BrowserAccessibilityPosition::AXRangeType AXTextMarkerRangeToRange(id);
+// Returns the AXRange representing the given AXTextMarkerRange.
+BrowserAccessibility::AXRange AXTextMarkerRangeToAXRange(id marker_range);
 
-// Returns AXTextMarker for the given browser accessibility position.
+// Returns an AXTextMarker representing the given position in the tree.
 id AXTextMarkerFrom(const BrowserAccessibilityCocoa* anchor,
                     int offset,
                     ax::mojom::TextAffinity affinity);
 
-// Returns AXTextMarkerRange for the given browser accessibility positions.
-id AXTextMarkerRangeFrom(id anchor_textmarker, id focus_textmarker);
+// Returns an AXTextMarkerRange that spans the given AXTextMarkers.
+id AXTextMarkerRangeFrom(id anchor_text_marker, id focus_text_marker);
 
 }  // namespace content
 
diff --git a/content/browser/accessibility/browser_accessibility_cocoa.mm b/content/browser/accessibility/browser_accessibility_cocoa.mm
index b594031..b0e3b8d 100644
--- a/content/browser/accessibility/browser_accessibility_cocoa.mm
+++ b/content/browser/accessibility/browser_accessibility_cocoa.mm
@@ -26,7 +26,6 @@
 #include "content/browser/accessibility/browser_accessibility_mac.h"
 #include "content/browser/accessibility/browser_accessibility_manager.h"
 #include "content/browser/accessibility/browser_accessibility_manager_mac.h"
-#include "content/browser/accessibility/browser_accessibility_position.h"
 #include "content/browser/accessibility/one_shot_accessibility_tree_search.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/use_zoom_for_dsf_policy.h"
@@ -40,16 +39,9 @@
 
 #import "ui/accessibility/platform/ax_platform_node_mac.h"
 
-using BrowserAccessibilityPositionInstance =
-    content::BrowserAccessibilityPosition::AXPositionInstance;
-using SerializedPosition =
-    content::BrowserAccessibilityPosition::SerializedPosition;
-using AXPlatformRange =
-    ui::AXRange<BrowserAccessibilityPositionInstance::element_type>;
 using AXTextMarkerRangeRef = CFTypeRef;
 using AXTextMarkerRef = CFTypeRef;
 using StringAttribute = ax::mojom::StringAttribute;
-using content::BrowserAccessibilityPosition;
 using content::AccessibilityMatchPredicate;
 using content::BrowserAccessibility;
 using content::BrowserAccessibilityDelegate;
@@ -62,8 +54,9 @@
 using ui::AXTreeIDRegistry;
 
 static_assert(
-    std::is_trivially_copyable<SerializedPosition>::value,
-    "SerializedPosition must be POD because it's used to back an AXTextMarker");
+    std::is_trivially_copyable<BrowserAccessibility::SerializedPosition>::value,
+    "BrowserAccessibility::SerializedPosition must be POD because it's used to "
+    "back an AXTextMarker");
 
 namespace {
 
@@ -274,48 +267,51 @@
 
 // AXTextMarkerCreate is a system function that makes a copy of the data buffer
 // given to it.
-id CreateTextMarker(BrowserAccessibilityPositionInstance position) {
-  SerializedPosition serialized = position->Serialize();
+id CreateTextMarker(BrowserAccessibility::AXPosition position) {
+  BrowserAccessibility::SerializedPosition serialized = position->Serialize();
   AXTextMarkerRef cf_text_marker = AXTextMarkerCreate(
       kCFAllocatorDefault, reinterpret_cast<const UInt8*>(&serialized),
-      sizeof(SerializedPosition));
+      sizeof(BrowserAccessibility::SerializedPosition));
   return [static_cast<id>(cf_text_marker) autorelease];
 }
 
-id CreateTextMarkerRange(const AXPlatformRange range) {
-  SerializedPosition serialized_anchor = range.anchor()->Serialize();
-  SerializedPosition serialized_focus = range.focus()->Serialize();
+id CreateTextMarkerRange(const BrowserAccessibility::AXRange range) {
+  BrowserAccessibility::SerializedPosition serialized_anchor =
+      range.anchor()->Serialize();
+  BrowserAccessibility::SerializedPosition serialized_focus =
+      range.focus()->Serialize();
   base::ScopedCFTypeRef<AXTextMarkerRef> start_marker(AXTextMarkerCreate(
       kCFAllocatorDefault, reinterpret_cast<const UInt8*>(&serialized_anchor),
-      sizeof(SerializedPosition)));
+      sizeof(BrowserAccessibility::SerializedPosition)));
   base::ScopedCFTypeRef<AXTextMarkerRef> end_marker(AXTextMarkerCreate(
       kCFAllocatorDefault, reinterpret_cast<const UInt8*>(&serialized_focus),
-      sizeof(SerializedPosition)));
+      sizeof(BrowserAccessibility::SerializedPosition)));
   AXTextMarkerRangeRef cf_marker_range =
       AXTextMarkerRangeCreate(kCFAllocatorDefault, start_marker, end_marker);
   return [static_cast<id>(cf_marker_range) autorelease];
 }
 
-BrowserAccessibilityPositionInstance CreatePositionFromTextMarker(
-    id text_marker) {
+BrowserAccessibility::AXPosition CreatePositionFromTextMarker(id text_marker) {
   if (!content::IsAXTextMarker(text_marker))
-    return BrowserAccessibilityPosition::CreateNullPosition();
+    return ui::AXNodePosition::CreateNullPosition();
 
   AXTextMarkerRef cf_text_marker = static_cast<AXTextMarkerRef>(text_marker);
-  if (AXTextMarkerGetLength(cf_text_marker) != sizeof(SerializedPosition))
-    return BrowserAccessibilityPosition::CreateNullPosition();
+  if (AXTextMarkerGetLength(cf_text_marker) !=
+      sizeof(BrowserAccessibility::SerializedPosition))
+    return ui::AXNodePosition::CreateNullPosition();
 
   const UInt8* source_buffer = AXTextMarkerGetBytePtr(cf_text_marker);
   if (!source_buffer)
-    return BrowserAccessibilityPosition::CreateNullPosition();
+    return ui::AXNodePosition::CreateNullPosition();
 
-  return BrowserAccessibilityPosition::Unserialize(
-      *reinterpret_cast<const SerializedPosition*>(source_buffer));
+  return ui::AXNodePosition::Unserialize(
+      *reinterpret_cast<const BrowserAccessibility::SerializedPosition*>(
+          source_buffer));
 }
 
-AXPlatformRange CreateRangeFromTextMarkerRange(id marker_range) {
+BrowserAccessibility::AXRange CreateRangeFromTextMarkerRange(id marker_range) {
   if (!content::IsAXTextMarkerRange(marker_range)) {
-    return AXPlatformRange();
+    return BrowserAccessibility::AXRange();
   }
 
   AXTextMarkerRangeRef cf_marker_range =
@@ -326,54 +322,55 @@
   base::ScopedCFTypeRef<AXTextMarkerRef> end_marker(
       AXTextMarkerRangeCopyEndMarker(cf_marker_range));
   if (!start_marker.get() || !end_marker.get())
-    return AXPlatformRange();
+    return BrowserAccessibility::AXRange();
 
-  BrowserAccessibilityPositionInstance anchor =
+  BrowserAccessibility::AXPosition anchor =
       CreatePositionFromTextMarker(static_cast<id>(start_marker.get()));
-  BrowserAccessibilityPositionInstance focus =
+  BrowserAccessibility::AXPosition focus =
       CreatePositionFromTextMarker(static_cast<id>(end_marker.get()));
-  // |AXPlatformRange| takes ownership of its anchor and focus.
-  return AXPlatformRange(std::move(anchor), std::move(focus));
+  // |BrowserAccessibility::AXRange| takes ownership of its anchor and focus.
+  return BrowserAccessibility::AXRange(std::move(anchor), std::move(focus));
 }
 
-BrowserAccessibilityPositionInstance CreateTreePosition(
+BrowserAccessibility::AXPosition CreateTreePosition(
     const BrowserAccessibility& object,
     int offset) {
   const BrowserAccessibilityManager* manager = object.manager();
   DCHECK(manager);
-  return BrowserAccessibilityPosition::CreateTreePosition(
-      manager->ax_tree_id(), object.GetId(), offset);
+  return ui::AXNodePosition::CreateTreePosition(manager->ax_tree_id(),
+                                                object.GetId(), offset);
 }
 
-BrowserAccessibilityPositionInstance CreateTextPosition(
+BrowserAccessibility::AXPosition CreateTextPosition(
     const BrowserAccessibility& object,
     int offset,
     ax::mojom::TextAffinity affinity) {
   const BrowserAccessibilityManager* manager = object.manager();
   DCHECK(manager);
-  return BrowserAccessibilityPosition::CreateTextPosition(
+  return ui::AXNodePosition::CreateTextPosition(
       manager->ax_tree_id(), object.GetId(), offset, affinity);
 }
 
-AXPlatformRange CreateAXPlatformRange(const BrowserAccessibility& start_object,
-                                      int start_offset,
-                                      ax::mojom::TextAffinity start_affinity,
-                                      const BrowserAccessibility& end_object,
-                                      int end_offset,
-                                      ax::mojom::TextAffinity end_affinity) {
-  BrowserAccessibilityPositionInstance anchor =
+BrowserAccessibility::AXRange CreateAXRange(
+    const BrowserAccessibility& start_object,
+    int start_offset,
+    ax::mojom::TextAffinity start_affinity,
+    const BrowserAccessibility& end_object,
+    int end_offset,
+    ax::mojom::TextAffinity end_affinity) {
+  BrowserAccessibility::AXPosition anchor =
       start_object.PlatformIsLeaf()
           ? CreateTextPosition(start_object, start_offset, start_affinity)
           : CreateTreePosition(start_object, start_offset);
-  BrowserAccessibilityPositionInstance focus =
+  BrowserAccessibility::AXPosition focus =
       end_object.PlatformIsLeaf()
           ? CreateTextPosition(end_object, end_offset, end_affinity)
           : CreateTreePosition(end_object, end_offset);
-  // |AXPlatformRange| takes ownership of its anchor and focus.
-  return AXPlatformRange(std::move(anchor), std::move(focus));
+  // |BrowserAccessibility::AXRange| takes ownership of its anchor and focus.
+  return BrowserAccessibility::AXRange(std::move(anchor), std::move(focus));
 }
 
-AXPlatformRange GetSelectedRange(BrowserAccessibility& owner) {
+BrowserAccessibility::AXRange GetSelectedRange(BrowserAccessibility& owner) {
   const BrowserAccessibilityManager* manager = owner.manager();
   if (!manager)
     return {};
@@ -401,20 +398,26 @@
   ax::mojom::TextAffinity anchor_affinity = unignored_selection.anchor_affinity;
   ax::mojom::TextAffinity focus_affinity = unignored_selection.focus_affinity;
 
-  return CreateAXPlatformRange(*anchor_object, anchor_offset, anchor_affinity,
-                               *focus_object, focus_offset, focus_affinity);
+  return CreateAXRange(*anchor_object, anchor_offset, anchor_affinity,
+                       *focus_object, focus_offset, focus_affinity);
 }
 
-void AddMisspelledTextAttributes(const AXPlatformRange& ax_range,
+void AddMisspelledTextAttributes(const BrowserAccessibility::AXRange& ax_range,
                                  NSMutableAttributedString* attributed_string) {
   int anchor_start_offset = 0;
   [attributed_string beginEditing];
-  for (const AXPlatformRange& leaf_text_range : ax_range) {
+  for (const BrowserAccessibility::AXRange& leaf_text_range : ax_range) {
     DCHECK(!leaf_text_range.IsNull());
     DCHECK_EQ(leaf_text_range.anchor()->GetAnchor(),
               leaf_text_range.focus()->GetAnchor())
         << "An anchor range should only span a single object.";
-    const BrowserAccessibility* anchor = leaf_text_range.focus()->GetAnchor();
+
+    auto* manager =
+        BrowserAccessibilityManager::FromID(leaf_text_range.focus()->tree_id());
+    DCHECK(manager) << "A non-null position should have an associated AX tree.";
+    const BrowserAccessibility* anchor =
+        manager->GetFromID(leaf_text_range.focus()->anchor_id());
+    DCHECK(anchor) << "A non-null position should have a non-null anchor node.";
     const std::vector<int32_t>& marker_types =
         anchor->GetIntListAttribute(ax::mojom::IntListAttribute::kMarkerTypes);
     const std::vector<int>& marker_starts =
@@ -445,14 +448,16 @@
 }
 
 NSString* GetTextForTextMarkerRange(id marker_range) {
-  AXPlatformRange range = CreateRangeFromTextMarkerRange(marker_range);
+  BrowserAccessibility::AXRange range =
+      CreateRangeFromTextMarkerRange(marker_range);
   if (range.IsNull())
     return nil;
   return base::SysUTF16ToNSString(range.GetText());
 }
 
 NSAttributedString* GetAttributedTextForTextMarkerRange(id marker_range) {
-  AXPlatformRange ax_range = CreateRangeFromTextMarkerRange(marker_range);
+  BrowserAccessibility::AXRange ax_range =
+      CreateRangeFromTextMarkerRange(marker_range);
   if (ax_range.IsNull())
     return nil;
 
@@ -757,13 +762,13 @@
   return CFGetTypeID(cf_marker_range) == AXTextMarkerRangeGetTypeID();
 }
 
-BrowserAccessibilityPosition::AXPositionInstance
-content::AXTextMarkerToPosition(id text_marker) {
+BrowserAccessibility::AXPosition content::AXTextMarkerToAXPosition(
+    id text_marker) {
   return CreatePositionFromTextMarker(text_marker);
 }
 
-BrowserAccessibilityPosition::AXRangeType
-content::AXTextMarkerRangeToRange(id text_marker_range) {
+BrowserAccessibility::AXRange content::AXTextMarkerRangeToAXRange(
+    id text_marker_range) {
   return CreateRangeFromTextMarkerRange(text_marker_range);
 }
 
@@ -771,7 +776,7 @@
                              int offset,
                              ax::mojom::TextAffinity affinity) {
   BrowserAccessibility* anchor_node = [anchor owner];
-  BrowserAccessibilityPositionInstance position =
+  BrowserAccessibility::AXPosition position =
       CreateTextPosition(*anchor_node, offset, affinity);
   return CreateTextMarker(std::move(position));
 }
@@ -1362,7 +1367,7 @@
 - (id)endTextMarker {
   if (![self instanceActive])
     return nil;
-  BrowserAccessibilityPositionInstance position = _owner->CreatePositionAt(0);
+  BrowserAccessibility::AXPosition position = _owner->CreatePositionAt(0);
   return CreateTextMarker(position->CreatePositionAtEndOfContent());
 }
 
@@ -1571,14 +1576,14 @@
   if (!_owner->HasVisibleCaretOrSelection())
     return nil;
 
-  const AXPlatformRange range = GetSelectedRange(*_owner);
+  const BrowserAccessibility::AXRange range = GetSelectedRange(*_owner);
   // If the selection is not collapsed, then there is no visible caret.
   if (!range.IsCollapsed())
     return nil;
 
   // "ax::mojom::MoveDirection" is only relevant on platforms that use object
   // replacement characters in the accessibility tree. Mac is not one of them.
-  const BrowserAccessibilityPositionInstance caretPosition =
+  const BrowserAccessibility::AXPosition caretPosition =
       range.focus()->LowestCommonAncestor(*_owner->CreatePositionAt(0),
                                           ax::mojom::MoveDirection::kForward);
   DCHECK(!caretPosition->IsNullPosition())
@@ -2273,7 +2278,7 @@
   if (!_owner->HasVisibleCaretOrSelection())
     return nil;
 
-  const AXPlatformRange range = GetSelectedRange(*_owner);
+  const BrowserAccessibility::AXRange range = GetSelectedRange(*_owner);
   if (range.IsNull())
     return nil;
   return base::SysUTF16ToNSString(range.GetText());
@@ -2289,13 +2294,14 @@
   if (!_owner->HasVisibleCaretOrSelection())
     return nil;
 
-  const AXPlatformRange range = GetSelectedRange(*_owner).AsForwardRange();
+  const BrowserAccessibility::AXRange range =
+      GetSelectedRange(*_owner).AsForwardRange();
   if (range.IsNull())
     return nil;
 
   // "ax::mojom::MoveDirection" is only relevant on platforms that use object
   // replacement characters in the accessibility tree. Mac is not one of them.
-  const BrowserAccessibilityPositionInstance startPosition =
+  const BrowserAccessibility::AXPosition startPosition =
       range.anchor()->LowestCommonAncestor(*_owner->CreatePositionAt(0),
                                            ax::mojom::MoveDirection::kForward);
   DCHECK(!startPosition->IsNullPosition())
@@ -2310,7 +2316,7 @@
 - (id)selectedTextMarkerRange {
   if (![self instanceActive])
     return nil;
-  AXPlatformRange ax_range = GetSelectedRange(*_owner);
+  BrowserAccessibility::AXRange ax_range = GetSelectedRange(*_owner);
   if (ax_range.IsNull())
     return nil;
 
@@ -2355,7 +2361,7 @@
 - (id)startTextMarker {
   if (![self instanceActive])
     return nil;
-  BrowserAccessibilityPositionInstance position = _owner->CreatePositionAt(0);
+  BrowserAccessibility::AXPosition position = _owner->CreatePositionAt(0);
   return CreateTextMarker(position->CreatePositionAtStartOfContent());
 }
 
@@ -2688,8 +2694,9 @@
       [[[NSMutableAttributedString alloc]
           initWithString:base::SysUTF16ToNSString(innerText)] autorelease];
   if (!_owner->IsText()) {
-    AXPlatformRange ax_range(_owner->CreatePositionAt(0),
-                             _owner->CreatePositionAt(int{innerText.length()}));
+    BrowserAccessibility::AXRange ax_range(
+        _owner->CreatePositionAt(0),
+        _owner->CreatePositionAt(int{innerText.length()}));
     AddMisspelledTextAttributes(ax_range, attributedInnerText);
   }
 
@@ -2807,10 +2814,12 @@
   if ([attribute
           isEqualToString:
               NSAccessibilityUIElementForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibilityPositionInstance position =
+    BrowserAccessibility::AXPosition position =
         CreatePositionFromTextMarker(parameter);
-    if (!position->IsNullPosition())
-      return ToBrowserAccessibilityCocoa(position->GetAnchor());
+    if (!position->IsNullPosition()) {
+      return ToBrowserAccessibilityCocoa(
+          _owner->manager()->GetFromAXNode(position->GetAnchor()));
+    }
 
     return nil;
   }
@@ -2818,12 +2827,12 @@
   if ([attribute
           isEqualToString:
               NSAccessibilityTextMarkerRangeForUIElementParameterizedAttribute]) {
-    BrowserAccessibilityPositionInstance startPosition =
+    BrowserAccessibility::AXPosition startPosition =
         _owner->CreatePositionAt(0);
-    BrowserAccessibilityPositionInstance endPosition =
+    BrowserAccessibility::AXPosition endPosition =
         startPosition->CreatePositionAtEndOfAnchor();
-    AXPlatformRange range =
-        AXPlatformRange(std::move(startPosition), std::move(endPosition));
+    BrowserAccessibility::AXRange range = BrowserAccessibility::AXRange(
+        std::move(startPosition), std::move(endPosition));
     return CreateTextMarkerRange(std::move(range));
   }
 
@@ -2840,7 +2849,7 @@
   if ([attribute
           isEqualToString:
               NSAccessibilityNextTextMarkerForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibilityPositionInstance position =
+    BrowserAccessibility::AXPosition position =
         CreatePositionFromTextMarker(parameter);
     if (position->IsNullPosition())
       return nil;
@@ -2851,7 +2860,7 @@
   if ([attribute
           isEqualToString:
               NSAccessibilityPreviousTextMarkerForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibilityPositionInstance position =
+    BrowserAccessibility::AXPosition position =
         CreatePositionFromTextMarker(parameter);
     if (position->IsNullPosition())
       return nil;
@@ -2862,49 +2871,51 @@
   if ([attribute
           isEqualToString:
               NSAccessibilityLeftWordTextMarkerRangeForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibilityPositionInstance endPosition =
+    BrowserAccessibility::AXPosition endPosition =
         CreatePositionFromTextMarker(parameter);
     if (endPosition->IsNullPosition())
       return nil;
 
-    BrowserAccessibilityPositionInstance startWordPosition =
+    BrowserAccessibility::AXPosition startWordPosition =
         endPosition->CreatePreviousWordStartPosition(
             ui::AXBoundaryBehavior::StopAtAnchorBoundary);
-    BrowserAccessibilityPositionInstance endWordPosition =
+    BrowserAccessibility::AXPosition endWordPosition =
         endPosition->CreatePreviousWordEndPosition(
             ui::AXBoundaryBehavior::StopAtAnchorBoundary);
-    BrowserAccessibilityPositionInstance startPosition =
+    BrowserAccessibility::AXPosition startPosition =
         *startWordPosition <= *endWordPosition ? std::move(endWordPosition)
                                                : std::move(startWordPosition);
-    AXPlatformRange range(std::move(startPosition), std::move(endPosition));
+    BrowserAccessibility::AXRange range(std::move(startPosition),
+                                        std::move(endPosition));
     return CreateTextMarkerRange(std::move(range));
   }
 
   if ([attribute
           isEqualToString:
               NSAccessibilityRightWordTextMarkerRangeForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibilityPositionInstance startPosition =
+    BrowserAccessibility::AXPosition startPosition =
         CreatePositionFromTextMarker(parameter);
     if (startPosition->IsNullPosition())
       return nil;
 
-    BrowserAccessibilityPositionInstance endWordPosition =
+    BrowserAccessibility::AXPosition endWordPosition =
         startPosition->CreateNextWordEndPosition(
             ui::AXBoundaryBehavior::StopAtAnchorBoundary);
-    BrowserAccessibilityPositionInstance startWordPosition =
+    BrowserAccessibility::AXPosition startWordPosition =
         startPosition->CreateNextWordStartPosition(
             ui::AXBoundaryBehavior::StopAtAnchorBoundary);
-    BrowserAccessibilityPositionInstance endPosition =
+    BrowserAccessibility::AXPosition endPosition =
         *startWordPosition <= *endWordPosition ? std::move(startWordPosition)
                                                : std::move(endWordPosition);
-    AXPlatformRange range(std::move(startPosition), std::move(endPosition));
+    BrowserAccessibility::AXRange range(std::move(startPosition),
+                                        std::move(endPosition));
     return CreateTextMarkerRange(std::move(range));
   }
 
   if ([attribute
           isEqualToString:
               NSAccessibilityNextWordEndTextMarkerForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibilityPositionInstance position =
+    BrowserAccessibility::AXPosition position =
         CreatePositionFromTextMarker(parameter);
     if (position->IsNullPosition())
       return nil;
@@ -2915,7 +2926,7 @@
   if ([attribute
           isEqualToString:
               NSAccessibilityPreviousWordStartTextMarkerForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibilityPositionInstance position =
+    BrowserAccessibility::AXPosition position =
         CreatePositionFromTextMarker(parameter);
     if (position->IsNullPosition())
       return nil;
@@ -2925,7 +2936,7 @@
 
   if ([attribute isEqualToString:
                      NSAccessibilityLineForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibilityPositionInstance position =
+    BrowserAccessibility::AXPosition position =
         CreatePositionFromTextMarker(parameter);
     if (position->IsNullPosition())
       return nil;
@@ -2947,7 +2958,7 @@
       return nil;
 
     int lineStartOffset = (lineIndex > 0) ? lineBreaks[lineIndex - 1] : 0;
-    BrowserAccessibilityPositionInstance lineStartPosition = CreateTextPosition(
+    BrowserAccessibility::AXPosition lineStartPosition = CreateTextPosition(
         *_owner, lineStartOffset, ax::mojom::TextAffinity::kDownstream);
     if (lineStartPosition->IsNullPosition())
       return nil;
@@ -2956,60 +2967,62 @@
     // current line.
     lineStartPosition = lineStartPosition->CreatePreviousLineStartPosition(
         ui::AXBoundaryBehavior::StopIfAlreadyAtBoundary);
-    BrowserAccessibilityPositionInstance lineEndPosition =
+    BrowserAccessibility::AXPosition lineEndPosition =
         lineStartPosition->CreateNextLineEndPosition(
             ui::AXBoundaryBehavior::StopAtAnchorBoundary);
-    AXPlatformRange range(std::move(lineStartPosition),
-                          std::move(lineEndPosition));
+    BrowserAccessibility::AXRange range(std::move(lineStartPosition),
+                                        std::move(lineEndPosition));
     return CreateTextMarkerRange(std::move(range));
   }
 
   if ([attribute
           isEqualToString:
               NSAccessibilityLeftLineTextMarkerRangeForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibilityPositionInstance endPosition =
+    BrowserAccessibility::AXPosition endPosition =
         CreatePositionFromTextMarker(parameter);
     if (endPosition->IsNullPosition())
       return nil;
 
-    BrowserAccessibilityPositionInstance startLinePosition =
+    BrowserAccessibility::AXPosition startLinePosition =
         endPosition->CreatePreviousLineStartPosition(
             ui::AXBoundaryBehavior::StopAtLastAnchorBoundary);
-    BrowserAccessibilityPositionInstance endLinePosition =
+    BrowserAccessibility::AXPosition endLinePosition =
         endPosition->CreatePreviousLineEndPosition(
             ui::AXBoundaryBehavior::StopAtLastAnchorBoundary);
-    BrowserAccessibilityPositionInstance startPosition =
+    BrowserAccessibility::AXPosition startPosition =
         *startLinePosition <= *endLinePosition ? std::move(endLinePosition)
                                                : std::move(startLinePosition);
-    AXPlatformRange range(std::move(startPosition), std::move(endPosition));
+    BrowserAccessibility::AXRange range(std::move(startPosition),
+                                        std::move(endPosition));
     return CreateTextMarkerRange(std::move(range));
   }
 
   if ([attribute
           isEqualToString:
               NSAccessibilityRightLineTextMarkerRangeForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibilityPositionInstance startPosition =
+    BrowserAccessibility::AXPosition startPosition =
         CreatePositionFromTextMarker(parameter);
     if (startPosition->IsNullPosition())
       return nil;
 
-    BrowserAccessibilityPositionInstance startLinePosition =
+    BrowserAccessibility::AXPosition startLinePosition =
         startPosition->CreateNextLineStartPosition(
             ui::AXBoundaryBehavior::StopAtLastAnchorBoundary);
-    BrowserAccessibilityPositionInstance endLinePosition =
+    BrowserAccessibility::AXPosition endLinePosition =
         startPosition->CreateNextLineEndPosition(
             ui::AXBoundaryBehavior::StopAtLastAnchorBoundary);
-    BrowserAccessibilityPositionInstance endPosition =
+    BrowserAccessibility::AXPosition endPosition =
         *startLinePosition <= *endLinePosition ? std::move(startLinePosition)
                                                : std::move(endLinePosition);
-    AXPlatformRange range(std::move(startPosition), std::move(endPosition));
+    BrowserAccessibility::AXRange range(std::move(startPosition),
+                                        std::move(endPosition));
     return CreateTextMarkerRange(std::move(range));
   }
 
   if ([attribute
           isEqualToString:
               NSAccessibilityNextLineEndTextMarkerForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibilityPositionInstance position =
+    BrowserAccessibility::AXPosition position =
         CreatePositionFromTextMarker(parameter);
     if (position->IsNullPosition())
       return nil;
@@ -3020,7 +3033,7 @@
   if ([attribute
           isEqualToString:
               NSAccessibilityPreviousLineStartTextMarkerForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibilityPositionInstance position =
+    BrowserAccessibility::AXPosition position =
         CreatePositionFromTextMarker(parameter);
     if (position->IsNullPosition())
       return nil;
@@ -3031,25 +3044,26 @@
   if ([attribute
           isEqualToString:
               NSAccessibilityParagraphTextMarkerRangeForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibilityPositionInstance position =
+    BrowserAccessibility::AXPosition position =
         CreatePositionFromTextMarker(parameter);
     if (position->IsNullPosition())
       return nil;
 
-    BrowserAccessibilityPositionInstance startPosition =
+    BrowserAccessibility::AXPosition startPosition =
         position->CreatePreviousParagraphStartPosition(
             ui::AXBoundaryBehavior::StopIfAlreadyAtBoundary);
-    BrowserAccessibilityPositionInstance endPosition =
+    BrowserAccessibility::AXPosition endPosition =
         position->CreateNextParagraphEndPosition(
             ui::AXBoundaryBehavior::StopIfAlreadyAtBoundary);
-    AXPlatformRange range(std::move(startPosition), std::move(endPosition));
+    BrowserAccessibility::AXRange range(std::move(startPosition),
+                                        std::move(endPosition));
     return CreateTextMarkerRange(std::move(range));
   }
 
   if ([attribute
           isEqualToString:
               NSAccessibilityNextParagraphEndTextMarkerForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibilityPositionInstance position =
+    BrowserAccessibility::AXPosition position =
         CreatePositionFromTextMarker(parameter);
     if (position->IsNullPosition())
       return nil;
@@ -3060,7 +3074,7 @@
   if ([attribute
           isEqualToString:
               NSAccessibilityPreviousParagraphStartTextMarkerForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibilityPositionInstance position =
+    BrowserAccessibility::AXPosition position =
         CreatePositionFromTextMarker(parameter);
     if (position->IsNullPosition())
       return nil;
@@ -3071,18 +3085,19 @@
   if ([attribute
           isEqualToString:
               NSAccessibilityStyleTextMarkerRangeForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibilityPositionInstance position =
+    BrowserAccessibility::AXPosition position =
         CreatePositionFromTextMarker(parameter);
     if (position->IsNullPosition())
       return nil;
 
-    BrowserAccessibilityPositionInstance startPosition =
+    BrowserAccessibility::AXPosition startPosition =
         position->CreatePreviousFormatStartPosition(
             ui::AXBoundaryBehavior::StopIfAlreadyAtBoundary);
-    BrowserAccessibilityPositionInstance endPosition =
+    BrowserAccessibility::AXPosition endPosition =
         position->CreateNextFormatEndPosition(
             ui::AXBoundaryBehavior::StopIfAlreadyAtBoundary);
-    AXPlatformRange range(std::move(startPosition), std::move(endPosition));
+    BrowserAccessibility::AXRange range(std::move(startPosition),
+                                        std::move(endPosition));
     return CreateTextMarkerRange(std::move(range));
   }
 
@@ -3100,7 +3115,7 @@
 
   if ([attribute isEqualToString:
                      NSAccessibilityIndexForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibilityPositionInstance position =
+    BrowserAccessibility::AXPosition position =
         CreatePositionFromTextMarker(parameter);
     if (position->IsNullPosition())
       return nil;
@@ -3152,7 +3167,7 @@
   if ([attribute
           isEqualToString:
               NSAccessibilityLineTextMarkerRangeForTextMarkerParameterizedAttribute]) {
-    BrowserAccessibilityPositionInstance position =
+    BrowserAccessibility::AXPosition position =
         CreatePositionFromTextMarker(parameter);
     if (position->IsNullPosition())
       return nil;
@@ -3162,13 +3177,14 @@
     // the previous line. This is what Safari does.
     //
     // Note that hard line breaks are on a line of their own.
-    BrowserAccessibilityPositionInstance startPosition =
+    BrowserAccessibility::AXPosition startPosition =
         position->CreatePreviousLineStartPosition(
             ui::AXBoundaryBehavior::StopIfAlreadyAtBoundary);
-    BrowserAccessibilityPositionInstance endPosition =
+    BrowserAccessibility::AXPosition endPosition =
         startPosition->CreateNextLineStartPosition(
             ui::AXBoundaryBehavior::StopAtLastAnchorBoundary);
-    AXPlatformRange range(std::move(startPosition), std::move(endPosition));
+    BrowserAccessibility::AXRange range(std::move(startPosition),
+                                        std::move(endPosition));
     return CreateTextMarkerRange(std::move(range));
   }
 
@@ -3178,12 +3194,13 @@
     BrowserAccessibility* startObject;
     BrowserAccessibility* endObject;
     int startOffset, endOffset;
-    AXPlatformRange range = CreateRangeFromTextMarkerRange(parameter);
+    BrowserAccessibility::AXRange range =
+        CreateRangeFromTextMarkerRange(parameter);
     if (range.IsNull())
       return nil;
 
-    startObject = range.anchor()->GetAnchor();
-    endObject = range.focus()->GetAnchor();
+    startObject = _owner->manager()->GetFromAXNode(range.anchor()->GetAnchor());
+    endObject = _owner->manager()->GetFromAXNode(range.focus()->GetAnchor());
     startOffset = range.anchor()->text_offset();
     endOffset = range.focus()->text_offset();
     DCHECK(startObject && endObject);
@@ -3207,23 +3224,23 @@
     if ([textMarkerArray count] != 2)
       return nil;
 
-    BrowserAccessibilityPositionInstance startPosition =
+    BrowserAccessibility::AXPosition startPosition =
         CreatePositionFromTextMarker([textMarkerArray objectAtIndex:0]);
-    BrowserAccessibilityPositionInstance endPosition =
+    BrowserAccessibility::AXPosition endPosition =
         CreatePositionFromTextMarker([textMarkerArray objectAtIndex:1]);
     if (*startPosition <= *endPosition) {
-      return CreateTextMarkerRange(
-          AXPlatformRange(std::move(startPosition), std::move(endPosition)));
+      return CreateTextMarkerRange(BrowserAccessibility::AXRange(
+          std::move(startPosition), std::move(endPosition)));
     } else {
-      return CreateTextMarkerRange(
-          AXPlatformRange(std::move(endPosition), std::move(startPosition)));
+      return CreateTextMarkerRange(BrowserAccessibility::AXRange(
+          std::move(endPosition), std::move(startPosition)));
     }
   }
 
   if ([attribute
           isEqualToString:
               NSAccessibilityTextMarkerDebugDescriptionParameterizedAttribute]) {
-    BrowserAccessibilityPositionInstance position =
+    BrowserAccessibility::AXPosition position =
         CreatePositionFromTextMarker(parameter);
     return base::SysUTF8ToNSString(position->ToString());
   }
@@ -3231,19 +3248,20 @@
   if ([attribute
           isEqualToString:
               NSAccessibilityTextMarkerRangeDebugDescriptionParameterizedAttribute]) {
-    AXPlatformRange range = CreateRangeFromTextMarkerRange(parameter);
+    BrowserAccessibility::AXRange range =
+        CreateRangeFromTextMarkerRange(parameter);
     return base::SysUTF8ToNSString(range.ToString());
   }
 
   if ([attribute
           isEqualToString:
               NSAccessibilityTextMarkerNodeDebugDescriptionParameterizedAttribute]) {
-    BrowserAccessibilityPositionInstance position =
+    BrowserAccessibility::AXPosition position =
         CreatePositionFromTextMarker(parameter);
     if (position->IsNullPosition())
       return @"nil";
     DCHECK(position->GetAnchor());
-    return base::SysUTF8ToNSString(position->GetAnchor()->ToString());
+    return base::SysUTF8ToNSString(position->GetAnchor()->data().ToString());
   }
 
   if ([attribute
@@ -3788,18 +3806,19 @@
   if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
     NSRange range = [(NSValue*)value rangeValue];
     BrowserAccessibilityManager* manager = _owner->manager();
-    manager->SetSelection(
-        AXPlatformRange(_owner->CreatePositionAt(range.location),
-                        _owner->CreatePositionAt(NSMaxRange(range))));
+    manager->SetSelection(BrowserAccessibility::AXRange(
+        _owner->CreatePositionAt(range.location),
+        _owner->CreatePositionAt(NSMaxRange(range))));
   }
   if ([attribute
           isEqualToString:NSAccessibilitySelectedTextMarkerRangeAttribute]) {
-    AXPlatformRange range = CreateRangeFromTextMarkerRange(value);
+    BrowserAccessibility::AXRange range = CreateRangeFromTextMarkerRange(value);
     if (range.IsNull())
       return;
     BrowserAccessibilityManager* manager = _owner->manager();
-    manager->SetSelection(AXPlatformRange(range.anchor()->AsLeafTextPosition(),
-                                          range.focus()->AsLeafTextPosition()));
+    manager->SetSelection(
+        BrowserAccessibility::AXRange(range.anchor()->AsLeafTextPosition(),
+                                      range.focus()->AsLeafTextPosition()));
   }
 }
 
diff --git a/content/browser/accessibility/browser_accessibility_cocoa_browsertest.mm b/content/browser/accessibility/browser_accessibility_cocoa_browsertest.mm
index 046bf1d..75086413 100644
--- a/content/browser/accessibility/browser_accessibility_cocoa_browsertest.mm
+++ b/content/browser/accessibility/browser_accessibility_cocoa_browsertest.mm
@@ -2,10 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "content/browser/accessibility/browser_accessibility_cocoa.h"
+
 #include "base/check.h"
 #include "base/strings/sys_string_conversions.h"
 #include "content/browser/accessibility/browser_accessibility.h"
-#include "content/browser/accessibility/browser_accessibility_cocoa.h"
 #include "content/browser/accessibility/browser_accessibility_mac.h"
 #include "content/browser/accessibility/browser_accessibility_manager.h"
 #include "content/browser/accessibility/browser_accessibility_manager_mac.h"
@@ -124,7 +125,7 @@
   AXTextEdit text_edit = [cocoa_text_field computeTextEdit];
   EXPECT_NE(text_edit.edit_text_marker, nil);
 
-  EXPECT_EQ(AXTextMarkerToPosition(text_edit.edit_text_marker)->ToString(),
+  EXPECT_EQ(AXTextMarkerToAXPosition(text_edit.edit_text_marker)->ToString(),
             "TextPosition anchor_id=4 text_offset=1 affinity=downstream "
             "annotated_text=B<>");
 }
diff --git a/content/browser/accessibility/browser_accessibility_com_win.cc b/content/browser/accessibility/browser_accessibility_com_win.cc
index e477505..fee6af4 100644
--- a/content/browser/accessibility/browser_accessibility_com_win.cc
+++ b/content/browser/accessibility/browser_accessibility_com_win.cc
@@ -39,11 +39,6 @@
 
 namespace content {
 
-using BrowserAccessibilityPositionInstance =
-    BrowserAccessibilityPosition::AXPositionInstance;
-using AXPlatformRange =
-    ui::AXRange<BrowserAccessibilityPositionInstance::element_type>;
-
 void AddAccessibilityModeFlags(ui::AXMode mode_flags) {
   BrowserAccessibilityStateImpl::GetInstance()->AddAccessibilityModeFlags(
       mode_flags);
diff --git a/content/browser/accessibility/browser_accessibility_manager.cc b/content/browser/accessibility/browser_accessibility_manager.cc
index 22e75e73..76d150c 100644
--- a/content/browser/accessibility/browser_accessibility_manager.cc
+++ b/content/browser/accessibility/browser_accessibility_manager.cc
@@ -17,11 +17,9 @@
 #include "base/metrics/user_metrics.h"
 #include "base/no_destructor.h"
 #include "build/build_config.h"
-#include "content/browser/accessibility/browser_accessibility.h"
 #include "content/common/render_accessibility.mojom.h"
 #include "content/public/common/use_zoom_for_dsf_policy.h"
 #include "ui/accessibility/ax_language_detection.h"
-#include "ui/accessibility/ax_node_position.h"
 #include "ui/accessibility/ax_tree_data.h"
 #include "ui/accessibility/ax_tree_manager_map.h"
 #include "ui/accessibility/ax_tree_serializer.h"
@@ -746,7 +744,7 @@
     return GetRoot();
 
   if (obj->HasStringAttribute(ax::mojom::StringAttribute::kChildTreeId)) {
-    AXTreeID child_tree_id = AXTreeID::FromString(
+    ui::AXTreeID child_tree_id = ui::AXTreeID::FromString(
         obj->GetStringAttribute(ax::mojom::StringAttribute::kChildTreeId));
     const BrowserAccessibilityManager* child_manager =
         BrowserAccessibilityManager::FromID(child_tree_id);
@@ -951,7 +949,7 @@
 }
 
 void BrowserAccessibilityManager::SetSelection(
-    const BrowserAccessibilityRange& range) {
+    const BrowserAccessibility::AXRange& range) {
   if (!delegate_ || range.IsNull())
     return;
 
@@ -1482,11 +1480,11 @@
   ax_tree()->RemoveObserver(observer);
 }
 
-AXTreeID BrowserAccessibilityManager::GetTreeID() const {
+ui::AXTreeID BrowserAccessibilityManager::GetTreeID() const {
   return ax_tree_id();
 }
 
-AXTreeID BrowserAccessibilityManager::GetParentTreeID() const {
+ui::AXTreeID BrowserAccessibilityManager::GetParentTreeID() const {
   return GetTreeData().parent_tree_id;
 }
 
@@ -1523,7 +1521,7 @@
         parent_manager->GetNodeFromTree(parent_tree_id, host_node_id);
     if (parent_node) {
       DCHECK_EQ(ax_tree_id_,
-                AXTreeID::FromString(parent_node->GetStringAttribute(
+                ui::AXTreeID::FromString(parent_node->GetStringAttribute(
                     ax::mojom::StringAttribute::kChildTreeId)));
       return parent_node;
     }
diff --git a/content/browser/accessibility/browser_accessibility_manager.h b/content/browser/accessibility/browser_accessibility_manager.h
index 14f0504..b28a614 100644
--- a/content/browser/accessibility/browser_accessibility_manager.h
+++ b/content/browser/accessibility/browser_accessibility_manager.h
@@ -18,7 +18,7 @@
 #include "base/macros.h"
 #include "base/scoped_observation.h"
 #include "build/build_config.h"
-#include "content/browser/accessibility/browser_accessibility_position.h"
+#include "content/browser/accessibility/browser_accessibility.h"
 #include "content/common/content_export.h"
 #include "content/common/render_accessibility.mojom-forward.h"
 #include "content/public/browser/ax_event_notification_details.h"
@@ -28,6 +28,7 @@
 #include "ui/accessibility/ax_event_generator.h"
 #include "ui/accessibility/ax_node.h"
 #include "ui/accessibility/ax_node_data.h"
+#include "ui/accessibility/ax_node_position.h"
 #include "ui/accessibility/ax_range.h"
 #include "ui/accessibility/ax_serializable_tree.h"
 #include "ui/accessibility/ax_tree.h"
@@ -40,7 +41,7 @@
 #include "ui/gfx/native_widget_types.h"
 
 namespace content {
-class BrowserAccessibility;
+
 class BrowserAccessibilityDelegate;
 class BrowserAccessibilityManager;
 #if defined(OS_ANDROID)
@@ -135,12 +136,6 @@
 class CONTENT_EXPORT BrowserAccessibilityManager : public ui::AXTreeObserver,
                                                    public ui::AXTreeManager,
                                                    public WebContentsObserver {
- protected:
-  using BrowserAccessibilityPositionInstance =
-      BrowserAccessibilityPosition::AXPositionInstance;
-  using BrowserAccessibilityRange =
-      ui::AXRange<BrowserAccessibilityPositionInstance::element_type>;
-
  public:
   // Creates the platform-specific BrowserAccessibilityManager.
   static BrowserAccessibilityManager* Create(
@@ -300,7 +295,7 @@
   void SetScrollOffset(const BrowserAccessibility& node, gfx::Point offset);
   void SetValue(const BrowserAccessibility& node, const std::string& value);
   void SetSelection(const ui::AXActionData& action_data);
-  void SetSelection(const BrowserAccessibilityRange& range);
+  void SetSelection(const BrowserAccessibility::AXRange& range);
   void ShowContextMenu(const BrowserAccessibility& node);
   void SignalEndOfTest();
 
@@ -470,8 +465,8 @@
   ui::AXNode* GetNodeFromTree(ui::AXNodeID node_id) const override;
   void AddObserver(ui::AXTreeObserver* observer) override;
   void RemoveObserver(ui::AXTreeObserver* observer) override;
-  AXTreeID GetTreeID() const override;
-  AXTreeID GetParentTreeID() const override;
+  ui::AXTreeID GetTreeID() const override;
+  ui::AXTreeID GetParentTreeID() const override;
   ui::AXNode* GetRootAsAXNode() const override;
   ui::AXNode* GetParentNodeFromParentTreeAsAXNode() const override;
   void WillBeRemovedFromMap() override;
diff --git a/content/browser/accessibility/browser_accessibility_position.cc b/content/browser/accessibility/browser_accessibility_position.cc
deleted file mode 100644
index 57cdc75..0000000
--- a/content/browser/accessibility/browser_accessibility_position.cc
+++ /dev/null
@@ -1,278 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/accessibility/browser_accessibility_position.h"
-
-#include "base/strings/string_util.h"
-#include "build/build_config.h"
-#include "content/browser/accessibility/browser_accessibility.h"
-#include "content/browser/accessibility/browser_accessibility_manager.h"
-#include "ui/accessibility/ax_enums.mojom.h"
-#include "ui/accessibility/ax_node_data.h"
-#include "ui/base/buildflags.h"
-
-namespace content {
-
-BrowserAccessibilityPosition::BrowserAccessibilityPosition() = default;
-
-BrowserAccessibilityPosition::~BrowserAccessibilityPosition() = default;
-
-BrowserAccessibilityPosition::BrowserAccessibilityPosition(
-    const BrowserAccessibilityPosition& other)
-    : ui::AXPosition<BrowserAccessibilityPosition, BrowserAccessibility>(
-          other) {}
-
-BrowserAccessibilityPosition::AXPositionInstance
-BrowserAccessibilityPosition::Clone() const {
-  return AXPositionInstance(new BrowserAccessibilityPosition(*this));
-}
-
-base::string16 BrowserAccessibilityPosition::GetText() const {
-  if (IsNullPosition())
-    return {};
-
-  // Special case, if a position's anchor node has only ignored descendants,
-  // i.e., it appears to be empty to assistive software, on some platforms we
-  // need to still treat it as a character and a word boundary. We achieve this
-  // by adding an embedded object character in the text representation used by
-  // this class, but we don't expose that character to assistive software that
-  // tries to retrieve the node's inner text.
-  if (IsEmptyObjectReplacedByCharacter())
-    return ui::AXNode::kEmbeddedCharacter;
-
-  DCHECK(GetAnchor());
-  return GetAnchor()->GetText();
-}
-
-ax::mojom::Role BrowserAccessibilityPosition::GetRole() const {
-  if (IsNullPosition())
-    return ax::mojom::Role::kNone;
-  return GetAnchor()->GetData().role;
-}
-
-bool BrowserAccessibilityPosition::IsInLineBreak() const {
-  if (IsNullPosition())
-    return false;
-  DCHECK(GetAnchor());
-  return GetAnchor()->IsLineBreakObject();
-}
-
-bool BrowserAccessibilityPosition::IsInTextObject() const {
-  if (IsNullPosition())
-    return false;
-  DCHECK(GetAnchor());
-  return GetAnchor()->IsText();
-}
-
-bool BrowserAccessibilityPosition::IsInWhiteSpace() const {
-  if (IsNullPosition())
-    return false;
-  DCHECK(GetAnchor());
-  return GetAnchor()->IsLineBreakObject() ||
-         base::ContainsOnlyChars(GetText(), base::kWhitespaceUTF16);
-}
-
-void BrowserAccessibilityPosition::AnchorChild(int child_index,
-                                               AXTreeID* tree_id,
-                                               ui::AXNodeID* child_id) const {
-  DCHECK(tree_id);
-  DCHECK(child_id);
-
-  if (!GetAnchor() || child_index < 0 || child_index >= AnchorChildCount()) {
-    *tree_id = ui::AXTreeIDUnknown();
-    *child_id = ui::kInvalidAXNodeID;
-    return;
-  }
-
-  BrowserAccessibility* child = nullptr;
-  if (GetAnchor()->PlatformIsLeaf()) {
-    child = GetAnchor()->InternalGetChild(child_index);
-  } else {
-    child = GetAnchor()->PlatformGetChild(child_index);
-  }
-  DCHECK(child);
-  *tree_id = child->manager()->ax_tree_id();
-  *child_id = child->GetId();
-}
-
-int BrowserAccessibilityPosition::AnchorChildCount() const {
-  if (!GetAnchor())
-    return 0;
-
-  if (GetAnchor()->PlatformIsLeaf()) {
-    return static_cast<int>(GetAnchor()->InternalChildCount());
-  } else {
-    return static_cast<int>(GetAnchor()->PlatformChildCount());
-  }
-}
-
-int BrowserAccessibilityPosition::AnchorUnignoredChildCount() const {
-  if (!GetAnchor())
-    return 0;
-
-  return static_cast<int>(GetAnchor()->InternalChildCount());
-}
-
-int BrowserAccessibilityPosition::AnchorIndexInParent() const {
-  return GetAnchor() ? GetAnchor()->GetIndexInParent()
-                     : AXPosition::INVALID_INDEX;
-}
-
-int BrowserAccessibilityPosition::GetAnchorSiblingCount() const {
-  BrowserAccessibility* parent = GetAnchor()->PlatformGetParent();
-  if (parent)
-    return static_cast<int>(parent->InternalChildCount());
-  return 0;
-}
-
-base::stack<BrowserAccessibility*>
-BrowserAccessibilityPosition::GetAncestorAnchors() const {
-  base::stack<BrowserAccessibility*> anchors;
-  BrowserAccessibility* current_anchor = GetAnchor();
-  while (current_anchor) {
-    anchors.push(current_anchor);
-    current_anchor = current_anchor->PlatformGetParent();
-  }
-  return anchors;
-}
-
-BrowserAccessibility* BrowserAccessibilityPosition::GetLowestUnignoredAncestor()
-    const {
-  if (!GetAnchor())
-    return nullptr;
-
-  return GetAnchor()->PlatformGetParent();
-}
-
-void BrowserAccessibilityPosition::AnchorParent(AXTreeID* tree_id,
-                                                ui::AXNodeID* parent_id) const {
-  DCHECK(tree_id);
-  DCHECK(parent_id);
-
-  if (!GetAnchor() || !GetAnchor()->PlatformGetParent()) {
-    *tree_id = ui::AXTreeIDUnknown();
-    *parent_id = ui::kInvalidAXNodeID;
-    return;
-  }
-
-  BrowserAccessibility* parent = GetAnchor()->PlatformGetParent();
-  *tree_id = parent->manager()->ax_tree_id();
-  *parent_id = parent->GetId();
-}
-
-BrowserAccessibility* BrowserAccessibilityPosition::GetNodeInTree(
-    AXTreeID tree_id,
-    ui::AXNodeID node_id) const {
-  if (tree_id == ui::AXTreeIDUnknown() || node_id == ui::kInvalidAXNodeID) {
-    return nullptr;
-  }
-
-  auto* manager = BrowserAccessibilityManager::FromID(tree_id);
-  if (!manager)
-    return nullptr;
-  return manager->GetFromID(node_id);
-}
-
-int32_t BrowserAccessibilityPosition::GetAnchorID(
-    BrowserAccessibility* node) const {
-  return node->GetId();
-}
-
-AXTreeID BrowserAccessibilityPosition::GetTreeID(
-    BrowserAccessibility* node) const {
-  return node->manager()->ax_tree_id();
-}
-
-bool BrowserAccessibilityPosition::IsEmbeddedObjectInParent() const {
-  // On some platforms, most objects are represented in the text of their
-  // parents with a special (embedded object) character and not with their
-  // actual text contents.
-#if defined(OS_WIN) || BUILDFLAG(USE_ATK)
-  // Not all objects in the internal accessibility tree are exposed to platform
-  // APIs.
-  return !IsNullPosition() && !GetAnchor()->IsText() &&
-         !GetAnchor()->IsChildOfLeaf();
-#else
-  return false;
-#endif
-}
-
-bool BrowserAccessibilityPosition::IsInLineBreakingObject() const {
-  if (IsNullPosition())
-    return false;
-  DCHECK(GetAnchor());
-  return GetAnchor()->GetBoolAttribute(
-             ax::mojom::BoolAttribute::kIsLineBreakingObject) &&
-         !GetAnchor()->IsInListMarker();
-}
-
-ax::mojom::Role BrowserAccessibilityPosition::GetAnchorRole() const {
-  if (IsNullPosition())
-    return ax::mojom::Role::kNone;
-  DCHECK(GetAnchor());
-  return GetRole(GetAnchor());
-}
-
-ax::mojom::Role BrowserAccessibilityPosition::GetRole(
-    BrowserAccessibility* node) const {
-  return node->GetRole();
-}
-
-ui::AXNodeTextStyles BrowserAccessibilityPosition::GetTextStyles() const {
-  // Check either the current anchor or its parent for text styles.
-  ui::AXNodeTextStyles current_anchor_text_styles =
-      !IsNullPosition() ? GetAnchor()->GetData().GetTextStyles()
-                        : ui::AXNodeTextStyles();
-  if (current_anchor_text_styles.IsUnset()) {
-    AXPositionInstance parent = CreateParentPosition();
-    if (!parent->IsNullPosition())
-      return parent->GetAnchor()->GetData().GetTextStyles();
-  }
-  return current_anchor_text_styles;
-}
-
-std::vector<int32_t> BrowserAccessibilityPosition::GetWordStartOffsets() const {
-  if (IsNullPosition())
-    return std::vector<int32_t>();
-  DCHECK(GetAnchor());
-  return GetAnchor()->GetIntListAttribute(
-      ax::mojom::IntListAttribute::kWordStarts);
-}
-
-std::vector<int32_t> BrowserAccessibilityPosition::GetWordEndOffsets() const {
-  if (IsNullPosition())
-    return std::vector<int32_t>();
-  DCHECK(GetAnchor());
-  return GetAnchor()->GetIntListAttribute(
-      ax::mojom::IntListAttribute::kWordEnds);
-}
-
-ui::AXNodeID BrowserAccessibilityPosition::GetNextOnLineID(
-    ui::AXNodeID node_id) const {
-  if (IsNullPosition())
-    return ui::kInvalidAXNodeID;
-  BrowserAccessibility* node = GetNodeInTree(tree_id(), node_id);
-  int next_on_line_id;
-  if (!node || !node->GetIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
-                                      &next_on_line_id)) {
-    return ui::kInvalidAXNodeID;
-  }
-  return static_cast<ui::AXNodeID>(next_on_line_id);
-}
-
-ui::AXNodeID BrowserAccessibilityPosition::GetPreviousOnLineID(
-    ui::AXNodeID node_id) const {
-  if (IsNullPosition())
-    return ui::kInvalidAXNodeID;
-  BrowserAccessibility* node = GetNodeInTree(tree_id(), node_id);
-  int previous_on_line_id;
-  if (!node ||
-      !node->GetIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
-                             &previous_on_line_id)) {
-    return ui::kInvalidAXNodeID;
-  }
-  return static_cast<ui::AXNodeID>(previous_on_line_id);
-}
-
-}  // namespace content
diff --git a/content/browser/accessibility/browser_accessibility_position.h b/content/browser/accessibility/browser_accessibility_position.h
deleted file mode 100644
index 24fa6182..0000000
--- a/content/browser/accessibility/browser_accessibility_position.h
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_POSITION_H_
-#define CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_POSITION_H_
-
-#include <stdint.h>
-
-#include <string>
-#include <vector>
-
-#include "base/strings/string16.h"
-#include "content/common/content_export.h"
-#include "ui/accessibility/ax_position.h"
-#include "ui/accessibility/ax_tree_id_registry.h"
-
-namespace content {
-
-class BrowserAccessibility;
-
-using AXTreeID = ui::AXTreeID;
-
-class CONTENT_EXPORT BrowserAccessibilityPosition
-    : public ui::AXPosition<BrowserAccessibilityPosition,
-                            BrowserAccessibility> {
- public:
-  BrowserAccessibilityPosition();
-  ~BrowserAccessibilityPosition() override;
-  BrowserAccessibilityPosition(const BrowserAccessibilityPosition& other);
-
-  AXPositionInstance Clone() const override;
-
-  base::string16 GetText() const override;
-  ax::mojom::Role GetRole() const override;
-  bool IsInLineBreak() const override;
-  bool IsInTextObject() const override;
-  bool IsInWhiteSpace() const override;
-
- protected:
-  void AnchorChild(int child_index,
-                   AXTreeID* tree_id,
-                   ui::AXNodeID* child_id) const override;
-  int AnchorChildCount() const override;
-  int AnchorUnignoredChildCount() const override;
-  int AnchorIndexInParent() const override;
-  int GetAnchorSiblingCount() const override;
-  base::stack<BrowserAccessibility*> GetAncestorAnchors() const override;
-  BrowserAccessibility* GetLowestUnignoredAncestor() const override;
-  void AnchorParent(AXTreeID* tree_id, ui::AXNodeID* parent_id) const override;
-  BrowserAccessibility* GetNodeInTree(AXTreeID tree_id,
-                                      ui::AXNodeID node_id) const override;
-  int32_t GetAnchorID(BrowserAccessibility* node) const override;
-  AXTreeID GetTreeID(BrowserAccessibility* node) const override;
-
-  bool IsEmbeddedObjectInParent() const override;
-
-  bool IsInLineBreakingObject() const override;
-  ax::mojom::Role GetAnchorRole() const override;
-  ax::mojom::Role GetRole(BrowserAccessibility* node) const override;
-  ui::AXNodeTextStyles GetTextStyles() const override;
-  std::vector<int32_t> GetWordStartOffsets() const override;
-  std::vector<int32_t> GetWordEndOffsets() const override;
-  ui::AXNodeID GetNextOnLineID(ui::AXNodeID node_id) const override;
-  ui::AXNodeID GetPreviousOnLineID(ui::AXNodeID node_id) const override;
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_POSITION_H_
diff --git a/content/browser/accessibility/browser_accessibility_unittest.cc b/content/browser/accessibility/browser_accessibility_unittest.cc
index b1c93100..49061e3 100644
--- a/content/browser/accessibility/browser_accessibility_unittest.cc
+++ b/content/browser/accessibility/browser_accessibility_unittest.cc
@@ -9,6 +9,7 @@
 #include "content/browser/accessibility/browser_accessibility_manager.h"
 #include "content/browser/accessibility/test_browser_accessibility_delegate.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/accessibility/ax_node_position.h"
 
 namespace content {
 
@@ -803,8 +804,9 @@
   ASSERT_NE(nullptr, input_accessible);
 
   // Create a text position at offset 0 in the input control
-  auto position = input_accessible->CreatePositionAt(
-      0, ax::mojom::TextAffinity::kDownstream);
+  BrowserAccessibility::AXPosition position =
+      input_accessible->CreatePositionAt(0,
+                                         ax::mojom::TextAffinity::kDownstream);
 
   // On platforms that expose IA2 or ATK hypertext, moving by word should work
   // the same as if the value of the text field is equal to the placeholder
@@ -816,8 +818,9 @@
   // "read current line". Only once the user starts typing should the
   // placeholder disappear.
 
-  auto next_word_start = position->CreateNextWordStartPosition(
-      ui::AXBoundaryBehavior::CrossBoundary);
+  BrowserAccessibility::AXPosition next_word_start =
+      position->CreateNextWordStartPosition(
+          ui::AXBoundaryBehavior::CrossBoundary);
   if (position->MaxTextOffset() == 0) {
     EXPECT_TRUE(next_word_start->IsNullPosition());
   } else {
@@ -827,8 +830,9 @@
         next_word_start->ToString());
   }
 
-  auto next_word_end = position->CreateNextWordEndPosition(
-      ui::AXBoundaryBehavior::CrossBoundary);
+  BrowserAccessibility::AXPosition next_word_end =
+      position->CreateNextWordEndPosition(
+          ui::AXBoundaryBehavior::CrossBoundary);
   if (position->MaxTextOffset() == 0) {
     EXPECT_TRUE(next_word_end->IsNullPosition());
   } else {
diff --git a/content/browser/accessibility/browser_accessibility_win.cc b/content/browser/accessibility/browser_accessibility_win.cc
index ecd59f8..e49d135c 100644
--- a/content/browser/accessibility/browser_accessibility_win.cc
+++ b/content/browser/accessibility/browser_accessibility_win.cc
@@ -66,10 +66,6 @@
   GetCOM()->FireNativeEvent(EVENT_OBJECT_LOCATIONCHANGE);
 }
 
-base::string16 BrowserAccessibilityWin::GetText() const {
-  return GetHypertext();
-}
-
 base::string16 BrowserAccessibilityWin::GetHypertext() const {
   return GetCOM()->AXPlatformNodeWin::GetHypertext();
 }
diff --git a/content/browser/accessibility/browser_accessibility_win.h b/content/browser/accessibility/browser_accessibility_win.h
index f7dcfab..7d54b402 100644
--- a/content/browser/accessibility/browser_accessibility_win.h
+++ b/content/browser/accessibility/browser_accessibility_win.h
@@ -31,7 +31,6 @@
   bool CanFireEvents() const override;
   ui::AXPlatformNode* GetAXPlatformNode() const override;
   void OnLocationChanged() override;
-  base::string16 GetText() const override;
   base::string16 GetHypertext() const override;
 
   const std::vector<gfx::NativeViewAccessible> GetUIADescendants()
diff --git a/content/browser/accessibility/cross_platform_accessibility_browsertest.cc b/content/browser/accessibility/cross_platform_accessibility_browsertest.cc
index fb139513..28a78ff 100644
--- a/content/browser/accessibility/cross_platform_accessibility_browsertest.cc
+++ b/content/browser/accessibility/cross_platform_accessibility_browsertest.cc
@@ -451,7 +451,7 @@
   // into a separate accessibility tree. (See "out-of-process cross-origin
   // iframes in Chromium documentation.)
   ASSERT_EQ(0u, iframe->children().size());
-  const ui::AXTreeID iframe_tree_id = AXTreeID::FromString(
+  const ui::AXTreeID iframe_tree_id = ui::AXTreeID::FromString(
       GetAttr(iframe, ax::mojom::StringAttribute::kChildTreeId));
   const BrowserAccessibilityManager* iframe_manager =
       BrowserAccessibilityManager::FromID(iframe_tree_id);
diff --git a/content/browser/accessibility/web_contents_accessibility_android.cc b/content/browser/accessibility/web_contents_accessibility_android.cc
index 405ef1f..ffc8a6b 100644
--- a/content/browser/accessibility/web_contents_accessibility_android.cc
+++ b/content/browser/accessibility/web_contents_accessibility_android.cc
@@ -999,15 +999,11 @@
     jint unique_id,
     jint start,
     jint end) {
-  using AXPlatformPositionInstance =
-      BrowserAccessibilityPosition::AXPositionInstance;
-  using AXPlatformRange = ui::AXRange<AXPlatformPositionInstance::element_type>;
-
   BrowserAccessibilityAndroid* node = GetAXFromUniqueID(unique_id);
   if (node) {
     node->manager()->SetSelection(
-        AXPlatformRange(node->CreatePositionForSelectionAt(start),
-                        node->CreatePositionForSelectionAt(end)));
+        BrowserAccessibility::AXRange(node->CreatePositionForSelectionAt(start),
+                                      node->CreatePositionForSelectionAt(end)));
   }
 }
 
diff --git a/content/browser/back_forward_cache_browsertest.cc b/content/browser/back_forward_cache_browsertest.cc
index 64b80367..ee88c93e 100644
--- a/content/browser/back_forward_cache_browsertest.cc
+++ b/content/browser/back_forward_cache_browsertest.cc
@@ -263,49 +263,20 @@
   void ExpectRestored(base::Location location) {
     ExpectOutcome(BackForwardCacheMetrics::HistoryNavigationOutcome::kRestored,
                   location);
+    ExpectReasons({}, {}, {}, {}, location);
   }
 
   void ExpectNotRestored(
-      std::vector<BackForwardCacheMetrics::NotRestoredReason> reasons,
+      std::vector<BackForwardCacheMetrics::NotRestoredReason> not_restored,
+      std::vector<blink::scheduler::WebSchedulerTrackedFeature> block_listed,
+      const std::vector<ShouldSwapBrowsingInstance>& not_swapped,
+      const std::vector<std::string>& disabled_for_render_frame_host,
       base::Location location) {
     ExpectOutcome(
         BackForwardCacheMetrics::HistoryNavigationOutcome::kNotRestored,
         location);
-    ExpectNotRestoredReasons(reasons, location);
-  }
-
-  void ExpectNotRestoredReasons(
-      std::vector<BackForwardCacheMetrics::NotRestoredReason> reasons,
-      base::Location location) {
-    uint64_t not_restored_reasons_bits = 0;
-    for (BackForwardCacheMetrics::NotRestoredReason reason : reasons) {
-      base::HistogramBase::Sample sample = base::HistogramBase::Sample(reason);
-      AddSampleToBuckets(&expected_not_restored_, sample);
-      not_restored_reasons_bits |= 1ull << static_cast<int>(reason);
-    }
-
-    EXPECT_THAT(histogram_tester_.GetAllSamples(
-                    "BackForwardCache.HistoryNavigationOutcome."
-                    "NotRestoredReason"),
-                UnorderedElementsAreArray(expected_not_restored_))
-        << location.ToString();
-
-    if (!check_all_sites_)
-      return;
-
-    EXPECT_THAT(histogram_tester_.GetAllSamples(
-                    "BackForwardCache.AllSites.HistoryNavigationOutcome."
-                    "NotRestoredReason"),
-                UnorderedElementsAreArray(expected_not_restored_))
-        << location.ToString();
-
-    std::string not_restored_reasons = "BackForwardCache.NotRestoredReasons";
-    expected_ukm_not_restored_reasons_.push_back(
-        {{not_restored_reasons, not_restored_reasons_bits}});
-    EXPECT_THAT(
-        ukm_recorder_->GetMetrics("HistoryNavigation", {not_restored_reasons}),
-        expected_ukm_not_restored_reasons_)
-        << location.ToString();
+    ExpectReasons(not_restored, block_listed, not_swapped,
+                  disabled_for_render_frame_host, location);
   }
 
   void ExpectNotRestoredDidNotChange(base::Location location) {
@@ -338,74 +309,6 @@
     ExpectBlocklistedFeatures({feature}, location);
   }
 
-  void ExpectBlocklistedFeatures(
-      std::vector<blink::scheduler::WebSchedulerTrackedFeature> features,
-      base::Location location) {
-    for (auto feature : features) {
-      base::HistogramBase::Sample sample = base::HistogramBase::Sample(feature);
-      AddSampleToBuckets(&expected_blocklisted_features_, sample);
-    }
-
-    EXPECT_THAT(histogram_tester_.GetAllSamples(
-                    "BackForwardCache.HistoryNavigationOutcome."
-                    "BlocklistedFeature"),
-                UnorderedElementsAreArray(expected_blocklisted_features_))
-        << location.ToString();
-
-    if (!check_all_sites_)
-      return;
-
-    EXPECT_THAT(histogram_tester_.GetAllSamples(
-                    "BackForwardCache.AllSites.HistoryNavigationOutcome."
-                    "BlocklistedFeature"),
-                UnorderedElementsAreArray(expected_blocklisted_features_))
-        << location.ToString();
-  }
-
-  void ExpectDisabledWithReasons(const std::vector<std::string>& reasons,
-                                 base::Location location) {
-    for (auto reason : reasons) {
-      base::HistogramBase::Sample sample =
-          base::HistogramBase::Sample(base::HashMetricName(reason));
-      AddSampleToBuckets(&expected_disabled_reasons_, sample);
-    }
-    EXPECT_THAT(histogram_tester_.GetAllSamples(
-                    "BackForwardCache.HistoryNavigationOutcome."
-                    "DisabledForRenderFrameHostReason"),
-                UnorderedElementsAreArray(expected_disabled_reasons_))
-        << location.ToString();
-  }
-
-  void ExpectDisabledWithReason(const std::string& reason,
-                                base::Location location) {
-    ExpectDisabledWithReasons({reason}, location);
-  }
-
-  void ExpectBrowsingInstanceNotSwappedReasons(
-      const std::vector<ShouldSwapBrowsingInstance>& reasons,
-      base::Location location) {
-    for (auto reason : reasons) {
-      base::HistogramBase::Sample sample = base::HistogramBase::Sample(reason);
-      AddSampleToBuckets(&expected_browsing_instance_not_swapped_reasons_,
-                         sample);
-    }
-    EXPECT_THAT(histogram_tester_.GetAllSamples(
-                    "BackForwardCache.HistoryNavigationOutcome."
-                    "BrowsingInstanceNotSwappedReason"),
-                UnorderedElementsAreArray(
-                    expected_browsing_instance_not_swapped_reasons_))
-        << location.ToString();
-    if (!check_all_sites_)
-      return;
-
-    EXPECT_THAT(histogram_tester_.GetAllSamples(
-                    "BackForwardCache.AllSites.HistoryNavigationOutcome."
-                    "BrowsingInstanceNotSwappedReason"),
-                UnorderedElementsAreArray(
-                    expected_browsing_instance_not_swapped_reasons_))
-        << location.ToString();
-  }
-
   void ExpectBrowsingInstanceNotSwappedReason(ShouldSwapBrowsingInstance reason,
                                               base::Location location) {
     ExpectBrowsingInstanceNotSwappedReasons({reason}, location);
@@ -540,15 +443,6 @@
                     "BackForwardCache.HistoryNavigationOutcome"),
                 UnorderedElementsAreArray(expected_outcomes_))
         << location.ToString();
-
-    // If we restored the page, there should be no blocking reasons logged.
-    if (outcome ==
-        BackForwardCacheMetrics::HistoryNavigationOutcome::kRestored) {
-      ExpectNotRestoredReasons({}, location);
-      ExpectBlocklistedFeatures({}, FROM_HERE);
-      ExpectDisabledWithReasons({}, FROM_HERE);
-      ExpectBrowsingInstanceNotSwappedReasons({}, FROM_HERE);
-    }
     if (!check_all_sites_)
       return;
 
@@ -569,6 +463,133 @@
         << location.ToString();
   }
 
+  void ExpectReasons(
+      std::vector<BackForwardCacheMetrics::NotRestoredReason> not_restored,
+      std::vector<blink::scheduler::WebSchedulerTrackedFeature> block_listed,
+      const std::vector<ShouldSwapBrowsingInstance>& not_swapped,
+      const std::vector<std::string>& disabled_for_render_frame_host,
+      base::Location location) {
+    // Check that the expected reasons are consistent.
+    bool expect_blocklisted =
+        std::count(
+            not_restored.begin(), not_restored.end(),
+            BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures) >
+        0;
+    bool has_blocklisted = block_listed.size() > 0;
+    EXPECT_EQ(expect_blocklisted, has_blocklisted);
+    bool expect_disabled_for_render_frame_host =
+        std::count(not_restored.begin(), not_restored.end(),
+                   BackForwardCacheMetrics::NotRestoredReason::
+                       kDisableForRenderFrameHostCalled) > 0;
+    bool has_disabled_for_render_frame_host =
+        disabled_for_render_frame_host.size() > 0;
+    EXPECT_EQ(expect_disabled_for_render_frame_host,
+              has_disabled_for_render_frame_host);
+
+    // Check that the reasons are as expected.
+    ExpectNotRestoredReasons(not_restored, location);
+    ExpectBlocklistedFeatures(block_listed, location);
+    ExpectBrowsingInstanceNotSwappedReasons(not_swapped, location);
+    ExpectDisabledWithReasons(disabled_for_render_frame_host, location);
+  }
+
+  void ExpectNotRestoredReasons(
+      std::vector<BackForwardCacheMetrics::NotRestoredReason> reasons,
+      base::Location location) {
+    uint64_t not_restored_reasons_bits = 0;
+    for (BackForwardCacheMetrics::NotRestoredReason reason : reasons) {
+      base::HistogramBase::Sample sample = base::HistogramBase::Sample(reason);
+      AddSampleToBuckets(&expected_not_restored_, sample);
+      not_restored_reasons_bits |= 1ull << static_cast<int>(reason);
+    }
+
+    EXPECT_THAT(histogram_tester_.GetAllSamples(
+                    "BackForwardCache.HistoryNavigationOutcome."
+                    "NotRestoredReason"),
+                UnorderedElementsAreArray(expected_not_restored_))
+        << location.ToString();
+
+    if (!check_all_sites_)
+      return;
+
+    EXPECT_THAT(histogram_tester_.GetAllSamples(
+                    "BackForwardCache.AllSites.HistoryNavigationOutcome."
+                    "NotRestoredReason"),
+                UnorderedElementsAreArray(expected_not_restored_))
+        << location.ToString();
+
+    std::string not_restored_reasons = "BackForwardCache.NotRestoredReasons";
+    expected_ukm_not_restored_reasons_.push_back(
+        {{not_restored_reasons, not_restored_reasons_bits}});
+    EXPECT_THAT(
+        ukm_recorder_->GetMetrics("HistoryNavigation", {not_restored_reasons}),
+        expected_ukm_not_restored_reasons_)
+        << location.ToString();
+  }
+
+  void ExpectBlocklistedFeatures(
+      std::vector<blink::scheduler::WebSchedulerTrackedFeature> features,
+      base::Location location) {
+    for (auto feature : features) {
+      base::HistogramBase::Sample sample = base::HistogramBase::Sample(feature);
+      AddSampleToBuckets(&expected_blocklisted_features_, sample);
+    }
+
+    EXPECT_THAT(histogram_tester_.GetAllSamples(
+                    "BackForwardCache.HistoryNavigationOutcome."
+                    "BlocklistedFeature"),
+                UnorderedElementsAreArray(expected_blocklisted_features_))
+        << location.ToString();
+
+    if (!check_all_sites_)
+      return;
+
+    EXPECT_THAT(histogram_tester_.GetAllSamples(
+                    "BackForwardCache.AllSites.HistoryNavigationOutcome."
+                    "BlocklistedFeature"),
+                UnorderedElementsAreArray(expected_blocklisted_features_))
+        << location.ToString();
+  }
+
+  void ExpectDisabledWithReasons(const std::vector<std::string>& reasons,
+                                 base::Location location) {
+    for (auto reason : reasons) {
+      base::HistogramBase::Sample sample =
+          base::HistogramBase::Sample(base::HashMetricName(reason));
+      AddSampleToBuckets(&expected_disabled_reasons_, sample);
+    }
+    EXPECT_THAT(histogram_tester_.GetAllSamples(
+                    "BackForwardCache.HistoryNavigationOutcome."
+                    "DisabledForRenderFrameHostReason"),
+                UnorderedElementsAreArray(expected_disabled_reasons_))
+        << location.ToString();
+  }
+
+  void ExpectBrowsingInstanceNotSwappedReasons(
+      const std::vector<ShouldSwapBrowsingInstance>& reasons,
+      base::Location location) {
+    for (auto reason : reasons) {
+      base::HistogramBase::Sample sample = base::HistogramBase::Sample(reason);
+      AddSampleToBuckets(&expected_browsing_instance_not_swapped_reasons_,
+                         sample);
+    }
+    EXPECT_THAT(histogram_tester_.GetAllSamples(
+                    "BackForwardCache.HistoryNavigationOutcome."
+                    "BrowsingInstanceNotSwappedReason"),
+                UnorderedElementsAreArray(
+                    expected_browsing_instance_not_swapped_reasons_))
+        << location.ToString();
+    if (!check_all_sites_)
+      return;
+
+    EXPECT_THAT(histogram_tester_.GetAllSamples(
+                    "BackForwardCache.AllSites.HistoryNavigationOutcome."
+                    "BrowsingInstanceNotSwappedReason"),
+                UnorderedElementsAreArray(
+                    expected_browsing_instance_not_swapped_reasons_))
+        << location.ToString();
+  }
+
   base::test::ScopedFeatureList feature_list_;
 
   FrameTreeVisualizer visualizer_;
@@ -1121,7 +1142,7 @@
   web_contents()->GetController().GoToOffset(-2);
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored({BackForwardCacheMetrics::NotRestoredReason::kCacheLimit},
-                    FROM_HERE);
+                    {}, {}, {}, FROM_HERE);
 }
 
 class HighCacheSizeBackForwardCacheBrowserTest
@@ -1642,9 +1663,8 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored(
       {BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures},
+      {blink::scheduler::WebSchedulerTrackedFeature::kSharedWorker}, {}, {},
       FROM_HERE);
-  ExpectBlocklistedFeature(
-      blink::scheduler::WebSchedulerTrackedFeature::kSharedWorker, FROM_HERE);
 }
 
 #if defined(OS_MAC) || defined(OS_LINUX) || defined(OS_CHROMEOS)
@@ -1680,10 +1700,8 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored(
       {BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures},
-      FROM_HERE);
-  ExpectBlocklistedFeature(
-      blink::scheduler::WebSchedulerTrackedFeature::kDedicatedWorkerOrWorklet,
-      FROM_HERE);
+      {blink::scheduler::WebSchedulerTrackedFeature::kDedicatedWorkerOrWorklet},
+      {}, {}, FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
@@ -1804,7 +1822,7 @@
       {BackForwardCacheMetrics::NotRestoredReason::kWasGrantedMediaAccess,
        BackForwardCacheMetrics::NotRestoredReason::
            kDisableForRenderFrameHostCalled},
-      FROM_HERE);
+      {}, {}, {"MediaDevicesDispatcherHost"}, FROM_HERE);
   EXPECT_TRUE(tester.IsDisabledForFrameWithReason(
       process_id, routing_id, "MediaDevicesDispatcherHost"));
 }
@@ -1850,7 +1868,7 @@
       {BackForwardCacheMetrics::NotRestoredReason::kWasGrantedMediaAccess,
        BackForwardCacheMetrics::NotRestoredReason::
            kDisableForRenderFrameHostCalled},
-      FROM_HERE);
+      {}, {}, {"MediaDevicesDispatcherHost"}, FROM_HERE);
   EXPECT_TRUE(tester.IsDisabledForFrameWithReason(
       process_id, routing_id, "MediaDevicesDispatcherHost"));
 }
@@ -1890,7 +1908,7 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored({BackForwardCacheMetrics::NotRestoredReason::
                          kDisableForRenderFrameHostCalled},
-                    FROM_HERE);
+                    {}, {}, {"MediaDevicesDispatcherHost"}, FROM_HERE);
   EXPECT_TRUE(tester.IsDisabledForFrameWithReason(
       process_id, routing_id, "MediaDevicesDispatcherHost"));
 }
@@ -1933,8 +1951,8 @@
   // 3) Go back.
   web_contents()->GetController().GoBack();
   EXPECT_FALSE(WaitForLoadStop(shell()->web_contents()));
-  ExpectNotRestored({BackForwardCacheMetrics::NotRestoredReason::kLoading},
-                    FROM_HERE);
+  ExpectNotRestored({BackForwardCacheMetrics::NotRestoredReason::kLoading}, {},
+                    {}, {}, FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
@@ -1968,10 +1986,9 @@
   navigation_manager_back.WaitForNavigationFinished();
   ExpectNotRestored(
       {BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures},
-      FROM_HERE);
-  ExpectBlocklistedFeature(blink::scheduler::WebSchedulerTrackedFeature::
-                               kOutstandingNetworkRequestOthers,
-                           FROM_HERE);
+      {blink::scheduler::WebSchedulerTrackedFeature::
+           kOutstandingNetworkRequestOthers},
+      {}, {}, FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
@@ -2012,7 +2029,7 @@
           BackForwardCacheMetrics::NotRestoredReason::kLoading,
           BackForwardCacheMetrics::NotRestoredReason::kSubframeIsNavigating,
       },
-      FROM_HERE);
+      {}, {}, {}, FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
@@ -2054,7 +2071,7 @@
           BackForwardCacheMetrics::NotRestoredReason::kLoading,
           BackForwardCacheMetrics::NotRestoredReason::kSubframeIsNavigating,
       },
-      FROM_HERE);
+      {}, {}, {}, FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, CacheIfWebGL) {
@@ -2111,9 +2128,8 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored(
       {BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures},
+      {blink::scheduler::WebSchedulerTrackedFeature::kWebHID}, {}, {},
       FROM_HERE);
-  ExpectBlocklistedFeature(
-      blink::scheduler::WebSchedulerTrackedFeature::kWebHID, FROM_HERE);
 }
 #endif  // !defined(OS_ANDROID)
 
@@ -2165,9 +2181,8 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored(
       {BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures},
+      {blink::scheduler::WebSchedulerTrackedFeature::kWebFileSystem}, {}, {},
       FROM_HERE);
-  ExpectBlocklistedFeature(
-      blink::scheduler::WebSchedulerTrackedFeature::kWebFileSystem, FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, DoesNotCacheIfHttpError) {
@@ -2191,8 +2206,8 @@
   web_contents()->GetController().GoBack();
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored(
-      {BackForwardCacheMetrics::NotRestoredReason::kHTTPStatusNotOK},
-      FROM_HERE);
+      {BackForwardCacheMetrics::NotRestoredReason::kHTTPStatusNotOK}, {}, {},
+      {}, FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, DoesNotCacheIdleManager) {
@@ -2226,9 +2241,8 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored(
       {BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures},
+      {blink::scheduler::WebSchedulerTrackedFeature::kIdleManager}, {}, {},
       FROM_HERE);
-  ExpectBlocklistedFeature(
-      blink::scheduler::WebSchedulerTrackedFeature::kIdleManager, FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, DoesNotCacheSMSService) {
@@ -2308,6 +2322,7 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored(
       {BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures},
+      {blink::scheduler::WebSchedulerTrackedFeature::kPaymentManager}, {}, {},
       FROM_HERE);
 
   // Note that on Mac10.10, there is occasionally blocklisting for network
@@ -2364,9 +2379,8 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored(
       {BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures},
+      {blink::scheduler::WebSchedulerTrackedFeature::kKeyboardLock}, {}, {},
       FROM_HERE);
-  ExpectBlocklistedFeature(
-      blink::scheduler::WebSchedulerTrackedFeature::kKeyboardLock, FROM_HERE);
 }
 
 // Tests which blocklisted features are tracked in the metrics when we used
@@ -2412,15 +2426,13 @@
   web_contents()->GetController().GoBack();
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
 
-  ExpectNotRestored(
-      {BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures},
-      FROM_HERE);
   // All features (sticky and non-sticky) will be tracked, because they're
   // tracked in RenderFrameHostManager::UnloadOldFrame.
-  ExpectBlocklistedFeatures(
+  ExpectNotRestored(
+      {BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures},
       {blink::scheduler::WebSchedulerTrackedFeature::kBroadcastChannel,
        blink::scheduler::WebSchedulerTrackedFeature::kKeyboardLock},
-      FROM_HERE);
+      {}, {}, FROM_HERE);
 }
 
 // Tests which blocklisted features are tracked in the metrics when we used
@@ -2465,19 +2477,15 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
 
   if (AreStrictSiteInstancesEnabled()) {
+    // All features (sticky and non-sticky) will be tracked, because they're
+    // tracked in RenderFrameHostManager::UnloadOldFrame.
     ExpectNotRestored(
         {BackForwardCacheMetrics::NotRestoredReason::
              kRelatedActiveContentsExist,
          BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures},
-        FROM_HERE);
-    // All features (sticky and non-sticky) will be tracked, because they're
-    // tracked in RenderFrameHostManager::UnloadOldFrame.
-    ExpectBlocklistedFeatures(
         {blink::scheduler::WebSchedulerTrackedFeature::kBroadcastChannel,
          blink::scheduler::WebSchedulerTrackedFeature::kKeyboardLock},
-        FROM_HERE);
-    ExpectBrowsingInstanceNotSwappedReason(
-        ShouldSwapBrowsingInstance::kNo_NotNeededForBackForwardCache,
+        {ShouldSwapBrowsingInstance::kNo_NotNeededForBackForwardCache}, {},
         FROM_HERE);
 
     web_contents()->GetController().GoForward();
@@ -2494,14 +2502,12 @@
         ShouldSwapBrowsingInstance::kNo_AlreadyHasMatchingBrowsingInstance,
         FROM_HERE);
   } else {
+    // Non-sticky reasons are not recorded here.
     ExpectNotRestored(
         {BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures,
          BackForwardCacheMetrics::NotRestoredReason::
              kRenderFrameHostReused_CrossSite},
-        FROM_HERE);
-    // Non-sticky reasons are not recorded here.
-    ExpectBlocklistedFeatures(
-        {blink::scheduler::WebSchedulerTrackedFeature::kKeyboardLock},
+        {blink::scheduler::WebSchedulerTrackedFeature::kKeyboardLock}, {}, {},
         FROM_HERE);
   }
 }
@@ -2547,14 +2553,13 @@
   web_contents()->GetController().GoBack();
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
 
+  // Non-sticky reasons are not recorded here.
   ExpectNotRestored(
       {BackForwardCacheMetrics::NotRestoredReason::
            kRenderFrameHostReused_SameSite,
        BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures},
+      {blink::scheduler::WebSchedulerTrackedFeature::kKeyboardLock}, {}, {},
       FROM_HERE);
-  // Non-sticky reasons are not recorded here.
-  ExpectBlocklistedFeatures(
-      {blink::scheduler::WebSchedulerTrackedFeature::kKeyboardLock}, FROM_HERE);
 }
 
 // Tests which blocklisted features are tracked in the metrics when we used a
@@ -2596,9 +2601,7 @@
   // be tracked in RenderFrameHostManager::UnloadOldFrame.
   ExpectNotRestored(
       {BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures},
-      FROM_HERE);
-  ExpectBlocklistedFeature(
-      blink::scheduler::WebSchedulerTrackedFeature::kBroadcastChannel,
+      {blink::scheduler::WebSchedulerTrackedFeature::kBroadcastChannel}, {}, {},
       FROM_HERE);
 }
 
@@ -2641,9 +2644,7 @@
   // be tracked in RenderFrameHostManager::UnloadOldFrame.
   ExpectNotRestored(
       {BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures},
-      FROM_HERE);
-  ExpectBlocklistedFeature(
-      blink::scheduler::WebSchedulerTrackedFeature::kBroadcastChannel,
+      {blink::scheduler::WebSchedulerTrackedFeature::kBroadcastChannel}, {}, {},
       FROM_HERE);
 }
 
@@ -2684,9 +2685,7 @@
   // be tracked in RenderFrameHostManager::UnloadOldFrame.
   ExpectNotRestored(
       {BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures},
-      FROM_HERE);
-  ExpectBlocklistedFeature(
-      blink::scheduler::WebSchedulerTrackedFeature::kBroadcastChannel,
+      {blink::scheduler::WebSchedulerTrackedFeature::kBroadcastChannel}, {}, {},
       FROM_HERE);
 }
 
@@ -2818,9 +2817,8 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored(
       {BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures},
+      {blink::scheduler::WebSchedulerTrackedFeature::kAppBanner}, {}, {},
       FROM_HERE);
-  ExpectBlocklistedFeature(
-      blink::scheduler::WebSchedulerTrackedFeature::kAppBanner, FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, DoesNotCacheIfWebDatabase) {
@@ -2842,9 +2840,8 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored(
       {BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures},
+      {blink::scheduler::WebSchedulerTrackedFeature::kWebDatabase}, {}, {},
       FROM_HERE);
-  ExpectBlocklistedFeature(
-      blink::scheduler::WebSchedulerTrackedFeature::kWebDatabase, FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
@@ -2884,8 +2881,8 @@
   web_contents()->GetController().GoBack();
   EXPECT_FALSE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored(
-      {BackForwardCacheMetrics::NotRestoredReason::kHTTPStatusNotOK},
-      FROM_HERE);
+      {BackForwardCacheMetrics::NotRestoredReason::kHTTPStatusNotOK}, {}, {},
+      {}, FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
@@ -2942,8 +2939,8 @@
   web_contents()->GetController().GoBack();
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored(
-      {BackForwardCacheMetrics::NotRestoredReason::kJavaScriptExecution},
-      FROM_HERE);
+      {BackForwardCacheMetrics::NotRestoredReason::kJavaScriptExecution}, {},
+      {}, {}, FROM_HERE);
 }
 
 // Similar to BackForwardCacheBrowserTest.EvictionOnJavaScriptExecution.
@@ -2986,8 +2983,8 @@
   web_contents()->GetController().GoBack();
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored(
-      {BackForwardCacheMetrics::NotRestoredReason::kJavaScriptExecution},
-      FROM_HERE);
+      {BackForwardCacheMetrics::NotRestoredReason::kJavaScriptExecution}, {},
+      {}, {}, FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
@@ -3027,8 +3024,8 @@
   web_contents()->GetController().GoBack();
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored(
-      {BackForwardCacheMetrics::NotRestoredReason::kJavaScriptExecution},
-      FROM_HERE);
+      {BackForwardCacheMetrics::NotRestoredReason::kJavaScriptExecution}, {},
+      {}, {}, FROM_HERE);
 }
 
 // Tests the events are fired when going back from the cache.
@@ -3317,8 +3314,8 @@
   web_contents()->GetController().GoBack();
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored(
-      {BackForwardCacheMetrics::NotRestoredReason::kTimeoutPuttingInCache},
-      FROM_HERE);
+      {BackForwardCacheMetrics::NotRestoredReason::kTimeoutPuttingInCache}, {},
+      {}, {}, FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
@@ -3454,8 +3451,8 @@
   EXPECT_EQ(rfh_a2->GetLastCommittedURL(), url_a);
 
   ExpectNotRestored(
-      {BackForwardCacheMetrics::NotRestoredReason::kJavaScriptExecution},
-      FROM_HERE);
+      {BackForwardCacheMetrics::NotRestoredReason::kJavaScriptExecution}, {},
+      {}, {}, FROM_HERE);
 }
 
 // Similar to ReissuesNavigationIfEvictedDuringNavigation, except that
@@ -3539,8 +3536,8 @@
   web_contents()->GetController().GoBack();
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored(
-      {BackForwardCacheMetrics::NotRestoredReason::kRendererProcessKilled},
-      FROM_HERE);
+      {BackForwardCacheMetrics::NotRestoredReason::kRendererProcessKilled}, {},
+      {}, {}, FROM_HERE);
 }
 
 // The test is simulating a race condition. The scheduler tracked features are
@@ -3766,7 +3763,7 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored({BackForwardCacheMetrics::NotRestoredReason::
                          kNetworkRequestDatapipeDrained},
-                    FROM_HERE);
+                    {}, {}, {}, FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestWithUnfreezableLoading,
@@ -3838,7 +3835,7 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored(
       {BackForwardCacheMetrics::NotRestoredReason::kNetworkExceedsBufferLimit},
-      FROM_HERE);
+      {}, {}, {}, FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(
@@ -3909,7 +3906,7 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored(
       {BackForwardCacheMetrics::NotRestoredReason::kNetworkExceedsBufferLimit},
-      FROM_HERE);
+      {}, {}, {}, FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(
@@ -3994,7 +3991,7 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored(
       {BackForwardCacheMetrics::NotRestoredReason::kNetworkExceedsBufferLimit},
-      FROM_HERE);
+      {}, {}, {}, FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(
@@ -4128,7 +4125,7 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored(
       {BackForwardCacheMetrics::NotRestoredReason::kNetworkExceedsBufferLimit},
-      FROM_HERE);
+      {}, {}, {}, FROM_HERE);
 
   // The second page was still loading images when we navigated away, but it's
   // still eligible for back-forward cache.
@@ -4188,8 +4185,8 @@
   web_contents()->GetController().GoBack();
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored(
-      {BackForwardCacheMetrics::NotRestoredReason::kNetworkRequestTimeout},
-      FROM_HERE);
+      {BackForwardCacheMetrics::NotRestoredReason::kNetworkRequestTimeout}, {},
+      {}, {}, FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(
@@ -4230,7 +4227,7 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored(
       {BackForwardCacheMetrics::NotRestoredReason::kNetworkExceedsBufferLimit},
-      FROM_HERE);
+      {}, {}, {}, FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(
@@ -4307,7 +4304,7 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored(
       {BackForwardCacheMetrics::NotRestoredReason::kNetworkExceedsBufferLimit},
-      FROM_HERE);
+      {}, {}, {}, FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestWithUnfreezableLoading,
@@ -4787,7 +4784,7 @@
           BackForwardCacheMetrics::NotRestoredReason::
               kServiceWorkerVersionActivation,
       },
-      FROM_HERE);
+      {}, {}, {}, FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestWithServiceWorkerEnabled,
@@ -4849,7 +4846,7 @@
   EXPECT_TRUE(WaitForLoadStop(tab_to_be_bfcached->web_contents()));
   ExpectNotRestored(
       {BackForwardCacheMetrics::NotRestoredReason::kServiceWorkerPostMessage},
-      FROM_HERE);
+      {}, {}, {}, FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestWithServiceWorkerEnabled,
@@ -4899,8 +4896,8 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   EXPECT_TRUE(deleted.deleted());
   ExpectNotRestored(
-      {BackForwardCacheMetrics::NotRestoredReason::kServiceWorkerClaim},
-      FROM_HERE);
+      {BackForwardCacheMetrics::NotRestoredReason::kServiceWorkerClaim}, {}, {},
+      {}, FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, CachePagesWithBeacon) {
@@ -5083,7 +5080,7 @@
           BackForwardCacheMetrics::NotRestoredReason::
               kRenderFrameHostReused_SameSite,
       },
-      FROM_HERE);
+      {}, {}, {}, FROM_HERE);
 
   // 5) Go to A2.
   web_contents()->GetController().GoForward();
@@ -5094,7 +5091,7 @@
           BackForwardCacheMetrics::NotRestoredReason::
               kConflictingBrowsingInstance,
       },
-      FROM_HERE);
+      {}, {}, {}, FROM_HERE);
 }
 
 // When same-site bfcache is disabled, we should not cache on same-site
@@ -5165,10 +5162,9 @@
   // 4) Go back to A2.
   web_contents()->GetController().GoBack();
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
-  ExpectDisabledWithReason(kDisabledReasonForTest, FROM_HERE);
   ExpectNotRestored({BackForwardCacheMetrics::NotRestoredReason::
                          kDisableForRenderFrameHostCalled},
-                    FROM_HERE);
+                    {}, {}, {kDisabledReasonForTest}, FROM_HERE);
 }
 
 // The BackForwardCache caches same-website navigations.
@@ -5644,8 +5640,8 @@
   // 7) Go back to A.
   web_contents()->GetController().GoBack();
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
-  ExpectNotRestored({BackForwardCacheMetrics::NotRestoredReason::kTimeout},
-                    FROM_HERE);
+  ExpectNotRestored({BackForwardCacheMetrics::NotRestoredReason::kTimeout}, {},
+                    {}, {}, FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(
@@ -5670,10 +5666,9 @@
   // 3) Go back to A.
   web_contents()->GetController().GoBack();
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
-  ExpectDisabledWithReason(kDisabledReasonForTest, FROM_HERE);
   ExpectNotRestored({BackForwardCacheMetrics::NotRestoredReason::
                          kDisableForRenderFrameHostCalled},
-                    FROM_HERE);
+                    {}, {}, {kDisabledReasonForTest}, FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
@@ -5701,10 +5696,9 @@
   // 3) Go back to A.
   web_contents()->GetController().GoBack();
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
-  ExpectDisabledWithReason(kDisabledReasonForTest, FROM_HERE);
   ExpectNotRestored({BackForwardCacheMetrics::NotRestoredReason::
                          kDisableForRenderFrameHostCalled},
-                    FROM_HERE);
+                    {}, {}, {kDisabledReasonForTest}, FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
@@ -5733,7 +5727,7 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored({BackForwardCacheMetrics::NotRestoredReason::
                          kDisableForRenderFrameHostCalled},
-                    FROM_HERE);
+                    {}, {}, {kDisabledReasonForTest}, FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
@@ -5761,10 +5755,9 @@
   // 3) Go back to A.
   web_contents()->GetController().GoBack();
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
-  ExpectDisabledWithReason(kDisabledReasonForTest, FROM_HERE);
   ExpectNotRestored({BackForwardCacheMetrics::NotRestoredReason::
                          kDisableForRenderFrameHostCalled},
-                    FROM_HERE);
+                    {}, {}, {kDisabledReasonForTest}, FROM_HERE);
 }
 
 // Confirm that same-document navigation and not history-navigation does not
@@ -5978,8 +5971,8 @@
   web_contents()->GetController().GoBack();
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored(
-      {BackForwardCacheMetrics::NotRestoredReason::kHaveInnerContents},
-      FROM_HERE);
+      {BackForwardCacheMetrics::NotRestoredReason::kHaveInnerContents}, {}, {},
+      {}, FROM_HERE);
 }
 
 // Check the BackForwardCache is disabled when the WebUSB feature is used.
@@ -6709,7 +6702,9 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored(
       {BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures},
-      FROM_HERE);
+      {blink::scheduler::WebSchedulerTrackedFeature::
+           kRequestedBackForwardCacheBlockedSensors},
+      {}, {}, FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(SensorBackForwardCacheBrowserTest, OrientationCached) {
@@ -6950,8 +6945,8 @@
   web_contents()->GetController().GoBack();
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored(
-      {BackForwardCacheMetrics::NotRestoredReason::kIgnoreEventAndEvict},
-      FROM_HERE);
+      {BackForwardCacheMetrics::NotRestoredReason::kIgnoreEventAndEvict}, {},
+      {}, {}, FROM_HERE);
 }
 
 // Check BackForwardCache is enabled and works for devices with very low memory.
@@ -7262,9 +7257,8 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored(
       {BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures},
+      {blink::scheduler::WebSchedulerTrackedFeature::kWebRTC}, {}, {},
       FROM_HERE);
-  ExpectBlocklistedFeature(
-      blink::scheduler::WebSchedulerTrackedFeature::kWebRTC, FROM_HERE);
 }
 #endif  // !defined(OS_ANDROID)
 
@@ -7300,9 +7294,8 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored(
       {BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures},
+      {blink::scheduler::WebSchedulerTrackedFeature::kWebLocks}, {}, {},
       FROM_HERE);
-  ExpectBlocklistedFeature(
-      blink::scheduler::WebSchedulerTrackedFeature::kWebLocks, FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
@@ -7524,10 +7517,8 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored(
       {BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures},
-      FROM_HERE);
-  ExpectBlocklistedFeature(
-      blink::scheduler::WebSchedulerTrackedFeature::kRequestedMIDIPermission,
-      FROM_HERE);
+      {blink::scheduler::WebSchedulerTrackedFeature::kRequestedMIDIPermission},
+      {}, {}, FROM_HERE);
 }
 
 #if defined(OS_ANDROID)
@@ -7714,10 +7705,9 @@
 
   ExpectNotRestored(
       {BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures},
-      FROM_HERE);
-  ExpectBlocklistedFeature(blink::scheduler::WebSchedulerTrackedFeature::
-                               kOutstandingNetworkRequestFetch,
-                           FROM_HERE);
+      {blink::scheduler::WebSchedulerTrackedFeature::
+           kOutstandingNetworkRequestFetch},
+      {}, {}, FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, OutstandingXHRNotCached) {
@@ -7755,10 +7745,9 @@
 
   ExpectNotRestored(
       {BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures},
-      FROM_HERE);
-  ExpectBlocklistedFeature(blink::scheduler::WebSchedulerTrackedFeature::
-                               kOutstandingNetworkRequestXHR,
-                           FROM_HERE);
+      {blink::scheduler::WebSchedulerTrackedFeature::
+           kOutstandingNetworkRequestXHR},
+      {}, {}, FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, NotFetchedScriptNotCached) {
@@ -7793,10 +7782,9 @@
 
   ExpectNotRestored(
       {BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures},
-      FROM_HERE);
-  ExpectBlocklistedFeature(blink::scheduler::WebSchedulerTrackedFeature::
-                               kOutstandingNetworkRequestOthers,
-                           FROM_HERE);
+      {blink::scheduler::WebSchedulerTrackedFeature::
+           kOutstandingNetworkRequestOthers},
+      {}, {}, FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, PageshowMetrics) {
@@ -7942,9 +7930,7 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored(
       {BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures},
-      FROM_HERE);
-  ExpectBlocklistedFeature(
-      blink::scheduler::WebSchedulerTrackedFeature::kSpeechRecognizer,
+      {blink::scheduler::WebSchedulerTrackedFeature::kSpeechRecognizer}, {}, {},
       FROM_HERE);
 }
 
@@ -8022,9 +8008,7 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored(
       {BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures},
-      FROM_HERE);
-  ExpectBlocklistedFeature(
-      blink::scheduler::WebSchedulerTrackedFeature::kSpeechSynthesis,
+      {blink::scheduler::WebSchedulerTrackedFeature::kSpeechSynthesis}, {}, {},
       FROM_HERE);
 }
 
@@ -8073,7 +8057,7 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored({BackForwardCacheMetrics::NotRestoredReason::
                          kDisableForRenderFrameHostCalled},
-                    FROM_HERE);
+                    {}, {}, {"FileChooser"}, FROM_HERE);
 }
 
 // RenderFrameHostImpl::coep_reporter() must be preserved when doing a back
@@ -9279,7 +9263,7 @@
 
   ExpectNotRestored({BackForwardCacheMetrics::NotRestoredReason::
                          kNavigationCancelledWhileRestoring},
-                    FROM_HERE);
+                    {}, {}, {}, FROM_HERE);
 }
 
 class BackForwardCacheBrowserTestWithFileSystemAPISupported
@@ -9445,9 +9429,7 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored(
       {BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures},
-      FROM_HERE);
-  ExpectBlocklistedFeature(
-      blink::scheduler::WebSchedulerTrackedFeature::kBroadcastChannel,
+      {blink::scheduler::WebSchedulerTrackedFeature::kBroadcastChannel}, {}, {},
       FROM_HERE);
 
   RenderFrameHostImpl* rfh_a2 = current_frame_host();
@@ -9470,9 +9452,8 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectNotRestored(
       {BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures},
+      {blink::scheduler::WebSchedulerTrackedFeature::kKeyboardLock}, {}, {},
       FROM_HERE);
-  ExpectBlocklistedFeatures(
-      {blink::scheduler::WebSchedulerTrackedFeature::kKeyboardLock}, FROM_HERE);
 }
 
 }  // namespace content
diff --git a/content/browser/browsing_instance.cc b/content/browser/browsing_instance.cc
index 2fdd2b3f3..b21dc7ca8 100644
--- a/content/browser/browsing_instance.cc
+++ b/content/browser/browsing_instance.cc
@@ -189,8 +189,8 @@
 
 SiteInfo BrowsingInstance::ComputeSiteInfoForURL(
     const UrlInfo& url_info) const {
-  return SiteInstanceImpl::ComputeSiteInfo(isolation_context_, url_info,
-                                           cross_origin_isolated_info_);
+  return SiteInfo::Create(isolation_context_, url_info,
+                          cross_origin_isolated_info_);
 }
 
 }  // namespace content
diff --git a/content/browser/child_process_security_policy_impl.cc b/content/browser/child_process_security_policy_impl.cc
index 833a36a..df2a015 100644
--- a/content/browser/child_process_security_policy_impl.cc
+++ b/content/browser/child_process_security_policy_impl.cc
@@ -177,6 +177,25 @@
       SiteInfo(GURL(), GURL(), false, cross_origin_isolated_info));
 }
 
+// static
+ProcessLock ProcessLock::Create(
+    const IsolationContext& isolation_context,
+    const UrlInfo& url_info,
+    const CoopCoepCrossOriginIsolatedInfo& cross_origin_isolated_info) {
+  if (BrowserThread::CurrentlyOn(BrowserThread::UI))
+    return ProcessLock(SiteInfo::Create(isolation_context, url_info,
+                                        cross_origin_isolated_info));
+
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  // On the IO thread we need to use a special SiteInfo creation method because
+  // we cannot properly compute some SiteInfo fields on that thread.
+  // ProcessLocks must always match no matter which thread they were created on,
+  // but the SiteInfo objects used to create them may not always match.
+  return ProcessLock(SiteInfo::CreateOnIOThread(isolation_context, url_info,
+                                                cross_origin_isolated_info));
+}
+
 ProcessLock::ProcessLock(const SiteInfo& site_info) : site_info_(site_info) {}
 
 ProcessLock::ProcessLock() = default;
@@ -1497,9 +1516,8 @@
     // Check for special cases, like blob:null/ and data: URLs, where the
     // origin does not contain information to match against the process lock,
     // but using the whole URL can result in a process lock match.
-    const ProcessLock expected_process_lock =
-        SiteInstanceImpl::DetermineProcessLock(isolation_context, url_info,
-                                               cross_origin_isolated_info);
+    const auto expected_process_lock = ProcessLock::Create(
+        isolation_context, url_info, cross_origin_isolated_info);
     const ProcessLock& actual_process_lock = GetProcessLock(child_id);
     if (actual_process_lock == expected_process_lock)
       return CanCommitStatus::CAN_COMMIT_ORIGIN_AND_URL;
@@ -1628,10 +1646,11 @@
             "[BI=%d]", browsing_instance_id.GetUnsafeValue());
         IsolationContext isolation_context(browsing_instance_id,
                                            browser_or_resource_context);
-        // NOTE: If we're on the IO thread, the call to DetermineProcessLock()
-        // below will return a ProcessLock with an (internally) identical
-        // site_url, one that does not use effective URLs. That's ok in this
-        // instance since we only ever look at the lock url.
+        // NOTE: If we're on the IO thread, the call to
+        // ProcessLock::Create() below will return a ProcessLock with
+        // an (internally) identical site_url, one that does not use effective
+        // URLs. That's ok in this instance since we only ever look at the lock
+        // url.
         //
         // Since we are dealing with a valid ProcessLock at this point, we know
         // the lock contains valid COOP/COEP information because that
@@ -1652,7 +1671,7 @@
         // their request's require COOP/COEP handling, to pass in their
         // COOP/COEP information so it can be used here instead of the values in
         // |actual_process_lock|.
-        expected_process_lock = SiteInstanceImpl::DetermineProcessLock(
+        expected_process_lock = ProcessLock::Create(
             isolation_context,
             UrlInfo(url, false /* origin_requests_isolation */),
             actual_process_lock.coop_coep_cross_origin_isolated_info());
@@ -1694,9 +1713,9 @@
             url::Origin expected_origin =
                 url::Origin::Create(expected_process_lock.lock_url());
             if (actual_process_lock.lock_url() ==
-                    SiteInstanceImpl::GetSiteForOrigin(expected_origin) ||
+                    SiteInfo::GetSiteForOrigin(expected_origin) ||
                 expected_process_lock.lock_url() ==
-                    SiteInstanceImpl::GetSiteForOrigin(actual_origin)) {
+                    SiteInfo::GetSiteForOrigin(actual_origin)) {
               failure_reason += "[origin vs site mismatch] ";
             }
           } else {
@@ -1735,9 +1754,9 @@
               return true;
           }
 
-          // See the DetermineProcessLock() call above regarding why we pass
-          // 'false' for |origin_requests_isolation| below.
-          SiteInfo site_info = SiteInstanceImpl::ComputeSiteInfo(
+          // See the ProcessLock::Create() call above regarding why we
+          // pass 'false' for |origin_requests_isolation| below.
+          SiteInfo site_info = SiteInfo::Create(
               isolation_context,
               UrlInfo(url, false /* origin_requests_isolation */),
               actual_process_lock.coop_coep_cross_origin_isolated_info());
@@ -1789,8 +1808,7 @@
     const IsolationContext& isolation_context,
     int child_id,
     const GURL& url) {
-  SiteInfo site_info =
-      SiteInstanceImpl::ComputeSiteInfoForTesting(isolation_context, url);
+  SiteInfo site_info = SiteInfo::CreateForTesting(isolation_context, url);
   LockProcess(isolation_context, child_id, ProcessLock(site_info));
 }
 
@@ -1892,7 +1910,7 @@
     // here, but *is* typically needed for making process model decisions. Be
     // very careful about using GetSiteForOrigin() elsewhere, and consider
     // whether you should be using GetSiteForURL() instead.
-    GURL key(SiteInstanceImpl::GetSiteForOrigin(origin_to_add));
+    GURL key(SiteInfo::GetSiteForOrigin(origin_to_add));
 
     // Isolated origins should apply only to future BrowsingInstances and
     // processes.  Save the first BrowsingInstance ID to which they should
@@ -2015,7 +2033,7 @@
     const url::Origin& origin,
     IsolatedOriginSource source) {
   base::AutoLock isolated_origins_lock(isolated_origins_lock_);
-  GURL site_url = SiteInstanceImpl::GetSiteForOrigin(origin);
+  GURL site_url = SiteInfo::GetSiteForOrigin(origin);
   auto it = isolated_origins_.find(site_url);
   if (it == isolated_origins_.end())
     return false;
@@ -2039,9 +2057,9 @@
   // here, but *is* typically needed for making process model decisions. Be
   // very careful about using GetSiteForOrigin() elsewhere, and consider
   // whether you should be using GetSiteForURL() instead.
-  return GetMatchingIsolatedOrigin(
-      isolation_context, origin, origin_requests_isolation,
-      SiteInstanceImpl::GetSiteForOrigin(origin), result);
+  return GetMatchingIsolatedOrigin(isolation_context, origin,
+                                   origin_requests_isolation,
+                                   SiteInfo::GetSiteForOrigin(origin), result);
 }
 
 bool ChildProcessSecurityPolicyImpl::GetMatchingIsolatedOrigin(
@@ -2353,7 +2371,7 @@
 
 void ChildProcessSecurityPolicyImpl::RemoveIsolatedOriginForTesting(
     const url::Origin& origin) {
-  GURL key(SiteInstanceImpl::GetSiteForOrigin(origin));
+  GURL key(SiteInfo::GetSiteForOrigin(origin));
   base::AutoLock isolated_origins_lock(isolated_origins_lock_);
   base::EraseIf(isolated_origins_[key],
                 [&origin](const IsolatedOriginEntry& entry) {
diff --git a/content/browser/child_process_security_policy_impl.h b/content/browser/child_process_security_policy_impl.h
index 0968f2a..0e393c8c 100644
--- a/content/browser/child_process_security_policy_impl.h
+++ b/content/browser/child_process_security_policy_impl.h
@@ -80,6 +80,17 @@
   static ProcessLock CreateAllowAnySite(
       const CoopCoepCrossOriginIsolatedInfo& cross_origin_isolated_info);
 
+  // Create a lock for a specific UrlInfo and COOP/COEP information. This
+  // method can be called from both the UI and IO threads. Locks created with
+  // the same parameters must always be considered equal independent of what
+  // thread they are called on. Special care must be taken since SiteInfos
+  // created on different threads don't always have the same contents for
+  // all their fields (e.g. site_url field is thread dependent).
+  static ProcessLock Create(
+      const IsolationContext& isolation_context,
+      const UrlInfo& url_info,
+      const CoopCoepCrossOriginIsolatedInfo& cross_origin_isolated_info);
+
   ProcessLock();
   explicit ProcessLock(const SiteInfo& site_info);
   ProcessLock(const ProcessLock& rhs);
diff --git a/content/browser/child_process_security_policy_unittest.cc b/content/browser/child_process_security_policy_unittest.cc
index b604d627..e71f1e3 100644
--- a/content/browser/child_process_security_policy_unittest.cc
+++ b/content/browser/child_process_security_policy_unittest.cc
@@ -136,7 +136,7 @@
                               const url::Origin& origin,
                               bool isolate_all_subdomains = false) {
     return std::pair<GURL, std::vector<IsolatedOriginEntry>>(
-        SiteInstanceImpl::GetSiteForOrigin(origin),
+        SiteInfo::GetSiteForOrigin(origin),
         {IsolatedOriginEntry(
             origin,
             BrowsingInstanceId::FromUnsafeValue(min_browsing_instance_id),
@@ -162,10 +162,10 @@
                               const url::Origin& origin2,
                               bool origin1_isolate_all_subdomains = false,
                               bool origin2_isolate_all_subdomains = false) {
-    EXPECT_EQ(SiteInstanceImpl::GetSiteForOrigin(origin1),
-              SiteInstanceImpl::GetSiteForOrigin(origin2));
+    EXPECT_EQ(SiteInfo::GetSiteForOrigin(origin1),
+              SiteInfo::GetSiteForOrigin(origin2));
     return std::pair<GURL, std::vector<IsolatedOriginEntry>>(
-        SiteInstanceImpl::GetSiteForOrigin(origin1),
+        SiteInfo::GetSiteForOrigin(origin1),
         {IsolatedOriginEntry(origin1,
                              SiteInstanceImpl::NextBrowsingInstanceId(),
                              nullptr, nullptr, origin1_isolate_all_subdomains,
@@ -193,7 +193,7 @@
   int GetIsolatedOriginEntryCount(const url::Origin& origin) {
     ChildProcessSecurityPolicyImpl* p =
         ChildProcessSecurityPolicyImpl::GetInstance();
-    GURL key(SiteInstanceImpl::GetSiteForOrigin(origin));
+    GURL key(SiteInfo::GetSiteForOrigin(origin));
     base::AutoLock isolated_origins_lock(p->isolated_origins_lock_);
     auto origins_for_key = p->isolated_origins_[key];
     return std::count_if(origins_for_key.begin(), origins_for_key.end(),
@@ -205,10 +205,9 @@
   void CheckGetSiteForURL(BrowserContext* context,
                           std::map<GURL, GURL> to_test) {
     for (const auto& entry : to_test) {
-      EXPECT_EQ(SiteInstanceImpl::GetSiteForURL(
-                    IsolationContext(context),
-                    UrlInfo::CreateForTesting(entry.first)),
-                entry.second);
+      auto site_info =
+          SiteInfo::CreateForTesting(IsolationContext(context), entry.first);
+      EXPECT_EQ(site_info.site_url(), entry.second);
     }
   }
 
@@ -2642,4 +2641,70 @@
   EXPECT_THAT(p->GetIsolatedOrigins(), testing::IsEmpty());
 }
 
+TEST_F(ChildProcessSecurityPolicyTest, ProcessLockMatching) {
+  GURL nonapp_url("https://bar.com/");
+  GURL app_url("https://some.app.foo.com/");
+  GURL app_effective_url("https://app.com/");
+  EffectiveURLContentBrowserClient modified_client(
+      app_url, app_effective_url, /* requires_dedicated_process */ true);
+  ContentBrowserClient* original_client =
+      SetBrowserClientForTesting(&modified_client);
+
+  IsolationContext isolation_context(browser_context());
+  const auto coi_info = CoopCoepCrossOriginIsolatedInfo::CreateNonIsolated();
+
+  auto ui_nonapp_url_siteinfo = SiteInfo::Create(
+      isolation_context, UrlInfo::CreateForTesting(nonapp_url), coi_info);
+  auto ui_nonapp_url_lock = ProcessLock::Create(
+      isolation_context, UrlInfo::CreateForTesting(nonapp_url), coi_info);
+
+  auto ui_app_url_lock = ProcessLock::Create(
+      isolation_context, UrlInfo::CreateForTesting(app_url), coi_info);
+  auto ui_app_url_siteinfo = SiteInfo::Create(
+      isolation_context, UrlInfo::CreateForTesting(app_url), coi_info);
+
+  SiteInfo io_nonapp_url_siteinfo;
+  ProcessLock io_nonapp_url_lock;
+  SiteInfo io_app_url_siteinfo;
+  ProcessLock io_app_url_lock;
+
+  base::WaitableEvent io_locks_set_event;
+
+  // Post a task that will compute ProcessLocks for the same URLs in the
+  // IO thread.
+  GetIOThreadTaskRunner({})->PostTask(
+      FROM_HERE, base::BindLambdaForTesting([&]() {
+        io_nonapp_url_siteinfo = SiteInfo::CreateOnIOThread(
+            isolation_context, UrlInfo::CreateForTesting(nonapp_url), coi_info);
+        io_nonapp_url_lock = ProcessLock::Create(
+            isolation_context, UrlInfo::CreateForTesting(nonapp_url), coi_info);
+
+        io_app_url_siteinfo = SiteInfo::CreateOnIOThread(
+            isolation_context, UrlInfo::CreateForTesting(app_url), coi_info);
+        io_app_url_lock = ProcessLock::Create(
+            isolation_context, UrlInfo::CreateForTesting(app_url), coi_info);
+
+        // Tell the UI thread have computed the locks.
+        io_locks_set_event.Signal();
+      }));
+
+  io_locks_set_event.Wait();
+
+  // Expect URLs with effective URLs that match the original URL to have
+  // matching SiteInfos and matching ProcessLocks.
+  EXPECT_EQ(ui_nonapp_url_siteinfo, io_nonapp_url_siteinfo);
+  EXPECT_EQ(ui_nonapp_url_lock, io_nonapp_url_lock);
+
+  // Expect hosted app URLs where the effective URL does not match the original
+  // URL to have different SiteInfos but matching process locks. The SiteInfos,
+  // are expected to be different because the effective URL cannot be computed
+  // from the IO thread. This means the site_url fields will differ.
+  EXPECT_NE(ui_app_url_siteinfo, io_app_url_siteinfo);
+  EXPECT_NE(ui_app_url_siteinfo.site_url(), io_app_url_siteinfo.site_url());
+  EXPECT_EQ(ui_app_url_siteinfo.process_lock_url(),
+            io_app_url_siteinfo.process_lock_url());
+  EXPECT_EQ(ui_app_url_lock, io_app_url_lock);
+
+  SetBrowserClientForTesting(original_client);
+}
 }  // namespace content
diff --git a/content/browser/devtools/devtools_frontend_host_impl.cc b/content/browser/devtools/devtools_frontend_host_impl.cc
index c77e99a..538fc84 100644
--- a/content/browser/devtools/devtools_frontend_host_impl.cc
+++ b/content/browser/devtools/devtools_frontend_host_impl.cc
@@ -53,9 +53,8 @@
 DevToolsFrontendHost::GetFrontendResourceBytes(const std::string& path) {
 #if !defined(OS_FUCHSIA)
   for (size_t i = 0; i < kDevtoolsResourcesSize; ++i) {
-    if (path == kDevtoolsResources[i].name) {
-      return GetContentClient()->GetDataResourceBytes(
-          kDevtoolsResources[i].value);
+    if (path == kDevtoolsResources[i].path) {
+      return GetContentClient()->GetDataResourceBytes(kDevtoolsResources[i].id);
     }
   }
 #endif  // defined(OS_FUCHSIA)
diff --git a/content/browser/file_system_access/fake_file_system_access_permission_context.cc b/content/browser/file_system_access/fake_file_system_access_permission_context.cc
new file mode 100644
index 0000000..1839c8e
--- /dev/null
+++ b/content/browser/file_system_access/fake_file_system_access_permission_context.cc
@@ -0,0 +1,98 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/file_system_access/fake_file_system_access_permission_context.h"
+
+#include "base/files/file_path.h"
+#include "content/browser/file_system_access/fixed_file_system_access_permission_grant.h"
+
+namespace content {
+
+FakeFileSystemAccessPermissionContext::FakeFileSystemAccessPermissionContext() =
+    default;
+
+FakeFileSystemAccessPermissionContext::
+    ~FakeFileSystemAccessPermissionContext() = default;
+
+scoped_refptr<FileSystemAccessPermissionGrant>
+FakeFileSystemAccessPermissionContext::GetReadPermissionGrant(
+    const url::Origin& origin,
+    const base::FilePath& path,
+    HandleType handle_type,
+    UserAction user_action) {
+  return base::MakeRefCounted<FixedFileSystemAccessPermissionGrant>(
+      FileSystemAccessPermissionGrant::PermissionStatus::GRANTED, path);
+}
+
+scoped_refptr<FileSystemAccessPermissionGrant>
+FakeFileSystemAccessPermissionContext::GetWritePermissionGrant(
+    const url::Origin& origin,
+    const base::FilePath& path,
+    HandleType handle_type,
+    UserAction user_action) {
+  return base::MakeRefCounted<FixedFileSystemAccessPermissionGrant>(
+      FileSystemAccessPermissionGrant::PermissionStatus::GRANTED, path);
+}
+
+void FakeFileSystemAccessPermissionContext::ConfirmSensitiveDirectoryAccess(
+    const url::Origin& origin,
+    PathType path_type,
+    const base::FilePath& path,
+    HandleType handle_type,
+    GlobalFrameRoutingId frame_id,
+    base::OnceCallback<void(SensitiveDirectoryResult)> callback) {
+  std::move(callback).Run(SensitiveDirectoryResult::kAllowed);
+}
+
+void FakeFileSystemAccessPermissionContext::PerformAfterWriteChecks(
+    std::unique_ptr<FileSystemAccessWriteItem> item,
+    GlobalFrameRoutingId frame_id,
+    base::OnceCallback<void(AfterWriteCheckResult)> callback) {
+  std::move(callback).Run(AfterWriteCheckResult::kAllow);
+}
+
+bool FakeFileSystemAccessPermissionContext::CanObtainReadPermission(
+    const url::Origin& origin) {
+  return true;
+}
+bool FakeFileSystemAccessPermissionContext::CanObtainWritePermission(
+    const url::Origin& origin) {
+  return true;
+}
+
+void FakeFileSystemAccessPermissionContext::SetLastPickedDirectory(
+    const url::Origin& origin,
+    const std::string& id,
+    const base::FilePath& path,
+    const PathType type) {
+  PathInfo info;
+  info.path = path;
+  info.type = type;
+  id_pathinfo_map_.insert({id, info});
+}
+
+FakeFileSystemAccessPermissionContext::PathInfo
+FakeFileSystemAccessPermissionContext::GetLastPickedDirectory(
+    const url::Origin& origin,
+    const std::string& id) {
+  return id_pathinfo_map_.find(id) != id_pathinfo_map_.end()
+             ? id_pathinfo_map_[id]
+             : PathInfo();
+}
+
+void FakeFileSystemAccessPermissionContext::SetWellKnownDirectoryPath(
+    blink::mojom::WellKnownDirectory directory,
+    base::FilePath path) {
+  well_known_directory_map_.insert({directory, path});
+}
+
+base::FilePath FakeFileSystemAccessPermissionContext::GetWellKnownDirectoryPath(
+    blink::mojom::WellKnownDirectory directory) {
+  return well_known_directory_map_.find(directory) !=
+                 well_known_directory_map_.end()
+             ? well_known_directory_map_[directory]
+             : base::FilePath();
+}
+
+}  // namespace content
\ No newline at end of file
diff --git a/content/browser/file_system_access/fake_file_system_access_permission_context.h b/content/browser/file_system_access/fake_file_system_access_permission_context.h
new file mode 100644
index 0000000..1cf5f47
--- /dev/null
+++ b/content/browser/file_system_access/fake_file_system_access_permission_context.h
@@ -0,0 +1,74 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/files/file_path.h"
+#include "content/public/browser/file_system_access_permission_context.h"
+
+#ifndef CONTENT_BROWSER_FILE_SYSTEM_ACCESS_FAKE_FILE_SYSTEM_ACCESS_PERMISSION_CONTEXT_H_
+#define CONTENT_BROWSER_FILE_SYSTEM_ACCESS_FAKE_FILE_SYSTEM_ACCESS_PERMISSION_CONTEXT_H_
+
+namespace content {
+
+// Fake permission context which uses an in-memory map for
+// [GS]etLastPickedDirectory and returns permissions which are always granted.
+// Support for WellKnown directories is provided via a setter which allows
+// setting custom paths in an in-memory map.
+class FakeFileSystemAccessPermissionContext
+    : public content::FileSystemAccessPermissionContext {
+ public:
+  FakeFileSystemAccessPermissionContext();
+  ~FakeFileSystemAccessPermissionContext() override;
+
+  scoped_refptr<FileSystemAccessPermissionGrant> GetReadPermissionGrant(
+      const url::Origin& origin,
+      const base::FilePath& path,
+      HandleType handle_type,
+      UserAction user_action) override;
+
+  scoped_refptr<FileSystemAccessPermissionGrant> GetWritePermissionGrant(
+      const url::Origin& origin,
+      const base::FilePath& path,
+      HandleType handle_type,
+      UserAction user_action) override;
+
+  void ConfirmSensitiveDirectoryAccess(
+      const url::Origin& origin,
+      PathType path_type,
+      const base::FilePath& path,
+      HandleType handle_type,
+      GlobalFrameRoutingId frame_id,
+      base::OnceCallback<void(SensitiveDirectoryResult)> callback) override;
+
+  void PerformAfterWriteChecks(
+      std::unique_ptr<FileSystemAccessWriteItem> item,
+      GlobalFrameRoutingId frame_id,
+      base::OnceCallback<void(AfterWriteCheckResult)> callback) override;
+
+  bool CanObtainReadPermission(const url::Origin& origin) override;
+  bool CanObtainWritePermission(const url::Origin& origin) override;
+
+  void SetLastPickedDirectory(const url::Origin& origin,
+                              const std::string& id,
+                              const base::FilePath& path,
+                              const PathType type) override;
+  PathInfo GetLastPickedDirectory(const url::Origin& origin,
+                                  const std::string& id) override;
+
+  // Establishes a mapping between a WellKnownDirectory and a custom path.
+  void SetWellKnownDirectoryPath(blink::mojom::WellKnownDirectory directory,
+                                 base::FilePath path);
+  // Retrieves a path which was earlier specified via SetWellKnownDirectoryPath.
+  // Otherwise, returns an empty path.
+  base::FilePath GetWellKnownDirectoryPath(
+      blink::mojom::WellKnownDirectory directory) override;
+
+ private:
+  std::map<std::string, PathInfo> id_pathinfo_map_;
+  std::map<blink::mojom::WellKnownDirectory, base::FilePath>
+      well_known_directory_map_;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_FILE_SYSTEM_ACCESS_FAKE_FILE_SYSTEM_ACCESS_PERMISSION_CONTEXT_H_
\ No newline at end of file
diff --git a/content/browser/file_system_access/file_system_access_manager_impl.cc b/content/browser/file_system_access/file_system_access_manager_impl.cc
index f9a867ca..4030c8b 100644
--- a/content/browser/file_system_access/file_system_access_manager_impl.cc
+++ b/content/browser/file_system_access/file_system_access_manager_impl.cc
@@ -3,6 +3,8 @@
 // found in the LICENSE file.
 
 #include "content/browser/file_system_access/file_system_access_manager_impl.h"
+
+#include <algorithm>
 #include <string>
 
 #include "base/bind.h"
@@ -13,6 +15,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/notreached.h"
 #include "base/strings/strcat.h"
+#include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/thread_pool.h"
 #include "build/build_config.h"
@@ -224,6 +227,17 @@
                                                  : token->url().path();
 }
 
+bool IsValidIdChar(const char c) {
+  return base::IsAsciiAlpha(c) || base::IsAsciiDigit(c) || c == '_' || c == '-';
+}
+
+bool IsValidId(const std::string& id) {
+  return id.size() <= 32 &&
+         std::find_if(id.begin(), id.end(), [](const char c) {
+           return !IsValidIdChar(c);
+         }) == id.end();
+}
+
 }  // namespace
 
 FileSystemAccessManagerImpl::SharedHandleState::SharedHandleState(
@@ -310,6 +324,7 @@
 void FileSystemAccessManagerImpl::ChooseEntries(
     blink::mojom::ChooseFileSystemEntryType type,
     std::vector<blink::mojom::ChooseFileSystemEntryAcceptsOptionPtr> accepts,
+    const std::string& starting_directory_id,
     blink::mojom::WellKnownDirectory well_known_starting_directory,
     mojo::PendingRemote<blink::mojom::FileSystemAccessTransferToken>
         starting_directory_token,
@@ -330,6 +345,11 @@
       type != blink::mojom::ChooseFileSystemEntryType::kSaveFile) {
     receivers_.ReportBadMessage(
         "Suggested file name only allowed for save dialogs");
+  }
+
+  // Non-compromised renderers shouldn't be able to send an invalid id.
+  if (!IsValidId(starting_directory_id)) {
+    receivers_.ReportBadMessage("Invalid starting directory ID in browser");
     return;
   }
 
@@ -371,6 +391,7 @@
   auto resolve_default_directory_callback = base::BindOnce(
       &FileSystemAccessManagerImpl::ResolveDefaultDirectory,
       weak_factory_.GetWeakPtr(), context, type, std::move(accepts),
+      std::move(starting_directory_id),
       std::move(well_known_starting_directory), std::move(suggested_name),
       include_accepts_all, std::move(callback));
 
@@ -387,26 +408,37 @@
     const BindingContext& context,
     blink::mojom::ChooseFileSystemEntryType type,
     std::vector<blink::mojom::ChooseFileSystemEntryAcceptsOptionPtr> accepts,
+    const std::string& starting_directory_id,
     blink::mojom::WellKnownDirectory well_known_starting_directory,
     const std::string& suggested_name,
     bool include_accepts_all,
     ChooseEntriesCallback callback,
     FileSystemAccessTransferTokenImpl* resolved_starting_directory_token) {
   PathInfo path_info;
+
   if (resolved_starting_directory_token)
     HandleTransferTokenAsDefaultDirectory(resolved_starting_directory_token,
                                           path_info);
 
   if (path_info.path.empty() && permission_context_) {
-    if (well_known_starting_directory !=
-        blink::mojom::WellKnownDirectory::kDefault) {
-      // Prioritize an explicitly stated starting directory over an implicitly
-      // remembered last-picked directory.
-      path_info.path = permission_context_->GetWellKnownDirectoryPath(
-          well_known_starting_directory);
-    } else { /*well_known_starting_directory ==
-                blink::mojom::WellKnownDirectory::kDefault*/
-      path_info = permission_context_->GetLastPickedDirectory(context.origin);
+    if (!starting_directory_id.empty()) {
+      // Prioritize an `id` over a well-known directory.
+      path_info = permission_context_->GetLastPickedDirectory(
+          context.origin, starting_directory_id);
+    }
+    if (path_info.path.empty()) {
+      if (well_known_starting_directory !=
+          blink::mojom::WellKnownDirectory::kDefault) {
+        // Prioritize an explicitly stated well-known directory over an
+        // implicitly remembered LastPicked directory.
+        path_info.path = permission_context_->GetWellKnownDirectoryPath(
+            well_known_starting_directory);
+      } else { /*well_known_starting_directory ==
+                  blink::mojom::WellKnownDirectory::kDefault*/
+        // If `id` empty or unset, fall back to the default LastPickedDirectory.
+        path_info = permission_context_->GetLastPickedDirectory(context.origin,
+                                                                std::string());
+      }
     }
   }
 
@@ -421,7 +453,8 @@
               &FileSystemAccessManagerImpl::SetDefaultPathAndShowPicker,
               weak_factory_.GetWeakPtr(), context, type, std::move(accepts),
               std::move(suggested_name), include_accepts_all,
-              std::move(url).url.path(), std::move(callback)),
+              std::move(starting_directory_id), std::move(url).url.path(),
+              std::move(callback)),
           base::SequencedTaskRunnerHandle::Get()));
 }
 
@@ -431,6 +464,7 @@
     std::vector<blink::mojom::ChooseFileSystemEntryAcceptsOptionPtr> accepts,
     const std::string& suggested_name,
     bool include_accepts_all,
+    const std::string& starting_directory_id,
     base::FilePath default_directory,
     ChooseEntriesCallback callback,
     base::File::Error result) {
@@ -454,8 +488,8 @@
       std::move(default_directory), std::move(suggested_name_path));
 
   if (auto_file_picker_result_for_test_) {
-    DidChooseEntries(context, options, std::move(callback),
-                     file_system_access_error::Ok(),
+    DidChooseEntries(context, options, starting_directory_id,
+                     std::move(callback), file_system_access_error::Ok(),
                      {*auto_file_picker_result_for_test_});
     return;
   }
@@ -464,7 +498,7 @@
       context.origin, context.frame_id, options,
       base::BindOnce(&FileSystemAccessManagerImpl::DidChooseEntries,
                      weak_factory_.GetWeakPtr(), context, options,
-                     std::move(callback)));
+                     starting_directory_id, std::move(callback)));
 }
 
 void FileSystemAccessManagerImpl::CreateFileSystemAccessDragDropToken(
@@ -1000,6 +1034,7 @@
 void FileSystemAccessManagerImpl::DidChooseEntries(
     const BindingContext& binding_context,
     const FileSystemChooser::Options& options,
+    const std::string& starting_directory_id,
     ChooseEntriesCallback callback,
     blink::mojom::FileSystemAccessErrorPtr result,
     std::vector<FileSystemChooser::ResultEntry> entries) {
@@ -1013,9 +1048,9 @@
   }
 
   if (!permission_context_) {
-    DidVerifySensitiveDirectoryAccess(binding_context, options,
-                                      std::move(callback), std::move(entries),
-                                      SensitiveDirectoryResult::kAllowed);
+    DidVerifySensitiveDirectoryAccess(
+        binding_context, options, starting_directory_id, std::move(callback),
+        std::move(entries), SensitiveDirectoryResult::kAllowed);
     return;
   }
 
@@ -1032,12 +1067,13 @@
       base::BindOnce(
           &FileSystemAccessManagerImpl::DidVerifySensitiveDirectoryAccess,
           weak_factory_.GetWeakPtr(), binding_context, options,
-          std::move(callback), std::move(entries)));
+          starting_directory_id, std::move(callback), std::move(entries)));
 }
 
 void FileSystemAccessManagerImpl::DidVerifySensitiveDirectoryAccess(
     const BindingContext& binding_context,
     const FileSystemChooser::Options& options,
+    const std::string& starting_directory_id,
     ChooseEntriesCallback callback,
     std::vector<FileSystemChooser::ResultEntry> entries,
     SensitiveDirectoryResult result) {
@@ -1057,7 +1093,7 @@
         binding_context.origin, binding_context.frame_id, options,
         base::BindOnce(&FileSystemAccessManagerImpl::DidChooseEntries,
                        weak_factory_.GetWeakPtr(), binding_context, options,
-                       std::move(callback)));
+                       starting_directory_id, std::move(callback)));
     return;
   }
 
@@ -1068,7 +1104,8 @@
             ? entries.front().path
             : entries.front().path.DirName();
     permission_context_->SetLastPickedDirectory(
-        binding_context.origin, picked_directory, entries.front().type);
+        binding_context.origin, starting_directory_id, picked_directory,
+        entries.front().type);
   }
 
   if (options.type() ==
diff --git a/content/browser/file_system_access/file_system_access_manager_impl.h b/content/browser/file_system_access/file_system_access_manager_impl.h
index a24008f..a661354b 100644
--- a/content/browser/file_system_access/file_system_access_manager_impl.h
+++ b/content/browser/file_system_access/file_system_access_manager_impl.h
@@ -104,6 +104,7 @@
   void ChooseEntries(
       blink::mojom::ChooseFileSystemEntryType type,
       std::vector<blink::mojom::ChooseFileSystemEntryAcceptsOptionPtr> accepts,
+      const std::string& starting_directory_id,
       blink::mojom::WellKnownDirectory well_known_starting_directory,
       mojo::PendingRemote<blink::mojom::FileSystemAccessTransferToken>
           starting_directory_token,
@@ -270,6 +271,7 @@
       const BindingContext& context,
       blink::mojom::ChooseFileSystemEntryType type,
       std::vector<blink::mojom::ChooseFileSystemEntryAcceptsOptionPtr> accepts,
+      const std::string& starting_directory_id,
       blink::mojom::WellKnownDirectory well_known_starting_directory,
       const std::string& suggested_name,
       bool include_accepts_all,
@@ -281,6 +283,7 @@
       std::vector<blink::mojom::ChooseFileSystemEntryAcceptsOptionPtr> accepts,
       const std::string& suggested_name,
       bool include_accepts_all,
+      const std::string& starting_directory_id,
       base::FilePath default_directory,
       ChooseEntriesCallback callback,
       base::File::Error result);
@@ -292,12 +295,14 @@
 
   void DidChooseEntries(const BindingContext& binding_context,
                         const FileSystemChooser::Options& options,
+                        const std::string& starting_directory_id,
                         ChooseEntriesCallback callback,
                         blink::mojom::FileSystemAccessErrorPtr result,
                         std::vector<FileSystemChooser::ResultEntry> entries);
   void DidVerifySensitiveDirectoryAccess(
       const BindingContext& binding_context,
       const FileSystemChooser::Options& options,
+      const std::string& starting_directory_id,
       ChooseEntriesCallback callback,
       std::vector<FileSystemChooser::ResultEntry> entries,
       FileSystemAccessPermissionContext::SensitiveDirectoryResult result);
diff --git a/content/browser/file_system_access/file_system_access_manager_impl_unittest.cc b/content/browser/file_system_access/file_system_access_manager_impl_unittest.cc
index 54b90d4..632f68b 100644
--- a/content/browser/file_system_access/file_system_access_manager_impl_unittest.cc
+++ b/content/browser/file_system_access/file_system_access_manager_impl_unittest.cc
@@ -1127,11 +1127,12 @@
       permission_context_,
       GetWellKnownDirectoryPath(blink::mojom::WellKnownDirectory::kDefault))
       .WillOnce(testing::Return(base::FilePath()));
-  EXPECT_CALL(permission_context_, GetLastPickedDirectory(kTestOrigin))
+  EXPECT_CALL(permission_context_,
+              GetLastPickedDirectory(kTestOrigin, std::string()))
       .WillOnce(testing::Return(PathInfo()));
   EXPECT_CALL(permission_context_,
-              SetLastPickedDirectory(kTestOrigin, test_file.DirName(),
-                                     PathType::kLocal));
+              SetLastPickedDirectory(kTestOrigin, std::string(),
+                                     test_file.DirName(), PathType::kLocal));
 
   EXPECT_CALL(
       permission_context_,
@@ -1158,8 +1159,10 @@
   base::RunLoop loop;
   manager_remote->ChooseEntries(
       blink::mojom::ChooseFileSystemEntryType::kOpenFile, /*accepts=*/{},
+      /*starting_directory_id=*/std::string(),
       blink::mojom::WellKnownDirectory::kDefault,
-      /*starting_directory_token=*/mojo::NullRemote(), /*suggested_name=*/"",
+      /*starting_directory_token=*/mojo::NullRemote(),
+      /*suggested_name=*/std::string(),
       /*include_accepts_all=*/true,
       base::BindLambdaForTesting(
           [&](blink::mojom::FileSystemAccessErrorPtr result,
@@ -1197,11 +1200,12 @@
       permission_context_,
       GetWellKnownDirectoryPath(blink::mojom::WellKnownDirectory::kDefault))
       .WillOnce(testing::Return(base::FilePath()));
-  EXPECT_CALL(permission_context_, GetLastPickedDirectory(kTestOrigin))
+  EXPECT_CALL(permission_context_,
+              GetLastPickedDirectory(kTestOrigin, std::string()))
       .WillOnce(testing::Return(PathInfo()));
   EXPECT_CALL(permission_context_,
-              SetLastPickedDirectory(kTestOrigin, test_file.DirName(),
-                                     PathType::kLocal));
+              SetLastPickedDirectory(kTestOrigin, std::string(),
+                                     test_file.DirName(), PathType::kLocal));
 
   EXPECT_CALL(
       permission_context_,
@@ -1228,8 +1232,10 @@
   base::RunLoop loop;
   manager_remote->ChooseEntries(
       blink::mojom::ChooseFileSystemEntryType::kSaveFile, /*accepts=*/{},
+      /*starting_directory_id=*/std::string(),
       blink::mojom::WellKnownDirectory::kDefault,
-      /*starting_directory_token=*/mojo::NullRemote(), /*suggested_name=*/"",
+      /*starting_directory_token=*/mojo::NullRemote(),
+      /*suggested_name=*/std::string(),
       /*include_accepts_all=*/true,
       base::BindLambdaForTesting(
           [&](blink::mojom::FileSystemAccessErrorPtr result,
@@ -1264,10 +1270,12 @@
       permission_context_,
       GetWellKnownDirectoryPath(blink::mojom::WellKnownDirectory::kDefault))
       .WillOnce(testing::Return(base::FilePath()));
-  EXPECT_CALL(permission_context_, GetLastPickedDirectory(kTestOrigin))
+  EXPECT_CALL(permission_context_,
+              GetLastPickedDirectory(kTestOrigin, std::string()))
       .WillOnce(testing::Return(PathInfo()));
   EXPECT_CALL(permission_context_,
-              SetLastPickedDirectory(kTestOrigin, test_dir, PathType::kLocal));
+              SetLastPickedDirectory(kTestOrigin, std::string(), test_dir,
+                                     PathType::kLocal));
 
   EXPECT_CALL(
       permission_context_,
@@ -1293,9 +1301,11 @@
 
   base::RunLoop loop;
   manager_remote->ChooseEntries(
-      blink::mojom::ChooseFileSystemEntryType::kOpenDirectory, {},
+      blink::mojom::ChooseFileSystemEntryType::kOpenDirectory, /*accepts=*/{},
+      /*starting_directory_id=*/std::string(),
       blink::mojom::WellKnownDirectory::kDefault,
-      /*starting_directory_token=*/mojo::NullRemote(), /*suggested_name=*/"",
+      /*starting_directory_token=*/mojo::NullRemote(),
+      /*suggested_name=*/std::string(),
       /*include_accepts_all=*/true,
       base::BindLambdaForTesting(
           [&](blink::mojom::FileSystemAccessErrorPtr result,
@@ -1305,4 +1315,36 @@
   loop.Run();
 }
 
+TEST_F(FileSystemAccessManagerImplTest, ChooseEntries_InvalidStartInID) {
+  base::FilePath test_dir = dir_.GetPath();
+
+  FileSystemChooser::ResultEntry entry;
+  entry.type = PathType::kLocal;
+  entry.path = test_dir;
+  manager_->SetFilePickerResultForTesting(std::move(entry));
+
+  static_cast<TestRenderFrameHost*>(web_contents_->GetMainFrame())
+      ->SimulateUserActivation();
+
+  mojo::Remote<blink::mojom::FileSystemAccessManager> manager_remote;
+  FileSystemAccessManagerImpl::BindingContext binding_context = {
+      kTestOrigin, kTestURL,
+      web_contents_->GetMainFrame()->GetGlobalFrameRoutingId()};
+  manager_->BindReceiver(binding_context,
+                         manager_remote.BindNewPipeAndPassReceiver());
+
+  // Specifying a `starting_directory_id` with invalid characters should trigger
+  // a bad message callback.
+  mojo::test::BadMessageObserver bad_message_observer;
+  manager_remote->ChooseEntries(
+      blink::mojom::ChooseFileSystemEntryType::kOpenDirectory, /*accepts=*/{},
+      /*starting_directory_id=*/"inv*l!d <hars",
+      blink::mojom::WellKnownDirectory::kDefault,
+      /*starting_directory_token=*/mojo::NullRemote(),
+      /*suggested_name=*/std::string(),
+      /*include_accepts_all=*/true, base::DoNothing());
+  EXPECT_EQ("Invalid starting directory ID in browser",
+            bad_message_observer.WaitForBadMessage());
+}
+
 }  // namespace content
diff --git a/content/browser/file_system_access/file_system_chooser_browsertest.cc b/content/browser/file_system_access/file_system_chooser_browsertest.cc
index 111a7cd4..72ff09a 100644
--- a/content/browser/file_system_access/file_system_chooser_browsertest.cc
+++ b/content/browser/file_system_access/file_system_chooser_browsertest.cc
@@ -12,6 +12,7 @@
 #include "base/test/gmock_callback_support.h"
 #include "base/threading/thread_restrictions.h"
 #include "build/build_config.h"
+#include "content/browser/file_system_access/fake_file_system_access_permission_context.h"
 #include "content/browser/file_system_access/file_system_access_manager_impl.h"
 #include "content/browser/file_system_access/file_system_chooser_test_helpers.h"
 #include "content/browser/file_system_access/fixed_file_system_access_permission_grant.h"
@@ -495,10 +496,11 @@
       permission_context,
       GetWellKnownDirectoryPath(blink::mojom::WellKnownDirectory::kDefault))
       .WillOnce(testing::Return(base::FilePath()));
-  EXPECT_CALL(permission_context, GetLastPickedDirectory(origin))
+  EXPECT_CALL(permission_context, GetLastPickedDirectory(origin, std::string()))
       .WillOnce(testing::Return(PathInfo()));
   EXPECT_CALL(permission_context,
-              SetLastPickedDirectory(origin, test_dir, PathType::kLocal));
+              SetLastPickedDirectory(origin, std::string(), test_dir,
+                                     PathType::kLocal));
 
   EXPECT_CALL(permission_context,
               ConfirmSensitiveDirectoryAccess_(
@@ -570,7 +572,7 @@
       permission_context,
       GetWellKnownDirectoryPath(blink::mojom::WellKnownDirectory::kDefault))
       .WillOnce(testing::Return(base::FilePath()));
-  EXPECT_CALL(permission_context, GetLastPickedDirectory(origin))
+  EXPECT_CALL(permission_context, GetLastPickedDirectory(origin, std::string()))
       .WillOnce(testing::Return(PathInfo()));
 
   EXPECT_CALL(permission_context,
@@ -632,7 +634,7 @@
       permission_context,
       GetWellKnownDirectoryPath(blink::mojom::WellKnownDirectory::kDefault))
       .WillOnce(testing::Return(base::FilePath()));
-  EXPECT_CALL(permission_context, GetLastPickedDirectory(origin))
+  EXPECT_CALL(permission_context, GetLastPickedDirectory(origin, std::string()))
       .WillOnce(testing::Return(PathInfo()));
 
   EXPECT_CALL(permission_context,
@@ -749,10 +751,11 @@
   PathInfo good_dir_info;
   good_dir_info.path = temp_dir_.GetPath();
 
-  EXPECT_CALL(permission_context, GetLastPickedDirectory(origin))
+  EXPECT_CALL(permission_context, GetLastPickedDirectory(origin, std::string()))
       .WillOnce(testing::Return(good_dir_info));
   EXPECT_CALL(permission_context,
-              SetLastPickedDirectory(origin, test_dir, PathType::kLocal));
+              SetLastPickedDirectory(origin, std::string(), test_dir,
+                                     PathType::kLocal));
 
   EXPECT_CALL(permission_context,
               ConfirmSensitiveDirectoryAccess_(
@@ -837,10 +840,11 @@
       permission_context,
       GetWellKnownDirectoryPath(blink::mojom::WellKnownDirectory::kDefault))
       .WillOnce(testing::Return(default_dir));
-  EXPECT_CALL(permission_context, GetLastPickedDirectory(origin))
+  EXPECT_CALL(permission_context, GetLastPickedDirectory(origin, std::string()))
       .WillOnce(testing::Return(bad_dir_info));
   EXPECT_CALL(permission_context,
-              SetLastPickedDirectory(origin, test_dir, PathType::kLocal));
+              SetLastPickedDirectory(origin, std::string(), test_dir,
+                                     PathType::kLocal));
 
   EXPECT_CALL(permission_context,
               ConfirmSensitiveDirectoryAccess_(
@@ -919,10 +923,11 @@
   good_dir_info.path = base::FilePath::FromUTF8Unsafe(kTestMountPoint);
   good_dir_info.type = PathType::kExternal;
 
-  EXPECT_CALL(permission_context, GetLastPickedDirectory(origin))
+  EXPECT_CALL(permission_context, GetLastPickedDirectory(origin, std::string()))
       .WillOnce(testing::Return(good_dir_info));
   EXPECT_CALL(permission_context,
-              SetLastPickedDirectory(origin, test_dir, PathType::kLocal));
+              SetLastPickedDirectory(origin, std::string(), test_dir,
+                                     PathType::kLocal));
 
   EXPECT_CALL(permission_context,
               ConfirmSensitiveDirectoryAccess_(
@@ -1009,10 +1014,11 @@
       permission_context,
       GetWellKnownDirectoryPath(blink::mojom::WellKnownDirectory::kDefault))
       .WillOnce(testing::Return(default_dir));
-  EXPECT_CALL(permission_context, GetLastPickedDirectory(origin))
+  EXPECT_CALL(permission_context, GetLastPickedDirectory(origin, std::string()))
       .WillOnce(testing::Return(bad_dir_info));
   EXPECT_CALL(permission_context,
-              SetLastPickedDirectory(origin, test_dir, PathType::kLocal));
+              SetLastPickedDirectory(origin, std::string(), test_dir,
+                                     PathType::kLocal));
 
   EXPECT_CALL(permission_context,
               ConfirmSensitiveDirectoryAccess_(
@@ -1058,7 +1064,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest,
-                       StartInWellKnownDirectory) {
+                       StartIn_WellKnownDirectory) {
   base::FilePath test_dir = CreateTestDir();
 
   SelectFileDialogParams dialog_params;
@@ -1101,7 +1107,8 @@
       GetWellKnownDirectoryPath(blink::mojom::WellKnownDirectory::kDirDesktop))
       .WillOnce(testing::Return(desktop_dir));
   EXPECT_CALL(permission_context,
-              SetLastPickedDirectory(origin, test_dir, PathType::kLocal));
+              SetLastPickedDirectory(origin, std::string(), test_dir,
+                                     PathType::kLocal));
 
   EXPECT_CALL(permission_context,
               ConfirmSensitiveDirectoryAccess_(
@@ -1147,8 +1154,7 @@
   EXPECT_EQ(desktop_dir, dialog_params.default_path);
 }
 
-IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest,
-                       OpenFile_StartIn_FileHandle) {
+IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, StartIn_FileHandle) {
   // Ensure test file exists in a directory which could not be a default.
   base::FilePath test_file_dir;
   {
@@ -1186,8 +1192,7 @@
   EXPECT_EQ(test_file_dir, dialog_params.default_path);
 }
 
-IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest,
-                       OpenFile_StartIn_DirectoryHandle) {
+IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, StartIn_DirectoryHandle) {
   // Ensure test directory exists and could not be a default.
   base::FilePath test_dir;
   {
@@ -1224,7 +1229,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest,
-                       OpenFile_StartIn_FileHandle_External) {
+                       StartIn_FileHandle_External) {
   const base::FilePath test_file = CreateTestFile("");
   const base::FilePath virtual_path =
       base::FilePath::FromUTF8Unsafe(kTestMountPoint)
@@ -1410,4 +1415,182 @@
   }
 }
 
+IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, StartIn_ID) {
+  base::FilePath test_dir;
+  {
+    base::ScopedAllowBlockingForTesting allow_blocking;
+    // Ensure directory we're selecting exists.
+    EXPECT_TRUE(base::CreateTemporaryDirInDir(
+        temp_dir_.GetPath(), FILE_PATH_LITERAL("test123"), &test_dir));
+  }
+
+  SelectFileDialogParams dialog_params;
+  ui::SelectFileDialog::SetFactory(
+      new FakeSelectFileDialogFactory({test_dir}, &dialog_params));
+
+  FakeFileSystemAccessPermissionContext permission_context;
+  static_cast<FileSystemAccessManagerImpl*>(
+      BrowserContext::GetStoragePartition(
+          shell()->web_contents()->GetBrowserContext(),
+          shell()->web_contents()->GetSiteInstance())
+          ->GetFileSystemAccessEntryFactory())
+      ->SetPermissionContextForTesting(&permission_context);
+
+  // Specify an `id` for the directory that is picked.
+  std::string id = "testing";
+  PathInfo test_dir_info;
+  test_dir_info.path = test_dir;
+
+  ASSERT_TRUE(
+      NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
+  // #1: `id` is unset. Fall back to the default starting directory.
+  EXPECT_EQ(
+      test_dir.BaseName().AsUTF8Unsafe(),
+      EvalJs(shell(),
+             JsReplace("(async () => {"
+                       "  let e = await self.showDirectoryPicker({ id: $1 });"
+                       "  self.selected_entry = e;"
+                       "  return e.name; })()",
+                       id)));
+  EXPECT_EQ(ui::SelectFileDialog::SELECT_FOLDER, dialog_params.type);
+  EXPECT_EQ(base::FilePath(), dialog_params.default_path);
+
+  // #2: `id` is set. Use the LastPickedDirectory given this `id`.
+  EXPECT_EQ(
+      test_dir.BaseName().AsUTF8Unsafe(),
+      EvalJs(shell(),
+             JsReplace("(async () => {"
+                       "  let e = await self.showDirectoryPicker({ id: $1 });"
+                       "  self.selected_entry = e;"
+                       "  return e.name; })()",
+                       id)));
+  EXPECT_EQ(ui::SelectFileDialog::SELECT_FOLDER, dialog_params.type);
+  EXPECT_EQ(test_dir, dialog_params.default_path);
+}
+
+IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, StartIn_Priority) {
+  // Priority:
+  //   1) `startIn` via a file/directory handle
+  //   2) non-empty `id, if stored
+  //   3) `startIn` via a well-known directory
+  //   4) default `id`, if stored
+  //   5) default path
+  //
+  // Test A checks #5
+  //      B checks #4
+  //      C checks #3
+  //      D checks #2
+  //      E checks #1
+
+  base::FilePath test_dir;
+  base::FilePath desktop_dir;
+  base::FilePath music_dir;
+  base::FilePath dir_handle;
+  {
+    base::ScopedAllowBlockingForTesting allow_blocking;
+    // Ensure directories we're selecting exist.
+    EXPECT_TRUE(base::CreateTemporaryDirInDir(
+        temp_dir_.GetPath(), FILE_PATH_LITERAL("test123"), &test_dir));
+    EXPECT_TRUE(base::CreateTemporaryDirInDir(
+        temp_dir_.GetPath(), FILE_PATH_LITERAL("Desktop"), &desktop_dir));
+    EXPECT_TRUE(base::CreateTemporaryDirInDir(
+        temp_dir_.GetPath(), FILE_PATH_LITERAL("Music"), &music_dir));
+    EXPECT_TRUE(base::CreateTemporaryDirInDir(
+        temp_dir_.GetPath(), FILE_PATH_LITERAL("handle"), &dir_handle));
+  }
+
+  FakeFileSystemAccessPermissionContext permission_context;
+  static_cast<FileSystemAccessManagerImpl*>(
+      BrowserContext::GetStoragePartition(
+          shell()->web_contents()->GetBrowserContext(),
+          shell()->web_contents()->GetSiteInstance())
+          ->GetFileSystemAccessEntryFactory())
+      ->SetPermissionContextForTesting(&permission_context);
+
+  permission_context.SetWellKnownDirectoryPath(
+      blink::mojom::WellKnownDirectory::kDirDesktop, desktop_dir);
+  permission_context.SetWellKnownDirectoryPath(
+      blink::mojom::WellKnownDirectory::kDirMusic, music_dir);
+
+  // Specify an `id` for the directory that is picked.
+  std::string id = "testing";
+  PathInfo test_dir_info;
+  test_dir_info.path = test_dir;
+  PathInfo desktop_dir_info;
+  desktop_dir_info.path = desktop_dir;
+
+  ASSERT_TRUE(
+      NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
+
+  SelectFileDialogParams dialog_params;
+  // (A) Acquire a handle to the "handle" directory to be used later. Use the
+  // default `id`.
+  ui::SelectFileDialog::SetFactory(
+      new FakeSelectFileDialogFactory({dir_handle}, &dialog_params));
+  EXPECT_EQ(
+      dir_handle.BaseName().AsUTF8Unsafe(),
+      EvalJs(shell(), JsReplace("(async () => {"
+                                "  let e = await self.showDirectoryPicker();"
+                                "  self.handle = e;"
+                                "  return e.name; })()",
+                                id)));
+  EXPECT_EQ(ui::SelectFileDialog::SELECT_FOLDER, dialog_params.type);
+  EXPECT_EQ(base::FilePath(), dialog_params.default_path);
+
+  // (B) Use the default `id`, which should have been set.
+  ui::SelectFileDialog::SetFactory(
+      new FakeSelectFileDialogFactory({dir_handle}, &dialog_params));
+  EXPECT_EQ(
+      dir_handle.BaseName().AsUTF8Unsafe(),
+      EvalJs(shell(), JsReplace("(async () => {"
+                                "  let e = await self.showDirectoryPicker();"
+                                "  self.handle = e;"
+                                "  return e.name; })()",
+                                id)));
+  EXPECT_EQ(ui::SelectFileDialog::SELECT_FOLDER, dialog_params.type);
+  EXPECT_EQ(dir_handle, dialog_params.default_path);
+
+  // (C) Since this new `id` has not yet been set, fall back on using the
+  // WellKnownDirectory specified via `startIn`.
+  ui::SelectFileDialog::SetFactory(
+      new FakeSelectFileDialogFactory({desktop_dir}, &dialog_params));
+  EXPECT_EQ(
+      desktop_dir.BaseName().AsUTF8Unsafe(),
+      EvalJs(shell(), JsReplace("(async () => {"
+                                "  let e = await self.showDirectoryPicker({ "
+                                "id: $1, startIn: 'desktop' });"
+                                "  self.selected_entry = e;"
+                                "  return e.name; })()",
+                                id)));
+  EXPECT_EQ(ui::SelectFileDialog::SELECT_FOLDER, dialog_params.type);
+  EXPECT_EQ(desktop_dir, dialog_params.default_path);
+
+  // (D) The `id` is set. Use the LastPickedDirectory given this `id`.
+  EXPECT_EQ(
+      desktop_dir.BaseName().AsUTF8Unsafe(),
+      EvalJs(shell(), JsReplace("(async () => {"
+                                "  let e = await self.showDirectoryPicker({ "
+                                "id: $1, startIn: 'music' });"
+                                "  self.selected_entry = e;"
+                                "  return e.name; })()",
+                                id)));
+  EXPECT_EQ(ui::SelectFileDialog::SELECT_FOLDER, dialog_params.type);
+  EXPECT_EQ(desktop_dir, dialog_params.default_path);
+
+  // (E) A directory handle is specified via `startIn`, so prioritize this over
+  // the stored ID.
+  ui::SelectFileDialog::SetFactory(
+      new FakeSelectFileDialogFactory({dir_handle}, &dialog_params));
+  EXPECT_EQ(
+      dir_handle.BaseName().AsUTF8Unsafe(),
+      EvalJs(shell(), JsReplace("(async () => {"
+                                "  let e = await self.showDirectoryPicker({ "
+                                "id: $1, startIn: self.handle });"
+                                "  self.selected_entry = e;"
+                                "  return e.name; })()",
+                                id)));
+  EXPECT_EQ(ui::SelectFileDialog::SELECT_FOLDER, dialog_params.type);
+  EXPECT_EQ(dir_handle, dialog_params.default_path);
+}
+
 }  // namespace content
diff --git a/content/browser/file_system_access/mock_file_system_access_permission_context.h b/content/browser/file_system_access/mock_file_system_access_permission_context.h
index c2ba5e3..7f97f210 100644
--- a/content/browser/file_system_access/mock_file_system_access_permission_context.h
+++ b/content/browser/file_system_access/mock_file_system_access_permission_context.h
@@ -71,12 +71,13 @@
   MOCK_METHOD(void,
               SetLastPickedDirectory,
               (const url::Origin& origin,
+               const std::string& id,
                const base::FilePath& path,
                const PathType type),
               (override));
   MOCK_METHOD(PathInfo,
               GetLastPickedDirectory,
-              (const url::Origin& origin),
+              (const url::Origin& origin, const std::string& id),
               (override));
 
   MOCK_METHOD(base::FilePath,
diff --git a/content/browser/isolated_origin_browsertest.cc b/content/browser/isolated_origin_browsertest.cc
index 1bf299b5..56db17a2 100644
--- a/content/browser/isolated_origin_browsertest.cc
+++ b/content/browser/isolated_origin_browsertest.cc
@@ -1777,11 +1777,11 @@
   // Calculate the expected SiteInfo for each URL.  Both |foo_url| and
   // |bar_url| should have a site URL of |app_url|, but the process locks
   // should be foo.com and bar.com.
-  SiteInfo foo_site_info = SiteInstanceImpl::ComputeSiteInfoForTesting(
+  SiteInfo foo_site_info = SiteInfo::CreateForTesting(
       web_contents()->GetSiteInstance()->GetIsolationContext(), foo_url);
   EXPECT_EQ(app_url, foo_site_info.site_url());
   EXPECT_EQ(foo_url.GetOrigin(), foo_site_info.process_lock_url());
-  SiteInfo bar_site_info = SiteInstanceImpl::ComputeSiteInfoForTesting(
+  SiteInfo bar_site_info = SiteInfo::CreateForTesting(
       web_contents()->GetSiteInstance()->GetIsolationContext(), bar_url);
   EXPECT_EQ(app_url, bar_site_info.site_url());
   EXPECT_EQ(bar_url.GetOrigin(), bar_site_info.process_lock_url());
@@ -2345,7 +2345,7 @@
                                                const GURL& url) {
     return RenderProcessHostImpl::IsSuitableHost(
         process, isolation_context,
-        SiteInstanceImpl::ComputeSiteInfoForTesting(isolation_context, url));
+        SiteInfo::CreateForTesting(isolation_context, url));
   };
   EXPECT_TRUE(is_suitable_host(foo_process, foo_url));
   EXPECT_FALSE(is_suitable_host(foo_process, isolated_foo_url));
diff --git a/content/browser/renderer_host/back_forward_cache_metrics.cc b/content/browser/renderer_host/back_forward_cache_metrics.cc
index b9fc313..2930a0e 100644
--- a/content/browser/renderer_host/back_forward_cache_metrics.cc
+++ b/content/browser/renderer_host/back_forward_cache_metrics.cc
@@ -233,10 +233,10 @@
     //
     // [1]
     // https://chromium.googlesource.com/chromium/src/+/HEAD/docs/security/origin-vs-url.md#avoid-converting-urls-to-origins
-    GURL previous_site = SiteInstanceImpl::GetSiteForOrigin(
+    GURL previous_site = SiteInfo::GetSiteForOrigin(
         url::Origin::Create(details->previous_main_frame_url));
-    GURL new_site = SiteInstanceImpl::GetSiteForOrigin(
-        url::Origin::Create(navigation->GetURL()));
+    GURL new_site =
+        SiteInfo::GetSiteForOrigin(url::Origin::Create(navigation->GetURL()));
     if (previous_site == new_site) {
       page_store_result_->No(
           NotRestoredReason::kRenderFrameHostReused_SameSite);
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
index 348f2e1..307cc38 100644
--- a/content/browser/renderer_host/navigation_request.cc
+++ b/content/browser/renderer_host/navigation_request.cc
@@ -2576,9 +2576,9 @@
     // https://crbug.com/738634.
     SiteInstanceImpl* instance = render_frame_host_->GetSiteInstance();
     const IsolationContext& isolation_context = instance->GetIsolationContext();
-    auto site_info = SiteInstanceImpl::ComputeSiteInfo(
-        isolation_context, GetUrlInfo(),
-        instance->GetCoopCoepCrossOriginIsolatedInfo());
+    auto site_info =
+        SiteInfo::Create(isolation_context, GetUrlInfo(),
+                         instance->GetCoopCoepCrossOriginIsolatedInfo());
     if (!instance->HasSite() &&
         site_info.RequiresDedicatedProcess(isolation_context)) {
       instance->ConvertToDefaultOrSetSite(GetUrlInfo());
@@ -4568,9 +4568,8 @@
     const CoopCoepCrossOriginIsolatedInfo& cross_origin_isolated_info) {
   // TODO(alexmos): Using |starting_site_instance_|'s IsolationContext may not
   // be correct for cross-BrowsingInstance redirects.
-  return SiteInstanceImpl::ComputeSiteInfo(
-      starting_site_instance_->GetIsolationContext(), GetUrlInfo(),
-      cross_origin_isolated_info);
+  return SiteInfo::Create(starting_site_instance_->GetIsolationContext(),
+                          GetUrlInfo(), cross_origin_isolated_info);
 }
 
 // TODO(zetamoo): Try to merge this function inside its callers.
diff --git a/content/browser/renderer_host/navigator_unittest.cc b/content/browser/renderer_host/navigator_unittest.cc
index 6a3f6d8..a35b6fc7 100644
--- a/content/browser/renderer_host/navigator_unittest.cc
+++ b/content/browser/renderer_host/navigator_unittest.cc
@@ -1252,7 +1252,7 @@
     if (AreDefaultSiteInstancesEnabled()) {
       ASSERT_TRUE(related_instance_impl->IsDefaultSiteInstance());
     } else {
-      EXPECT_EQ(SiteInstanceImpl::ComputeSiteInfoForTesting(
+      EXPECT_EQ(SiteInfo::CreateForTesting(
                     current_instance->GetIsolationContext(), kUrlSameSiteAs2),
                 related_instance_impl->GetSiteInfo());
     }
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index a1c2aaa..9d8d0ac 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -8435,7 +8435,7 @@
   SiteInfo site_info =
       url.is_empty()
           ? SiteInfo()
-          : SiteInstanceImpl::ComputeSiteInfo(
+          : SiteInfo::Create(
                 GetSiteInstance()->GetIsolationContext(),
                 UrlInfo(url, false /* origin_requests_isolation */),
                 GetSiteInstance()->GetCoopCoepCrossOriginIsolatedInfo());
diff --git a/content/browser/renderer_host/render_frame_host_manager.cc b/content/browser/renderer_host/render_frame_host_manager.cc
index 944b50a..001c9e3 100644
--- a/content/browser/renderer_host/render_frame_host_manager.cc
+++ b/content/browser/renderer_host/render_frame_host_manager.cc
@@ -108,9 +108,9 @@
   // process if we left it in the current BrowsingInstance.  If so, there's no
   // need to swap BrowsingInstances.
   auto& current_isolation_context = current_instance->GetIsolationContext();
-  auto current_site_info = SiteInstanceImpl::ComputeSiteInfo(
-      current_isolation_context, destination_effective_url_info,
-      cross_origin_isolated_info);
+  auto current_site_info = SiteInfo::Create(current_isolation_context,
+                                            destination_effective_url_info,
+                                            cross_origin_isolated_info);
   if (current_site_info.RequiresDedicatedProcess(current_isolation_context))
     return false;
 
@@ -120,9 +120,9 @@
   // current_instance->GetIsolationContext().
   IsolationContext future_isolation_context(
       current_instance->GetBrowserContext());
-  auto future_site_info = SiteInstanceImpl::ComputeSiteInfo(
-      future_isolation_context, destination_effective_url_info,
-      cross_origin_isolated_info);
+  auto future_site_info =
+      SiteInfo::Create(future_isolation_context, destination_effective_url_info,
+                       cross_origin_isolated_info);
   return future_site_info.RequiresDedicatedProcess(future_isolation_context);
 }
 
@@ -2135,8 +2135,8 @@
       auto& parent_isolation_context =
           parent->GetSiteInstance()->GetIsolationContext();
 
-      auto site_info = SiteInstanceImpl::ComputeSiteInfo(
-          parent_isolation_context, dest_url_info, cross_origin_isolated_info);
+      auto site_info = SiteInfo::Create(parent_isolation_context, dest_url_info,
+                                        cross_origin_isolated_info);
       if (!parent->GetSiteInstance()->RequiresDedicatedProcess() &&
           !site_info.RequiresDedicatedProcess(parent_isolation_context)) {
         AppendReason(reason,
diff --git a/content/browser/renderer_host/render_frame_host_manager_browsertest.cc b/content/browser/renderer_host/render_frame_host_manager_browsertest.cc
index ee37618..5bf544c1 100644
--- a/content/browser/renderer_host/render_frame_host_manager_browsertest.cc
+++ b/content/browser/renderer_host/render_frame_host_manager_browsertest.cc
@@ -8580,8 +8580,8 @@
   TestNavigationManager foo_manager(web_contents, foo_url);
   auto& current_isolation_context =
       root->current_frame_host()->GetSiteInstance()->GetIsolationContext();
-  auto site_info = SiteInstanceImpl::ComputeSiteInfoForTesting(
-      current_isolation_context, GURL("http://foo.com"));
+  auto site_info = SiteInfo::CreateForTesting(current_isolation_context,
+                                              GURL("http://foo.com"));
   EXPECT_TRUE(site_info.RequiresDedicatedProcess(current_isolation_context));
 
   // Set up the work to be done after the renderer is asked to commit
diff --git a/content/browser/renderer_host/render_frame_host_manager_unittest.cc b/content/browser/renderer_host/render_frame_host_manager_unittest.cc
index 02eace6..53fb8b93 100644
--- a/content/browser/renderer_host/render_frame_host_manager_unittest.cc
+++ b/content/browser/renderer_host/render_frame_host_manager_unittest.cc
@@ -3343,7 +3343,7 @@
   NavigationSimulator::NavigateAndCommitFromBrowser(contents(), kFooUrl);
   scoped_refptr<SiteInstanceImpl> initial_instance =
       main_test_rfh()->GetSiteInstance();
-  SiteInfo foo_site_info = SiteInstanceImpl::ComputeSiteInfoForTesting(
+  SiteInfo foo_site_info = SiteInfo::CreateForTesting(
       initial_instance->GetIsolationContext(), kFooUrl);
   if (AreDefaultSiteInstancesEnabled()) {
     EXPECT_TRUE(initial_instance->IsDefaultSiteInstance());
@@ -3363,7 +3363,7 @@
       main_test_rfh()->GetSiteInstance()));
   EXPECT_EQ(kOriginalUrl, main_test_rfh()->GetSiteInstance()->original_url());
 
-  SiteInfo expected_site_info = SiteInstanceImpl::ComputeSiteInfoForTesting(
+  SiteInfo expected_site_info = SiteInfo::CreateForTesting(
       main_test_rfh()->GetSiteInstance()->GetIsolationContext(), kOriginalUrl);
   EXPECT_EQ(expected_site_info,
             main_test_rfh()->GetSiteInstance()->GetSiteInfo());
diff --git a/content/browser/service_worker/service_worker_process_manager_unittest.cc b/content/browser/service_worker/service_worker_process_manager_unittest.cc
index eee9858..535fc99 100644
--- a/content/browser/service_worker/service_worker_process_manager_unittest.cc
+++ b/content/browser/service_worker/service_worker_process_manager_unittest.cc
@@ -103,7 +103,7 @@
        AllocateWorkerProcess_WithProcessReuse) {
   const int kEmbeddedWorkerId = 100;
   const GURL kSiteUrl = GURL("http://example.com");
-  SiteInfo site_info = SiteInstanceImpl::ComputeSiteInfoForTesting(
+  SiteInfo site_info = SiteInfo::CreateForTesting(
       IsolationContext(browser_context_.get()), kSiteUrl);
 
   // Create a process that is hosting a frame with kSiteUrl.
@@ -148,7 +148,7 @@
        AllocateWorkerProcess_WithoutProcessReuse) {
   const int kEmbeddedWorkerId = 100;
   const GURL kSiteUrl = GURL("http://example.com");
-  SiteInfo site_info = SiteInstanceImpl::ComputeSiteInfoForTesting(
+  SiteInfo site_info = SiteInfo::CreateForTesting(
       IsolationContext(browser_context_.get()), kSiteUrl);
 
   // Create a process that is hosting a frame with kSiteUrl.
diff --git a/content/browser/site_instance_impl.cc b/content/browser/site_instance_impl.cc
index 5dc7e7e2..cef08cb 100644
--- a/content/browser/site_instance_impl.cc
+++ b/content/browser/site_instance_impl.cc
@@ -131,6 +131,59 @@
                   true /* is_guest */);
 }
 
+// static
+SiteInfo SiteInfo::Create(
+    const IsolationContext& isolation_context,
+    const UrlInfo& url_info,
+    const CoopCoepCrossOriginIsolatedInfo& cross_origin_isolated_info) {
+  // The call to GetSiteForURL() below is only allowed on the UI thread, due to
+  // its possible use of effective urls.
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  return CreateInternal(isolation_context, url_info, cross_origin_isolated_info,
+                        /*compute_site_url=*/true);
+}
+
+// static
+SiteInfo SiteInfo::CreateOnIOThread(
+    const IsolationContext& isolation_context,
+    const UrlInfo& url_info,
+    const CoopCoepCrossOriginIsolatedInfo& cross_origin_isolated_info) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  return CreateInternal(isolation_context, url_info, cross_origin_isolated_info,
+                        /*compute_site_url=*/false);
+}
+
+// static
+SiteInfo SiteInfo::CreateInternal(
+    const IsolationContext& isolation_context,
+    const UrlInfo& url_info,
+    const CoopCoepCrossOriginIsolatedInfo& cross_origin_isolated_info,
+    bool compute_site_url) {
+  if (url_info.url.SchemeIs(kChromeErrorScheme))
+    return CreateForErrorPage(cross_origin_isolated_info);
+
+  GURL lock_url = DetermineProcessLockURL(isolation_context, url_info);
+  GURL site_url = lock_url;
+  if (compute_site_url) {
+    site_url = GetSiteForURLInternal(isolation_context, url_info,
+                                     true /* should_use_effective_urls */);
+  }
+  bool is_origin_keyed =
+      ChildProcessSecurityPolicyImpl::GetInstance()
+          ->ShouldOriginGetOptInIsolation(isolation_context,
+                                          url::Origin::Create(url_info.url),
+                                          url_info.origin_requests_isolation);
+  return SiteInfo(site_url, lock_url, is_origin_keyed,
+                  cross_origin_isolated_info);
+}
+
+// static
+SiteInfo SiteInfo::CreateForTesting(const IsolationContext& isolation_context,
+                                    const GURL& url) {
+  return Create(isolation_context, UrlInfo::CreateForTesting(url),
+                CoopCoepCrossOriginIsolatedInfo::CreateNonIsolated());
+}
+
 SiteInfo::SiteInfo() = default;
 SiteInfo::SiteInfo(const SiteInfo& rhs) = default;
 
@@ -309,6 +362,146 @@
   return !is_guest_ && site_url_ == GetErrorPageSiteAndLockURL();
 }
 
+// static
+GURL SiteInfo::DetermineProcessLockURL(
+    const IsolationContext& isolation_context,
+    const UrlInfo& url_info) {
+  // For WebUI URLs of the form chrome://foo.bar/ compute the LockURL based on
+  // the TLD (ie chrome://bar/). This allows WebUI to continue to differentiate
+  // WebUIType via SiteURL while allowing WebUI with a shared TLD to share a
+  // RenderProcessHost.
+  // TODO(tluk): Remove this and replace it with SiteInstance groups once the
+  // support lands.
+  if (IsWebUIAndUsesTLDForProcessLockURL(url_info.url))
+    return GetProcessLockForWebUIURL(url_info.url);
+
+  // For the process lock URL, convert |url| to a site without resolving |url|
+  // to an effective URL.
+  return GetSiteForURLInternal(isolation_context, url_info,
+                               false /* should_use_effective_urls */);
+}
+
+// static
+GURL SiteInfo::GetSiteForURLInternal(const IsolationContext& isolation_context,
+                                     const UrlInfo& real_url_info,
+                                     bool should_use_effective_urls) {
+  const GURL& real_url = real_url_info.url;
+  // Explicitly map all chrome-error: URLs to a single URL so that they all
+  // end up in a dedicated error process.
+  if (real_url.SchemeIs(kChromeErrorScheme))
+    return GetErrorPageSiteAndLockURL();
+
+  if (should_use_effective_urls)
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  GURL url = should_use_effective_urls
+                 ? SiteInstanceImpl::GetEffectiveURL(
+                       isolation_context.browser_or_resource_context()
+                           .ToBrowserContext(),
+                       real_url)
+                 : real_url;
+
+  // Navigations to urn: URLs served from Web Bundles [1] require special care
+  // to use the origin of the bundle rather than the urn: URL, which lacks any
+  // origin information.
+  // [1] bit.ly/subresource-web-bundles-doc
+  // TODO(acolwell): Update this so we can use url::Origin::Resolve() for all
+  // cases.
+  url::Origin origin;
+  if (url.SchemeIs("urn") && real_url_info.origin.opaque()) {
+    auto precursor = real_url_info.origin.GetTupleOrPrecursorTupleIfOpaque();
+    if (precursor.IsValid()) {
+      // Use the precursor as the origin. This should be the origin of the
+      // bundle.
+      origin = url::Origin::CreateFromNormalizedTuple(
+          precursor.scheme(), precursor.host(), precursor.port());
+    } else {
+      origin = url::Origin::Resolve(url, real_url_info.origin);
+    }
+  } else {
+    origin = url::Origin::Create(url);
+  }
+
+  // If the url has a host, then determine the site.  Skip file URLs to avoid a
+  // situation where site URL of file://localhost/ would mismatch Blink's origin
+  // (which ignores the hostname in this case - see https://crbug.com/776160).
+  GURL site_url;
+  if (!origin.host().empty() && origin.scheme() != url::kFileScheme) {
+    // For Strict Origin Isolation, use the full origin instead of site for all
+    // HTTP/HTTPS URLs.  Note that the HTTP/HTTPS restriction guarantees that
+    // we won't hit this for hosted app effective URLs (see
+    // https://crbug.com/961386).
+    if (SiteIsolationPolicy::IsStrictOriginIsolationEnabled() &&
+        origin.GetURL().SchemeIsHTTPOrHTTPS())
+      return origin.GetURL();
+
+    site_url = GetSiteForOrigin(origin);
+
+    // Isolated origins should use the full origin as their site URL. A
+    // subdomain of an isolated origin should also use that isolated origin's
+    // site URL. It is important to check |origin| (based on |url|) rather than
+    // |real_url| here, since some effective URLs (such as for NTP) need to be
+    // resolved prior to the isolated origin lookup.
+    auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
+    url::Origin isolated_origin;
+    if (policy->GetMatchingIsolatedOrigin(
+            isolation_context, origin, real_url_info.origin_requests_isolation,
+            site_url, &isolated_origin)) {
+      return isolated_origin.GetURL();
+    }
+  } else {
+    // If there is no host but there is a scheme, return the scheme.
+    // This is useful for cases like file URLs.
+    if (!origin.opaque()) {
+      // Prefer to use the scheme of |origin| rather than |url|, to correctly
+      // cover blob:file: and filesystem:file: URIs (see also
+      // https://crbug.com/697111).
+      DCHECK(!origin.scheme().empty());
+      site_url = GURL(origin.scheme() + ":");
+    } else if (url.has_scheme()) {
+      // In some cases, it is not safe to use just the scheme as a site URL, as
+      // that might allow two URLs created by different sites to share a
+      // process. See https://crbug.com/863623 and https://crbug.com/863069.
+      //
+      // TODO(alexmos,creis): This should eventually be expanded to certain
+      // other schemes, such as file:.
+      if (url.SchemeIsBlob() || url.scheme() == url::kDataScheme) {
+        // We get here for blob URLs of form blob:null/guid.  Use the full URL
+        // with the guid in that case, which isolates all blob URLs with unique
+        // origins from each other.  We also get here for browser-initiated
+        // navigations to data URLs, which have a unique origin and should only
+        // share a process when they are identical.  Remove hash from the URL in
+        // either case, since same-document navigations shouldn't use a
+        // different site URL.
+        if (url.has_ref()) {
+          GURL::Replacements replacements;
+          replacements.ClearRef();
+          url = url.ReplaceComponents(replacements);
+        }
+        site_url = url;
+      } else {
+        DCHECK(!url.scheme().empty());
+        site_url = GURL(url.scheme() + ":");
+      }
+    } else {
+      // Otherwise the URL should be invalid; return an empty site.
+      DCHECK(!url.is_valid()) << url;
+      return GURL();
+    }
+  }
+
+  return site_url;
+}
+
+// static
+GURL SiteInfo::GetSiteForOrigin(const url::Origin& origin) {
+  // Only keep the scheme and registered domain of |origin|.
+  std::string domain = net::registry_controlled_domains::GetDomainAndRegistry(
+      origin, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
+  return SchemeAndHostToSite(origin.scheme(),
+                             domain.empty() ? origin.host() : domain);
+}
+
 class SiteInstanceImpl::DefaultSiteInstanceState {
  public:
   void AddSiteInfo(const SiteInfo& site_info) {
@@ -735,7 +928,7 @@
   DCHECK(!has_site_);
 
   if (!browsing_instance_->HasDefaultSiteInstance()) {
-    const SiteInfo site_info = ComputeSiteInfo(
+    const SiteInfo site_info = SiteInfo::Create(
         GetIsolationContext(), url_info, GetCoopCoepCrossOriginIsolatedInfo());
     if (CanBePlacedInDefaultSiteInstance(GetIsolationContext(), url_info.url,
                                          site_info)) {
@@ -776,8 +969,8 @@
         url_info, /* allow_default_instance */ true);
   }
 
-  return ComputeSiteInfo(GetIsolationContext(), url_info,
-                         GetCoopCoepCrossOriginIsolatedInfo());
+  return SiteInfo::Create(GetIsolationContext(), url_info,
+                          GetCoopCoepCrossOriginIsolatedInfo());
 }
 
 const ProcessLock SiteInstanceImpl::GetProcessLock() const {
@@ -980,8 +1173,8 @@
     // prevent SiteInstances with no site URL from being used for URLs
     // that should be routed to the default SiteInstance.
     DCHECK_EQ(site_info_.site_url(), GetDefaultSiteURL());
-    auto site_info = ComputeSiteInfo(GetIsolationContext(), url_info,
-                                     GetCoopCoepCrossOriginIsolatedInfo());
+    auto site_info = SiteInfo::Create(GetIsolationContext(), url_info,
+                                      GetCoopCoepCrossOriginIsolatedInfo());
     return CanBePlacedInDefaultSiteInstance(GetIsolationContext(), url,
                                             site_info) &&
            !browsing_instance_->HasSiteInstance(site_info);
@@ -1230,8 +1423,8 @@
   // TODO(acolwell, ahemery): Update callers to pass in COOP/COEP info into
   // this method. The code is currently safe because the caller checks to make
   // sure the COOP/COEP info matches on this object before calling this method.
-  auto site_info = ComputeSiteInfo(GetIsolationContext(), url_info,
-                                   GetCoopCoepCrossOriginIsolatedInfo());
+  auto site_info = SiteInfo::Create(GetIsolationContext(), url_info,
+                                    GetCoopCoepCrossOriginIsolatedInfo());
   if (kCreateForURLAllowsDefaultSiteInstance &&
       CanBePlacedInDefaultSiteInstance(GetIsolationContext(), url_info.url,
                                        site_info)) {
@@ -1265,205 +1458,10 @@
   // where needed.  Eventually, GetSiteForURL should always require an
   // IsolationContext to be passed in, and this implementation should just
   // become SiteInstanceImpl::GetSiteForURL.
-  return SiteInstanceImpl::GetSiteForURL(
-      IsolationContext(browser_context),
-      UrlInfo(url, false /* origin_requests_isolation */));
-}
-
-// static
-SiteInfo SiteInstanceImpl::ComputeSiteInfo(
-    const IsolationContext& isolation_context,
-    const UrlInfo& url_info,
-    const CoopCoepCrossOriginIsolatedInfo& cross_origin_isolated_info) {
-  // The call to GetSiteForURL() below is only allowed on the UI thread, due to
-  // its possible use of effective urls.
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  if (url_info.url.SchemeIs(kChromeErrorScheme))
-    return SiteInfo::CreateForErrorPage(cross_origin_isolated_info);
-
-  // This function will expand as more information is included in SiteInfo.
-  bool is_origin_keyed =
-      ChildProcessSecurityPolicyImpl::GetInstance()
-          ->ShouldOriginGetOptInIsolation(isolation_context,
-                                          url::Origin::Create(url_info.url),
-                                          url_info.origin_requests_isolation);
-
-  return SiteInfo(GetSiteForURL(isolation_context, url_info),
-                  DetermineProcessLockURL(isolation_context, url_info),
-                  is_origin_keyed, cross_origin_isolated_info);
-}
-
-// static
-SiteInfo SiteInstanceImpl::ComputeSiteInfoForTesting(
-    const IsolationContext& isolation_context,
-    const GURL& url) {
-  return ComputeSiteInfo(isolation_context,
-                         UrlInfo(url, false /* origin_requests_isolation */),
-                         CoopCoepCrossOriginIsolatedInfo::CreateNonIsolated());
-}
-
-// static
-ProcessLock SiteInstanceImpl::DetermineProcessLock(
-    const IsolationContext& isolation_context,
-    const UrlInfo& url_info,
-    const CoopCoepCrossOriginIsolatedInfo& cross_origin_isolated_info) {
-  if (BrowserThread::CurrentlyOn(BrowserThread::UI))
-    return ProcessLock(ComputeSiteInfo(isolation_context, url_info,
-                                       cross_origin_isolated_info));
-
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  GURL lock_url = DetermineProcessLockURL(isolation_context, url_info);
-  bool is_origin_keyed =
-      ChildProcessSecurityPolicyImpl::GetInstance()
-          ->ShouldOriginGetOptInIsolation(isolation_context,
-                                          url::Origin::Create(url_info.url),
-                                          url_info.origin_requests_isolation);
-  // In the SiteInfo constructor below we pass the lock url as the site URL
-  // also, assuming the IO-thread caller won't be looking at the site url.
-  return ProcessLock(SiteInfo(lock_url, lock_url, is_origin_keyed,
-                              cross_origin_isolated_info));
-}
-
-// static
-// TODO(wjmaclean): remove this if the sole call from the IO thread can be
-// removed.
-GURL SiteInstanceImpl::DetermineProcessLockURL(
-    const IsolationContext& isolation_context,
-    const UrlInfo& url_info) {
-  // For WebUI URLs of the form chrome://foo.bar/ compute the LockURL based on
-  // the TLD (ie chrome://bar/). This allows WebUI to continue to differentiate
-  // WebUIType via SiteURL while allowing WebUI with a shared TLD to share a
-  // RenderProcessHost.
-  // TODO(tluk): Remove this and replace it with SiteInstance groups once the
-  // support lands.
-  if (IsWebUIAndUsesTLDForProcessLockURL(url_info.url))
-    return GetProcessLockForWebUIURL(url_info.url);
-
-  // For the process lock URL, convert |url| to a site without resolving |url|
-  // to an effective URL.
-  return SiteInstanceImpl::GetSiteForURLInternal(
-      isolation_context, url_info, false /* should_use_effective_urls */);
-}
-
-// static
-GURL SiteInstanceImpl::GetSiteForURL(const IsolationContext& isolation_context,
-                                     const UrlInfo& real_url_info) {
-  return GetSiteForURLInternal(isolation_context, real_url_info,
-                               true /* should_use_effective_urls */);
-}
-
-// static
-GURL SiteInstanceImpl::GetSiteForURLInternal(
-    const IsolationContext& isolation_context,
-    const UrlInfo& real_url_info,
-    bool should_use_effective_urls) {
-  const GURL& real_url = real_url_info.url;
-  // Explicitly map all chrome-error: URLs to a single URL so that they all
-  // end up in a dedicated error process.
-  if (real_url.SchemeIs(kChromeErrorScheme))
-    return GetErrorPageSiteAndLockURL();
-
-  if (should_use_effective_urls)
-    DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  GURL url = should_use_effective_urls
-                 ? SiteInstanceImpl::GetEffectiveURL(
-                       isolation_context.browser_or_resource_context()
-                           .ToBrowserContext(),
-                       real_url)
-                 : real_url;
-
-  // Navigations to urn: URLs served from Web Bundles [1] require special care
-  // to use the origin of the bundle rather than the urn: URL, which lacks any
-  // origin information.
-  // [1] bit.ly/subresource-web-bundles-doc
-  // TODO(acolwell): Update this so we can use url::Origin::Resolve() for all
-  // cases.
-  url::Origin origin;
-  if (url.SchemeIs("urn") && real_url_info.origin.opaque()) {
-    auto precursor = real_url_info.origin.GetTupleOrPrecursorTupleIfOpaque();
-    if (precursor.IsValid()) {
-      // Use the precursor as the origin. This should be the origin of the
-      // bundle.
-      origin = url::Origin::CreateFromNormalizedTuple(
-          precursor.scheme(), precursor.host(), precursor.port());
-    } else {
-      origin = url::Origin::Resolve(url, real_url_info.origin);
-    }
-  } else {
-    origin = url::Origin::Create(url);
-  }
-
-  // If the url has a host, then determine the site.  Skip file URLs to avoid a
-  // situation where site URL of file://localhost/ would mismatch Blink's origin
-  // (which ignores the hostname in this case - see https://crbug.com/776160).
-  GURL site_url;
-  if (!origin.host().empty() && origin.scheme() != url::kFileScheme) {
-    // For Strict Origin Isolation, use the full origin instead of site for all
-    // HTTP/HTTPS URLs.  Note that the HTTP/HTTPS restriction guarantees that
-    // we won't hit this for hosted app effective URLs (see
-    // https://crbug.com/961386).
-    if (SiteIsolationPolicy::IsStrictOriginIsolationEnabled() &&
-        origin.GetURL().SchemeIsHTTPOrHTTPS())
-      return origin.GetURL();
-
-    site_url = GetSiteForOrigin(origin);
-
-    // Isolated origins should use the full origin as their site URL. A
-    // subdomain of an isolated origin should also use that isolated origin's
-    // site URL. It is important to check |origin| (based on |url|) rather than
-    // |real_url| here, since some effective URLs (such as for NTP) need to be
-    // resolved prior to the isolated origin lookup.
-    auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
-    url::Origin isolated_origin;
-    if (policy->GetMatchingIsolatedOrigin(
-            isolation_context, origin, real_url_info.origin_requests_isolation,
-            site_url, &isolated_origin)) {
-      return isolated_origin.GetURL();
-    }
-  } else {
-    // If there is no host but there is a scheme, return the scheme.
-    // This is useful for cases like file URLs.
-    if (!origin.opaque()) {
-      // Prefer to use the scheme of |origin| rather than |url|, to correctly
-      // cover blob:file: and filesystem:file: URIs (see also
-      // https://crbug.com/697111).
-      DCHECK(!origin.scheme().empty());
-      site_url = GURL(origin.scheme() + ":");
-    } else if (url.has_scheme()) {
-      // In some cases, it is not safe to use just the scheme as a site URL, as
-      // that might allow two URLs created by different sites to share a
-      // process. See https://crbug.com/863623 and https://crbug.com/863069.
-      //
-      // TODO(alexmos,creis): This should eventually be expanded to certain
-      // other schemes, such as file:.
-      if (url.SchemeIsBlob() || url.scheme() == url::kDataScheme) {
-        // We get here for blob URLs of form blob:null/guid.  Use the full URL
-        // with the guid in that case, which isolates all blob URLs with unique
-        // origins from each other.  We also get here for browser-initiated
-        // navigations to data URLs, which have a unique origin and should only
-        // share a process when they are identical.  Remove hash from the URL in
-        // either case, since same-document navigations shouldn't use a
-        // different site URL.
-        if (url.has_ref()) {
-          GURL::Replacements replacements;
-          replacements.ClearRef();
-          url = url.ReplaceComponents(replacements);
-        }
-        site_url = url;
-      } else {
-        DCHECK(!url.scheme().empty());
-        site_url = GURL(url.scheme() + ":");
-      }
-    } else {
-      // Otherwise the URL should be invalid; return an empty site.
-      DCHECK(!url.is_valid()) << url;
-      return GURL();
-    }
-  }
-
-  return site_url;
+  return SiteInfo::Create(IsolationContext(browser_context),
+                          UrlInfo(url, false /* origin_requests_isolation */),
+                          CoopCoepCrossOriginIsolatedInfo::CreateNonIsolated())
+      .site_url();
 }
 
 // static
@@ -1507,15 +1505,6 @@
 }
 
 // static
-GURL SiteInstanceImpl::GetSiteForOrigin(const url::Origin& origin) {
-  // Only keep the scheme and registered domain of |origin|.
-  std::string domain = net::registry_controlled_domains::GetDomainAndRegistry(
-      origin, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
-  return SchemeAndHostToSite(origin.scheme(),
-                             domain.empty() ? origin.host() : domain);
-}
-
-// static
 GURL SiteInstanceImpl::GetEffectiveURL(BrowserContext* browser_context,
                                        const GURL& url) {
   DCHECK(browser_context);
@@ -1669,7 +1658,7 @@
   // Convert |url| to a site, to avoid breaking document.domain.  Note that
   // this doesn't use effective URL resolution or other special cases from
   // GetSiteForURL() and simply converts |origin| to a scheme and eTLD+1.
-  GURL site(SiteInstanceImpl::GetSiteForOrigin(origin));
+  GURL site(SiteInfo::GetSiteForOrigin(origin));
 
   ChildProcessSecurityPolicyImpl* policy =
       ChildProcessSecurityPolicyImpl::GetInstance();
diff --git a/content/browser/site_instance_impl.h b/content/browser/site_instance_impl.h
index 6aae409..fcc42b5 100644
--- a/content/browser/site_instance_impl.h
+++ b/content/browser/site_instance_impl.h
@@ -25,6 +25,61 @@
 class RenderProcessHostFactory;
 class StoragePartitionImpl;
 
+// This struct is used to package a GURL together with extra state required to
+// make SiteInstance/process allocation decisions, e.g. whether the url's origin
+// is requesting isolation as determined by response headers in the
+// corresponding navigation request. The extra state is generally most relevant
+// when navigation to the URL is in progress, since once placed into a
+// SiteInstance, the extra state will be available via SiteInfo. Otherwise, most
+// callsites requiring a UrlInfo can create with a GURL, specifying false for
+// |origin_requests_isolation|. Some examples of where passing false for
+// |origin_requests_isolation| is safe are:
+// * at DidCommitNavigation time, since at that point the SiteInstance has
+//   already been picked and the navigation can be considered finished,
+// * before a response is received (the only way to request isolation is via
+//   response headers), and
+// * outside of a navigation.
+//
+// If UrlInfo::origin_requests_isolation is false, that does *not* imply that
+// the URL will not be origin-isolated, and vice versa.  The origin isolation
+// decision involves both response headers and consistency within a
+// BrowsingInstance, and once we decide on the isolation outcome for an origin,
+// it won't change for the lifetime of the BrowsingInstance.  To check whether
+// or not a frame is origin-isolated, see SiteInfo::is_origin_keyed() on its
+// SiteInstance.
+//
+// Note: it is not expected that this struct will be exposed in content/public.
+struct CONTENT_EXPORT UrlInfo {
+ public:
+  UrlInfo() = default;  // Needed for inclusion in SiteInstanceDescriptor.
+  UrlInfo(const GURL& url_in, bool origin_requests_isolation_in)
+      : url(url_in),
+        origin_requests_isolation(origin_requests_isolation_in),
+        origin(url::Origin::Create(url_in)) {}
+  UrlInfo(const GURL& url_in,
+          bool origin_requests_isolation_in,
+          const url::Origin& origin_in)
+      : url(url_in),
+        origin_requests_isolation(origin_requests_isolation_in),
+        origin(origin_in) {}
+  static inline UrlInfo CreateForTesting(const GURL& url_in) {
+    // Used to convert GURL to UrlInfo in tests where opt-in isolation is not
+    // being tested.
+    return UrlInfo(url_in, false);
+  }
+
+  GURL url;
+  // This flag is only relevant (1) during a navigation request, (2) up to the
+  // point where the origin is placed into a SiteInstance, thus determining the
+  // opt-in isolation status of the origin. Other than these cases, this should
+  // be set to false.
+  bool origin_requests_isolation;
+  // If |url| represents a resource inside another resource (e.g. a resource
+  // with a urn: URL in WebBundle), origin of the original resource. Otherwise,
+  // this is just the origin of |url|.
+  url::Origin origin;
+};
+
 // SiteInfo represents the principal of a SiteInstance. All documents and
 // workers within a SiteInstance are considered part of this principal and will
 // share a renderer process. Any two documents within the same browsing context
@@ -57,6 +112,44 @@
       const CoopCoepCrossOriginIsolatedInfo& cross_origin_isolated_info);
   static SiteInfo CreateForGuest(const GURL& guest_site_url);
 
+  // This function returns a SiteInfo with the appropriate site_url and
+  // process_lock_url computed. This function can only be called on the UI
+  // thread because it must be able to compute an effective URL.
+  static SiteInfo Create(
+      const IsolationContext& isolation_context,
+      const UrlInfo& url_info,
+      const CoopCoepCrossOriginIsolatedInfo& cross_origin_isolated_info);
+
+  // Similar to the function above, but this method can only be called on the
+  // IO thread. All fields except for the site_url should be the same as
+  // the other method. The site_url field will match the process_lock_url
+  // in the object returned by this function. This is because we cannot compute
+  // the effective URL from the IO thread.
+  //
+  // NOTE: Do not use this method unless there is a very clear and good reason
+  // to do so. It primarily exists to facilitate the creation of ProcessLocks
+  // from any thread. ProcessLocks do not rely on the site_url field so the
+  // difference between this method and Create() does not cause problems for
+  // that usecase.
+  static SiteInfo CreateOnIOThread(
+      const IsolationContext& isolation_context,
+      const UrlInfo& url_info,
+      const CoopCoepCrossOriginIsolatedInfo& cross_origin_isolated_info);
+
+  // Method to make creating SiteInfo objects for tests easier. It is a thin
+  // wrapper around Create() that uses UrlInfo::CreateForTesting(),
+  // and CoopCoepCrossOriginIsolatedInfo::CreateNonIsolated() to generate the
+  // information that is not provided.
+  static SiteInfo CreateForTesting(const IsolationContext& isolation_context,
+                                   const GURL& url);
+
+  // Returns the site of a given |origin|.  Unlike Create(), this does
+  // not utilize effective URLs, isolated origins, or other special logic.  It
+  // only translates an origin into a site (i.e., scheme and eTLD+1) and is
+  // used internally by GetSiteForURLInternal().  For making process model
+  // decisions, Create() should be used instead.
+  static GURL GetSiteForOrigin(const url::Origin& origin);
+
   // The SiteInfo constructor should take in all values needed for comparing two
   // SiteInfos, to help ensure all creation sites are updated accordingly when
   // new values are added. The private function MakeTie() should be updated
@@ -169,6 +262,34 @@
  private:
   static auto MakeTie(const SiteInfo& site_info);
 
+  // Helper method containing common logic used by the public
+  // Create() and CreateOnIOThread() methods. Most of the parameters simply
+  // match the values passed into the caller. `compute_site_url` controls
+  // whether the site_url field is computed from an effective URL or simply
+  // copied from the `process_lock_url_`. `compute_site_url` is set to false in
+  // contexts where it may not be possible to get the effective URL (e.g. on the
+  // IO thread).
+  static SiteInfo CreateInternal(
+      const IsolationContext& isolation_context,
+      const UrlInfo& url_info,
+      const CoopCoepCrossOriginIsolatedInfo& cross_origin_isolated_info,
+      bool compute_site_url);
+
+  // Returns the URL to which a process should be locked for the given UrlInfo.
+  // This is computed similarly to the site URL but without resolving effective
+  // URLs.
+  static GURL DetermineProcessLockURL(const IsolationContext& isolation_context,
+                                      const UrlInfo& url_info);
+
+  // Returns the site for the given UrlInfo, which includes only the scheme and
+  // registered domain.  Returns an empty GURL if the UrlInfo has no host.
+  // |should_use_effective_urls| specifies whether to resolve |url| to an
+  // effective URL (via ContentBrowserClient::GetEffectiveURL()) before
+  // determining the site.
+  static GURL GetSiteForURLInternal(const IsolationContext& isolation_context,
+                                    const UrlInfo& url,
+                                    bool should_use_effective_urls);
+
   GURL site_url_;
   // The URL to use when locking a process to this SiteInstance's site via
   // SetProcessLock(). This is the same as |site_url_| except for cases
@@ -194,61 +315,6 @@
 CONTENT_EXPORT std::ostream& operator<<(std::ostream& out,
                                         const SiteInfo& site_info);
 
-// This struct is used to package a GURL together with extra state required to
-// make SiteInstance/process allocation decisions, e.g. whether the url's origin
-// is requesting isolation as determined by response headers in the
-// corresponding navigation request. The extra state is generally most relevant
-// when navigation to the URL is in progress, since once placed into a
-// SiteInstance, the extra state will be available via SiteInfo. Otherwise, most
-// callsites requiring a UrlInfo can create with a GURL, specifying false for
-// |origin_requests_isolation|. Some examples of where passing false for
-// |origin_requests_isolation| is safe are:
-// * at DidCommitNavigation time, since at that point the SiteInstance has
-//   already been picked and the navigation can be considered finished,
-// * before a response is received (the only way to request isolation is via
-//   response headers), and
-// * outside of a navigation.
-//
-// If UrlInfo::origin_requests_isolation is false, that does *not* imply that
-// the url will not be origin-isolated, and vice versa.  The origin isolation
-// decision involves both response headers and consistency within a
-// BrowsingInstance, and once we decide on the isolation outcome for an origin,
-// it won't change for the lifetime of the BrowsingInstance.  To check whether
-// or not a frame is origin-isolated, see SiteInfo::is_origin_keyed() on its
-// SiteInstance.
-//
-// Note: it is not expected that this struct will be exposed in content/public.
-struct CONTENT_EXPORT UrlInfo {
- public:
-  UrlInfo() = default;  // Needed for inclusion in SiteInstanceDescriptor.
-  UrlInfo(const GURL& url_in, bool origin_requests_isolation_in)
-      : url(url_in),
-        origin_requests_isolation(origin_requests_isolation_in),
-        origin(url::Origin::Create(url_in)) {}
-  UrlInfo(const GURL& url_in,
-          bool origin_requests_isolation_in,
-          const url::Origin& origin_in)
-      : url(url_in),
-        origin_requests_isolation(origin_requests_isolation_in),
-        origin(origin_in) {}
-  static inline UrlInfo CreateForTesting(const GURL& url_in) {
-    // Used to convert GURL to UrlInfo in tests where opt-in isolation is not
-    // being tested.
-    return UrlInfo(url_in, false);
-  }
-
-  GURL url;
-  // This flag is only relevant (1) during a navigation request, (2) up to the
-  // point where the origin is placed into a SiteInstance, thus determining the
-  // opt-in isolation status of the origin. Other than these cases, this should
-  // be set to false.
-  bool origin_requests_isolation;
-  // If |url| represents a resource inside another resource (e.g. a resource
-  // with a urn: URL in WebBundle), origin of the original resource. Otherwise,
-  // this is just the origin of |url|.
-  url::Origin origin;
-};
-
 class CONTENT_EXPORT SiteInstanceImpl final : public SiteInstance,
                                               public RenderProcessHostObserver {
  public:
@@ -434,16 +500,16 @@
 
   // Derives a new SiteInfo based on this SiteInstance's current state, and
   // the information provided in |url_info|. This function is slightly different
-  // than ComputeSiteInfo() because it takes into account information specific
-  // to this SiteInstance, like whether it is a guest or not, and changes its
-  // behavior accordingly.
-  // |is_related| - Controls the SiteInfo returned for non-guest SiteInstances.
+  // than SiteInfo::Create() because it takes into account information
+  // specific to this SiteInstance, like whether it is a guest or not, and
+  // changes its behavior accordingly. |is_related| - Controls the SiteInfo
+  // returned for non-guest SiteInstances.
   //  Set to true if the caller wants the SiteInfo for an existing related
   //  SiteInstance associated with |url_info|. This is identical to what you
   //  would get from GetRelatedSiteInstanceImpl(url_info)->GetSiteInfo(). This
   //  may return the SiteInfo for the default SiteInstance so callers must be
   //  prepared to deal with that. If set to false, a SiteInfo created with
-  //  ComputeSiteInfo() is returned.
+  //  SiteInfo::Create() is returned.
   //
   // For guest SiteInstances, |site_info_| is returned because guests are not
   // allowed to derive new guest SiteInfos. All guest navigations must stay in
@@ -468,45 +534,6 @@
   // safe.
   std::string GetPartitionDomain(StoragePartitionImpl* storage_partition);
 
-  // This function returns a SiteInfo with the appropriate site_url and
-  // process_lock_url computed. This function can only be called on the UI
-  // thread since it expects an effective URL.
-  // Note: eventually this function will replace GetSiteForURL().
-  static SiteInfo ComputeSiteInfo(
-      const IsolationContext& isolation_context,
-      const UrlInfo& url_info,
-      const CoopCoepCrossOriginIsolatedInfo& cross_origin_isolated_info);
-
-  // Helper method for tests that don't trigger special COOP/COEP
-  // functionality, or test opt-in origin isolation.
-  static SiteInfo ComputeSiteInfoForTesting(
-      const IsolationContext& isolation_context,
-      const GURL& url);
-
-  // Returns the site for the given UrlInfo, which includes only the scheme and
-  // registered domain.  Returns an empty GURL if the URL has no host.
-  // |url| will be resolved to an effective URL (via
-  // ContentBrowserClient::GetEffectiveURL()) before determining the site.
-  // NOTE: This function will soon be removed, and replaced by
-  // ComputeSiteInfo(). New code should use that function instead.
-  static GURL GetSiteForURL(const IsolationContext& isolation_context,
-                            const UrlInfo& url_info);
-
-  // Returns the site of a given |origin|.  Unlike GetSiteForURL(), this does
-  // not utilize effective URLs, isolated origins, or other special logic.  It
-  // only translates an origin into a site (i.e., scheme and eTLD+1) and is
-  // used internally by GetSiteForURL().  For making process model decisions,
-  // GetSiteForURL() should be used instead.
-  static GURL GetSiteForOrigin(const url::Origin& origin);
-
-  // Similar to above, but also computes a full SiteInfo (including a
-  // process_lock_url) and returns a ProcessLock. If called from the IO thread,
-  // this will return a ProcessLock that doesn't consider effective URLs.
-  static ProcessLock DetermineProcessLock(
-      const IsolationContext& isolation_context,
-      const UrlInfo& url_info,
-      const CoopCoepCrossOriginIsolatedInfo& cross_origin_isolated_info);
-
   // Set the web site that this SiteInstance is rendering pages for.
   // This includes the scheme and registered domain, but not the port.  If the
   // URL does not have a valid registered domain, then the full hostname is
@@ -639,7 +666,7 @@
  private:
   friend class BrowsingInstance;
   friend class SiteInstanceTestBrowserClient;
-  FRIEND_TEST_ALL_PREFIXES(SiteInstanceTest, ProcessLockDoesNotUseEffectiveURL);
+
   // Friend tests that need direct access to IsSameSite().
   friend class SiteInstanceTest;
 
@@ -663,12 +690,6 @@
   // "lock_to_site" lock.
   void LockProcessIfNeeded();
 
-  // Returns the URL to which a process should be locked for the given UrlInfo.
-  // This is computed similarly to the site URL (see GetSiteForURL), but
-  // without resolving effective URLs.
-  static GURL DetermineProcessLockURL(const IsolationContext& isolation_context,
-                                      const UrlInfo& url_info);
-
   // If kProcessSharingWithStrictSiteInstances is enabled, this will check
   // whether both a site and a process have been assigned to this SiteInstance,
   // and if this doesn't require a dedicated process, will offer process_ to
@@ -721,15 +742,6 @@
                          const UrlInfo& dest_url_info,
                          bool should_compare_effective_urls);
 
-  // Returns the site for the given UrlInfo, which includes only the scheme and
-  // registered domain.  Returns an empty GURL if the UrlInfo has no host.
-  // |should_use_effective_urls| specifies whether to resolve |url| to an
-  // effective URL (via ContentBrowserClient::GetEffectiveURL()) before
-  // determining the site.
-  static GURL GetSiteForURLInternal(const IsolationContext& isolation_context,
-                                    const UrlInfo& url,
-                                    bool should_use_effective_urls);
-
   // True if |url| resolves to an effective URL that is different from |url|.
   // See GetEffectiveURL().  This will be true for hosted apps as well as NTP
   // URLs.
@@ -740,7 +752,7 @@
   //
   // Note: |url| and |site_info| must be consistent with each other. In contexts
   // where the caller only has |url| it can use
-  // SiteInstanceImpl::ComputeSiteInfo() to generate |site_info|. This call is
+  // SiteInfo::Create() to generate |site_info|. This call is
   // intentionally not set as a default value to encourage the caller to reuse
   // a SiteInfo computation if they already have one.
   static bool CanBePlacedInDefaultSiteInstance(
diff --git a/content/browser/site_instance_impl_unittest.cc b/content/browser/site_instance_impl_unittest.cc
index 0c55db8..8656f61 100644
--- a/content/browser/site_instance_impl_unittest.cc
+++ b/content/browser/site_instance_impl_unittest.cc
@@ -56,7 +56,7 @@
 
 bool DoesURLRequireDedicatedProcess(const IsolationContext& isolation_context,
                                     const GURL& url) {
-  return SiteInstanceImpl::ComputeSiteInfoForTesting(isolation_context, url)
+  return SiteInfo::CreateForTesting(isolation_context, url)
       .RequiresDedicatedProcess(isolation_context);
 }
 
@@ -134,8 +134,11 @@
 
   GURL GetSiteForURL(const IsolationContext& isolation_context,
                      const GURL& url) {
-    return SiteInstanceImpl::GetSiteForURL(
-        isolation_context, UrlInfo(url, false /* origin_requests_isolation */));
+    return SiteInfo::Create(
+               isolation_context,
+               UrlInfo(url, false /* origin_requests_isolation */),
+               CoopCoepCrossOriginIsolatedInfo::CreateNonIsolated())
+        .site_url();
   }
 
   void SetUp() override {
@@ -189,13 +192,11 @@
   BrowserContext* context() { return &context_; }
 
   SiteInfo GetSiteInfoForURL(const std::string& url) {
-    return SiteInstanceImpl::ComputeSiteInfoForTesting(
-        IsolationContext(&context_), GURL(url));
+    return SiteInfo::CreateForTesting(IsolationContext(&context_), GURL(url));
   }
 
   SiteInfo GetSiteInfoForURL(const GURL& url) {
-    return SiteInstanceImpl::ComputeSiteInfoForTesting(
-        IsolationContext(&context_), url);
+    return SiteInfo::CreateForTesting(IsolationContext(&context_), url);
   }
 
   static bool IsSameSite(BrowserContext* context,
@@ -677,20 +678,14 @@
   std::unique_ptr<TestBrowserContext> browser_context(new TestBrowserContext());
   IsolationContext isolation_context(browser_context.get());
 
-  // Sanity check that GetSiteForURL's |use_effective_urls| option works
-  // properly.  When it's true, the site URL should correspond to the
-  // effective URL's site (app.com), rather than the original URL's site
+  // Sanity check that SiteInfo fields influenced by effective URLs are set
+  // properly.  The site URL should correspond to the effective URL's site
+  // (app.com), and the process lock URL should refer to the original URL's site
   // (foo.com).
   {
-    GURL site_url = SiteInstanceImpl::GetSiteForURLInternal(
-        isolation_context, UrlInfo::CreateForTesting(test_url),
-        false /* use_effective_urls */);
-    EXPECT_EQ(nonapp_site_url, site_url);
-
-    site_url = SiteInstanceImpl::GetSiteForURLInternal(
-        isolation_context, UrlInfo::CreateForTesting(test_url),
-        true /* use_effective_urls */);
-    EXPECT_EQ(app_url, site_url);
+    auto site_info = SiteInfo::CreateForTesting(isolation_context, test_url);
+    EXPECT_EQ(nonapp_site_url, site_info.process_lock_url());
+    EXPECT_EQ(app_url, site_info.site_url());
   }
 
   SiteInfo expected_site_info(
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 149bc1c7..f593dbe 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -15749,15 +15749,12 @@
   int process_id = root->current_frame_host()->GetProcess()->GetID();
   IsolationContext isolation_context(
       shell()->web_contents()->GetBrowserContext());
-  auto start_url_lock = SiteInstanceImpl::DetermineProcessLock(
-      isolation_context, UrlInfo::CreateForTesting(start_url),
-      CoopCoepCrossOriginIsolatedInfo::CreateNonIsolated());
-  auto another_url_lock = SiteInstanceImpl::DetermineProcessLock(
-      isolation_context, UrlInfo::CreateForTesting(another_url),
-      CoopCoepCrossOriginIsolatedInfo::CreateNonIsolated());
-  auto bad_url_lock = SiteInstanceImpl::DetermineProcessLock(
-      isolation_context, UrlInfo::CreateForTesting(bad_url),
-      CoopCoepCrossOriginIsolatedInfo::CreateNonIsolated());
+  ProcessLock start_url_lock(
+      SiteInfo::CreateForTesting(isolation_context, start_url));
+  ProcessLock another_url_lock(
+      SiteInfo::CreateForTesting(isolation_context, another_url));
+  ProcessLock bad_url_lock(
+      SiteInfo::CreateForTesting(isolation_context, bad_url));
   EXPECT_EQ(start_url_lock, policy->GetProcessLock(process_id));
   EXPECT_EQ(another_url_lock, policy->GetProcessLock(process_id));
   EXPECT_NE(bad_url_lock, policy->GetProcessLock(process_id));
diff --git a/content/browser/speech/tts_controller_impl.cc b/content/browser/speech/tts_controller_impl.cc
index 2c2fb911..0543da181 100644
--- a/content/browser/speech/tts_controller_impl.cc
+++ b/content/browser/speech/tts_controller_impl.cc
@@ -139,6 +139,8 @@
   if (TtsPlatformLoading() ||
       (engine_delegate_ && !engine_delegate_->IsBuiltInTtsEngineInitialized(
                                utterance->GetBrowserContext()))) {
+    GetTtsPlatform()->LoadBuiltInTtsEngine(utterance->GetBrowserContext());
+
     if (utterance->GetShouldClearQueue())
       ClearUtteranceQueue(true);
 
diff --git a/content/browser/webui/shared_resources_data_source.cc b/content/browser/webui/shared_resources_data_source.cc
index cf5cbea0..9c6f1c2 100644
--- a/content/browser/webui/shared_resources_data_source.cc
+++ b/content/browser/webui/shared_resources_data_source.cc
@@ -161,7 +161,7 @@
 void AddResourcesToMap(ResourcesMap* resources_map) {
   for (size_t i = 0; i < kWebuiResourcesSize; ++i) {
     const auto& resource = kWebuiResources[i];
-    AddResource(resource.name, resource.value, resources_map);
+    AddResource(resource.path, resource.id, resources_map);
   }
 }
 
@@ -176,11 +176,11 @@
   for (size_t i = 0; i < resources_size; ++i) {
     const auto& resource = resources[i];
 
-    const auto it = resource_aliases.find(resource.value);
+    const auto it = resource_aliases.find(resource.id);
     if (it == resource_aliases.end())
       continue;
 
-    AddResource(it->second, resource.value, resources_map);
+    AddResource(it->second, resource.id, resources_map);
   }
 }
 
@@ -189,7 +189,7 @@
 void AddGritResourcesToMap(base::span<const GritResourceMap> resources,
                            ResourcesMap* resources_map) {
   for (const GritResourceMap& entry : resources)
-    AddResource(entry.name, entry.value, resources_map);
+    AddResource(entry.path, entry.id, resources_map);
 }
 
 const ResourcesMap* CreateResourcesMap() {
diff --git a/content/browser/webui/web_ui_mojo_browsertest.cc b/content/browser/webui/web_ui_mojo_browsertest.cc
index 71e3e21..07c56d3 100644
--- a/content/browser/webui/web_ui_mojo_browsertest.cc
+++ b/content/browser/webui/web_ui_mojo_browsertest.cc
@@ -93,7 +93,7 @@
           "script-src chrome://resources 'self' 'unsafe-eval';");
       data_source->DisableTrustedTypesCSP();
       for (const GritResourceMap& resource : kMojoWebUiResources)
-        data_source->AddResourcePath(resource.name, resource.value);
+        data_source->AddResourcePath(resource.path, resource.id);
       data_source->AddResourcePath("", IDR_WEB_UI_MOJO_HTML);
       WebUIDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(),
                            data_source);
diff --git a/content/browser/webui/web_ui_navigation_browsertest.cc b/content/browser/webui/web_ui_navigation_browsertest.cc
index d689545..e13a8f7c 100644
--- a/content/browser/webui/web_ui_navigation_browsertest.cc
+++ b/content/browser/webui/web_ui_navigation_browsertest.cc
@@ -49,7 +49,7 @@
 
 bool DoesURLRequireDedicatedProcess(const IsolationContext& isolation_context,
                                     const GURL& url) {
-  return SiteInstanceImpl::ComputeSiteInfoForTesting(isolation_context, url)
+  return SiteInfo::CreateForTesting(isolation_context, url)
       .RequiresDedicatedProcess(isolation_context);
 }
 
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index 75d2356..a9111c0 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -74,6 +74,7 @@
     "cursors/webcursor.cc",
     "cursors/webcursor.h",
     "cursors/webcursor_aura.cc",
+    "cursors/webcursor_aurawin.cc",
     "cursors/webcursor_ozone.cc",
     "fetch/fetch_api_request_proto.cc",
     "fetch/fetch_api_request_proto.h",
@@ -379,6 +380,10 @@
     ]
   }
 
+  if (!is_win || !use_aura) {
+    sources -= [ "cursors/webcursor_aurawin.cc" ]
+  }
+
   if (is_mac) {
     deps += [ "//media/gpu" ]
   }
diff --git a/content/common/cursors/webcursor.h b/content/common/cursors/webcursor.h
index e9733c2..0d51b09 100644
--- a/content/common/cursors/webcursor.h
+++ b/content/common/cursors/webcursor.h
@@ -48,6 +48,8 @@
   gfx::NativeCursor GetNativeCursor();
 
 #if defined(USE_AURA)
+  ui::PlatformCursor GetPlatformCursor(const ui::Cursor& cursor);
+
   // Updates |device_scale_factor_| and |rotation_| based on |display|.
   void SetDisplayInfo(const display::Display& display);
 
@@ -75,7 +77,7 @@
 
 #if defined(USE_AURA) || defined(USE_OZONE)
   // Only used for custom cursors.
-  ui::PlatformCursor platform_cursor_ = nullptr;
+  ui::PlatformCursor platform_cursor_ = 0;
   float device_scale_factor_ = 1.f;
   display::Display::Rotation rotation_ = display::Display::ROTATE_0;
 #endif
diff --git a/content/common/cursors/webcursor_aura.cc b/content/common/cursors/webcursor_aura.cc
index 24a0fe3..4d4e0201 100644
--- a/content/common/cursors/webcursor_aura.cc
+++ b/content/common/cursors/webcursor_aura.cc
@@ -6,7 +6,6 @@
 
 #include "base/check_op.h"
 #include "ui/base/cursor/cursor.h"
-#include "ui/base/cursor/cursor_factory.h"
 #include "ui/base/cursor/cursor_util.h"
 #include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
 
@@ -23,11 +22,7 @@
       custom_cursor_->set_custom_bitmap(bitmap);
       custom_cursor_->set_custom_hotspot(hotspot);
       custom_cursor_->set_image_scale_factor(scale);
-
-      DCHECK(!platform_cursor_);
-      platform_cursor_ = ui::CursorFactory::GetInstance()->CreateImageCursor(
-          ui::mojom::CursorType::kCustom, bitmap, hotspot);
-      custom_cursor_->SetPlatformCursor(platform_cursor_);
+      custom_cursor_->SetPlatformCursor(GetPlatformCursor(*custom_cursor_));
     }
     return *custom_cursor_;
   }
@@ -62,26 +57,4 @@
 }
 #endif
 
-void WebCursor::CopyPlatformData(const WebCursor& other) {
-  custom_cursor_ = other.custom_cursor_;
-  if (platform_cursor_)
-    ui::CursorFactory::GetInstance()->UnrefImageCursor(platform_cursor_);
-  platform_cursor_ = other.platform_cursor_;
-  if (platform_cursor_)
-    ui::CursorFactory::GetInstance()->RefImageCursor(platform_cursor_);
-
-  device_scale_factor_ = other.device_scale_factor_;
-#if defined(USE_OZONE)
-  maximum_cursor_size_ = other.maximum_cursor_size_;
-#endif
-}
-
-void WebCursor::CleanupPlatformData() {
-  if (platform_cursor_) {
-    ui::CursorFactory::GetInstance()->UnrefImageCursor(platform_cursor_);
-    platform_cursor_ = nullptr;
-  }
-  custom_cursor_.reset();
-}
-
 }  // namespace content
diff --git a/content/common/cursors/webcursor_aurawin.cc b/content/common/cursors/webcursor_aurawin.cc
new file mode 100644
index 0000000..567733c
--- /dev/null
+++ b/content/common/cursors/webcursor_aurawin.cc
@@ -0,0 +1,40 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/common/cursors/webcursor.h"
+
+#include <windows.h>
+
+#include "base/check_op.h"
+#include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
+#include "ui/gfx/icon_util.h"
+
+namespace content {
+
+ui::PlatformCursor WebCursor::GetPlatformCursor(const ui::Cursor& cursor) {
+  // The other cursor types are set in CursorLoaderWin
+  DCHECK_EQ(cursor.type(), ui::mojom::CursorType::kCustom);
+
+  if (platform_cursor_)
+    return platform_cursor_;
+
+  platform_cursor_ = IconUtil::CreateCursorFromSkBitmap(cursor.custom_bitmap(),
+                                                        cursor.custom_hotspot())
+                         .release();
+  return platform_cursor_;
+}
+
+void WebCursor::CleanupPlatformData() {
+  if (platform_cursor_) {
+    DestroyIcon(platform_cursor_);
+    platform_cursor_ = nullptr;
+  }
+  custom_cursor_.reset();
+}
+
+void WebCursor::CopyPlatformData(const WebCursor& other) {
+  device_scale_factor_ = other.device_scale_factor_;
+}
+
+}  // namespace content
diff --git a/content/common/cursors/webcursor_ozone.cc b/content/common/cursors/webcursor_ozone.cc
index 8e291f92..0af37858 100644
--- a/content/common/cursors/webcursor_ozone.cc
+++ b/content/common/cursors/webcursor_ozone.cc
@@ -8,9 +8,24 @@
 
 #include "base/check_op.h"
 #include "build/chromeos_buildflags.h"
+#include "ui/base/cursor/cursor.h"
+#include "ui/base/cursor/cursor_factory.h"
+#include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
 
 namespace content {
 
+ui::PlatformCursor WebCursor::GetPlatformCursor(const ui::Cursor& cursor) {
+  // The other cursor types are set in CursorLoaderOzone
+  DCHECK_EQ(cursor.type(), ui::mojom::CursorType::kCustom);
+
+  if (!platform_cursor_) {
+    platform_cursor_ = ui::CursorFactory::GetInstance()->CreateImageCursor(
+        cursor.type(), cursor.custom_bitmap(), cursor.custom_hotspot());
+  }
+
+  return platform_cursor_;
+}
+
 #if defined(USE_OZONE)
 void WebCursor::SetDisplayInfo(const display::Display& display) {
   if (rotation_ == display.panel_rotation() &&
@@ -31,7 +46,7 @@
     maximum_cursor_size_ = gfx::Size(kDefaultMaxSize, kDefaultMaxSize);
   CleanupPlatformData();
   // It is not necessary to recreate platform_cursor_ yet, since it will be
-  // recreated on demand when GetNativeCursor is called.
+  // recreated on demand when GetPlatformCursor is called.
 }
 
 float WebCursor::GetCursorScaleFactor(SkBitmap* bitmap) {
@@ -44,4 +59,25 @@
 }
 #endif
 
+void WebCursor::CleanupPlatformData() {
+  if (platform_cursor_) {
+    ui::CursorFactory::GetInstance()->UnrefImageCursor(platform_cursor_);
+    platform_cursor_ = NULL;
+  }
+  custom_cursor_.reset();
+}
+
+void WebCursor::CopyPlatformData(const WebCursor& other) {
+  if (platform_cursor_)
+    ui::CursorFactory::GetInstance()->UnrefImageCursor(platform_cursor_);
+  platform_cursor_ = other.platform_cursor_;
+  if (platform_cursor_)
+    ui::CursorFactory::GetInstance()->RefImageCursor(platform_cursor_);
+
+  device_scale_factor_ = other.device_scale_factor_;
+#if defined(USE_OZONE)
+  maximum_cursor_size_ = other.maximum_cursor_size_;
+#endif
+}
+
 }  // namespace content
diff --git a/content/common/cursors/webcursor_unittest.cc b/content/common/cursors/webcursor_unittest.cc
index b3e0eb25..339faefc 100644
--- a/content/common/cursors/webcursor_unittest.cc
+++ b/content/common/cursors/webcursor_unittest.cc
@@ -14,8 +14,6 @@
 
 #if defined(OS_WIN)
 #include <windows.h>
-
-#include "ui/base/cursor/win/win_cursor.h"
 #endif
 
 namespace content {
@@ -210,9 +208,7 @@
   display.set_device_scale_factor(scale);
   webcursor.SetDisplayInfo(display);
 
-  HCURSOR windows_cursor_handle =
-      static_cast<ui::WinCursor*>(webcursor.GetNativeCursor().platform())
-          ->hcursor();
+  HCURSOR windows_cursor_handle = webcursor.GetNativeCursor().platform();
   EXPECT_NE(nullptr, windows_cursor_handle);
   ICONINFO windows_icon_info;
   EXPECT_TRUE(GetIconInfo(windows_cursor_handle, &windows_icon_info));
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/AssistViewStructureTest.java b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/AssistViewStructureTest.java
index e263e40..228b740f 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/AssistViewStructureTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/AssistViewStructureTest.java
@@ -67,7 +67,7 @@
         Assert.assertEquals(testViewStructure.toString(),
                 "\n"
                         + "  android.webkit.WebView\n"
-                        + "    android.view.View text='Hello World'\n"
+                        + "    android.view.View\n"
                         + "      android.widget.TextView text='Hello World'\n");
     }
 
@@ -90,15 +90,15 @@
                         + "  android.webkit.WebView\n"
                         + "    android.widget.ListView\n"
                         + "      android.view.View\n"
-                        + "        android.view.View text='1. '\n"
+                        + "        android.view.View\n"
                         + "          android.widget.TextView text='1. '\n"
                         + "        android.widget.TextView text='Kirk'\n"
                         + "      android.view.View\n"
-                        + "        android.view.View text='2. '\n"
+                        + "        android.view.View\n"
                         + "          android.widget.TextView text='2. '\n"
                         + "        android.widget.TextView text='Picard'\n"
                         + "      android.view.View\n"
-                        + "        android.view.View text='3. '\n"
+                        + "        android.view.View\n"
                         + "          android.widget.TextView text='3. '\n"
                         + "        android.widget.TextView text='Janeway'\n");
     }
@@ -125,4 +125,39 @@
         Assert.assertTrue(url.contains("Hello"));
         Assert.assertTrue(url.contains("World"));
     }
+
+    /**
+     * Test that accessible descriptions (like title, aria-label) augments visible
+     * text that's in the document, but that visible text isn't redundantly repeated
+     * otherwise.
+     *
+     * For example, a simple link like <a href="#">Hello</a> should not have the text
+     * "Hello" on both the link and the inner content node, but if the link has an
+     * aria-label like <a href="#" aria-label="Friday">Tomorrow</a> then the
+     * link's text should be the aria-label and the inner text should still be present.
+     */
+    @Test
+    @MediumTest
+    @MinAndroidSdkLevel(Build.VERSION_CODES.M)
+    @TargetApi(Build.VERSION_CODES.M)
+    @DisableIf.Build(sdk_is_less_than = Build.VERSION_CODES.M)
+    public void testAccessibleLabelsAugmentInnerText() throws Throwable {
+        TestViewStructureInterface testViewStructure =
+                getViewStructureFromHtml("<a href='#'>Link</a>"
+                        + "<a href='#' aria-label='AriaLabel'>Link</a>"
+                        + "<button>Button</button>"
+                        + "<button aria-label='AriaLabel'>Button</button>");
+        Assert.assertEquals(testViewStructure.toString(),
+                "\n"
+                        + "  android.webkit.WebView\n"
+                        + "    android.view.View\n"
+                        + "      android.view.View\n"
+                        + "        android.widget.TextView text='Link'\n"
+                        + "      android.view.View text='AriaLabel'\n"
+                        + "        android.widget.TextView text='Link'\n"
+                        + "      android.widget.Button\n"
+                        + "        android.widget.TextView text='Button'\n"
+                        + "      android.widget.Button text='AriaLabel'\n"
+                        + "        android.widget.TextView text='Button'\n");
+    }
 }
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/webcontents/AccessibilitySnapshotTest.java b/content/public/android/javatests/src/org/chromium/content/browser/webcontents/AccessibilitySnapshotTest.java
index 8b92b697..d283e70 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/webcontents/AccessibilitySnapshotTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/webcontents/AccessibilitySnapshotTest.java
@@ -102,9 +102,11 @@
         AccessibilitySnapshotNode child = root.children.get(0);
         Assert.assertEquals(1, child.children.size());
         Assert.assertEquals("", child.text);
-        AccessibilitySnapshotNode grandChild = child.children.get(0);
-        Assert.assertEquals(1, grandChild.children.size());
-        Assert.assertEquals("Click", grandChild.text);
+        AccessibilitySnapshotNode button = child.children.get(0);
+        Assert.assertEquals(1, button.children.size());
+        Assert.assertEquals("android.widget.Button", button.className);
+        AccessibilitySnapshotNode buttonText = button.children.get(0);
+        Assert.assertEquals("Click", buttonText.text);
     }
 
     @Test
@@ -114,11 +116,12 @@
         AccessibilitySnapshotNode root = receiveAccessibilitySnapshot(data, null);
         Assert.assertEquals(1, root.children.size());
         Assert.assertEquals("", root.text);
-        AccessibilitySnapshotNode child = root.children.get(0);
-        Assert.assertEquals("color", child.text);
-        Assert.assertTrue(child.hasStyle);
-        Assert.assertEquals("ff123456", Integer.toHexString(child.color));
-        Assert.assertEquals("ffabcdef", Integer.toHexString(child.bgcolor));
+        AccessibilitySnapshotNode para = root.children.get(0);
+        Assert.assertTrue(para.hasStyle);
+        Assert.assertEquals("ff123456", Integer.toHexString(para.color));
+        Assert.assertEquals("ffabcdef", Integer.toHexString(para.bgcolor));
+        AccessibilitySnapshotNode paraText = para.children.get(0);
+        Assert.assertEquals("color", paraText.text);
     }
 
     @Test
@@ -130,13 +133,14 @@
         AccessibilitySnapshotNode root = receiveAccessibilitySnapshot(data, null);
         Assert.assertEquals(1, root.children.size());
         Assert.assertEquals("", root.text);
-        AccessibilitySnapshotNode child = root.children.get(0);
-        Assert.assertTrue(child.hasStyle);
-        Assert.assertEquals("foo", child.text);
+        AccessibilitySnapshotNode para = root.children.get(0);
+        Assert.assertTrue(para.hasStyle);
+        AccessibilitySnapshotNode paraText = para.children.get(0);
+        Assert.assertEquals("foo", paraText.text);
 
         // The font size should take the scale into account.
         double expected = UseZoomForDSFPolicy.isUseZoomForDSFEnabled() ? cssToPixel(32.0) : 32.0;
-        Assert.assertEquals(expected, child.textSize, 1.0);
+        Assert.assertEquals(expected, para.textSize, 1.0);
     }
 
     @Test
@@ -148,13 +152,15 @@
         AccessibilitySnapshotNode root = receiveAccessibilitySnapshot(data, null);
         Assert.assertEquals(1, root.children.size());
         Assert.assertEquals("", root.text);
-        AccessibilitySnapshotNode child = root.children.get(0);
-        Assert.assertEquals("foo", child.text);
-        Assert.assertTrue(child.hasStyle);
-        Assert.assertTrue(child.bold);
-        Assert.assertTrue(child.italic);
-        Assert.assertFalse(child.lineThrough);
-        Assert.assertFalse(child.underline);
+        AccessibilitySnapshotNode para = root.children.get(0);
+        Assert.assertTrue(para.hasStyle);
+        Assert.assertTrue(para.bold);
+        Assert.assertTrue(para.italic);
+        Assert.assertFalse(para.lineThrough);
+        Assert.assertFalse(para.underline);
+
+        AccessibilitySnapshotNode paraText = para.children.get(0);
+        Assert.assertEquals("foo", paraText.text);
     }
 
     @Test
@@ -165,7 +171,7 @@
         Assert.assertEquals(2, root.children.size());
         Assert.assertEquals("", root.text);
         AccessibilitySnapshotNode child1 = root.children.get(0);
-        Assert.assertEquals("foo", child1.text);
+        Assert.assertEquals("foo", child1.children.get(0).text);
         Assert.assertTrue(child1.hasStyle);
         Assert.assertFalse(child1.bold);
         AccessibilitySnapshotNode child2 = root.children.get(1);
diff --git a/content/public/browser/file_system_access_permission_context.h b/content/public/browser/file_system_access_permission_context.h
index b231f167..63d5e56 100644
--- a/content/public/browser/file_system_access_permission_context.h
+++ b/content/public/browser/file_system_access_permission_context.h
@@ -124,12 +124,17 @@
   // shown if there is no need to ask for it.
   virtual bool CanObtainWritePermission(const url::Origin& origin) = 0;
 
-  // Store the directory recently chosen using a file picker.
+  // Store the directory recently chosen by a file picker. This can later be
+  // retrieved via a call to |GetLastPickedDirectory| with the corresponding
+  // |origin| and |id|.
   virtual void SetLastPickedDirectory(const url::Origin& origin,
+                                      const std::string& id,
                                       const base::FilePath& path,
                                       const PathType type) = 0;
-  // Returns the directory recently chosen using a file picker.
-  virtual PathInfo GetLastPickedDirectory(const url::Origin& origin) = 0;
+  // Returns the directory recently chosen by a file picker for a given
+  // |origin| and |id|.
+  virtual PathInfo GetLastPickedDirectory(const url::Origin& origin,
+                                          const std::string& id) = 0;
 
   // Return the path associated with well-known directories such as "desktop"
   // and "music", or a default path if the |directory| cannot be matched to a
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 9e66d5d..09dd4776 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -56,6 +56,8 @@
     "../browser/browsing_data/browsing_data_test_utils.h",
     "../browser/conversions/conversion_test_utils.cc",
     "../browser/conversions/conversion_test_utils.h",
+    "../browser/file_system_access/fake_file_system_access_permission_context.cc",
+    "../browser/file_system_access/fake_file_system_access_permission_context.h",
     "../browser/file_system_access/file_system_chooser_test_helpers.cc",
     "../browser/file_system_access/file_system_chooser_test_helpers.h",
     "../browser/file_system_access/mock_file_system_access_permission_context.cc",
@@ -2406,7 +2408,6 @@
       "//third_party/blink/public/common",
       "//third_party/blink/public/common:font_unique_name_table_proto",
       "//third_party/iaccessible2",
-      "//ui/base/cursor",
     ]
     libs = [
       "dwrite.lib",
diff --git a/content/test/data/accessibility/html/col-expected-auralinux.txt b/content/test/data/accessibility/html/col-expected-auralinux.txt
index 92bc14e..21eae7a 100644
--- a/content/test/data/accessibility/html/col-expected-auralinux.txt
+++ b/content/test/data/accessibility/html/col-expected-auralinux.txt
@@ -1,12 +1,12 @@
 [document web]
 ++[table] cols=2 headers=('Browser', 'Rendering Engine'); rows=2 headers=(NONE); caption=false; spans=(all: 1x1)
 ++++[table row]
-++++++[column header] name='Browser' (row=0, col=0, row_span=1, col_span=1, n_row_headers=0, n_col_headers=0)
+++++++[column header] name='Browser' table-cell-index:0 (row=0, col=0, row_span=1, col_span=1, n_row_headers=0, n_col_headers=0)
 ++++++++[static] name='Browser'
-++++++[column header] name='Rendering Engine' (row=0, col=1, row_span=1, col_span=1, n_row_headers=0, n_col_headers=0)
+++++++[column header] name='Rendering Engine' table-cell-index:1 (row=0, col=1, row_span=1, col_span=1, n_row_headers=0, n_col_headers=0)
 ++++++++[static] name='Rendering Engine'
 ++++[table row]
-++++++[table cell] name='Chrome' (row=1, col=0, row_span=1, col_span=1, n_row_headers=0, n_col_headers=1)
+++++++[table cell] name='Chrome' table-cell-index:2 (row=1, col=0, row_span=1, col_span=1, n_row_headers=0, n_col_headers=1)
 ++++++++[static] name='Chrome'
-++++++[table cell] name='Blink' (row=1, col=1, row_span=1, col_span=1, n_row_headers=0, n_col_headers=1)
+++++++[table cell] name='Blink' table-cell-index:3 (row=1, col=1, row_span=1, col_span=1, n_row_headers=0, n_col_headers=1)
 ++++++++[static] name='Blink'
diff --git a/content/test/data/accessibility/html/col-expected-blink.txt b/content/test/data/accessibility/html/col-expected-blink.txt
index 34d0f75..569f67e 100644
--- a/content/test/data/accessibility/html/col-expected-blink.txt
+++ b/content/test/data/accessibility/html/col-expected-blink.txt
@@ -1,19 +1,19 @@
 rootWebArea
 ++genericContainer ignored
 ++++genericContainer ignored
-++++++table
+++++++table ariaColumnCount=2 tableColumnCount=2
 ++++++++rowGroup ignored
 ++++++++++row
-++++++++++++columnHeader name='Browser'
+++++++++++++columnHeader name='Browser' ariaCellColumnIndex=1 tableCellColumnIndex=0
 ++++++++++++++staticText name='Browser'
 ++++++++++++++++inlineTextBox name='Browser'
-++++++++++++columnHeader name='Rendering Engine'
+++++++++++++columnHeader name='Rendering Engine' ariaCellColumnIndex=2 tableCellColumnIndex=1
 ++++++++++++++staticText name='Rendering Engine'
 ++++++++++++++++inlineTextBox name='Rendering Engine'
 ++++++++++row
-++++++++++++cell name='Chrome'
+++++++++++++cell name='Chrome' ariaCellColumnIndex=1 tableCellColumnIndex=0
 ++++++++++++++staticText name='Chrome'
 ++++++++++++++++inlineTextBox name='Chrome'
-++++++++++++cell name='Blink'
+++++++++++++cell name='Blink' ariaCellColumnIndex=2 tableCellColumnIndex=1
 ++++++++++++++staticText name='Blink'
 ++++++++++++++++inlineTextBox name='Blink'
diff --git a/content/test/data/accessibility/html/col.html b/content/test/data/accessibility/html/col.html
index 20ef1f4..ef9e258 100644
--- a/content/test/data/accessibility/html/col.html
+++ b/content/test/data/accessibility/html/col.html
@@ -1,3 +1,14 @@
+<!--
+@AURALINUX-ALLOW:table-cell-index*
+@BLINK-ALLOW:*ColumnCount*
+@BLINK-ALLOW:*ColumnIndex*
+@MAC-ALLOW:AXColumnIndexRange=*
+@MAC-ALLOW:AXIndex=*
+@MAC-ALLOW:AXRowIndexRange=*
+@WIN-ALLOW:column_*
+@WIN-ALLOW:row_*
+@WIN-ALLOW:table*
+-->
 <!DOCTYPE html>
 <html>
 <body>
diff --git a/content/test/data/accessibility/html/colgroup-expected-auralinux.txt b/content/test/data/accessibility/html/colgroup-expected-auralinux.txt
index af21893..bf30b7b8 100644
--- a/content/test/data/accessibility/html/colgroup-expected-auralinux.txt
+++ b/content/test/data/accessibility/html/colgroup-expected-auralinux.txt
@@ -1,12 +1,12 @@
 [document web]
 ++[table] cols=2 headers=('Single', 'Pair'); rows=2 headers=(NONE); caption=false; spans=(all: 1x1)
 ++++[table row]
-++++++[column header] name='Single' (row=0, col=0, row_span=1, col_span=1, n_row_headers=0, n_col_headers=0)
+++++++[column header] name='Single' table-cell-index:0 (row=0, col=0, row_span=1, col_span=1, n_row_headers=0, n_col_headers=0)
 ++++++++[static] name='Single'
-++++++[column header] name='Pair' (row=0, col=1, row_span=1, col_span=1, n_row_headers=0, n_col_headers=0)
+++++++[column header] name='Pair' table-cell-index:1 (row=0, col=1, row_span=1, col_span=1, n_row_headers=0, n_col_headers=0)
 ++++++++[static] name='Pair'
 ++++[table row]
-++++++[table cell] name='A' (row=1, col=0, row_span=1, col_span=1, n_row_headers=0, n_col_headers=1)
+++++++[table cell] name='A' table-cell-index:2 (row=1, col=0, row_span=1, col_span=1, n_row_headers=0, n_col_headers=1)
 ++++++++[static] name='A'
-++++++[table cell] name='AA' (row=1, col=1, row_span=1, col_span=1, n_row_headers=0, n_col_headers=1)
+++++++[table cell] name='AA' table-cell-index:3 (row=1, col=1, row_span=1, col_span=1, n_row_headers=0, n_col_headers=1)
 ++++++++[static] name='AA'
diff --git a/content/test/data/accessibility/html/colgroup-expected-blink.txt b/content/test/data/accessibility/html/colgroup-expected-blink.txt
index 94de0c2..2f6715a84 100644
--- a/content/test/data/accessibility/html/colgroup-expected-blink.txt
+++ b/content/test/data/accessibility/html/colgroup-expected-blink.txt
@@ -1,19 +1,19 @@
 rootWebArea
 ++genericContainer ignored
 ++++genericContainer ignored
-++++++table
+++++++table ariaColumnCount=2 tableColumnCount=2
 ++++++++rowGroup ignored
 ++++++++++row
-++++++++++++columnHeader name='Single'
+++++++++++++columnHeader name='Single' ariaCellColumnIndex=1 tableCellColumnIndex=0
 ++++++++++++++staticText name='Single'
 ++++++++++++++++inlineTextBox name='Single'
-++++++++++++columnHeader name='Pair'
+++++++++++++columnHeader name='Pair' ariaCellColumnIndex=2 tableCellColumnIndex=1
 ++++++++++++++staticText name='Pair'
 ++++++++++++++++inlineTextBox name='Pair'
 ++++++++++row
-++++++++++++cell name='A'
+++++++++++++cell name='A' ariaCellColumnIndex=1 tableCellColumnIndex=0
 ++++++++++++++staticText name='A'
 ++++++++++++++++inlineTextBox name='A'
-++++++++++++cell name='AA'
+++++++++++++cell name='AA' ariaCellColumnIndex=2 tableCellColumnIndex=1
 ++++++++++++++staticText name='AA'
 ++++++++++++++++inlineTextBox name='AA'
diff --git a/content/test/data/accessibility/html/colgroup.html b/content/test/data/accessibility/html/colgroup.html
index cebe772..45d0654 100644
--- a/content/test/data/accessibility/html/colgroup.html
+++ b/content/test/data/accessibility/html/colgroup.html
@@ -1,3 +1,14 @@
+<!--
+@AURALINUX-ALLOW:table-cell-index*
+@BLINK-ALLOW:*ColumnCount*
+@BLINK-ALLOW:*ColumnIndex*
+@MAC-ALLOW:AXColumnIndexRange=*
+@MAC-ALLOW:AXIndex=*
+@MAC-ALLOW:AXRowIndexRange=*
+@WIN-ALLOW:column_*
+@WIN-ALLOW:row_*
+@WIN-ALLOW:table*
+-->
 <!DOCTYPE html>
 <html>
 <body>
diff --git a/content/test/data/accessibility/html/table-multiple-row-and-column-headers-expected-blink.txt b/content/test/data/accessibility/html/table-multiple-row-and-column-headers-expected-blink.txt
index f7153bb..ab7a173 100644
--- a/content/test/data/accessibility/html/table-multiple-row-and-column-headers-expected-blink.txt
+++ b/content/test/data/accessibility/html/table-multiple-row-and-column-headers-expected-blink.txt
@@ -1,96 +1,96 @@
 rootWebArea name='Table example - multiple row and column headers'
 ++genericContainer ignored
 ++++genericContainer ignored
-++++++table
+++++++table ariaColumnCount=6 tableColumnCount=6
 ++++++++rowGroup ignored
 ++++++++++row
-++++++++++++cell
-++++++++++++columnHeader name='Mars'
+++++++++++++cell ariaCellColumnIndex=1 tableCellColumnIndex=0
+++++++++++++columnHeader name='Mars' ariaCellColumnIndex=3 tableCellColumnIndex=2
 ++++++++++++++staticText name='Mars'
 ++++++++++++++++inlineTextBox name='Mars'
-++++++++++++columnHeader name='Venus'
+++++++++++++columnHeader name='Venus' ariaCellColumnIndex=5 tableCellColumnIndex=4
 ++++++++++++++staticText name='Venus'
 ++++++++++++++++inlineTextBox name='Venus'
 ++++++++++row
-++++++++++++columnHeader name='Produced'
+++++++++++++columnHeader name='Produced' ariaCellColumnIndex=1 tableCellColumnIndex=2
 ++++++++++++++staticText name='Produced'
 ++++++++++++++++inlineTextBox name='Produced'
-++++++++++++columnHeader name='Sold'
+++++++++++++columnHeader name='Sold' ariaCellColumnIndex=2 tableCellColumnIndex=3
 ++++++++++++++staticText name='Sold'
 ++++++++++++++++inlineTextBox name='Sold'
-++++++++++++columnHeader name='Produced'
+++++++++++++columnHeader name='Produced' ariaCellColumnIndex=3 tableCellColumnIndex=4
 ++++++++++++++staticText name='Produced'
 ++++++++++++++++inlineTextBox name='Produced'
-++++++++++++columnHeader name='Sold'
+++++++++++++columnHeader name='Sold' ariaCellColumnIndex=4 tableCellColumnIndex=5
 ++++++++++++++staticText name='Sold'
 ++++++++++++++++inlineTextBox name='Sold'
 ++++++++++row
-++++++++++++rowHeader name='For Toddlers'
+++++++++++++rowHeader name='For Toddlers' ariaCellColumnIndex=1 tableCellColumnIndex=0
 ++++++++++++++staticText name='For Toddlers'
 ++++++++++++++++inlineTextBox name='For Toddlers'
-++++++++++++rowHeader name='Teddy Bears'
+++++++++++++rowHeader name='Teddy Bears' ariaCellColumnIndex=2 tableCellColumnIndex=1
 ++++++++++++++staticText name='Teddy Bears'
 ++++++++++++++++inlineTextBox name='Teddy Bears'
-++++++++++++cell name='50,000'
+++++++++++++cell name='50,000' ariaCellColumnIndex=3 tableCellColumnIndex=2
 ++++++++++++++staticText name='50,000'
 ++++++++++++++++inlineTextBox name='50,000'
-++++++++++++cell name='30,000'
+++++++++++++cell name='30,000' ariaCellColumnIndex=4 tableCellColumnIndex=3
 ++++++++++++++staticText name='30,000'
 ++++++++++++++++inlineTextBox name='30,000'
-++++++++++++cell name='100,000'
+++++++++++++cell name='100,000' ariaCellColumnIndex=5 tableCellColumnIndex=4
 ++++++++++++++staticText name='100,000'
 ++++++++++++++++inlineTextBox name='100,000'
-++++++++++++cell name='80,000'
+++++++++++++cell name='80,000' ariaCellColumnIndex=6 tableCellColumnIndex=5
 ++++++++++++++staticText name='80,000'
 ++++++++++++++++inlineTextBox name='80,000'
 ++++++++++row
-++++++++++++rowHeader name='Action Figures'
+++++++++++++rowHeader name='Action Figures' ariaCellColumnIndex=1 tableCellColumnIndex=1
 ++++++++++++++staticText name='Action Figures'
 ++++++++++++++++inlineTextBox name='Action Figures'
-++++++++++++cell name='25,000'
+++++++++++++cell name='25,000' ariaCellColumnIndex=2 tableCellColumnIndex=2
 ++++++++++++++staticText name='25,000'
 ++++++++++++++++inlineTextBox name='25,000'
-++++++++++++cell name='15,000'
+++++++++++++cell name='15,000' ariaCellColumnIndex=3 tableCellColumnIndex=3
 ++++++++++++++staticText name='15,000'
 ++++++++++++++++inlineTextBox name='15,000'
-++++++++++++cell name='50,000'
+++++++++++++cell name='50,000' ariaCellColumnIndex=4 tableCellColumnIndex=4
 ++++++++++++++staticText name='50,000'
 ++++++++++++++++inlineTextBox name='50,000'
-++++++++++++cell name='40,000'
+++++++++++++cell name='40,000' ariaCellColumnIndex=5 tableCellColumnIndex=5
 ++++++++++++++staticText name='40,000'
 ++++++++++++++++inlineTextBox name='40,000'
 ++++++++++row
-++++++++++++rowHeader name='For Teens'
+++++++++++++rowHeader name='For Teens' ariaCellColumnIndex=1 tableCellColumnIndex=0
 ++++++++++++++staticText name='For Teens'
 ++++++++++++++++inlineTextBox name='For Teens'
-++++++++++++rowHeader name='Board Games'
+++++++++++++rowHeader name='Board Games' ariaCellColumnIndex=2 tableCellColumnIndex=1
 ++++++++++++++staticText name='Board Games'
 ++++++++++++++++inlineTextBox name='Board Games'
-++++++++++++cell name='5,000'
+++++++++++++cell name='5,000' ariaCellColumnIndex=3 tableCellColumnIndex=2
 ++++++++++++++staticText name='5,000'
 ++++++++++++++++inlineTextBox name='5,000'
-++++++++++++cell name='2,000'
+++++++++++++cell name='2,000' ariaCellColumnIndex=4 tableCellColumnIndex=3
 ++++++++++++++staticText name='2,000'
 ++++++++++++++++inlineTextBox name='2,000'
-++++++++++++cell name='6,000'
+++++++++++++cell name='6,000' ariaCellColumnIndex=5 tableCellColumnIndex=4
 ++++++++++++++staticText name='6,000'
 ++++++++++++++++inlineTextBox name='6,000'
-++++++++++++cell name='4,000'
+++++++++++++cell name='4,000' ariaCellColumnIndex=6 tableCellColumnIndex=5
 ++++++++++++++staticText name='4,000'
 ++++++++++++++++inlineTextBox name='4,000'
 ++++++++++row
-++++++++++++rowHeader name='Video Games'
+++++++++++++rowHeader name='Video Games' ariaCellColumnIndex=1 tableCellColumnIndex=1
 ++++++++++++++staticText name='Video Games'
 ++++++++++++++++inlineTextBox name='Video Games'
-++++++++++++cell name='10,000'
+++++++++++++cell name='10,000' ariaCellColumnIndex=2 tableCellColumnIndex=2
 ++++++++++++++staticText name='10,000'
 ++++++++++++++++inlineTextBox name='10,000'
-++++++++++++cell name='5,000'
+++++++++++++cell name='5,000' ariaCellColumnIndex=3 tableCellColumnIndex=3
 ++++++++++++++staticText name='5,000'
 ++++++++++++++++inlineTextBox name='5,000'
-++++++++++++cell name='12,000'
+++++++++++++cell name='12,000' ariaCellColumnIndex=4 tableCellColumnIndex=4
 ++++++++++++++staticText name='12,000'
 ++++++++++++++++inlineTextBox name='12,000'
-++++++++++++cell name='9,000'
+++++++++++++cell name='9,000' ariaCellColumnIndex=5 tableCellColumnIndex=5
 ++++++++++++++staticText name='9,000'
 ++++++++++++++++inlineTextBox name='9,000'
diff --git a/content/test/data/accessibility/html/table-multiple-row-and-column-headers.html b/content/test/data/accessibility/html/table-multiple-row-and-column-headers.html
index ced62e2..1cd02de 100644
--- a/content/test/data/accessibility/html/table-multiple-row-and-column-headers.html
+++ b/content/test/data/accessibility/html/table-multiple-row-and-column-headers.html
@@ -1,3 +1,14 @@
+<!--
+@AURALINUX-ALLOW:table-cell-index*
+@BLINK-ALLOW:*ColumnCount*
+@BLINK-ALLOW:*ColumnIndex*
+@MAC-ALLOW:AXColumnIndexRange=*
+@MAC-ALLOW:AXIndex=*
+@MAC-ALLOW:AXRowIndexRange=*
+@WIN-ALLOW:column_*
+@WIN-ALLOW:row_*
+@WIN-ALLOW:table*
+-->
 <!DOCTYPE html>
 <html>
 <head>
diff --git a/content/test/data/accessibility/html/table-spans-expected-auralinux.txt b/content/test/data/accessibility/html/table-spans-expected-auralinux.txt
index 9681ddf..bcc5ce22 100644
--- a/content/test/data/accessibility/html/table-spans-expected-auralinux.txt
+++ b/content/test/data/accessibility/html/table-spans-expected-auralinux.txt
@@ -1,21 +1,21 @@
 [document web] name='Table example with rowspan and colspan'
 ++[table] cols=2 headers=(NONE); rows=2 headers=(NONE); caption=false; spans=(cell at 0,0: 2x1, cell at 1,0: 2x1)
 ++++[table row]
-++++++[table cell] name='AD' (row=0, col=0, row_span=2, col_span=1, n_row_headers=0, n_col_headers=0)
+++++++[table cell] name='AD' table-cell-index:0 (row=0, col=0, row_span=2, col_span=1, n_row_headers=0, n_col_headers=0)
 ++++++++[static] name='AD'
-++++++[table cell] name='BC' (row=0, col=1, row_span=1, col_span=1, n_row_headers=0, n_col_headers=0)
+++++++[table cell] name='BC' table-cell-index:1 (row=0, col=1, row_span=1, col_span=1, n_row_headers=0, n_col_headers=0)
 ++++++++[static] name='BC'
 ++++[table row]
-++++++[table cell] name='EF' (row=1, col=1, row_span=1, col_span=1, n_row_headers=0, n_col_headers=0)
+++++++[table cell] name='EF' table-cell-index:2 (row=1, col=1, row_span=1, col_span=1, n_row_headers=0, n_col_headers=0)
 ++++++++[static] name='EF'
 ++[table] cols=3 headers=(NONE); rows=2 headers=(NONE); caption=false; spans=(cell at 0,0: 2x1, cell at 0,1: 1x2, cell at 0,2: 1x2, cell at 1,0: 2x1)
 ++++[table row]
-++++++[table cell] name='AD' (row=0, col=0, row_span=2, col_span=1, n_row_headers=0, n_col_headers=0)
+++++++[table cell] name='AD' table-cell-index:0 (row=0, col=0, row_span=2, col_span=1, n_row_headers=0, n_col_headers=0)
 ++++++++[static] name='AD'
-++++++[table cell] name='BC' (row=0, col=1, row_span=1, col_span=2, n_row_headers=0, n_col_headers=0)
+++++++[table cell] name='BC' table-cell-index:1 (row=0, col=1, row_span=1, col_span=2, n_row_headers=0, n_col_headers=0)
 ++++++++[static] name='BC'
 ++++[table row]
-++++++[table cell] name='EF' (row=1, col=1, row_span=1, col_span=1, n_row_headers=0, n_col_headers=0)
+++++++[table cell] name='EF' table-cell-index:2 (row=1, col=1, row_span=1, col_span=1, n_row_headers=0, n_col_headers=0)
 ++++++++[static] name='EF'
-++++++[table cell] name='GH' (row=1, col=2, row_span=1, col_span=1, n_row_headers=0, n_col_headers=0)
+++++++[table cell] name='GH' table-cell-index:3 (row=1, col=2, row_span=1, col_span=1, n_row_headers=0, n_col_headers=0)
 ++++++++[static] name='GH'
diff --git a/content/test/data/accessibility/html/table-spans-expected-blink.txt b/content/test/data/accessibility/html/table-spans-expected-blink.txt
index 6fc1542..556add8 100644
--- a/content/test/data/accessibility/html/table-spans-expected-blink.txt
+++ b/content/test/data/accessibility/html/table-spans-expected-blink.txt
@@ -1,32 +1,32 @@
 rootWebArea name='Table example with rowspan and colspan'
 ++genericContainer ignored
 ++++genericContainer ignored
-++++++table
+++++++table ariaColumnCount=2 tableColumnCount=2
 ++++++++rowGroup ignored
 ++++++++++row
-++++++++++++cell name='AD'
+++++++++++++cell name='AD' ariaCellColumnIndex=1 tableCellColumnIndex=0
 ++++++++++++++staticText name='AD'
 ++++++++++++++++inlineTextBox name='AD'
-++++++++++++cell name='BC'
+++++++++++++cell name='BC' ariaCellColumnIndex=2 tableCellColumnIndex=1
 ++++++++++++++staticText name='BC'
 ++++++++++++++++inlineTextBox name='BC'
 ++++++++++row
-++++++++++++cell name='EF'
+++++++++++++cell name='EF' ariaCellColumnIndex=1 tableCellColumnIndex=1
 ++++++++++++++staticText name='EF'
 ++++++++++++++++inlineTextBox name='EF'
-++++++table
+++++++table ariaColumnCount=3 tableColumnCount=3
 ++++++++rowGroup ignored
 ++++++++++row
-++++++++++++cell name='AD'
+++++++++++++cell name='AD' ariaCellColumnIndex=1 tableCellColumnIndex=0
 ++++++++++++++staticText name='AD'
 ++++++++++++++++inlineTextBox name='AD'
-++++++++++++cell name='BC'
+++++++++++++cell name='BC' ariaCellColumnIndex=2 tableCellColumnIndex=1
 ++++++++++++++staticText name='BC'
 ++++++++++++++++inlineTextBox name='BC'
 ++++++++++row
-++++++++++++cell name='EF'
+++++++++++++cell name='EF' ariaCellColumnIndex=1 tableCellColumnIndex=1
 ++++++++++++++staticText name='EF'
 ++++++++++++++++inlineTextBox name='EF'
-++++++++++++cell name='GH'
+++++++++++++cell name='GH' ariaCellColumnIndex=2 tableCellColumnIndex=2
 ++++++++++++++staticText name='GH'
 ++++++++++++++++inlineTextBox name='GH'
diff --git a/content/test/data/accessibility/html/table-spans.html b/content/test/data/accessibility/html/table-spans.html
index 91fc4c0..9aaaebe 100644
--- a/content/test/data/accessibility/html/table-spans.html
+++ b/content/test/data/accessibility/html/table-spans.html
@@ -1,7 +1,13 @@
 <!--
-@MAC-ALLOW:AXIndex=*
+@AURALINUX-ALLOW:table-cell-index*
+@BLINK-ALLOW:*ColumnCount*
+@BLINK-ALLOW:*ColumnIndex*
 @MAC-ALLOW:AXColumnIndexRange=*
+@MAC-ALLOW:AXIndex=*
 @MAC-ALLOW:AXRowIndexRange=*
+@WIN-ALLOW:column_*
+@WIN-ALLOW:row_*
+@WIN-ALLOW:table*
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/html/table-th-colheader-expected-blink.txt b/content/test/data/accessibility/html/table-th-colheader-expected-blink.txt
index 93d681bd..824e1319 100644
--- a/content/test/data/accessibility/html/table-th-colheader-expected-blink.txt
+++ b/content/test/data/accessibility/html/table-th-colheader-expected-blink.txt
@@ -1,19 +1,19 @@
 rootWebArea
 ++genericContainer ignored
 ++++genericContainer ignored
-++++++table
+++++++table ariaColumnCount=2 tableColumnCount=2
 ++++++++rowGroup ignored
 ++++++++++row
-++++++++++++columnHeader name='Firstname'
+++++++++++++columnHeader name='Firstname' ariaCellColumnIndex=1 tableCellColumnIndex=0
 ++++++++++++++staticText name='Firstname'
 ++++++++++++++++inlineTextBox name='Firstname'
-++++++++++++columnHeader name='Lastname'
+++++++++++++columnHeader name='Lastname' ariaCellColumnIndex=2 tableCellColumnIndex=1
 ++++++++++++++staticText name='Lastname'
 ++++++++++++++++inlineTextBox name='Lastname'
 ++++++++++row
-++++++++++++cell name='Jill'
+++++++++++++cell name='Jill' ariaCellColumnIndex=1 tableCellColumnIndex=0
 ++++++++++++++staticText name='Jill'
 ++++++++++++++++inlineTextBox name='Jill'
-++++++++++++cell name='Smith'
+++++++++++++cell name='Smith' ariaCellColumnIndex=2 tableCellColumnIndex=1
 ++++++++++++++staticText name='Smith'
 ++++++++++++++++inlineTextBox name='Smith'
diff --git a/content/test/data/accessibility/html/table-th-colheader-expected-mac.txt b/content/test/data/accessibility/html/table-th-colheader-expected-mac.txt
index 0bc0cc3..06ae3f8 100644
--- a/content/test/data/accessibility/html/table-th-colheader-expected-mac.txt
+++ b/content/test/data/accessibility/html/table-th-colheader-expected-mac.txt
@@ -1,9 +1,9 @@
 AXWebArea
 ++AXTable AXColumnHeaderUIElements=[:4, :6]
 ++++AXRow AXIndex=0
-++++++AXCell AXColumnIndexRange={len: 1, loc: 0} AXRowIndexRange={len: 1, loc: 0}
+++++++AXCell AXColumnIndexRange={len: 1, loc: 0} AXDescription='Firstname' AXRowIndexRange={len: 1, loc: 0}
 ++++++++AXStaticText AXValue='Firstname'
-++++++AXCell AXColumnIndexRange={len: 1, loc: 1} AXRowIndexRange={len: 1, loc: 0}
+++++++AXCell AXColumnIndexRange={len: 1, loc: 1} AXDescription='Lastname' AXRowIndexRange={len: 1, loc: 0}
 ++++++++AXStaticText AXValue='Lastname'
 ++++AXRow AXIndex=1
 ++++++AXCell AXColumnHeaderUIElements=[:4] AXColumnIndexRange={len: 1, loc: 0} AXRowIndexRange={len: 1, loc: 1}
diff --git a/content/test/data/accessibility/html/table-th-colheader-expected-win.txt b/content/test/data/accessibility/html/table-th-colheader-expected-win.txt
index 32d89f1..60f39dc 100644
--- a/content/test/data/accessibility/html/table-th-colheader-expected-win.txt
+++ b/content/test/data/accessibility/html/table-th-colheader-expected-win.txt
@@ -1,12 +1,12 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
-++ROLE_SYSTEM_TABLE
+++ROLE_SYSTEM_TABLE table_rows=2 table_columns=2
 ++++ROLE_SYSTEM_ROW
-++++++ROLE_SYSTEM_COLUMNHEADER name='Firstname' column_headers='<Firstname>'
+++++++ROLE_SYSTEM_COLUMNHEADER name='Firstname' table-cell-index:0 column_headers='<Firstname>'
 ++++++++ROLE_SYSTEM_STATICTEXT name='Firstname'
-++++++ROLE_SYSTEM_COLUMNHEADER name='Lastname' column_headers='<Lastname>'
+++++++ROLE_SYSTEM_COLUMNHEADER name='Lastname' table-cell-index:1 column_headers='<Lastname>'
 ++++++++ROLE_SYSTEM_STATICTEXT name='Lastname'
 ++++ROLE_SYSTEM_ROW
-++++++ROLE_SYSTEM_CELL name='Jill' column_headers='<Firstname>'
+++++++ROLE_SYSTEM_CELL name='Jill' table-cell-index:2 column_headers='<Firstname>'
 ++++++++ROLE_SYSTEM_STATICTEXT name='Jill'
-++++++ROLE_SYSTEM_CELL name='Smith' column_headers='<Lastname>'
+++++++ROLE_SYSTEM_CELL name='Smith' table-cell-index:3 column_headers='<Lastname>'
 ++++++++ROLE_SYSTEM_STATICTEXT name='Smith'
diff --git a/content/test/data/accessibility/html/table-th-colheader.html b/content/test/data/accessibility/html/table-th-colheader.html
index 298a669..b16b8149 100644
--- a/content/test/data/accessibility/html/table-th-colheader.html
+++ b/content/test/data/accessibility/html/table-th-colheader.html
@@ -1,16 +1,21 @@
 <!--
-@AURALINUX-ALLOW:table-cell-index*
-@AURALINUX-ALLOW:posinset*
-@AURALINUX-ALLOW:setsize*
+@BLINK-ALLOW:*ColumnCount*
+@BLINK-ALLOW:*ColumnIndex*
 @WIN-ALLOW:row_*
 @WIN-ALLOW:column_*
+@WIN-ALLOW:table*
+@UIA-WIN-ALLOW
 @MAC-ALLOW:AXIndex=*
+@MAC-ALLOW:AXColumnIndexRange=*
+@MAC-ALLOW:AXRowIndexRange=*
 @MAC-ALLOW:AXColumnHeaderUIElements=*
 @MAC-ALLOW:AXColumnIndexRange=*
 @MAC-ALLOW:AXHeader=*
 @MAC-ALLOW:AXRowHeaderUIElements=*
 @MAC-ALLOW:AXRowIndexRange=*
-@MAC-DENY:AXDescription=*
+@AURALINUX-ALLOW:table-cell-index*
+@AURALINUX-ALLOW:posinset*
+@AURALINUX-ALLOW:setsize*
 -->
 <!DOCTYPE html>
 <html>
diff --git a/device/fido/aoa/android_accessory_discovery.cc b/device/fido/aoa/android_accessory_discovery.cc
index 3ff3aa3..a06ab91 100644
--- a/device/fido/aoa/android_accessory_discovery.cc
+++ b/device/fido/aoa/android_accessory_discovery.cc
@@ -34,9 +34,11 @@
 }
 
 AndroidAccessoryDiscovery::AndroidAccessoryDiscovery(
-    mojo::Remote<device::mojom::UsbDeviceManager> device_manager)
+    mojo::Remote<device::mojom::UsbDeviceManager> device_manager,
+    std::string request_description)
     : FidoDeviceDiscovery(FidoTransportProtocol::kUsbHumanInterfaceDevice),
-      device_manager_(std::move(device_manager)) {}
+      device_manager_(std::move(device_manager)),
+      request_description_(std::move(request_description)) {}
 
 AndroidAccessoryDiscovery::~AndroidAccessoryDiscovery() = default;
 
@@ -350,42 +352,54 @@
     return;
   }
 
-  static const size_t kNumStrings = 3;
-  static const char kStrings[kNumStrings][24] = {
-      "Chromium",              // manufacturer
-      "Chromium",              // model
-      "Security key request",  // description. TODO(agl): translate.
-  };
-
+  // The semantics of each step number are defined at
+  // https://source.android.com/devices/accessories/aoa#attempt-to-start-in-accessory-mode
   auto* device_ptr = device.get();
-  if (step < kNumStrings) {
-    device_ptr->ControlTransferOut(
-        ControlTransferParams(kSendString, step),
-        VectorFromString(kStrings[step]), kTimeoutMilliseconds,
-        base::BindOnce(&AndroidAccessoryDiscovery::OnConfigurationStepComplete,
-                       weak_factory_.GetWeakPtr(), std::move(device),
-                       step + 1));
-    return;
-  } else if (step == kNumStrings) {
-    device_ptr->ControlTransferOut(
-        ControlTransferParams(kSendString, step),
-        VectorFromString(
-            device::mojom::UsbControlTransferParams::kSecurityKeyAOAVersion),
-        kTimeoutMilliseconds,
-        base::BindOnce(&AndroidAccessoryDiscovery::OnConfigurationStepComplete,
-                       weak_factory_.GetWeakPtr(), std::move(device),
-                       step + 1));
-    return;
-  } else if (step == kNumStrings + 1) {
-    device_ptr->ControlTransferOut(
-        ControlTransferParams(kStart), {}, kTimeoutMilliseconds,
-        base::BindOnce(&AndroidAccessoryDiscovery::OnConfigurationStepComplete,
-                       weak_factory_.GetWeakPtr(), std::move(device),
-                       step + 1));
-    return;
+  std::vector<uint8_t> encoded_string;
+  switch (step) {
+    case 0:
+      // Manufacturer.
+      encoded_string = VectorFromString("Chromium");
+      break;
+
+    case 1:
+      // Model.
+      encoded_string = VectorFromString(
+          device::mojom::UsbControlTransferParams::kSecurityKeyAOAModel);
+      break;
+
+    case 2:
+      encoded_string = VectorFromString(request_description_.c_str());
+      break;
+
+    case 3:
+      // Version. Always some value as a version in order to avoid a potential
+      // Android crash. See https://crbug.com/1174217.
+      encoded_string = VectorFromString("1");
+      break;
+
+    case 4:
+      // Finished sending strings; request switch to AOA mode.
+      device_ptr->ControlTransferOut(
+          ControlTransferParams(kStart), {}, kTimeoutMilliseconds,
+          base::BindOnce(
+              &AndroidAccessoryDiscovery::OnConfigurationStepComplete,
+              weak_factory_.GetWeakPtr(), std::move(device), step + 1));
+      return;
+
+    case 5:
+      FIDO_LOG(DEBUG) << "Device requested to switch to accessory mode";
+      return;
+
+    default:
+      CHECK(false);
   }
 
-  FIDO_LOG(DEBUG) << "Device requested to switch to accessory mode";
+  device_ptr->ControlTransferOut(
+      ControlTransferParams(kSendString, step), encoded_string,
+      kTimeoutMilliseconds,
+      base::BindOnce(&AndroidAccessoryDiscovery::OnConfigurationStepComplete,
+                     weak_factory_.GetWeakPtr(), std::move(device), step + 1));
 }
 
 void AndroidAccessoryDiscovery::OnDeviceRemoved(
diff --git a/device/fido/aoa/android_accessory_discovery.h b/device/fido/aoa/android_accessory_discovery.h
index c22cc588..ae4bca3 100644
--- a/device/fido/aoa/android_accessory_discovery.h
+++ b/device/fido/aoa/android_accessory_discovery.h
@@ -49,8 +49,11 @@
     std::string guid;
   };
 
-  explicit AndroidAccessoryDiscovery(
-      mojo::Remote<device::mojom::UsbDeviceManager>);
+  // The |request_description| is a string that is sent to the device to
+  // describe the type of request and may appears in permissions UI on the
+  // device.
+  AndroidAccessoryDiscovery(mojo::Remote<device::mojom::UsbDeviceManager>,
+                            std::string request_description);
   ~AndroidAccessoryDiscovery() override;
 
  private:
@@ -101,6 +104,7 @@
       bool success);
 
   mojo::Remote<device::mojom::UsbDeviceManager> device_manager_;
+  const std::string request_description_;
   mojo::AssociatedReceiver<device::mojom::UsbDeviceManagerClient> receiver_{
       this};
   base::WeakPtrFactory<AndroidAccessoryDiscovery> weak_factory_{this};
diff --git a/device/fido/fido_discovery_factory.cc b/device/fido/fido_discovery_factory.cc
index 01b713f..a7074b0f 100644
--- a/device/fido/fido_discovery_factory.cc
+++ b/device/fido/fido_discovery_factory.cc
@@ -85,8 +85,11 @@
     }
     case FidoTransportProtocol::kAndroidAccessory:
       if (usb_device_manager_) {
-        return SingleDiscovery(std::make_unique<AndroidAccessoryDiscovery>(
-            std::move(usb_device_manager_.value())));
+        auto ret = SingleDiscovery(std::make_unique<AndroidAccessoryDiscovery>(
+            std::move(usb_device_manager_.value()),
+            std::move(aoa_request_description_)));
+        usb_device_manager_.reset();
+        return ret;
       }
       return {};
   }
@@ -108,9 +111,11 @@
   v2_pairings_ = std::move(v2_pairings);
 }
 
-void FidoDiscoveryFactory::set_usb_device_manager(
-    mojo::Remote<device::mojom::UsbDeviceManager> usb_device_manager) {
+void FidoDiscoveryFactory::set_android_accessory_params(
+    mojo::Remote<device::mojom::UsbDeviceManager> usb_device_manager,
+    std::string aoa_request_description) {
   usb_device_manager_.emplace(std::move(usb_device_manager));
+  aoa_request_description_ = std::move(aoa_request_description);
 }
 
 void FidoDiscoveryFactory::set_network_context(
diff --git a/device/fido/fido_discovery_factory.h b/device/fido/fido_discovery_factory.h
index e11a0dff..6d8e4b0 100644
--- a/device/fido/fido_discovery_factory.h
+++ b/device/fido/fido_discovery_factory.h
@@ -58,7 +58,13 @@
           qr_generator_key,
       std::vector<std::unique_ptr<cablev2::Pairing>> v2_pairings);
 
-  void set_usb_device_manager(mojo::Remote<device::mojom::UsbDeviceManager>);
+  // set_android_accessory_params configures values necessary for discovering
+  // Android AOA devices. The |aoa_request_description| is a string that is sent
+  // to the device to describe the type of request and may appears in
+  // permissions UI on the device.
+  void set_android_accessory_params(
+      mojo::Remote<device::mojom::UsbDeviceManager>,
+      std::string aoa_request_description);
 
   void set_network_context(network::mojom::NetworkContext*);
 
@@ -121,6 +127,7 @@
 #endif  // defined(OS_MAC)
   base::Optional<mojo::Remote<device::mojom::UsbDeviceManager>>
       usb_device_manager_;
+  std::string aoa_request_description_;
   network::mojom::NetworkContext* network_context_ = nullptr;
   base::Optional<std::vector<CableDiscoveryData>> cable_data_;
   base::Optional<std::array<uint8_t, cablev2::kQRKeySize>> qr_generator_key_;
diff --git a/fuchsia/cast_streaming/BUILD.gn b/fuchsia/cast_streaming/BUILD.gn
index ac0c8d0..c07607a 100644
--- a/fuchsia/cast_streaming/BUILD.gn
+++ b/fuchsia/cast_streaming/BUILD.gn
@@ -72,18 +72,19 @@
   testonly = true
   deps = [
     ":cast_streaming_core",
-    "//base",
-    "//components/openscreen_platform",
-    "//media",
     "//media/mojo/common",
     "//media/mojo/mojom",
     "//mojo/public/cpp/system",
-    "//third_party/openscreen/src/cast/common:public",
-    "//third_party/openscreen/src/cast/streaming:sender",
     "//third_party/openscreen/src/platform:api",
     "//third_party/openscreen/src/util",
   ]
-  visibility = [ ":*" ]
+  public_deps = [
+    "//base",
+    "//components/openscreen_platform",
+    "//media",
+    "//third_party/openscreen/src/cast/common:public",
+    "//third_party/openscreen/src/cast/streaming:sender",
+  ]
   sources = [
     "test/cast_message_port_sender_impl.cc",
     "test/cast_message_port_sender_impl.h",
diff --git a/fuchsia/engine/BUILD.gn b/fuchsia/engine/BUILD.gn
index 91486af0..ff563393 100644
--- a/fuchsia/engine/BUILD.gn
+++ b/fuchsia/engine/BUILD.gn
@@ -366,6 +366,7 @@
     ":switches",
     ":web_engine_core",
     "//base/test:test_support",
+    "//components/cast/message_port:message_port",
     "//components/policy/content:safe_sites_navigation_throttle",
     "//components/safe_search_api:safe_search_api",
     "//components/safe_search_api:test_support",
@@ -373,6 +374,8 @@
     "//content/test:test_support",
     "//fuchsia/base",
     "//fuchsia/base:test_support",
+    "//fuchsia/cast_streaming",
+    "//fuchsia/cast_streaming:cast_streaming_test_sender",
     "//net:test_support",
     "//testing/gmock",
     "//testing/gtest",
diff --git a/fuchsia/engine/browser/cast_streaming_browsertest.cc b/fuchsia/engine/browser/cast_streaming_browsertest.cc
index cc94207..f84ce82c 100644
--- a/fuchsia/engine/browser/cast_streaming_browsertest.cc
+++ b/fuchsia/engine/browser/cast_streaming_browsertest.cc
@@ -2,22 +2,46 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/callback_helpers.h"
+#include "base/threading/platform_thread.h"
+#include "components/cast/message_port/message_port_fuchsia.h"
 #include "content/public/test/browser_test.h"
 #include "fuchsia/base/fit_adapter.h"
 #include "fuchsia/base/frame_test_util.h"
 #include "fuchsia/base/mem_buffer_util.h"
 #include "fuchsia/base/result_receiver.h"
 #include "fuchsia/base/test_navigation_listener.h"
+#include "fuchsia/cast_streaming/test/cast_streaming_test_sender.h"
 #include "fuchsia/engine/browser/frame_impl.h"
 #include "fuchsia/engine/switches.h"
 #include "fuchsia/engine/test/test_data.h"
 #include "fuchsia/engine/test/web_engine_browser_test.h"
+#include "media/base/media_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
 
 const char kCastStreamingReceiverPath[] = "/cast_streaming_receiver.html";
 
+media::AudioDecoderConfig GetDefaultAudioConfig() {
+  return media::AudioDecoderConfig(
+      media::AudioCodec::kCodecOpus, media::SampleFormat::kSampleFormatF32,
+      media::ChannelLayout::CHANNEL_LAYOUT_STEREO,
+      48000 /* samples_per_second */, media::EmptyExtraData(),
+      media::EncryptionScheme::kUnencrypted);
+}
+
+media::VideoDecoderConfig GetDefaultVideoConfig() {
+  const gfx::Size kVideoSize = {1920, 1080};
+  const gfx::Rect kVideoRect(kVideoSize);
+
+  return media::VideoDecoderConfig(
+      media::VideoCodec::kCodecVP8, media::VideoCodecProfile::VP8PROFILE_MIN,
+      media::VideoDecoderConfig::AlphaMode::kIsOpaque, media::VideoColorSpace(),
+      media::VideoTransformation(), kVideoSize, kVideoRect, kVideoSize,
+      media::EmptyExtraData(), media::EncryptionScheme::kUnencrypted);
+}
+
 }  // namespace
 
 // Base test fixture for Cast Streaming tests.
@@ -114,16 +138,44 @@
 
 // Check that attempting to load the cast streaming media source URL when the
 // command line switch is set properly succeeds.
-// TODO(crbug.com/1087537): Re-enable when we have a test implementation for a
-// Cast Streaming Sender.
-IN_PROC_BROWSER_TEST_F(CastStreamingTest, DISABLED_LoadSuccess) {
+IN_PROC_BROWSER_TEST_F(CastStreamingTest, LoadSuccess) {
   ASSERT_TRUE(embedded_test_server()->Start());
-  GURL page_url(embedded_test_server()->GetURL(kCastStreamingReceiverPath));
+  const GURL kPageUrl(
+      embedded_test_server()->GetURL(kCastStreamingReceiverPath));
+  fuchsia::mem::Buffer ignored_message_string =
+      cr_fuchsia::MemBufferFromString("hi", "test");
 
+  std::unique_ptr<cast_api_bindings::MessagePort> sender_message_port;
+  std::unique_ptr<cast_api_bindings::MessagePort> receiver_message_port;
+  cast_api_bindings::MessagePort::CreatePair(&sender_message_port,
+                                             &receiver_message_port);
+
+  fidl::InterfaceRequest<::fuchsia::web::MessagePort> message_port_request =
+      cast_api_bindings::MessagePortFuchsia::FromMessagePort(
+          receiver_message_port.get())
+          ->TakeServiceRequest();
+
+  // Start the Sender
+  cast_streaming::CastStreamingTestSender sender;
+  EXPECT_TRUE(sender.Start(std::move(sender_message_port),
+                           net::IPAddress::IPv6Localhost(),
+                           GetDefaultAudioConfig(), GetDefaultVideoConfig()));
+
+  // Create a Frame and set the Receiver MessagePort on it.
   fuchsia::web::FramePtr frame = CreateFrame();
+  cr_fuchsia::ResultReceiver<fuchsia::web::Frame_PostMessage_Result>
+      post_result(base::DoNothing::Repeatedly());
+  frame->PostMessage(
+      "cast-streaming:receiver",
+      cr_fuchsia::CreateWebMessageWithMessagePortRequest(
+          std::move(message_port_request), std::move(ignored_message_string)),
+      cr_fuchsia::CallbackToFitFunction(post_result.GetReceiveCallback()));
+
   fuchsia::web::NavigationControllerPtr controller;
   frame->GetNavigationController(controller.NewRequest());
   EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
-      controller.get(), fuchsia::web::LoadUrlParams(), page_url.spec()));
+      controller.get(), fuchsia::web::LoadUrlParams(), kPageUrl.spec()));
+
+  sender.RunUntilStarted();
   navigation_listener_.RunUntilTitleEquals("canplay");
 }
diff --git a/fuchsia/engine/browser/frame_impl.cc b/fuchsia/engine/browser/frame_impl.cc
index 4be3454..781cdcb 100644
--- a/fuchsia/engine/browser/frame_impl.cc
+++ b/fuchsia/engine/browser/frame_impl.cc
@@ -1103,6 +1103,13 @@
          blink::mojom::PermissionStatus::GRANTED;
 }
 
+bool FrameImpl::CanOverscrollContent() {
+  // Don't process "overscroll" events (e.g. pull-to-refresh, swipe back,
+  // swipe forward).
+  // TODO(crbug/1177399): Add overscroll toggle to Frame API.
+  return false;
+}
+
 void FrameImpl::ReadyToCommitNavigation(
     content::NavigationHandle* navigation_handle) {
   if (!navigation_handle->IsInMainFrame() ||
diff --git a/fuchsia/engine/browser/frame_impl.h b/fuchsia/engine/browser/frame_impl.h
index ae33b07..9bdcbcc 100644
--- a/fuchsia/engine/browser/frame_impl.h
+++ b/fuchsia/engine/browser/frame_impl.h
@@ -256,6 +256,7 @@
   bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host,
                                   const GURL& security_origin,
                                   blink::mojom::MediaStreamType type) override;
+  bool CanOverscrollContent() override;
 
   // content::WebContentsObserver implementation.
   void ReadyToCommitNavigation(
diff --git a/gpu/config/gpu_finch_features.cc b/gpu/config/gpu_finch_features.cc
index bc23f9d..f1c350c 100644
--- a/gpu/config/gpu_finch_features.cc
+++ b/gpu/config/gpu_finch_features.cc
@@ -12,6 +12,7 @@
 #include "base/android/android_image_reader_compat.h"
 #include "base/android/build_info.h"
 #include "base/metrics/field_trial_params.h"
+#include "base/strings/pattern.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "ui/gfx/android/android_surface_control_compat.h"
@@ -47,6 +48,11 @@
 const base::Feature kAndroidSurfaceControl{"AndroidSurfaceControl",
                                            base::FEATURE_ENABLED_BY_DEFAULT};
 
+// https://crbug.com/1176185 List of devices on which SurfaceControl should be
+// disabled.
+const base::FeatureParam<std::string> kAndroidSurfaceControlBlocklist{
+    &kAndroidSurfaceControl, "AndroidSurfaceControlBlocklist", "capri|caprip"};
+
 // Hardware Overlays for WebView.
 const base::Feature kWebViewSurfaceControl{"WebViewSurfaceControl",
                                            base::FEATURE_DISABLED_BY_DEFAULT};
@@ -218,6 +224,15 @@
 }
 
 bool IsAndroidSurfaceControlEnabled() {
+  const auto* build_info = base::android::BuildInfo::GetInstance();
+  auto disable_patterns =
+      base::SplitString(kAndroidSurfaceControlBlocklist.Get(), "|",
+                        base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+  for (const auto& disable_pattern : disable_patterns) {
+    if (base::MatchPattern(build_info->device(), disable_pattern))
+      return false;
+  }
+
   if (!gfx::SurfaceControl::IsSupported())
     return false;
 
diff --git a/infra/config/dev/subprojects/chromium/ci.star b/infra/config/dev/subprojects/chromium/ci.star
index 2cad54a..2cb89c4 100644
--- a/infra/config/dev/subprojects/chromium/ci.star
+++ b/infra/config/dev/subprojects/chromium/ci.star
@@ -48,13 +48,17 @@
 )
 defaults.swarming_tags.set(["vpython:native-python-wrapper"])
 
-def ci_builder(*, name, **kwargs):
+def ci_builder(*, name, resultdb_bigquery_exports=None, **kwargs):
+    resultdb_bigquery_exports = resultdb_bigquery_exports or []
+    resultdb_bigquery_exports.append(
+        resultdb.export_test_results(
+            bq_table = "luci-resultdb-dev.chromium.ci_test_results",
+        )
+    )
     return builder(
         name = name,
         triggered_by = ["chromium-gitiles-trigger"],
-        resultdb_bigquery_exports = [resultdb.export_test_results(
-            bq_table = "luci-resultdb-dev.chromium.ci_test_results",
-        )],
+        resultdb_bigquery_exports = resultdb_bigquery_exports,
         isolated_server = "https://isolateserver-dev.appspot.com",
         goma_backend = goma.backend.RBE_PROD,
         **kwargs
@@ -71,6 +75,10 @@
 ci_builder(
     name = "linux-rel-swarming",
     description_html = "Test description. <b>Test HTML</b>.",
+    resultdb_bigquery_exports = [
+        resultdb.export_text_artifacts(
+            bq_table = "luci-resultdb-dev.chromium.ci_text_artifacts",
+        )]
 )
 
 ci_builder(
diff --git a/infra/config/generated/commit-queue.cfg b/infra/config/generated/commit-queue.cfg
index 481b3a86..44ea141 100644
--- a/infra/config/generated/commit-queue.cfg
+++ b/infra/config/generated/commit-queue.cfg
@@ -311,6 +311,10 @@
         includable_only: true
       }
       builders {
+        name: "chromium/try/android-weblayer-pie-x86-wpt-fyi-rel"
+        includable_only: true
+      }
+      builders {
         name: "chromium/try/android-webview-marshmallow-arm64-dbg"
         includable_only: true
       }
diff --git a/infra/config/generated/cr-buildbucket-dev.cfg b/infra/config/generated/cr-buildbucket-dev.cfg
index 43b976c..20de1d07 100644
--- a/infra/config/generated/cr-buildbucket-dev.cfg
+++ b/infra/config/generated/cr-buildbucket-dev.cfg
@@ -122,6 +122,12 @@
         bq_exports {
           project: "luci-resultdb-dev"
           dataset: "chromium"
+          table: "ci_text_artifacts"
+          text_artifacts {}
+        }
+        bq_exports {
+          project: "luci-resultdb-dev"
+          dataset: "chromium"
           table: "ci_test_results"
           test_results {}
         }
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index aaaa16ac..fd3b1da 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -26950,12 +26950,10 @@
       name: "rts-model-packager"
       swarming_host: "chromium-swarm.appspot.com"
       swarming_tags: "vpython:native-python-wrapper"
-      dimensions: "builderless:1"
-      dimensions: "cores:32"
+      dimensions: "builder:rts-model-packager"
       dimensions: "cpu:x86-64"
       dimensions: "os:Ubuntu-16.04"
       dimensions: "pool:luci.chromium.ci"
-      dimensions: "ssd:0"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
@@ -32860,6 +32858,68 @@
       }
     }
     builders {
+      name: "android-weblayer-pie-x86-wpt-fyi-rel"
+      swarming_host: "chromium-swarm.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "builderless:1"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Ubuntu-16.04"
+      dimensions: "pool:luci.chromium.try"
+      dimensions: "ssd:0"
+      exe {
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/master"
+        cmd: "recipes"
+      }
+      properties: "{\"$build/goma\":{\"enable_ats\":true,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"tryserver.chromium.android\",\"recipe\":\"chromium_trybot\"}"
+      execution_timeout_secs: 14400
+      expiration_secs: 7200
+      caches {
+        name: "win_toolchain"
+        path: "win_toolchain"
+      }
+      build_numbers: YES
+      service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
+      task_template_canary_percentage {
+        value: 5
+      }
+      experiments {
+        key: "chromium.resultdb.result_sink"
+        value: 100
+      }
+      experiments {
+        key: "chromium.resultdb.result_sink.junit_tests"
+        value: 100
+      }
+      experiments {
+        key: "luci.use_realms"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "luci-resultdb"
+          dataset: "chromium"
+          table: "try_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "luci-resultdb"
+          dataset: "chromium"
+          table: "gpu_try_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://(chrome/test:|content/test:fuchsia_)telemetry_gpu_integration_test/.+"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+    }
+    builders {
       name: "android-webview-marshmallow-arm64-dbg"
       swarming_host: "chromium-swarm.appspot.com"
       swarming_tags: "vpython:native-python-wrapper"
diff --git a/infra/config/generated/luci-milo.cfg b/infra/config/generated/luci-milo.cfg
index db57b34..a20798c 100644
--- a/infra/config/generated/luci-milo.cfg
+++ b/infra/config/generated/luci-milo.cfg
@@ -12102,6 +12102,9 @@
     name: "buildbucket/luci.chromium.try/android-pie-x86-rel"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/android-weblayer-pie-x86-wpt-fyi-rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/android-webview-marshmallow-arm64-dbg"
   }
   builders {
@@ -13075,6 +13078,9 @@
     name: "buildbucket/luci.chromium.try/android-pie-x86-rel"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/android-weblayer-pie-x86-wpt-fyi-rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/android-webview-marshmallow-arm64-dbg"
   }
   builders {
diff --git a/infra/config/subprojects/chromium/ci.star b/infra/config/subprojects/chromium/ci.star
index 49ef2fc..76fe8948 100644
--- a/infra/config/subprojects/chromium/ci.star
+++ b/infra/config/subprojects/chromium/ci.star
@@ -5776,13 +5776,14 @@
 
 ci.cipd_builder(
     name = "rts-model-packager",
+    builderless = False,
+    executable = "recipe:chromium_rts/create_model",
+    schedule = "0 10 * * *",  # at 2 AM PST, once a day.
+    triggered_by = [],
+    execution_timeout = 6 * time.hour,
+    cores = None,
     console_view_entry = consoles.console_view_entry(
         category = "rts",
         short_name = "create-model",
     ),
-    executable = "recipe:chromium_rts/create_model",
-    schedule = "0 10 * * *",  # at 2 AM PST, once a day.
-    triggered_by = [],
-    cores = 32,
-    execution_timeout = 6 * time.hour,
 )
diff --git a/infra/config/subprojects/chromium/try.star b/infra/config/subprojects/chromium/try.star
index 8b55809..e927ba6 100644
--- a/infra/config/subprojects/chromium/try.star
+++ b/infra/config/subprojects/chromium/try.star
@@ -449,6 +449,10 @@
 )
 
 try_.chromium_android_builder(
+    name = "android-weblayer-pie-x86-wpt-fyi-rel",
+)
+
+try_.chromium_android_builder(
     name = "android-webview-marshmallow-arm64-dbg",
 )
 
diff --git a/ios/chrome/browser/flags/BUILD.gn b/ios/chrome/browser/flags/BUILD.gn
index 965672b..44ef1a71 100644
--- a/ios/chrome/browser/flags/BUILD.gn
+++ b/ios/chrome/browser/flags/BUILD.gn
@@ -22,7 +22,6 @@
     "//components/feature_engagement/public",
     "//components/flags_ui",
     "//components/flags_ui:switches",
-    "//components/infobars/core:feature_flags",
     "//components/invalidation/impl:feature_list",
     "//components/ntp_tiles",
     "//components/omnibox/browser",
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index 5778776..1d28daa 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -34,7 +34,6 @@
 #include "components/flags_ui/feature_entry_macros.h"
 #include "components/flags_ui/flags_storage.h"
 #include "components/flags_ui/flags_ui_switches.h"
-#include "components/infobars/core/infobar_feature.h"
 #include "components/invalidation/impl/invalidation_switches.h"
 #include "components/ntp_tiles/switches.h"
 #include "components/omnibox/browser/omnibox_field_trial.h"
@@ -366,9 +365,6 @@
      flag_descriptions::kOmniboxLocalHistoryZeroSuggestName,
      flag_descriptions::kOmniboxLocalHistoryZeroSuggestDescription,
      flags_ui::kOsIos, FEATURE_VALUE_TYPE(omnibox::kLocalHistoryZeroSuggest)},
-    {"infobar-ui-reboot", flag_descriptions::kInfobarUIRebootName,
-     flag_descriptions::kInfobarUIRebootDescription, flags_ui::kOsIos,
-     FEATURE_VALUE_TYPE(kIOSInfobarUIReboot)},
     {"snapshot-draw-view", flag_descriptions::kSnapshotDrawViewName,
      flag_descriptions::kSnapshotDrawViewDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(kSnapshotDrawView)},
@@ -414,14 +410,6 @@
      flag_descriptions::kEmbedderBlockRestoreUrlName,
      flag_descriptions::kEmbedderBlockRestoreUrlDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(kEmbedderBlockRestoreUrl)},
-    {"messages-save-card-infobar",
-     flag_descriptions::kSaveCardInfobarMessagesUIName,
-     flag_descriptions::kSaveCardInfobarMessagesUIDescription, flags_ui::kOsIos,
-     FEATURE_VALUE_TYPE(kSaveCardInfobarMessagesUI)},
-    {"messages-translate-infobar",
-     flag_descriptions::kTranslateInfobarMessagesUIName,
-     flag_descriptions::kTranslateInfobarMessagesUIDescription,
-     flags_ui::kOsIos, FEATURE_VALUE_TYPE(kTranslateInfobarMessagesUI)},
     {"autofill-save-card-dismiss-on-navigation",
      flag_descriptions::kAutofillSaveCardDismissOnNavigationName,
      flag_descriptions::kAutofillSaveCardDismissOnNavigationDescription,
@@ -452,10 +440,6 @@
     {"managed-bookmarks-ios", flag_descriptions::kManagedBookmarksIOSName,
      flag_descriptions::kManagedBookmarksIOSDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(kManagedBookmarksIOS)},
-    {"infobar-ui-reboot-only-ios13",
-     flag_descriptions::kInfobarUIRebootOnlyiOS13Name,
-     flag_descriptions::kInfobarUIRebootOnlyiOS13Description, flags_ui::kOsIos,
-     FEATURE_VALUE_TYPE(kInfobarUIRebootOnlyiOS13)},
     {"edit-bookmarks-ios", flag_descriptions::kEditBookmarksIOSName,
      flag_descriptions::kEditBookmarksIOSDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(kEditBookmarksIOS)},
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
index e99f1489..78a05a6c 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -226,14 +226,6 @@
     "When enabled alongside the Infobar UI Reboot, infobars will be presented "
     "using OverlayPresenter.";
 
-const char kInfobarUIRebootName[] = "Infobar UI Reboot";
-const char kInfobarUIRebootDescription[] =
-    "When enabled, Infobar will use the new UI.";
-
-const char kInfobarUIRebootOnlyiOS13Name[] = "Infobar UI Reboot iOS13";
-const char kInfobarUIRebootOnlyiOS13Description[] =
-    "When enabled, Infobar will use the new UI only on iOS13";
-
 const char kSigninNotificationInfobarUsernameInTitleName[] =
     "Sign-in notification infobar title";
 const char kSigninNotificationInfobarUsernameInTitleDescription[] =
@@ -368,10 +360,6 @@
     "When enabled, the iOS version of safety check is available in Chrome "
     "settings.";
 
-const char kSaveCardInfobarMessagesUIName[] = "Save Card Infobar Messages UI";
-const char kSaveCardInfobarMessagesUIDescription[] =
-    "When enabled, Save Card Infobar uses the new Messages UI.";
-
 const char kScreenTimeIntegrationName[] = "Enables ScreenTime Integration";
 const char kScreenTimeIntegrationDescription[] =
     "Enables integration with ScreenTime in iOS 14.0 and above.";
@@ -429,11 +417,6 @@
     "When enabled, the toolbars and their fullscreen animations will be "
     "managed by the toolbar container coordinator rather than BVC.";
 
-const char kTranslateInfobarMessagesUIName[] =
-    "Enable Translate Infobar Messages UI";
-const char kTranslateInfobarMessagesUIDescription[] =
-    "When enabled, the Translate Infobar uses the new Messages UI.";
-
 const char kURLBlocklistIOSName[] = "URL Blocklist Policy";
 const char kURLBlocklistIOSDescription[] =
     "When enabled, URLs can be blocked/allowed by the URLBlocklist/URLAllowlist"
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
index 7d61a34..a74b064 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -202,15 +202,6 @@
 extern const char kInfobarOverlayUIName[];
 extern const char kInfobarOverlayUIDescription[];
 
-// Title and description for the flag to enable the new UI Reboot on Infobars.
-extern const char kInfobarUIRebootName[];
-extern const char kInfobarUIRebootDescription[];
-
-// Title and description for the flag to enable the new UI Reboot on Infobars
-// only on iOS13.
-extern const char kInfobarUIRebootOnlyiOS13Name[];
-extern const char kInfobarUIRebootOnlyiOS13Description[];
-
 // Title and description for the flag to enable feature_engagement::Tracker
 // demo mode.
 extern const char kInProductHelpDemoModeName[];
@@ -306,11 +297,6 @@
 extern const char kSafetyCheckIOSName[];
 extern const char kSafetyCheckIOSDescription[];
 
-// Title and description for the flag that enables Messages UI on
-// SaveCard Infobars.
-extern const char kSaveCardInfobarMessagesUIName[];
-extern const char kSaveCardInfobarMessagesUIDescription[];
-
 // Title and description for the flag to enable integration with the ScreenTime
 // system.
 extern const char kScreenTimeIntegrationName[];
@@ -372,11 +358,6 @@
 extern const char kToolbarContainerName[];
 extern const char kToolbarContainerDescription[];
 
-// Title and description for the flag to enable the Messages UI for Translate
-// Infobars.
-extern const char kTranslateInfobarMessagesUIName[];
-extern const char kTranslateInfobarMessagesUIDescription[];
-
 // Title and description for the flag to enable URLBlocklist/URLAllowlist
 // enterprise policy.
 extern const char kURLBlocklistIOSName[];
diff --git a/ios/chrome/browser/infobars/infobar_badge_tab_helper_unittest.mm b/ios/chrome/browser/infobars/infobar_badge_tab_helper_unittest.mm
index 4c637e9e..330e1e4 100644
--- a/ios/chrome/browser/infobars/infobar_badge_tab_helper_unittest.mm
+++ b/ios/chrome/browser/infobars/infobar_badge_tab_helper_unittest.mm
@@ -4,15 +4,12 @@
 
 #include "ios/chrome/browser/infobars/infobar_badge_tab_helper.h"
 
-#include "base/test/scoped_feature_list.h"
-#include "components/infobars/core/infobar_feature.h"
 #include "ios/chrome/browser/infobars/infobar_badge_tab_helper.h"
 #include "ios/chrome/browser/infobars/infobar_manager_impl.h"
 #import "ios/chrome/browser/infobars/test/fake_infobar_badge_tab_helper_delegate.h"
 #import "ios/chrome/browser/infobars/test/fake_infobar_ios.h"
 #import "ios/chrome/browser/ui/badges/badge_item.h"
 #include "ios/chrome/browser/ui/badges/badge_type_util.h"
-#import "ios/chrome/browser/ui/infobars/infobar_feature.h"
 #import "ios/chrome/browser/ui/infobars/test/fake_infobar_ui_delegate.h"
 #import "ios/web/public/test/fakes/fake_navigation_manager.h"
 #import "ios/web/public/test/fakes/fake_web_state.h"
@@ -33,9 +30,6 @@
  protected:
   InfobarBadgeTabHelperTest()
       : delegate_([[FakeInfobarTabHelperDelegate alloc] init]) {
-    // Enable kIOSInfobarUIReboot flag.
-    feature_list_.InitWithFeatures({kIOSInfobarUIReboot},
-                                   {kInfobarUIRebootOnlyiOS13});
 
     // Setup navigation manager. Needed for InfobarManager.
     web_state_.SetNavigationManager(
@@ -68,7 +62,6 @@
     return InfobarBadgeTabHelper::FromWebState(&web_state_);
   }
 
-  base::test::ScopedFeatureList feature_list_;
   web::FakeWebState web_state_;
   FakeInfobarTabHelperDelegate* delegate_ = nil;
 };
diff --git a/ios/chrome/browser/infobars/infobar_container_ios.mm b/ios/chrome/browser/infobars/infobar_container_ios.mm
index 1f80b07..3b76258 100644
--- a/ios/chrome/browser/infobars/infobar_container_ios.mm
+++ b/ios/chrome/browser/infobars/infobar_container_ios.mm
@@ -45,15 +45,8 @@
                                info_bar_manager_->infobar_count(), kMaxValue);
   }
 
-  if ([delegate isPresented]) {
-    // Only InfobarUIReboot Infobars should be presented using the non legacy
-    // consumer.
-    DCHECK(IsInfobarUIRebootEnabled());
-    [consumer_ addInfoBarWithDelegate:delegate
-                           skipBanner:infobar_ios->skip_banner()];
-  } else {
-    [legacyConsumer_ addInfoBarWithDelegate:delegate skipBanner:NO];
-  }
+  [consumer_ addInfoBarWithDelegate:delegate
+                         skipBanner:infobar_ios->skip_banner()];
 }
 
 void InfoBarContainerIOS::PlatformSpecificRemoveInfoBar(
diff --git a/ios/chrome/browser/infobars/infobar_utils.mm b/ios/chrome/browser/infobars/infobar_utils.mm
index 8e88ae3e..505a3c6 100644
--- a/ios/chrome/browser/infobars/infobar_utils.mm
+++ b/ios/chrome/browser/infobars/infobar_utils.mm
@@ -31,7 +31,6 @@
 
 std::unique_ptr<infobars::InfoBar> CreateHighPriorityConfirmInfoBar(
     std::unique_ptr<ConfirmInfoBarDelegate> delegate) {
-  DCHECK(IsInfobarUIRebootEnabled());
   // TODO(crbug.com/927064): Coordinators shouldn't be created at this level,
   // we should probably send only the delegate and have the presenting
   // Coordinator create the right Coordinator using that delegate.
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common/infobar_banner_interaction_handler_unittest.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common/infobar_banner_interaction_handler_unittest.mm
index 0ea532de..d4c08f78 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common/infobar_banner_interaction_handler_unittest.mm
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common/infobar_banner_interaction_handler_unittest.mm
@@ -4,8 +4,6 @@
 
 #import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common/infobar_banner_interaction_handler.h"
 
-#include "base/test/scoped_feature_list.h"
-#include "components/infobars/core/infobar_feature.h"
 #include "ios/chrome/browser/infobars/infobar_manager_impl.h"
 #import "ios/chrome/browser/infobars/overlays/infobar_overlay_request_inserter.h"
 #include "ios/chrome/browser/infobars/overlays/infobar_overlay_util.h"
@@ -13,7 +11,6 @@
 #import "ios/chrome/browser/overlays/public/infobar_banner/save_password_infobar_banner_overlay.h"
 #import "ios/chrome/browser/overlays/public/overlay_request_queue.h"
 #import "ios/chrome/browser/passwords/test/mock_ios_chrome_save_passwords_infobar_delegate.h"
-#import "ios/chrome/browser/ui/infobars/infobar_feature.h"
 #import "ios/web/public/test/fakes/fake_navigation_manager.h"
 #import "ios/web/public/test/fakes/fake_web_state.h"
 #include "testing/platform_test.h"
@@ -28,8 +25,6 @@
   InfobarBannerInteractionHandlerTest()
       : handler_(
             SavePasswordInfobarBannerOverlayRequestConfig::RequestSupport()) {
-    scoped_feature_list_.InitWithFeatures({kIOSInfobarUIReboot},
-                                          {kInfobarUIRebootOnlyiOS13});
     web_state_.SetNavigationManager(
         std::make_unique<web::FakeNavigationManager>());
     InfoBarManagerImpl::CreateForWebState(&web_state_);
@@ -50,7 +45,6 @@
   }
 
  protected:
-  base::test::ScopedFeatureList scoped_feature_list_;
   InfobarBannerInteractionHandler handler_;
   web::FakeWebState web_state_;
   InfoBarIOS* infobar_;
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/confirm/BUILD.gn b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/confirm/BUILD.gn
index bc0ce8f..f108929 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/confirm/BUILD.gn
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/confirm/BUILD.gn
@@ -33,7 +33,6 @@
   deps = [
     ":confirm",
     "//base/test:test_support",
-    "//components/infobars/core:feature_flags",
     "//ios/chrome/browser/infobars",
     "//ios/chrome/browser/infobars:public",
     "//ios/chrome/browser/infobars/overlays",
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/confirm/confirm_infobar_banner_interaction_handler_unittest.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/confirm/confirm_infobar_banner_interaction_handler_unittest.mm
index 08fe4a2..4272e911 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/confirm/confirm_infobar_banner_interaction_handler_unittest.mm
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/confirm/confirm_infobar_banner_interaction_handler_unittest.mm
@@ -4,15 +4,12 @@
 
 #import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/confirm/confirm_infobar_banner_interaction_handler.h"
 
-#include "base/test/scoped_feature_list.h"
-#include "components/infobars/core/infobar_feature.h"
 #include "ios/chrome/browser/infobars/infobar_manager_impl.h"
 #import "ios/chrome/browser/infobars/infobar_type.h"
 #import "ios/chrome/browser/infobars/overlays/infobar_overlay_request_inserter.h"
 #import "ios/chrome/browser/infobars/test/fake_infobar_ios.h"
 #include "ios/chrome/browser/infobars/test/mock_infobar_delegate.h"
 #import "ios/chrome/browser/overlays/public/overlay_request_queue.h"
-#import "ios/chrome/browser/ui/infobars/infobar_feature.h"
 #import "ios/web/public/test/fakes/fake_navigation_manager.h"
 #import "ios/web/public/test/fakes/fake_web_state.h"
 #include "testing/platform_test.h"
@@ -25,8 +22,6 @@
 class ConfirmInfobarBannerInteractionHandlerTest : public PlatformTest {
  public:
   ConfirmInfobarBannerInteractionHandlerTest() {
-    scoped_feature_list_.InitWithFeatures({kIOSInfobarUIReboot},
-                                          {kInfobarUIRebootOnlyiOS13});
     web_state_.SetNavigationManager(
         std::make_unique<web::FakeNavigationManager>());
     InfobarOverlayRequestInserter::CreateForWebState(&web_state_);
@@ -46,7 +41,6 @@
   }
 
  protected:
-  base::test::ScopedFeatureList scoped_feature_list_;
   ConfirmInfobarBannerInteractionHandler handler_;
   web::FakeWebState web_state_;
   InfoBarIOS* infobar_;
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/BUILD.gn b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/BUILD.gn
index 92bd5a5..a491a409 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/BUILD.gn
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/BUILD.gn
@@ -44,7 +44,6 @@
   deps = [
     ":passwords",
     "//base/test:test_support",
-    "//components/infobars/core:feature_flags",
     "//components/password_manager/core/browser:test_support",
     "//ios/chrome/browser/browser_state:test_support",
     "//ios/chrome/browser/infobars",
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_banner_interaction_handler_unittest.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_banner_interaction_handler_unittest.mm
index 88f1913..f26db61 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_banner_interaction_handler_unittest.mm
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_banner_interaction_handler_unittest.mm
@@ -4,8 +4,6 @@
 
 #import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_banner_interaction_handler.h"
 
-#include "base/test/scoped_feature_list.h"
-#include "components/infobars/core/infobar_feature.h"
 #include "ios/chrome/browser/infobars/infobar_manager_impl.h"
 #import "ios/chrome/browser/infobars/overlays/infobar_overlay_request_inserter.h"
 #include "ios/chrome/browser/infobars/overlays/infobar_overlay_util.h"
@@ -13,7 +11,6 @@
 #import "ios/chrome/browser/overlays/public/infobar_banner/save_password_infobar_banner_overlay.h"
 #import "ios/chrome/browser/overlays/public/overlay_request_queue.h"
 #import "ios/chrome/browser/passwords/test/mock_ios_chrome_save_passwords_infobar_delegate.h"
-#import "ios/chrome/browser/ui/infobars/infobar_feature.h"
 #import "ios/chrome/browser/ui/infobars/test/fake_infobar_ui_delegate.h"
 #import "ios/web/public/test/fakes/fake_navigation_manager.h"
 #import "ios/web/public/test/fakes/fake_web_state.h"
@@ -29,8 +26,6 @@
   PasswordInfobarBannerInteractionHandlerTest()
       : handler_(
             SavePasswordInfobarBannerOverlayRequestConfig::RequestSupport()) {
-    scoped_feature_list_.InitWithFeatures({kIOSInfobarUIReboot},
-                                          {kInfobarUIRebootOnlyiOS13});
     web_state_.SetNavigationManager(
         std::make_unique<web::FakeNavigationManager>());
     InfobarOverlayRequestInserter::CreateForWebState(&web_state_);
@@ -52,7 +47,6 @@
   }
 
  protected:
-  base::test::ScopedFeatureList scoped_feature_list_;
   PasswordInfobarBannerInteractionHandler handler_;
   web::FakeWebState web_state_;
   InfoBarIOS* infobar_;
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_interaction_handler_unittest.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_interaction_handler_unittest.mm
index d8e5135..a8ed627 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_interaction_handler_unittest.mm
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_interaction_handler_unittest.mm
@@ -4,15 +4,12 @@
 
 #import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_interaction_handler.h"
 
-#include "base/test/scoped_feature_list.h"
-#include "components/infobars/core/infobar_feature.h"
 #import "ios/chrome/browser/infobars/test/fake_infobar_ios.h"
 #import "ios/chrome/browser/main/test_browser.h"
 #import "ios/chrome/browser/overlays/public/overlay_request_queue.h"
 #import "ios/chrome/browser/passwords/test/mock_ios_chrome_save_passwords_infobar_delegate.h"
 #import "ios/chrome/browser/ui/commands/application_commands.h"
 #import "ios/chrome/browser/ui/commands/command_dispatcher.h"
-#import "ios/chrome/browser/ui/infobars/infobar_feature.h"
 #import "ios/chrome/browser/ui/infobars/test/fake_infobar_ui_delegate.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
 #import "ios/chrome/browser/web_state_list/web_state_opener.h"
@@ -35,8 +32,6 @@
             [[FakeInfobarUIDelegate alloc] init],
             MockIOSChromeSavePasswordInfoBarDelegate::Create(@"username",
                                                              @"password")) {
-    scoped_feature_list_.InitWithFeatures({kIOSInfobarUIReboot},
-                                          {kInfobarUIRebootOnlyiOS13});
     [browser_.GetCommandDispatcher()
         startDispatchingToTarget:mock_command_receiver_
                      forProtocol:@protocol(ApplicationSettingsCommands)];
@@ -55,7 +50,6 @@
   }
 
  protected:
-  base::test::ScopedFeatureList scoped_feature_list_;
   web::WebTaskEnvironment task_environment_;
   TestBrowser browser_;
   id mock_command_receiver_ = nil;
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_overlay_request_callback_installer_unittest.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_overlay_request_callback_installer_unittest.mm
index 8a93295..9625536 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_overlay_request_callback_installer_unittest.mm
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_overlay_request_callback_installer_unittest.mm
@@ -4,8 +4,6 @@
 
 #import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/password_infobar_modal_overlay_request_callback_installer.h"
 
-#include "base/test/scoped_feature_list.h"
-#include "components/infobars/core/infobar_feature.h"
 #include "ios/chrome/browser/infobars/infobar_ios.h"
 #include "ios/chrome/browser/infobars/infobar_manager_impl.h"
 #import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/passwords/test/mock_password_infobar_modal_interaction_handler.h"
@@ -19,7 +17,6 @@
 #include "ios/chrome/browser/overlays/public/overlay_response.h"
 #include "ios/chrome/browser/overlays/test/overlay_test_macros.h"
 #import "ios/chrome/browser/passwords/test/mock_ios_chrome_save_passwords_infobar_delegate.h"
-#import "ios/chrome/browser/ui/infobars/infobar_feature.h"
 #import "ios/chrome/browser/ui/infobars/test/fake_infobar_ui_delegate.h"
 #import "ios/web/public/test/fakes/fake_navigation_manager.h"
 #import "ios/web/public/test/fakes/fake_web_state.h"
@@ -42,8 +39,6 @@
       : installer_(&mock_handler_, password_modal::PasswordAction::kSave),
         update_installer_(&mock_handler_,
                           password_modal::PasswordAction::kUpdate) {
-    scoped_feature_list_.InitWithFeatures({kIOSInfobarUIReboot},
-                                          {kInfobarUIRebootOnlyiOS13});
     // Create the infobar and add it to the WebState's manager.
     web_state_.SetNavigationManager(
         std::make_unique<web::FakeNavigationManager>());
@@ -73,7 +68,6 @@
   }
 
  protected:
-  base::test::ScopedFeatureList scoped_feature_list_;
   web::FakeWebState web_state_;
   InfoBarIOS* infobar_ = nullptr;
   OverlayRequest* request_ = nullptr;
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/BUILD.gn b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/BUILD.gn
index 65a387ce..fb17ad2c 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/BUILD.gn
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/BUILD.gn
@@ -46,7 +46,6 @@
     ":save_card",
     "//base/test:test_support",
     "//components/autofill/core/browser:test_support",
-    "//components/infobars/core:feature_flags",
     "//components/prefs",
     "//ios/chrome/browser/infobars",
     "//ios/chrome/browser/infobars/overlays",
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_banner_interaction_handler_unittest.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_banner_interaction_handler_unittest.mm
index 0544380..b145c435 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_banner_interaction_handler_unittest.mm
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_banner_interaction_handler_unittest.mm
@@ -6,14 +6,11 @@
 
 #include "base/guid.h"
 #include "base/strings/sys_string_conversions.h"
-#include "base/test/scoped_feature_list.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
-#include "components/infobars/core/infobar_feature.h"
 #include "ios/chrome/browser/infobars/infobar_ios.h"
 #import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common/infobar_banner_interaction_handler.h"
 #include "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_card_infobar_delegate_mobile.h"
-#import "ios/chrome/browser/ui/infobars/infobar_feature.h"
 #include "testing/platform_test.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -27,8 +24,6 @@
       : delegate_factory_(),
         prefs_(autofill::test::PrefServiceForTesting()),
         card_(base::GenerateGUID(), "https://www.example.com/") {
-    scoped_feature_list_.InitWithFeatures({kIOSInfobarUIReboot},
-                                          {kInfobarUIRebootOnlyiOS13});
     infobar_ = std::make_unique<InfoBarIOS>(
         InfobarType::kInfobarTypeSaveCard,
         MockAutofillSaveCardInfoBarDelegateMobileFactory::
@@ -42,7 +37,6 @@
   }
 
  protected:
-  base::test::ScopedFeatureList scoped_feature_list_;
   SaveCardInfobarBannerInteractionHandler handler_;
   MockAutofillSaveCardInfoBarDelegateMobileFactory delegate_factory_;
   std::unique_ptr<PrefService> prefs_;
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_banner_overlay_request_callback_installer_unittest.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_banner_overlay_request_callback_installer_unittest.mm
index 5352f68..0ad0e09 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_banner_overlay_request_callback_installer_unittest.mm
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_banner_overlay_request_callback_installer_unittest.mm
@@ -6,10 +6,8 @@
 
 #include "base/guid.h"
 #include "base/strings/sys_string_conversions.h"
-#include "base/test/scoped_feature_list.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
-#include "components/infobars/core/infobar_feature.h"
 #include "ios/chrome/browser/infobars/infobar_ios.h"
 #include "ios/chrome/browser/infobars/infobar_manager_impl.h"
 #include "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_card_infobar_delegate_mobile.h"
@@ -21,7 +19,6 @@
 #include "ios/chrome/browser/overlays/public/overlay_request.h"
 #include "ios/chrome/browser/overlays/public/overlay_request_queue.h"
 #include "ios/chrome/browser/overlays/public/overlay_response.h"
-#import "ios/chrome/browser/ui/infobars/infobar_feature.h"
 #import "ios/web/public/test/fakes/fake_navigation_manager.h"
 #import "ios/web/public/test/fakes/fake_web_state.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -42,8 +39,6 @@
         card_(base::GenerateGUID(), "https://www.example.com/"),
         installer_(&mock_handler_),
         delegate_factory_() {
-    scoped_feature_list_.InitWithFeatures({kIOSInfobarUIReboot},
-                                          {kInfobarUIRebootOnlyiOS13});
     // Create the infobar and add it to the WebState's manager.
     web_state_.SetNavigationManager(
         std::make_unique<web::FakeNavigationManager>());
@@ -76,7 +71,6 @@
   }
 
  protected:
-  base::test::ScopedFeatureList scoped_feature_list_;
   std::unique_ptr<PrefService> prefs_;
   autofill::CreditCard card_;
   web::FakeWebState web_state_;
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_modal_interaction_handler_unittest.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_modal_interaction_handler_unittest.mm
index fe4aac51..572112cb 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_modal_interaction_handler_unittest.mm
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_modal_interaction_handler_unittest.mm
@@ -8,14 +8,11 @@
 
 #include "base/guid.h"
 #include "base/strings/sys_string_conversions.h"
-#include "base/test/scoped_feature_list.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
-#include "components/infobars/core/infobar_feature.h"
 #include "ios/chrome/browser/infobars/infobar_ios.h"
 #import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common/infobar_banner_interaction_handler.h"
 #include "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_card_infobar_delegate_mobile.h"
-#import "ios/chrome/browser/ui/infobars/infobar_feature.h"
 #include "testing/platform_test.h"
 #include "url/gurl.h"
 
@@ -30,8 +27,6 @@
       : delegate_factory_(),
         prefs_(autofill::test::PrefServiceForTesting()),
         card_(base::GenerateGUID(), "https://www.example.com/") {
-    scoped_feature_list_.InitWithFeatures({kIOSInfobarUIReboot},
-                                          {kInfobarUIRebootOnlyiOS13});
     infobar_ = std::make_unique<InfoBarIOS>(
         InfobarType::kInfobarTypeSaveCard,
         MockAutofillSaveCardInfoBarDelegateMobileFactory::
@@ -45,7 +40,6 @@
   }
 
  protected:
-  base::test::ScopedFeatureList scoped_feature_list_;
   SaveCardInfobarModalInteractionHandler handler_;
   MockAutofillSaveCardInfoBarDelegateMobileFactory delegate_factory_;
   std::unique_ptr<PrefService> prefs_;
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_modal_overlay_request_callback_installer_unittest.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_modal_overlay_request_callback_installer_unittest.mm
index 812d830..954e31a 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_modal_overlay_request_callback_installer_unittest.mm
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_modal_overlay_request_callback_installer_unittest.mm
@@ -6,10 +6,8 @@
 
 #include "base/guid.h"
 #include "base/strings/sys_string_conversions.h"
-#include "base/test/scoped_feature_list.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
-#include "components/infobars/core/infobar_feature.h"
 #include "ios/chrome/browser/infobars/infobar_ios.h"
 #include "ios/chrome/browser/infobars/infobar_manager_impl.h"
 #include "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_card_infobar_delegate_mobile.h"
@@ -21,7 +19,6 @@
 #include "ios/chrome/browser/overlays/public/overlay_request.h"
 #include "ios/chrome/browser/overlays/public/overlay_request_queue.h"
 #include "ios/chrome/browser/overlays/public/overlay_response.h"
-#import "ios/chrome/browser/ui/infobars/infobar_feature.h"
 #import "ios/web/public/test/fakes/fake_navigation_manager.h"
 #import "ios/web/public/test/fakes/fake_web_state.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -42,8 +39,6 @@
         card_(base::GenerateGUID(), "https://www.example.com/"),
         installer_(&mock_handler_),
         delegate_factory_() {
-    scoped_feature_list_.InitWithFeatures({kIOSInfobarUIReboot},
-                                          {kInfobarUIRebootOnlyiOS13});
     // Create the infobar and add it to the WebState's manager.
     web_state_.SetNavigationManager(
         std::make_unique<web::FakeNavigationManager>());
@@ -76,7 +71,6 @@
   }
 
  protected:
-  base::test::ScopedFeatureList scoped_feature_list_;
   std::unique_ptr<PrefService> prefs_;
   autofill::CreditCard card_;
   web::FakeWebState web_state_;
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/translate/BUILD.gn b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/translate/BUILD.gn
index 06075ba9..6e85124 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/translate/BUILD.gn
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/translate/BUILD.gn
@@ -45,7 +45,6 @@
   deps = [
     ":translate",
     "//base/test:test_support",
-    "//components/infobars/core:feature_flags",
     "//components/translate/core/browser:test_support",
     "//ios/chrome/browser/infobars",
     "//ios/chrome/browser/infobars/overlays",
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/translate/translate_infobar_banner_interaction_handler_unittest.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/translate/translate_infobar_banner_interaction_handler_unittest.mm
index 5edfd00..096e44b7 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/translate/translate_infobar_banner_interaction_handler_unittest.mm
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/translate/translate_infobar_banner_interaction_handler_unittest.mm
@@ -4,12 +4,9 @@
 
 #import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/translate/translate_infobar_banner_interaction_handler.h"
 
-#include "base/test/scoped_feature_list.h"
-#include "components/infobars/core/infobar_feature.h"
 #include "components/translate/core/browser/mock_translate_infobar_delegate.h"
 #include "ios/chrome/browser/infobars/infobar_ios.h"
 #import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common/infobar_banner_interaction_handler.h"
-#import "ios/chrome/browser/ui/infobars/infobar_feature.h"
 #include "testing/platform_test.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -20,10 +17,7 @@
 class TranslateInfobarBannerInteractionHandlerTest : public PlatformTest {
  public:
   TranslateInfobarBannerInteractionHandlerTest()
-      : handler_(), delegate_factory_("fr", "en") {
-    scoped_feature_list_.InitWithFeatures(
-        {kIOSInfobarUIReboot, kTranslateInfobarMessagesUI}, {});
-  }
+      : handler_(), delegate_factory_("fr", "en") {}
 
   translate::testing::MockTranslateInfoBarDelegate& GetMockDelegate(
       InfoBarIOS* infobar) {
@@ -32,7 +26,6 @@
   }
 
  protected:
-  base::test::ScopedFeatureList scoped_feature_list_;
   TranslateInfobarBannerInteractionHandler handler_;
   translate::testing::MockTranslateInfoBarDelegateFactory delegate_factory_;
 };
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/translate/translate_infobar_modal_interaction_handler_unittest.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/translate/translate_infobar_modal_interaction_handler_unittest.mm
index cb0c09c..3335f5b 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/translate/translate_infobar_modal_interaction_handler_unittest.mm
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/translate/translate_infobar_modal_interaction_handler_unittest.mm
@@ -4,12 +4,9 @@
 
 #import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/translate/translate_infobar_modal_interaction_handler.h"
 
-#include "base/test/scoped_feature_list.h"
-#include "components/infobars/core/infobar_feature.h"
 #include "components/translate/core/browser/mock_translate_infobar_delegate.h"
 #include "ios/chrome/browser/infobars/infobar_ios.h"
 #import "ios/chrome/browser/infobars/test/fake_infobar_ios.h"
-#import "ios/chrome/browser/ui/infobars/infobar_feature.h"
 #include "testing/platform_test.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -20,10 +17,7 @@
 class TranslateInfobarModalInteractionHandlerTest : public PlatformTest {
  public:
   TranslateInfobarModalInteractionHandlerTest()
-      : handler_(), delegate_factory_("fr", "en") {
-    scoped_feature_list_.InitWithFeatures(
-        {kIOSInfobarUIReboot, kTranslateInfobarMessagesUI}, {});
-  }
+      : handler_(), delegate_factory_("fr", "en") {}
 
   translate::testing::MockTranslateInfoBarDelegate& mock_delegate(
       InfoBarIOS* infobar) {
@@ -32,7 +26,6 @@
   }
 
  protected:
-  base::test::ScopedFeatureList scoped_feature_list_;
   TranslateInfobarModalInteractionHandler handler_;
   translate::testing::MockTranslateInfoBarDelegateFactory delegate_factory_;
 };
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/translate/translate_infobar_modal_overlay_request_callback_installer_unittest.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/translate/translate_infobar_modal_overlay_request_callback_installer_unittest.mm
index 1b2e63c..a15eed7 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/translate/translate_infobar_modal_overlay_request_callback_installer_unittest.mm
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/translate/translate_infobar_modal_overlay_request_callback_installer_unittest.mm
@@ -4,8 +4,6 @@
 
 #import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/translate/translate_infobar_modal_overlay_request_callback_installer.h"
 
-#include "base/test/scoped_feature_list.h"
-#include "components/infobars/core/infobar_feature.h"
 #include "ios/chrome/browser/infobars/infobar_ios.h"
 #include "ios/chrome/browser/infobars/infobar_manager_impl.h"
 #import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_translate_infobar_interaction_handler.h"
@@ -19,7 +17,6 @@
 #include "ios/chrome/browser/overlays/public/overlay_request_queue.h"
 #include "ios/chrome/browser/overlays/public/overlay_response.h"
 #import "ios/chrome/browser/translate/fake_translate_infobar_delegate.h"
-#import "ios/chrome/browser/ui/infobars/infobar_feature.h"
 #import "ios/web/public/test/fakes/fake_navigation_manager.h"
 #import "ios/web/public/test/fakes/fake_web_state.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -35,8 +32,6 @@
  public:
   TranslateInfobarModalOverlayRequestCallbackInstallerTest()
       : installer_(&mock_handler_) {
-    scoped_feature_list_.InitWithFeatures({kIOSInfobarUIReboot},
-                                          {kInfobarUIRebootOnlyiOS13});
     // Create the infobar and add it to the WebState's manager.
     web_state_.SetNavigationManager(
         std::make_unique<web::FakeNavigationManager>());
@@ -68,7 +63,6 @@
   }
 
  protected:
-  base::test::ScopedFeatureList scoped_feature_list_;
   web::FakeWebState web_state_;
   InfoBarIOS* infobar_ = nullptr;
   OverlayRequest* request_ = nullptr;
diff --git a/ios/chrome/browser/infobars/overlays/infobar_overlay_request_factory_impl_unittest.mm b/ios/chrome/browser/infobars/overlays/infobar_overlay_request_factory_impl_unittest.mm
index 2766404..e0b7f46 100644
--- a/ios/chrome/browser/infobars/overlays/infobar_overlay_request_factory_impl_unittest.mm
+++ b/ios/chrome/browser/infobars/overlays/infobar_overlay_request_factory_impl_unittest.mm
@@ -6,11 +6,9 @@
 
 #include "base/feature_list.h"
 #include "base/guid.h"
-#include "base/test/scoped_feature_list.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/infobars/core/infobar.h"
-#include "components/infobars/core/infobar_feature.h"
 #include "components/password_manager/core/browser/mock_password_form_manager_for_ui.h"
 #include "components/password_manager/core/browser/password_form.h"
 #include "components/translate/core/browser/mock_translate_infobar_delegate.h"
@@ -27,7 +25,6 @@
 #import "ios/chrome/browser/overlays/public/infobar_modal/translate_infobar_modal_overlay_request_config.h"
 #import "ios/chrome/browser/passwords/ios_chrome_save_password_infobar_delegate.h"
 #import "ios/chrome/browser/passwords/test/mock_ios_chrome_save_passwords_infobar_delegate.h"
-#import "ios/chrome/browser/ui/infobars/infobar_feature.h"
 #import "ios/chrome/browser/ui/infobars/test/fake_infobar_ui_delegate.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -51,15 +48,11 @@
   InfobarOverlayRequestFactoryImplTest()
       : prefs_(autofill::test::PrefServiceForTesting()),
         card_(base::GenerateGUID(), "https://www.example.com/"),
-        translate_delegate_factory_("fr", "en") {
-    feature_list_.InitWithFeatures({kIOSInfobarUIReboot},
-                                   {kInfobarUIRebootOnlyiOS13});
-  }
+        translate_delegate_factory_("fr", "en") {}
 
   InfobarOverlayRequestFactory* factory() { return &factory_; }
 
  protected:
-  base::test::ScopedFeatureList feature_list_;
   InfobarOverlayRequestFactoryImpl factory_;
   std::unique_ptr<PrefService> prefs_;
   autofill::CreditCard card_;
diff --git a/ios/chrome/browser/infobars/overlays/translate_overlay_tab_helper_unittest.mm b/ios/chrome/browser/infobars/overlays/translate_overlay_tab_helper_unittest.mm
index 4ae4e33..76a2251f 100644
--- a/ios/chrome/browser/infobars/overlays/translate_overlay_tab_helper_unittest.mm
+++ b/ios/chrome/browser/infobars/overlays/translate_overlay_tab_helper_unittest.mm
@@ -4,8 +4,6 @@
 
 #import "ios/chrome/browser/infobars/overlays/translate_overlay_tab_helper.h"
 
-#include "base/test/scoped_feature_list.h"
-#include "components/infobars/core/infobar_feature.h"
 #include "ios/chrome/browser/infobars/infobar_ios.h"
 #include "ios/chrome/browser/infobars/infobar_manager_impl.h"
 #import "ios/chrome/browser/infobars/overlays/infobar_overlay_request_inserter.h"
@@ -18,7 +16,6 @@
 #include "ios/chrome/browser/overlays/test/overlay_test_macros.h"
 #import "ios/chrome/browser/passwords/test/mock_ios_chrome_save_passwords_infobar_delegate.h"
 #import "ios/chrome/browser/translate/fake_translate_infobar_delegate.h"
-#import "ios/chrome/browser/ui/infobars/infobar_feature.h"
 #import "ios/web/public/test/fakes/fake_navigation_manager.h"
 #import "ios/web/public/test/fakes/fake_web_state.h"
 #include "testing/platform_test.h"
@@ -50,8 +47,6 @@
     : public PlatformTest {
  public:
   TranslateInfobarOverlayTranslateOverlayTabHelperTest() {
-    scoped_feature_list_.InitWithFeatures({kIOSInfobarUIReboot},
-                                          {kInfobarUIRebootOnlyiOS13});
     web_state_.SetNavigationManager(
         std::make_unique<web::FakeNavigationManager>());
     InfoBarManagerImpl::CreateForWebState(&web_state_);
@@ -76,7 +71,6 @@
   }
 
  protected:
-  base::test::ScopedFeatureList scoped_feature_list_;
   web::FakeWebState web_state_;
   FakeTranslateInfoBarDelegateFactory delegate_factory_;
   FakeTranslateInfoBarDelegate* delegate_ = nullptr;
diff --git a/ios/chrome/browser/translate/chrome_ios_translate_client.mm b/ios/chrome/browser/translate/chrome_ios_translate_client.mm
index ec5ade5..98580d0 100644
--- a/ios/chrome/browser/translate/chrome_ios_translate_client.mm
+++ b/ios/chrome/browser/translate/chrome_ios_translate_client.mm
@@ -93,27 +93,17 @@
 
 std::unique_ptr<infobars::InfoBar> ChromeIOSTranslateClient::CreateInfoBar(
     std::unique_ptr<translate::TranslateInfoBarDelegate> delegate) const {
-  if (IsTranslateInfobarMessagesUIEnabled()) {
-    bool skip_banner = delegate->translate_step() ==
-                       translate::TranslateStep::TRANSLATE_STEP_TRANSLATING;
-    if (IsInfobarOverlayUIEnabled()) {
-      return std::make_unique<InfoBarIOS>(InfobarType::kInfobarTypeTranslate,
-                                          std::move(delegate), skip_banner);
-    } else {
-      TranslateInfobarCoordinator* coordinator =
-          [[TranslateInfobarCoordinator alloc]
-              initWithInfoBarDelegate:delegate.get()];
-      return std::make_unique<InfoBarIOS>(coordinator, std::move(delegate),
-                                          skip_banner);
-    }
+  bool skip_banner = delegate->translate_step() ==
+                     translate::TranslateStep::TRANSLATE_STEP_TRANSLATING;
+  if (IsInfobarOverlayUIEnabled()) {
+    return std::make_unique<InfoBarIOS>(InfobarType::kInfobarTypeTranslate,
+                                        std::move(delegate), skip_banner);
   } else {
-    TranslateInfoBarController* controller = [[TranslateInfoBarController alloc]
-        initWithInfoBarDelegate:delegate.get()];
-    controller.languageSelectionHandler = language_selection_handler_;
-    controller.translateOptionSelectionHandler =
-        translate_option_selection_handler_;
-    controller.translateNotificationHandler = translate_notification_handler_;
-    return std::make_unique<InfoBarIOS>(controller, std::move(delegate));
+    TranslateInfobarCoordinator* coordinator =
+        [[TranslateInfobarCoordinator alloc]
+            initWithInfoBarDelegate:delegate.get()];
+    return std::make_unique<InfoBarIOS>(coordinator, std::move(delegate),
+                                        skip_banner);
   }
 }
 
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 702c0d6..e9b53a3c 100644
--- a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm
+++ b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm
@@ -64,16 +64,9 @@
 // Creates and returns an infobar for saving credit cards.
 std::unique_ptr<infobars::InfoBar> CreateSaveCardInfoBarMobile(
     std::unique_ptr<AutofillSaveCardInfoBarDelegateMobile> delegate) {
-  if (IsSaveCardInfobarMessagesUIEnabled()) {
-    InfobarSaveCardCoordinator* coordinator =
-        [[InfobarSaveCardCoordinator alloc]
-            initWithInfoBarDelegate:delegate.get()];
-    return std::make_unique<InfoBarIOS>(coordinator, std::move(delegate));
-  } else {
-    SaveCardInfoBarController* controller = [[SaveCardInfoBarController alloc]
-        initWithInfoBarDelegate:delegate.get()];
-    return std::make_unique<InfoBarIOS>(controller, std::move(delegate));
-  }
+  InfobarSaveCardCoordinator* coordinator = [[InfobarSaveCardCoordinator alloc]
+      initWithInfoBarDelegate:delegate.get()];
+  return std::make_unique<InfoBarIOS>(coordinator, std::move(delegate));
 }
 
 CardUnmaskPromptView* CreateCardUnmaskPromptViewBridge(
diff --git a/ios/chrome/browser/ui/badges/badge_mediator_unittest.mm b/ios/chrome/browser/ui/badges/badge_mediator_unittest.mm
index bce8508..b89d41c0 100644
--- a/ios/chrome/browser/ui/badges/badge_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/badges/badge_mediator_unittest.mm
@@ -5,9 +5,7 @@
 #import "ios/chrome/browser/ui/badges/badge_mediator.h"
 
 #include "base/strings/utf_string_conversions.h"
-#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
-#include "components/infobars/core/infobar_feature.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "ios/chrome/browser/infobars/infobar_badge_model.h"
 #include "ios/chrome/browser/infobars/infobar_badge_tab_helper.h"
@@ -25,7 +23,6 @@
 #import "ios/chrome/browser/ui/badges/badge_item.h"
 #import "ios/chrome/browser/ui/badges/badge_type.h"
 #include "ios/chrome/browser/ui/badges/badge_type_util.h"
-#import "ios/chrome/browser/ui/infobars/infobar_feature.h"
 #import "ios/chrome/browser/ui/infobars/test_infobar_delegate.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"
@@ -87,8 +84,6 @@
       : badge_consumer_([[FakeBadgeConsumer alloc] init]),
         browser_state_(TestChromeBrowserState::Builder().Build()),
         web_state_list_(&web_state_list_delegate_) {
-    feature_list_.InitWithFeatures({kIOSInfobarUIReboot},
-                                   {kInfobarUIRebootOnlyiOS13});
     OverlayPresenter::FromBrowser(browser(), OverlayModality::kInfobarBanner)
         ->SetPresentationContext(&overlay_presentation_context_);
     badge_mediator_ = [[BadgeMediator alloc] initWithBrowser:browser()];
@@ -158,7 +153,6 @@
     return InfobarBadgeTabHelper::FromWebState(web_state());
   }
 
-  base::test::ScopedFeatureList feature_list_;
   base::test::TaskEnvironment environment_;
   FakeBadgeConsumer* badge_consumer_;
   std::unique_ptr<ChromeBrowserState> browser_state_;
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
index ad19182f..602635c 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
@@ -1051,9 +1051,8 @@
 - (void)userEnteredTabSwitcher {
   // TODO(crbug.com/977761): In preparation for dismissing BVC, make sure any
   // ongoing ViewController presentations are stopped.
-  if (IsInfobarUIRebootEnabled() &&
-      (self.infobarContainerCoordinator.infobarBannerState !=
-       InfobarBannerPresentationState::NotPresented)) {
+  if (self.infobarContainerCoordinator.infobarBannerState !=
+      InfobarBannerPresentationState::NotPresented) {
     [self.infobarContainerCoordinator dismissInfobarBannerAnimated:NO
                                                         completion:nil];
   }
@@ -1552,8 +1551,7 @@
 
   // TODO(crbug.com/976411):This should probably move to the BannerVC once/if
   // the dismiss event from BVC is observable.
-  if (IsInfobarUIRebootEnabled() &&
-      !base::FeatureList::IsEnabled(kInfobarOverlayUI)) {
+  if (!base::FeatureList::IsEnabled(kInfobarOverlayUI)) {
     [self.infobarContainerCoordinator baseViewWillDisappear];
     if (self.infobarContainerCoordinator.infobarBannerState !=
         InfobarBannerPresentationState::NotPresented) {
@@ -1827,8 +1825,7 @@
   // controller that allows interaction with the rest of the App while its being
   // presented. Dismiss it in case the user or system has triggered another
   // presentation.
-  if (IsInfobarUIRebootEnabled() &&
-      !base::FeatureList::IsEnabled(kInfobarOverlayUI) &&
+  if (!base::FeatureList::IsEnabled(kInfobarOverlayUI) &&
       (self.infobarContainerCoordinator.infobarBannerState !=
        InfobarBannerPresentationState::NotPresented)) {
     [self.infobarContainerCoordinator
@@ -3620,8 +3617,7 @@
   // TODO(crbug.com/965688): An Infobar message is currently the only presented
   // controller that allows interaction with the rest of the App while its being
   // presented. Dismiss it in case the user or system has triggered repost form.
-  if (IsInfobarUIRebootEnabled() &&
-      !base::FeatureList::IsEnabled(kInfobarOverlayUI) &&
+  if (!base::FeatureList::IsEnabled(kInfobarOverlayUI) &&
       (self.infobarContainerCoordinator.infobarBannerState !=
        InfobarBannerPresentationState::NotPresented)) {
     [self.infobarContainerCoordinator
diff --git a/ios/chrome/browser/ui/download/BUILD.gn b/ios/chrome/browser/ui/download/BUILD.gn
index 09c2b08..bf0cdac6 100644
--- a/ios/chrome/browser/ui/download/BUILD.gn
+++ b/ios/chrome/browser/ui/download/BUILD.gn
@@ -130,7 +130,6 @@
   ]
 
   deps = [
-    "//components/infobars/core:feature_flags",
     "//ios/chrome/app/strings:ios_strings_grit",
     "//ios/chrome/browser:chrome_url_constants",
     "//ios/chrome/browser/download:mime_types",
diff --git a/ios/chrome/browser/ui/download/pass_kit_egtest.mm b/ios/chrome/browser/ui/download/pass_kit_egtest.mm
index 6f2c8c4..036a967 100644
--- a/ios/chrome/browser/ui/download/pass_kit_egtest.mm
+++ b/ios/chrome/browser/ui/download/pass_kit_egtest.mm
@@ -8,11 +8,9 @@
 
 #include "base/bind.h"
 #import "base/test/ios/wait_util.h"
-#include "components/infobars/core/infobar_feature.h"
 #include "ios/chrome/browser/download/download_test_util.h"
 #include "ios/chrome/browser/download/pass_kit_mime_type.h"
 #import "ios/chrome/browser/ui/infobars/banners/infobar_banner_constants.h"
-#import "ios/chrome/browser/ui/infobars/infobar_feature.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
@@ -78,13 +76,6 @@
   GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
 }
 
-- (AppLaunchConfiguration)appConfigurationForTestCase {
-  AppLaunchConfiguration config;
-  config.features_enabled.push_back(kIOSInfobarUIReboot);
-  config.features_disabled.push_back(kInfobarUIRebootOnlyiOS13);
-  return config;
-}
-
 // Tests that Chrome presents PassKit error infobar if pkpass file cannot be
 // parsed.
 - (void)testPassKitParsingError {
diff --git a/ios/chrome/browser/ui/infobars/BUILD.gn b/ios/chrome/browser/ui/infobars/BUILD.gn
index 9cad2a9..79b1f58 100644
--- a/ios/chrome/browser/ui/infobars/BUILD.gn
+++ b/ios/chrome/browser/ui/infobars/BUILD.gn
@@ -55,7 +55,6 @@
   ]
   public_deps = [ "//base" ]
   deps = [
-    "//components/infobars/core:feature_flags",
     "//components/version_info",
     "//ios/chrome/common",
   ]
@@ -160,7 +159,6 @@
     ":eg_test_support+eg2",
     "//base",
     "//base/test:test_support",
-    "//components/infobars/core:feature_flags",
     "//components/strings:components_strings_grit",
     "//components/translate/core/browser:translate_pref_names",
     "//components/translate/core/common",
diff --git a/ios/chrome/browser/ui/infobars/infobar_container_coordinator.mm b/ios/chrome/browser/ui/infobars/infobar_container_coordinator.mm
index 7787f11..d482d32 100644
--- a/ios/chrome/browser/ui/infobars/infobar_container_coordinator.mm
+++ b/ios/chrome/browser/ui/infobars/infobar_container_coordinator.mm
@@ -156,8 +156,6 @@
 
 - (void)dismissInfobarBannerAnimated:(BOOL)animated
                           completion:(void (^)())completion {
-  DCHECK(IsInfobarUIRebootEnabled());
-
   for (InfobarCoordinator* infobarCoordinator in self.infobarCoordinators) {
     if (infobarCoordinator.infobarBannerState !=
         InfobarBannerPresentationState::NotPresented) {
@@ -193,7 +191,6 @@
 #pragma mark - Accessors
 
 - (InfobarBannerPresentationState)infobarBannerState {
-  DCHECK(IsInfobarUIRebootEnabled());
   for (InfobarCoordinator* infobarCoordinator in self.infobarCoordinators) {
     if (infobarCoordinator.infobarBannerState !=
         InfobarBannerPresentationState::NotPresented) {
@@ -210,7 +207,6 @@
 
 - (void)addInfoBarWithDelegate:(id<InfobarUIDelegate>)infoBarDelegate
                     skipBanner:(BOOL)skipBanner {
-  DCHECK(IsInfobarUIRebootEnabled());
   InfobarCoordinator* infobarCoordinator =
       static_cast<InfobarCoordinator*>(infoBarDelegate);
 
@@ -238,9 +234,7 @@
 }
 
 - (void)infobarManagerWillChange {
-  if (IsInfobarUIRebootEnabled()) {
-    [self dismissInfobarBannerAnimated:NO completion:nil];
-  }
+  [self dismissInfobarBannerAnimated:NO completion:nil];
   self.infobarCoordinators = [NSMutableArray array];
   self.infobarCoordinatorsToPresent = [NSMutableArray array];
 }
@@ -250,7 +244,6 @@
 }
 
 - (void)updateLayoutAnimated:(BOOL)animated {
-  DCHECK(IsInfobarUIRebootEnabled());
   // TODO(crbug.com/927064): NO-OP - This shouldn't be needed in the new UI
   // since we use autolayout for the contained Infobars.
 }
diff --git a/ios/chrome/browser/ui/infobars/infobar_container_coordinator_unittest.mm b/ios/chrome/browser/ui/infobars/infobar_container_coordinator_unittest.mm
index be729f9..ff03d3c 100644
--- a/ios/chrome/browser/ui/infobars/infobar_container_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/infobars/infobar_container_coordinator_unittest.mm
@@ -7,9 +7,7 @@
 #import <WebKit/WebKit.h>
 
 #import "base/test/ios/wait_util.h"
-#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
-#include "components/infobars/core/infobar_feature.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "ios/chrome/browser/infobars/infobar_badge_tab_helper.h"
 #include "ios/chrome/browser/infobars/infobar_ios.h"
@@ -21,7 +19,6 @@
 #import "ios/chrome/browser/ui/infobars/coordinators/infobar_confirm_coordinator.h"
 #import "ios/chrome/browser/ui/infobars/coordinators/infobar_password_coordinator.h"
 #import "ios/chrome/browser/ui/infobars/infobar_constants.h"
-#import "ios/chrome/browser/ui/infobars/infobar_feature.h"
 #import "ios/chrome/browser/ui/infobars/infobar_positioner.h"
 #import "ios/chrome/browser/ui/infobars/test/test_infobar_password_delegate.h"
 #import "ios/chrome/browser/ui/infobars/test_infobar_delegate.h"
@@ -75,9 +72,6 @@
         content_view_([[CRWWebViewContentView alloc]
             initWithWebView:web_view_
                  scrollView:web_view_.scrollView]) {
-    // Enable kIOSInfobarUIReboot flag.
-    feature_list_.InitWithFeatures({kIOSInfobarUIReboot},
-                                   {kInfobarUIRebootOnlyiOS13});
 
     // Setup WebstateList, Webstate and NavigationManager (Needed for
     // InfobarManager).
@@ -211,7 +205,6 @@
 
   base::test::TaskEnvironment environment_;
   InfobarContainerCoordinator* infobar_container_coordinator_;
-  base::test::ScopedFeatureList feature_list_;
   std::unique_ptr<Browser> browser_;
   web::FakeNavigationManager* navigation_manager_;
   ScopedKeyWindow scoped_key_window_;
diff --git a/ios/chrome/browser/ui/infobars/infobar_egtest.mm b/ios/chrome/browser/ui/infobars/infobar_egtest.mm
index 7f7a9591..b0d0ab6 100644
--- a/ios/chrome/browser/ui/infobars/infobar_egtest.mm
+++ b/ios/chrome/browser/ui/infobars/infobar_egtest.mm
@@ -6,11 +6,8 @@
 
 #include "base/strings/sys_string_conversions.h"
 #import "base/test/ios/wait_util.h"
-#include "base/test/scoped_feature_list.h"
-#include "components/infobars/core/infobar_feature.h"
 #import "ios/chrome/browser/ui/infobars/banners/infobar_banner_constants.h"
 #import "ios/chrome/browser/ui/infobars/infobar_constants.h"
-#import "ios/chrome/browser/ui/infobars/infobar_feature.h"
 #import "ios/chrome/browser/ui/infobars/infobar_manager_app_interface.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
@@ -64,16 +61,7 @@
 @interface InfobarTestCase : ChromeTestCase
 @end
 
-@implementation InfobarTestCase {
-  base::test::ScopedFeatureList _featureList;
-}
-
-- (AppLaunchConfiguration)appConfigurationForTestCase {
-  AppLaunchConfiguration config;
-  config.features_enabled.push_back(kIOSInfobarUIReboot);
-  config.features_disabled.push_back(kInfobarUIRebootOnlyiOS13);
-  return config;
-}
+@implementation InfobarTestCase
 
 - (void)setUp {
   [super setUp];
diff --git a/ios/chrome/browser/ui/infobars/infobar_feature.h b/ios/chrome/browser/ui/infobars/infobar_feature.h
index 21aca604..0001ab7 100644
--- a/ios/chrome/browser/ui/infobars/infobar_feature.h
+++ b/ios/chrome/browser/ui/infobars/infobar_feature.h
@@ -8,39 +8,11 @@
 #include "base/feature_list.h"
 
 // Feature to choose whether to use OverlayPresenter to show the new Messages
-// Infobar design.  In order for it to work, kIOSInfobarUIReboot needs to also
-// be enabled. Use IsInfobarOverlayUIEnabled() instead of this constant
+// Infobar design. Use IsInfobarOverlayUIEnabled() instead of this constant
 // directly.
 extern const base::Feature kInfobarOverlayUI;
 
-// Feature to choose whether Save Card Infobar uses the new Messages UI or the
-// legacy one. Also, in order for it to work kIOSInfobarUIReboot needs to be
-// enabled.
-// Use IsSaveCardInfobarMessagesUIEnabled() instead of this constant directly.
-extern const base::Feature kSaveCardInfobarMessagesUI;
-
-// Feature to choose whether Translate Infobar uses the new Messages UI or the
-// legacy one. In order for it to work, kIOSInfobarUIReboot needs to also be
-// enabled.
-// Use IsTranslateInfobarMessagesUIEnabled() instead of this constant directly.
-extern const base::Feature kTranslateInfobarMessagesUI;
-
-// Feature to choose whether to use the new Messages Infobar design on iOS13, or
-// the legacy one.
-// Use IsInfobarUIRebootEnabled() instead of this constant directly.
-extern const base::Feature kInfobarUIRebootOnlyiOS13;
-
-// Whether the Messages Infobar UI is enabled. Prefer to use this method instead
-// of kIOSInfobarUIReboot directly.
-bool IsInfobarUIRebootEnabled();
-
 // Whether the Messages Infobar UI is presented using OverlayPresenter.
 bool IsInfobarOverlayUIEnabled();
 
-// Whether the SaveCard Infobar Messages UI is enabled.
-bool IsSaveCardInfobarMessagesUIEnabled();
-
-// Whether the Translate Infobar Messages UI is enabled.
-bool IsTranslateInfobarMessagesUIEnabled();
-
 #endif  // IOS_CHROME_BROWSER_UI_INFOBARS_INFOBAR_FEATURE_H_
diff --git a/ios/chrome/browser/ui/infobars/infobar_feature.mm b/ios/chrome/browser/ui/infobars/infobar_feature.mm
index 72395882..8667bbc 100644
--- a/ios/chrome/browser/ui/infobars/infobar_feature.mm
+++ b/ios/chrome/browser/ui/infobars/infobar_feature.mm
@@ -4,10 +4,6 @@
 
 #import "ios/chrome/browser/ui/infobars/infobar_feature.h"
 
-#include "components/infobars/core/infobar_feature.h"
-#include "components/version_info/version_info.h"
-#include "ios/chrome/common/channel_info.h"
-
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
@@ -15,36 +11,6 @@
 const base::Feature kInfobarOverlayUI{"InfobarOverlayUI",
                                       base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Feature enabled by default since it will always be checked along
-// kIOSInfobarUIReboot, effectively working as a kill switch. Meaning that if
-// kIOSInfobarUIReboot is not enabled this feature won't work.
-const base::Feature kSaveCardInfobarMessagesUI{
-    "SaveCardInfobarMessagesUI", base::FEATURE_ENABLED_BY_DEFAULT};
-
-// Feature enabled by default since it will always be guarded with
-// |kIOSInfobarUIReboot|, meaning that if necessary,
-// |kTranslateInfobarMessagesUI| can be used as a kill switch.
-const base::Feature kTranslateInfobarMessagesUI{
-    "TranslateInfobarMessagesUI", base::FEATURE_ENABLED_BY_DEFAULT};
-
-const base::Feature kInfobarUIRebootOnlyiOS13{"InfobarUIRebootOnlyiOS13",
-                                              base::FEATURE_ENABLED_BY_DEFAULT};
-
-bool IsInfobarUIRebootEnabled() {
-  return base::FeatureList::IsEnabled(kIOSInfobarUIReboot);
-}
-
 bool IsInfobarOverlayUIEnabled() {
-  return IsInfobarUIRebootEnabled() &&
-         base::FeatureList::IsEnabled(kInfobarOverlayUI);
-}
-
-bool IsSaveCardInfobarMessagesUIEnabled() {
-  return base::FeatureList::IsEnabled(kSaveCardInfobarMessagesUI) &&
-         IsInfobarUIRebootEnabled();
-}
-
-bool IsTranslateInfobarMessagesUIEnabled() {
-  return base::FeatureList::IsEnabled(kTranslateInfobarMessagesUI) &&
-         IsInfobarUIRebootEnabled();
+  return base::FeatureList::IsEnabled(kInfobarOverlayUI);
 }
diff --git a/ios/chrome/browser/ui/infobars/translate_infobar_egtest.mm b/ios/chrome/browser/ui/infobars/translate_infobar_egtest.mm
index 8d3a027..c5c574c 100644
--- a/ios/chrome/browser/ui/infobars/translate_infobar_egtest.mm
+++ b/ios/chrome/browser/ui/infobars/translate_infobar_egtest.mm
@@ -10,7 +10,6 @@
 #include "base/strings/stringprintf.h"
 #include "base/strings/sys_string_conversions.h"
 #import "base/test/ios/wait_util.h"
-#include "components/infobars/core/infobar_feature.h"
 #include "components/translate/core/browser/translate_pref_names.h"
 #include "components/translate/core/common/translate_constants.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
@@ -18,7 +17,6 @@
 #import "ios/chrome/browser/ui/badges/badge_constants.h"
 #import "ios/chrome/browser/ui/infobars/banners/infobar_banner_constants.h"
 #import "ios/chrome/browser/ui/infobars/infobar_constants.h"
-#import "ios/chrome/browser/ui/infobars/infobar_feature.h"
 #import "ios/chrome/browser/ui/infobars/modals/infobar_modal_constants.h"
 #import "ios/chrome/browser/ui/infobars/modals/infobar_translate_modal_constants.h"
 #import "ios/chrome/browser/ui/popup_menu/popup_menu_constants.h"
@@ -264,13 +262,6 @@
   [super tearDown];
 }
 
-- (AppLaunchConfiguration)appConfigurationForTestCase {
-  AppLaunchConfiguration config;
-  config.features_enabled.push_back(kIOSInfobarUIReboot);
-  config.features_disabled.push_back(kInfobarUIRebootOnlyiOS13);
-  return config;
-}
-
 #pragma mark - Test Cases
 
 // Tests that different language signals are detected correctly.
diff --git a/ios/chrome/browser/ui/overlays/infobar_banner/confirm/confirm_infobar_banner_overlay_mediator_unittest.mm b/ios/chrome/browser/ui/overlays/infobar_banner/confirm/confirm_infobar_banner_overlay_mediator_unittest.mm
index fb2fc0f..88a4e8a 100644
--- a/ios/chrome/browser/ui/overlays/infobar_banner/confirm/confirm_infobar_banner_overlay_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/overlays/infobar_banner/confirm/confirm_infobar_banner_overlay_mediator_unittest.mm
@@ -7,15 +7,12 @@
 #include "base/feature_list.h"
 #include "base/strings/string16.h"
 #include "base/strings/sys_string_conversions.h"
-#include "base/test/scoped_feature_list.h"
 #include "components/infobars/core/infobar.h"
-#include "components/infobars/core/infobar_feature.h"
 #include "ios/chrome/browser/infobars/infobar_ios.h"
 #include "ios/chrome/browser/infobars/test/fake_infobar_delegate.h"
 #import "ios/chrome/browser/overlays/public/infobar_banner/confirm_infobar_banner_overlay_request_config.h"
 #include "ios/chrome/browser/overlays/public/overlay_request.h"
 #import "ios/chrome/browser/ui/infobars/banners/test/fake_infobar_banner_consumer.h"
-#import "ios/chrome/browser/ui/infobars/infobar_feature.h"
 #import "testing/gtest_mac.h"
 #include "testing/platform_test.h"
 
@@ -26,16 +23,7 @@
 using confirm_infobar_overlays::ConfirmBannerRequestConfig;
 
 // Test fixture for ConfirmInfobarBannerOverlayMediator.
-class ConfirmInfobarBannerOverlayMediatorTest : public PlatformTest {
- public:
-  ConfirmInfobarBannerOverlayMediatorTest() {
-    feature_list_.InitWithFeatures({kIOSInfobarUIReboot},
-                                   {kInfobarUIRebootOnlyiOS13});
-  }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-};
+using ConfirmInfobarBannerOverlayMediatorTest = PlatformTest;
 
 // Tests that a ConfirmInfobarBannerOverlayMediator correctly sets up its
 // consumer with a title and display message.
diff --git a/ios/chrome/browser/ui/overlays/infobar_banner/passwords/save_password_infobar_banner_overlay_mediator_unittest.mm b/ios/chrome/browser/ui/overlays/infobar_banner/passwords/save_password_infobar_banner_overlay_mediator_unittest.mm
index 990853c..af20152 100644
--- a/ios/chrome/browser/ui/overlays/infobar_banner/passwords/save_password_infobar_banner_overlay_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/overlays/infobar_banner/passwords/save_password_infobar_banner_overlay_mediator_unittest.mm
@@ -8,9 +8,7 @@
 #include "base/strings/string16.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/test/scoped_feature_list.h"
 #include "components/infobars/core/infobar.h"
-#include "components/infobars/core/infobar_feature.h"
 #include "ios/chrome/browser/infobars/infobar_ios.h"
 #import "ios/chrome/browser/overlays/public/infobar_banner/save_password_infobar_banner_overlay.h"
 #include "ios/chrome/browser/overlays/public/overlay_request.h"
@@ -18,7 +16,6 @@
 #import "ios/chrome/browser/passwords/ios_chrome_save_password_infobar_delegate.h"
 #import "ios/chrome/browser/passwords/test/mock_ios_chrome_save_passwords_infobar_delegate.h"
 #import "ios/chrome/browser/ui/infobars/banners/test/fake_infobar_banner_consumer.h"
-#import "ios/chrome/browser/ui/infobars/infobar_feature.h"
 #import "ios/chrome/browser/ui/infobars/test/fake_infobar_ui_delegate.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "testing/gtest_mac.h"
@@ -36,16 +33,7 @@
 }
 
 // Test fixture for SavePasswordInfobarBannerOverlayMediator.
-class SavePasswordInfobarBannerOverlayMediatorTest : public PlatformTest {
- public:
-  SavePasswordInfobarBannerOverlayMediatorTest() {
-    feature_list_.InitWithFeatures({kIOSInfobarUIReboot},
-                                   {kInfobarUIRebootOnlyiOS13});
-  }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-};
+using SavePasswordInfobarBannerOverlayMediatorTest = PlatformTest;
 
 // Tests that a SavePasswordInfobarBannerOverlayMediator correctly sets up its
 // consumer.
diff --git a/ios/chrome/browser/ui/overlays/infobar_banner/passwords/update_password_infobar_banner_overlay_mediator_unittest.mm b/ios/chrome/browser/ui/overlays/infobar_banner/passwords/update_password_infobar_banner_overlay_mediator_unittest.mm
index 9c292530..d4e130e 100644
--- a/ios/chrome/browser/ui/overlays/infobar_banner/passwords/update_password_infobar_banner_overlay_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/overlays/infobar_banner/passwords/update_password_infobar_banner_overlay_mediator_unittest.mm
@@ -8,9 +8,7 @@
 #include "base/strings/string16.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/test/scoped_feature_list.h"
 #include "components/infobars/core/infobar.h"
-#include "components/infobars/core/infobar_feature.h"
 #include "ios/chrome/browser/infobars/infobar_ios.h"
 #import "ios/chrome/browser/overlays/public/infobar_banner/update_password_infobar_banner_overlay.h"
 #include "ios/chrome/browser/overlays/public/overlay_request.h"
@@ -18,7 +16,6 @@
 #import "ios/chrome/browser/passwords/ios_chrome_save_password_infobar_delegate.h"
 #import "ios/chrome/browser/passwords/test/mock_ios_chrome_save_passwords_infobar_delegate.h"
 #import "ios/chrome/browser/ui/infobars/banners/test/fake_infobar_banner_consumer.h"
-#import "ios/chrome/browser/ui/infobars/infobar_feature.h"
 #import "ios/chrome/browser/ui/infobars/test/fake_infobar_ui_delegate.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "testing/gtest_mac.h"
@@ -36,16 +33,7 @@
 }
 
 // Test fixture for UpdatePasswordInfobarBannerOverlayMediator.
-class UpdatePasswordInfobarBannerOverlayMediatorTest : public PlatformTest {
- public:
-  UpdatePasswordInfobarBannerOverlayMediatorTest() {
-    feature_list_.InitWithFeatures({kIOSInfobarUIReboot},
-                                   {kInfobarUIRebootOnlyiOS13});
-  }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-};
+using UpdatePasswordInfobarBannerOverlayMediatorTest = PlatformTest;
 
 // Tests that a UpdatePasswordInfobarBannerOverlayMediator correctly sets up its
 // consumer.
diff --git a/ios/chrome/browser/ui/overlays/infobar_banner/save_card/save_card_infobar_banner_overlay_mediator_unittest.mm b/ios/chrome/browser/ui/overlays/infobar_banner/save_card/save_card_infobar_banner_overlay_mediator_unittest.mm
index fe6af0b..9be593d 100644
--- a/ios/chrome/browser/ui/overlays/infobar_banner/save_card/save_card_infobar_banner_overlay_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/overlays/infobar_banner/save_card/save_card_infobar_banner_overlay_mediator_unittest.mm
@@ -8,12 +8,10 @@
 #include "base/feature_list.h"
 #include "base/guid.h"
 #include "base/strings/sys_string_conversions.h"
-#include "base/test/scoped_feature_list.h"
 #include "components/autofill/core/browser/autofill_client.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.h"
-#include "components/infobars/core/infobar_feature.h"
 #include "components/prefs/pref_service.h"
 #include "ios/chrome/browser/infobars/infobar_ios.h"
 #include "ios/chrome/browser/overlays/public/infobar_banner/infobar_banner_overlay_responses.h"
@@ -21,7 +19,6 @@
 #import "ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_responses.h"
 #include "ios/chrome/browser/overlays/test/fake_overlay_request_callback_installer.h"
 #import "ios/chrome/browser/ui/infobars/banners/test/fake_infobar_banner_consumer.h"
-#import "ios/chrome/browser/ui/infobars/infobar_feature.h"
 #import "testing/gtest_mac.h"
 #include "testing/platform_test.h"
 
@@ -39,12 +36,9 @@
       : callback_installer_(&callback_receiver_,
                             {InfobarBannerShowModalResponse::ResponseSupport(),
                              SaveCardMainAction::ResponseSupport()}) {
-    feature_list_.InitWithFeatures({kIOSInfobarUIReboot},
-                                   {kInfobarUIRebootOnlyiOS13});
   }
 
  protected:
-  base::test::ScopedFeatureList feature_list_;
   MockOverlayRequestCallbackReceiver callback_receiver_;
   FakeOverlayRequestCallbackInstaller callback_installer_;
 };
diff --git a/ios/chrome/browser/ui/overlays/infobar_banner/translate/translate_infobar_banner_overlay_mediator_unittest.mm b/ios/chrome/browser/ui/overlays/infobar_banner/translate/translate_infobar_banner_overlay_mediator_unittest.mm
index b56b9bda..e8baa06b 100644
--- a/ios/chrome/browser/ui/overlays/infobar_banner/translate/translate_infobar_banner_overlay_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/overlays/infobar_banner/translate/translate_infobar_banner_overlay_mediator_unittest.mm
@@ -6,14 +6,11 @@
 
 #include "base/feature_list.h"
 #include "base/strings/sys_string_conversions.h"
-#include "base/test/scoped_feature_list.h"
 #include "components/infobars/core/infobar.h"
-#include "components/infobars/core/infobar_feature.h"
 #include "ios/chrome/browser/infobars/infobar_ios.h"
 #import "ios/chrome/browser/overlays/public/infobar_banner/translate_infobar_banner_overlay_request_config.h"
 #import "ios/chrome/browser/translate/fake_translate_infobar_delegate.h"
 #import "ios/chrome/browser/ui/infobars/banners/test/fake_infobar_banner_consumer.h"
-#import "ios/chrome/browser/ui/infobars/infobar_feature.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "testing/gtest_mac.h"
 #include "testing/platform_test.h"
@@ -29,13 +26,9 @@
 // Test fixture for TranslateInfobarBannerOverlayMediator.
 class TranslateInfobarBannerOverlayMediatorTest : public PlatformTest {
  public:
-  TranslateInfobarBannerOverlayMediatorTest() {
-    feature_list_.InitWithFeatures({kIOSInfobarUIReboot},
-                                   {kInfobarUIRebootOnlyiOS13});
-  }
+  TranslateInfobarBannerOverlayMediatorTest() {}
 
  protected:
-  base::test::ScopedFeatureList feature_list_;
   FakeTranslateInfoBarDelegateFactory delegate_factory_;
 };
 
diff --git a/ios/chrome/browser/ui/overlays/infobar_modal/passwords/BUILD.gn b/ios/chrome/browser/ui/overlays/infobar_modal/passwords/BUILD.gn
index f23ddeb7..00847bd 100644
--- a/ios/chrome/browser/ui/overlays/infobar_modal/passwords/BUILD.gn
+++ b/ios/chrome/browser/ui/overlays/infobar_modal/passwords/BUILD.gn
@@ -34,7 +34,6 @@
     ":passwords",
     "//base/test:test_support",
     "//components/infobars/core",
-    "//components/infobars/core:feature_flags",
     "//ios/chrome/browser/infobars",
     "//ios/chrome/browser/infobars/test",
     "//ios/chrome/browser/overlays",
diff --git a/ios/chrome/browser/ui/overlays/infobar_modal/passwords/password_infobar_modal_overlay_mediator_unittest.mm b/ios/chrome/browser/ui/overlays/infobar_modal/passwords/password_infobar_modal_overlay_mediator_unittest.mm
index 76f5126..6fc02488 100644
--- a/ios/chrome/browser/ui/overlays/infobar_modal/passwords/password_infobar_modal_overlay_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/overlays/infobar_modal/passwords/password_infobar_modal_overlay_mediator_unittest.mm
@@ -6,8 +6,6 @@
 
 #import "base/bind.h"
 #include "base/strings/sys_string_conversions.h"
-#include "base/test/scoped_feature_list.h"
-#include "components/infobars/core/infobar_feature.h"
 #include "ios/chrome/browser/infobars/infobar_ios.h"
 #import "ios/chrome/browser/overlays/public/infobar_modal/infobar_modal_overlay_responses.h"
 #import "ios/chrome/browser/overlays/public/infobar_modal/password_infobar_modal_overlay_request_config.h"
@@ -17,7 +15,6 @@
 #include "ios/chrome/browser/overlays/public/overlay_response.h"
 #include "ios/chrome/browser/overlays/test/fake_overlay_request_callback_installer.h"
 #import "ios/chrome/browser/passwords/test/mock_ios_chrome_save_passwords_infobar_delegate.h"
-#import "ios/chrome/browser/ui/infobars/infobar_feature.h"
 #import "ios/chrome/browser/ui/infobars/modals/test/fake_infobar_password_modal_consumer.h"
 #import "ios/chrome/browser/ui/infobars/test/fake_infobar_ui_delegate.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -58,8 +55,6 @@
                              PresentPasswordSettings::ResponseSupport()}),
         delegate_(
             OCMStrictProtocolMock(@protocol(OverlayRequestMediatorDelegate))) {
-    scoped_feature_list_.InitWithFeatures({kIOSInfobarUIReboot},
-                                          {kInfobarUIRebootOnlyiOS13});
     request_ = OverlayRequest::CreateWithConfig<
         PasswordInfobarModalOverlayRequestConfig>(&infobar_);
     callback_installer_.InstallCallbacks(request_.get());
@@ -79,7 +74,6 @@
   }
 
  protected:
-  base::test::ScopedFeatureList scoped_feature_list_;
   GURL url_;
   InfoBarIOS infobar_;
   MockOverlayRequestCallbackReceiver callback_receiver_;
diff --git a/ios/chrome/browser/ui/overlays/infobar_modal/save_card/BUILD.gn b/ios/chrome/browser/ui/overlays/infobar_modal/save_card/BUILD.gn
index 802a94a1..c903c27a 100644
--- a/ios/chrome/browser/ui/overlays/infobar_modal/save_card/BUILD.gn
+++ b/ios/chrome/browser/ui/overlays/infobar_modal/save_card/BUILD.gn
@@ -41,7 +41,6 @@
     "//components/autofill/core/browser",
     "//components/autofill/core/browser:test_support",
     "//components/infobars/core",
-    "//components/infobars/core:feature_flags",
     "//components/prefs",
     "//ios/chrome/browser/infobars",
     "//ios/chrome/browser/infobars/test",
diff --git a/ios/chrome/browser/ui/overlays/infobar_modal/save_card/save_card_infobar_modal_overlay_mediator_unittest.mm b/ios/chrome/browser/ui/overlays/infobar_modal/save_card/save_card_infobar_modal_overlay_mediator_unittest.mm
index 44ad691..878394a 100644
--- a/ios/chrome/browser/ui/overlays/infobar_modal/save_card/save_card_infobar_modal_overlay_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/overlays/infobar_modal/save_card/save_card_infobar_modal_overlay_mediator_unittest.mm
@@ -8,20 +8,17 @@
 #include "base/feature_list.h"
 #include "base/guid.h"
 #include "base/strings/sys_string_conversions.h"
-#include "base/test/scoped_feature_list.h"
 #include "components/autofill/core/browser/autofill_client.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.h"
 #include "components/autofill/core/browser/payments/test_legal_message_line.h"
-#include "components/infobars/core/infobar_feature.h"
 #include "components/prefs/pref_service.h"
 #include "ios/chrome/browser/infobars/infobar_ios.h"
 #include "ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_request_config.h"
 #import "ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_responses.h"
 #include "ios/chrome/browser/overlays/test/fake_overlay_request_callback_installer.h"
 #include "ios/chrome/browser/ui/autofill/save_card_message_with_links.h"
-#include "ios/chrome/browser/ui/infobars/infobar_feature.h"
 #include "ios/chrome/browser/ui/infobars/modals/infobar_save_card_modal_consumer.h"
 #import "ios/chrome/browser/ui/overlays/infobar_modal/save_card/save_card_infobar_modal_overlay_mediator_delegate.h"
 #include "testing/gtest_mac.h"
@@ -82,8 +79,6 @@
                             {SaveCardMainAction::ResponseSupport()}),
         mediator_delegate_(
             OCMStrictProtocolMock(@protocol(OverlayRequestMediatorDelegate))) {
-    scoped_feature_list_.InitWithFeatures({kIOSInfobarUIReboot},
-                                          {kInfobarUIRebootOnlyiOS13});
 
     autofill::LegalMessageLines legal_message_lines =
         autofill::LegalMessageLines(
@@ -121,7 +116,6 @@
   }
 
  protected:
-  base::test::ScopedFeatureList scoped_feature_list_;
   std::unique_ptr<PrefService> prefs_;
   autofill::AutofillSaveCardInfoBarDelegateMobile* delegate_;
   std::unique_ptr<InfoBarIOS> infobar_;
diff --git a/ios/chrome/browser/ui/overlays/infobar_modal/translate/BUILD.gn b/ios/chrome/browser/ui/overlays/infobar_modal/translate/BUILD.gn
index 2a473b7..ab1a2f8 100644
--- a/ios/chrome/browser/ui/overlays/infobar_modal/translate/BUILD.gn
+++ b/ios/chrome/browser/ui/overlays/infobar_modal/translate/BUILD.gn
@@ -45,7 +45,6 @@
     ":translate",
     "//base/test:test_support",
     "//components/infobars/core",
-    "//components/infobars/core:feature_flags",
     "//components/translate/core/browser:test_support",
     "//ios/chrome/browser/infobars",
     "//ios/chrome/browser/infobars/test",
diff --git a/ios/chrome/browser/ui/overlays/infobar_modal/translate/translate_infobar_modal_overlay_mediator_unittest.mm b/ios/chrome/browser/ui/overlays/infobar_modal/translate/translate_infobar_modal_overlay_mediator_unittest.mm
index 73e8df4d..81b0305 100644
--- a/ios/chrome/browser/ui/overlays/infobar_modal/translate/translate_infobar_modal_overlay_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/overlays/infobar_modal/translate/translate_infobar_modal_overlay_mediator_unittest.mm
@@ -6,8 +6,6 @@
 
 #include "base/ios/ios_util.h"
 #include "base/strings/sys_string_conversions.h"
-#include "base/test/scoped_feature_list.h"
-#include "components/infobars/core/infobar_feature.h"
 #include "ios/chrome/browser/infobars/infobar_ios.h"
 #import "ios/chrome/browser/overlays/public/infobar_modal/infobar_modal_overlay_responses.h"
 #import "ios/chrome/browser/overlays/public/infobar_modal/translate_infobar_modal_overlay_request_config.h"
@@ -18,7 +16,6 @@
 #include "ios/chrome/browser/overlays/test/fake_overlay_request_callback_installer.h"
 #import "ios/chrome/browser/translate/fake_translate_infobar_delegate.h"
 #import "ios/chrome/browser/ui/infobars/coordinators/infobar_translate_modal_consumer.h"
-#import "ios/chrome/browser/ui/infobars/infobar_feature.h"
 #import "ios/chrome/browser/ui/infobars/modals/test/fake_infobar_translate_modal_consumer.h"
 #import "ios/chrome/browser/ui/infobars/test/fake_infobar_ui_delegate.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -55,8 +52,6 @@
              ToggleAlwaysTranslate::ResponseSupport()}),
         delegate_(
             OCMStrictProtocolMock(@protocol(OverlayRequestMediatorDelegate))) {
-    scoped_feature_list_.InitWithFeatures({kIOSInfobarUIReboot},
-                                          {kInfobarUIRebootOnlyiOS13});
     request_ = OverlayRequest::CreateWithConfig<TranslateModalRequestConfig>(
         &infobar_);
     callback_installer_.InstallCallbacks(request_.get());
@@ -75,7 +70,6 @@
   }
 
  protected:
-  base::test::ScopedFeatureList scoped_feature_list_;
   FakeTranslateInfoBarDelegateFactory delegate_factory_;
   InfoBarIOS infobar_;
   MockOverlayRequestCallbackReceiver callback_receiver_;
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
index 14b63e8..1c2d645 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-9eb6cc47783fd80e6ef8530be9321a3317a4587b
\ No newline at end of file
+49b7dca295ab05af2f66fce69700b6d9b9b73ca1
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
index 70443fc..f7e514c 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-ad0e32decf9d058b12a3af5707b732cc3853f157
\ No newline at end of file
+736db3de0abf25e6b349f934f3ffff21a4cd6732
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
index 424ebb34..d9558cc 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-be442284bdde0159b3f94ebf0493498b2a41028e
\ No newline at end of file
+6e2108646c01c5e3c2c6361b40a6f1b95daee803
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1
index 83c214a..516a974 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-27b0240749f9508be43cadb9be3ab69a4fc254b0
\ No newline at end of file
+b006c088b772e5b996e4ec9b544da9099e765b1a
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
index b49032ac..7a02729 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-07ecb67d5fe0affbed30c3436235fe752b508199
\ No newline at end of file
+9f42c0f12a0bce1bcbbe6ab73268d46b2ad01502
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
index 539630b..155f5b1 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-57cfe183347cc5e5ffea5563042aa0a7c75aade9
\ No newline at end of file
+1b4a573349cb14da7aeb654f6fef2066c34b15f6
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
index 71afe2b..d25a901 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-bdbec3f32e10f4797b6afba1684314b36c894d62
\ No newline at end of file
+5851295494936ba66e7ef606570c939c67cf9fa0
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
index 20b6dd2..d76167bd 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-0c614477fe14cb0c004ff64fd725659f6676a403
\ No newline at end of file
+300b350bec453f8cccd360aa6362c1d639adac4a
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
index 253c1e0..bab582e 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-0d21dbf91f0eb0701d8de2ae16191a3cf928aa18
\ No newline at end of file
+be599227596fe2c6008126c056187a107a21f061
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
index f0c3f648..5f86f6b 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-b3ef33bb678c8428a0175bbf0064c2fcb02e6c57
\ No newline at end of file
+cb512eeb9897c3550282f0de0ac68afda531865f
\ No newline at end of file
diff --git a/ios/web/js_messaging/web_frames_manager_impl.mm b/ios/web/js_messaging/web_frames_manager_impl.mm
index 5f1b9e63..3ec157ff 100644
--- a/ios/web/js_messaging/web_frames_manager_impl.mm
+++ b/ios/web/js_messaging/web_frames_manager_impl.mm
@@ -82,8 +82,9 @@
                         webView:new_web_view];
     [message_router
         setScriptMessageHandler:^(WKScriptMessage* message) {
-          DCHECK(!delegate_.GetWebState()->IsBeingDestroyed());
           if (weak_ptr) {
+            DCHECK(weak_ptr->delegate_.GetWebState() &&
+                   !weak_ptr->delegate_.GetWebState()->IsBeingDestroyed());
             weak_ptr->OnFrameBecameUnavailable(message);
           }
         }
diff --git a/ios/web/webui/shared_resources_data_source_ios.mm b/ios/web/webui/shared_resources_data_source_ios.mm
index 3be90d9..9b1d1663 100644
--- a/ios/web/webui/shared_resources_data_source_ios.mm
+++ b/ios/web/webui/shared_resources_data_source_ios.mm
@@ -33,11 +33,11 @@
 // nullptr if not found.
 const GritResourceMap* PathToResource(const std::string& path) {
   for (size_t i = 0; i < kWebuiResourcesSize; ++i) {
-    if (path == kWebuiResources[i].name)
+    if (path == kWebuiResources[i].path)
       return &kWebuiResources[i];
   }
   for (size_t i = 0; i < kWebuiGeneratedResourcesSize; ++i) {
-    if (path == kWebuiGeneratedResources[i].name)
+    if (path == kWebuiGeneratedResources[i].path)
       return &kWebuiGeneratedResources[i];
   }
   return nullptr;
@@ -62,7 +62,7 @@
 
   WebClient* web_client = GetWebClient();
 
-  int idr = resource ? resource->value : -1;
+  int idr = resource ? resource->id : -1;
   if (idr == IDR_WEBUI_CSS_TEXT_DEFAULTS_CSS) {
     std::string css = webui::GetWebUiCssTextDefaults();
     bytes = base::RefCountedString::TakeString(&css);
diff --git a/ios/web_view/internal/cwv_web_view_configuration.mm b/ios/web_view/internal/cwv_web_view_configuration.mm
index c6c8eb08..9ef4306e 100644
--- a/ios/web_view/internal/cwv_web_view_configuration.mm
+++ b/ios/web_view/internal/cwv_web_view_configuration.mm
@@ -27,6 +27,12 @@
 #error "This file requires ARC support."
 #endif
 
+namespace {
+CWVWebViewConfiguration* gDefaultConfiguration = nil;
+CWVWebViewConfiguration* gIncognitoConfiguration = nil;
+NSHashTable<CWVWebViewConfiguration*>* gNonPersistentConfigurations = nil;
+}  // namespace
+
 @interface CWVWebViewConfiguration () {
   // The BrowserState for this configuration.
   std::unique_ptr<ios_web_view::WebViewBrowserState> _browserState;
@@ -44,16 +50,22 @@
 @synthesize syncController = _syncController;
 @synthesize userContentController = _userContentController;
 
-namespace {
-CWVWebViewConfiguration* gDefaultConfiguration = nil;
-CWVWebViewConfiguration* gIncognitoConfiguration = nil;
-}  // namespace
++ (void)initialize {
+  if (self != [CWVWebViewConfiguration class]) {
+    return;
+  }
+
+  ios_web_view::InitializeGlobalState();
+}
 
 + (void)shutDown {
-  // Incognito should be shut down first because it holds onto members of the
-  // non-incognito browser state. This ensures that the non-incognito browser
-  // state will not leave any dangling references.
-  [gIncognitoConfiguration shutDown];
+  // Non-persistent configurations should be shut down first because its browser
+  // state holds on to the default configuration's browser state. This ensures
+  // the non-persistent configurations will not reference a dangling pointer.
+  for (CWVWebViewConfiguration* nonPersistentConfiguration in
+           gNonPersistentConfigurations) {
+    [nonPersistentConfiguration shutDown];
+  }
   [gDefaultConfiguration shutDown];
 }
 
@@ -71,21 +83,28 @@
 + (instancetype)incognitoConfiguration {
   static dispatch_once_t onceToken;
   dispatch_once(&onceToken, ^{
-    CWVWebViewConfiguration* defaultConfiguration = [self defaultConfiguration];
-    auto browserState = std::make_unique<ios_web_view::WebViewBrowserState>(
-        /* off_the_record = */ true, defaultConfiguration.browserState);
-    gIncognitoConfiguration = [[CWVWebViewConfiguration alloc]
-        initWithBrowserState:std::move(browserState)];
+    gIncognitoConfiguration = [self nonPersistentConfiguration];
   });
   return gIncognitoConfiguration;
 }
 
-+ (void)initialize {
-  if (self != [CWVWebViewConfiguration class]) {
-    return;
-  }
++ (CWVWebViewConfiguration*)nonPersistentConfiguration {
+  CWVWebViewConfiguration* defaultConfiguration = [self defaultConfiguration];
+  auto browserState = std::make_unique<ios_web_view::WebViewBrowserState>(
+      /* off_the_record = */ true, defaultConfiguration.browserState);
+  CWVWebViewConfiguration* nonPersistentConfiguration =
+      [[CWVWebViewConfiguration alloc]
+          initWithBrowserState:std::move(browserState)];
 
-  ios_web_view::InitializeGlobalState();
+  // Save a weak pointer to nonpersistent configurations so they may be shut
+  // down later.
+  static dispatch_once_t onceToken;
+  dispatch_once(&onceToken, ^{
+    gNonPersistentConfigurations = [NSHashTable weakObjectsHashTable];
+  });
+  [gNonPersistentConfigurations addObject:nonPersistentConfiguration];
+
+  return nonPersistentConfiguration;
 }
 
 - (instancetype)initWithBrowserState:
diff --git a/ios/web_view/internal/cwv_web_view_configuration_unittest.mm b/ios/web_view/internal/cwv_web_view_configuration_unittest.mm
index 0334b99..49c9c20f6 100644
--- a/ios/web_view/internal/cwv_web_view_configuration_unittest.mm
+++ b/ios/web_view/internal/cwv_web_view_configuration_unittest.mm
@@ -51,4 +51,22 @@
   EXPECT_FALSE(configuration.browserState);
 }
 
+// Test CWVWebViewConfiguration shuts down all outstanding configurations.
+TEST_F(CWVWebViewConfigurationTest, ShutDownAllConfigurations) {
+  CWVWebViewConfiguration* defaultConfiguration =
+      [CWVWebViewConfiguration defaultConfiguration];
+  CWVWebViewConfiguration* nonPersistentConfigurationA =
+      [CWVWebViewConfiguration nonPersistentConfiguration];
+  CWVWebViewConfiguration* nonPersistentConfigurationB =
+      [CWVWebViewConfiguration nonPersistentConfiguration];
+
+  // Non persistent configurations must not be singletons.
+  ASSERT_NE(nonPersistentConfigurationA, nonPersistentConfigurationB);
+
+  [CWVWebViewConfiguration shutDown];
+  EXPECT_FALSE(defaultConfiguration.browserState);
+  EXPECT_FALSE(nonPersistentConfigurationA.browserState);
+  EXPECT_FALSE(nonPersistentConfigurationB.browserState);
+}
+
 }  // namespace ios_web_view
diff --git a/ios/web_view/internal/web_view_web_main_parts.mm b/ios/web_view/internal/web_view_web_main_parts.mm
index 438e185..9069b31 100644
--- a/ios/web_view/internal/web_view_web_main_parts.mm
+++ b/ios/web_view/internal/web_view_web_main_parts.mm
@@ -11,7 +11,6 @@
 #include "base/strings/string_util.h"
 #include "components/autofill/core/common/autofill_features.h"
 #include "components/autofill/core/common/autofill_payments_features.h"
-#include "components/infobars/core/infobar_feature.h"
 #include "components/password_manager/core/common/password_manager_features.h"
 #include "ios/web/public/webui/web_ui_ios_controller_factory.h"
 #include "ios/web_view/internal/app/application_context.h"
@@ -63,7 +62,6 @@
       {
           // ios/web_view does not support editing card info in the save dialog.
           autofill::features::kAutofillSaveCardInfobarEditSupport.name,
-          kIOSInfobarUIReboot.name,
       },
       ",");
   feature_list->InitializeFromCommandLine(
diff --git a/ios/web_view/public/cwv_web_view_configuration.h b/ios/web_view/public/cwv_web_view_configuration.h
index 8bd4d391..d9b4497 100644
--- a/ios/web_view/public/cwv_web_view_configuration.h
+++ b/ios/web_view/public/cwv_web_view_configuration.h
@@ -22,11 +22,18 @@
 @interface CWVWebViewConfiguration : NSObject
 
 // Configuration with persistent data store which stores all data on disk.
+// Every call returns the same instance.
 + (instancetype)defaultConfiguration;
 
 // Configuration with ephemeral data store that never stores data on disk.
+// Every call returns the same instance.
+// Deprecated. Use |nonPersistentConfiguration| instead.
 + (instancetype)incognitoConfiguration;
 
+// Configuration with non-persistent data store that never stores data on disk.
+// Every call returns a new instance.
++ (instancetype)nonPersistentConfiguration;
+
 - (instancetype)init NS_UNAVAILABLE;
 
 // The preferences object associated with this web view configuration.
@@ -37,16 +44,16 @@
 @property(nonatomic, readonly) CWVUserContentController* userContentController;
 
 // This web view configuration's sync controller.
-// nil if CWVWebViewConfiguration is created with +incognitoConfiguration.
+// nil if -[CWVWebViewConfiguration isPersistent] is NO.
 @property(nonatomic, readonly, nullable) CWVSyncController* syncController;
 
 // This web view configuration's autofill data manager.
-// nil if CWVWebViewConfiguration is created with +incognitoConfiguration.
+// nil if -[CWVWebViewConfiguration isPersistent] is NO.
 @property(nonatomic, readonly, nullable)
     CWVAutofillDataManager* autofillDataManager;
 
-// YES if it is a configuration with persistent data store which stores all data
-// on disk.
+// YES if this is a configuration with a persistent data store which stores all
+// data on disk, for example cookies.
 @property(nonatomic, readonly, getter=isPersistent) BOOL persistent;
 
 @end
diff --git a/ios/web_view/shell/shell_view_controller.m b/ios/web_view/shell/shell_view_controller.m
index 14031c5..b20a3959 100644
--- a/ios/web_view/shell/shell_view_controller.m
+++ b/ios/web_view/shell/shell_view_controller.m
@@ -666,7 +666,7 @@
   BOOL wasPersistent = _webView.configuration.persistent;
   [self removeWebView];
   CWVWebViewConfiguration* newConfiguration =
-      wasPersistent ? [CWVWebViewConfiguration incognitoConfiguration]
+      wasPersistent ? [CWVWebViewConfiguration nonPersistentConfiguration]
                     : [CWVWebViewConfiguration defaultConfiguration];
   self.webView = [self createWebViewWithConfiguration:newConfiguration];
 }
diff --git a/media/audio/audio_encoders_unittest.cc b/media/audio/audio_encoders_unittest.cc
index 1997ae2..7e12431 100644
--- a/media/audio/audio_encoders_unittest.cc
+++ b/media/audio/audio_encoders_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include <cstring>
+#include <limits>
 #include <memory>
 #include <utility>
 #include <vector>
@@ -210,6 +211,34 @@
   }
 }
 
+TEST_P(AudioEncodersTest, OpusExtraData) {
+  auto output_cb =
+      base::BindLambdaForTesting([&](EncodedAudioBuffer output) {});
+
+  SetEncoder(std::make_unique<AudioOpusEncoder>(
+      input_params(), std::move(output_cb),
+      base::BindRepeating(&AudioEncodersTest::OnErrorCallback,
+                          base::Unretained(this)),
+      /*opus_bitrate=*/0));
+  std::vector<uint8_t> extra = encoder_->GetExtraData();
+  EXPECT_EQ(extra[0], 'O');
+  EXPECT_EQ(extra[1], 'p');
+  EXPECT_EQ(extra[2], 'u');
+  EXPECT_EQ(extra[3], 's');
+
+  uint16_t* sample_rate_ptr = reinterpret_cast<uint16_t*>(extra.data() + 12);
+  if (input_params().sample_rate() < std::numeric_limits<uint16_t>::max())
+    EXPECT_EQ(*sample_rate_ptr, input_params().sample_rate());
+  else
+    EXPECT_EQ(*sample_rate_ptr, 48000);
+
+  uint8_t* channels_ptr = reinterpret_cast<uint8_t*>(extra.data() + 9);
+  EXPECT_EQ(*channels_ptr, input_params().channels());
+
+  uint16_t* skip_ptr = reinterpret_cast<uint16_t*>(extra.data() + 10);
+  EXPECT_GT(*skip_ptr, 0);
+}
+
 // Check how Opus encoder reacts to breaks in continuity of incoming sound.
 // Capture times are expected to be exactly buffer durations apart,
 // but the encoder should be ready to handle situations when it's not the case.
diff --git a/media/audio/audio_opus_encoder.cc b/media/audio/audio_opus_encoder.cc
index 52f89fd..6a4b5128 100644
--- a/media/audio/audio_opus_encoder.cc
+++ b/media/audio/audio_opus_encoder.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/logging.h"
+#include "base/numerics/checked_math.h"
 #include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "media/base/audio_timestamp_helper.h"
@@ -206,6 +207,8 @@
   converter_.PrimeWithSilence();
   fifo_.Reset(converter_.GetMaxInputFramesRequested(
       converted_params_.frames_per_buffer()));
+
+  PrepareExtraData();
 }
 
 AudioOpusEncoder::~AudioOpusEncoder() = default;
@@ -241,6 +244,54 @@
   fifo_.Push(audio_bus);
 }
 
+const std::vector<uint8_t>& AudioOpusEncoder::GetExtraData() {
+  return extra_data_;
+}
+
+void AudioOpusEncoder::PrepareExtraData() {
+  // RFC #7845  Ogg Encapsulation for the Opus Audio Codec
+  // https://tools.ietf.org/html/rfc7845
+  static const uint8_t kExtraDataTemplate[19] = {
+      'O', 'p', 'u', 's', 'H', 'e', 'a', 'd',
+      1,                 // offset 8, version, always 1
+      0,                 // offset 9, channel count
+      0,   0,            // offset 10, pre-skip
+      0,   0,   0,   0,  // offset 12, original input sample rate in Hz
+      0,   0,   0};
+
+  extra_data_.assign(kExtraDataTemplate,
+                     kExtraDataTemplate + sizeof(kExtraDataTemplate));
+
+  // Save number of channels
+  base::CheckedNumeric<uint8_t> channels(converted_params_.channels());
+  if (channels.IsValid())
+    extra_data_.data()[9] = channels.ValueOrDie();
+
+  // Number of samples to skip from the start of the decoder's output.
+  // Real data begins this many samples late. These samples need to be skipped
+  // only at the very beginning of the audio stream, NOT at beginning of each
+  // decoded output.
+  if (opus_encoder_) {
+    int32_t samples_to_skip = 0;
+
+    opus_encoder_ctl(opus_encoder_.get(), OPUS_GET_LOOKAHEAD(&samples_to_skip));
+    base::CheckedNumeric<uint16_t> samples_to_skip_safe = samples_to_skip;
+    if (samples_to_skip_safe.IsValid())
+      *reinterpret_cast<uint16_t*>(extra_data_.data() + 10) =
+          samples_to_skip_safe.ValueOrDie();
+  }
+
+  // Save original sample rate
+  base::CheckedNumeric<uint16_t> sample_rate =
+      audio_input_params().sample_rate();
+  uint16_t* sample_rate_ptr =
+      reinterpret_cast<uint16_t*>(extra_data_.data() + 12);
+  if (sample_rate.IsValid())
+    *sample_rate_ptr = sample_rate.ValueOrDie();
+  else
+    *sample_rate_ptr = uint16_t{kOpusPreferredSamplingRate};
+}
+
 void AudioOpusEncoder::FlushImpl() {
   // Initializing the opus encoder may have failed.
   if (!opus_encoder_)
diff --git a/media/audio/audio_opus_encoder.h b/media/audio/audio_opus_encoder.h
index 1c48048..2f28da51 100644
--- a/media/audio/audio_opus_encoder.h
+++ b/media/audio/audio_opus_encoder.h
@@ -33,6 +33,8 @@
   AudioOpusEncoder& operator=(const AudioOpusEncoder&) = delete;
   ~AudioOpusEncoder() override;
 
+  const std::vector<uint8_t>& GetExtraData() override;
+
  protected:
   // AudioEncoder:
   void EncodeAudioImpl(const AudioBus& audio_bus,
@@ -44,6 +46,8 @@
   // buffered.
   void OnFifoOutput(const AudioBus& output_bus, int frame_delay);
 
+  void PrepareExtraData();
+
   // Target bitrate for Opus. If 0, Opus-provided automatic bitrate is used.
   // Note: As of 2013-10-31, the encoder in "auto bitrate" mode would use a
   // variable bitrate up to 102 kbps for 2-channel, 48 kHz audio and a 10 ms
@@ -80,6 +84,8 @@
 
   // Timestamp that should be reported by the next call of encode_callback()
   base::TimeTicks next_timestamp_;
+
+  std::vector<uint8_t> extra_data_;
 };
 
 }  // namespace media
diff --git a/media/base/audio_encoder.cc b/media/base/audio_encoder.cc
index a07682e..ac475490 100644
--- a/media/base/audio_encoder.cc
+++ b/media/base/audio_encoder.cc
@@ -5,6 +5,7 @@
 #include "media/base/audio_encoder.h"
 
 #include "base/logging.h"
+#include "base/no_destructor.h"
 #include "base/time/time.h"
 #include "media/base/audio_timestamp_helper.h"
 
@@ -63,6 +64,12 @@
   EncodeAudioImpl(audio_bus, capture_time);
 }
 
+// Returns codec's extra data if the codec needs it. (e.g Opus header)
+const std::vector<uint8_t>& AudioEncoder::GetExtraData() {
+  static base::NoDestructor<std::vector<uint8_t>> g_empty_extra_data;
+  return *g_empty_extra_data;
+}
+
 void AudioEncoder::Flush() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
diff --git a/media/base/audio_encoder.h b/media/base/audio_encoder.h
index c25218c..2a212d15 100644
--- a/media/base/audio_encoder.h
+++ b/media/base/audio_encoder.h
@@ -6,6 +6,7 @@
 #define MEDIA_BASE_AUDIO_ENCODER_H_
 
 #include <memory>
+#include <vector>
 
 #include "base/callback.h"
 #include "base/sequence_checker.h"
@@ -76,6 +77,9 @@
     return audio_input_params_;
   }
 
+  // Returns codec's extra data if the codec needs it. (e.g Opus header)
+  virtual const std::vector<uint8_t>& GetExtraData();
+
   // Performs various checks before calling EncodeAudioImpl() which does the
   // actual encoding.
   void EncodeAudio(const AudioBus& audio_bus, base::TimeTicks capture_time);
diff --git a/net/socket/ssl_client_socket_impl.cc b/net/socket/ssl_client_socket_impl.cc
index eef0420b..f9be5b5a 100644
--- a/net/socket/ssl_client_socket_impl.cc
+++ b/net/socket/ssl_client_socket_impl.cc
@@ -1211,9 +1211,7 @@
   // version.
   if (result == OK &&
       SSL_version(ssl_.get()) < context_->config().version_min_warn &&
-      base::FeatureList::IsEnabled(features::kLegacyTLSEnforced) &&
-      !context_->ssl_config_service()->ShouldSuppressLegacyTLSWarning(
-          host_and_port_.host())) {
+      base::FeatureList::IsEnabled(features::kLegacyTLSEnforced)) {
     server_cert_verify_result_.cert_status |= CERT_STATUS_LEGACY_TLS;
 
     // Only set the resulting net error if it hasn't been previously bypassed.
diff --git a/net/spdy/spdy_session_unittest.cc b/net/spdy/spdy_session_unittest.cc
index 07dca05..ce22bb7 100644
--- a/net/spdy/spdy_session_unittest.cc
+++ b/net/spdy/spdy_session_unittest.cc
@@ -6887,11 +6887,6 @@
     return false;
   }
 
-  bool ShouldSuppressLegacyTLSWarning(
-      const std::string& hostname) const override {
-    return false;
-  }
-
   void SetDomainsForPooling(const std::vector<std::string>& domains) {
     domains_for_pooling_ = domains;
   }
diff --git a/net/ssl/ssl_config_service.h b/net/ssl/ssl_config_service.h
index acf7126..8fc5cbf7 100644
--- a/net/ssl/ssl_config_service.h
+++ b/net/ssl/ssl_config_service.h
@@ -93,12 +93,6 @@
   virtual bool CanShareConnectionWithClientCerts(
       const std::string& hostname) const = 0;
 
-  // Returns true if connections to |hostname| should not trigger legacy TLS
-  // warnings. This allows implementations to override the warnings for specific
-  // sites.
-  virtual bool ShouldSuppressLegacyTLSWarning(
-      const std::string& hostname) const = 0;
-
   // Add an observer of this service.
   void AddObserver(Observer* observer);
 
diff --git a/net/ssl/ssl_config_service_defaults.cc b/net/ssl/ssl_config_service_defaults.cc
index 4c9617f4..9348bb16 100644
--- a/net/ssl/ssl_config_service_defaults.cc
+++ b/net/ssl/ssl_config_service_defaults.cc
@@ -18,9 +18,4 @@
   return false;
 }
 
-bool SSLConfigServiceDefaults::ShouldSuppressLegacyTLSWarning(
-    const std::string& hostname) const {
-  return false;
-}
-
 }  // namespace net
diff --git a/net/ssl/ssl_config_service_defaults.h b/net/ssl/ssl_config_service_defaults.h
index 446687a..ab5bb4b 100644
--- a/net/ssl/ssl_config_service_defaults.h
+++ b/net/ssl/ssl_config_service_defaults.h
@@ -25,9 +25,6 @@
   bool CanShareConnectionWithClientCerts(
       const std::string& hostname) const override;
 
-  bool ShouldSuppressLegacyTLSWarning(
-      const std::string& hostname) const override;
-
  private:
   // Default value of prefs.
   const SSLContextConfig default_config_;
diff --git a/net/ssl/ssl_config_service_unittest.cc b/net/ssl/ssl_config_service_unittest.cc
index 712e070b..15d06cc 100644
--- a/net/ssl/ssl_config_service_unittest.cc
+++ b/net/ssl/ssl_config_service_unittest.cc
@@ -27,10 +27,6 @@
     return false;
   }
 
-  bool ShouldSuppressLegacyTLSWarning(const std::string& host) const override {
-    return false;
-  }
-
   // Sets the SSLContextConfig to be returned by GetSSLContextConfig and
   // processes any updates.
   void SetSSLContextConfig(const SSLContextConfig& config) {
diff --git a/net/ssl/test_ssl_config_service.cc b/net/ssl/test_ssl_config_service.cc
index 14503b6..1d11fe7 100644
--- a/net/ssl/test_ssl_config_service.cc
+++ b/net/ssl/test_ssl_config_service.cc
@@ -26,9 +26,4 @@
   NotifySSLContextConfigChange();
 }
 
-bool TestSSLConfigService::ShouldSuppressLegacyTLSWarning(
-    const std::string& hostname) const {
-  return false;
-}
-
 }  // namespace net
diff --git a/net/ssl/test_ssl_config_service.h b/net/ssl/test_ssl_config_service.h
index 4149f42..cd7b04e 100644
--- a/net/ssl/test_ssl_config_service.h
+++ b/net/ssl/test_ssl_config_service.h
@@ -20,9 +20,6 @@
   bool CanShareConnectionWithClientCerts(
       const std::string& hostname) const override;
 
-  bool ShouldSuppressLegacyTLSWarning(
-      const std::string& hostname) const override;
-
  private:
   SSLContextConfig config_;
 };
diff --git a/pdf/out_of_process_instance.cc b/pdf/out_of_process_instance.cc
index 1b55a8c..8e8e266 100644
--- a/pdf/out_of_process_instance.cc
+++ b/pdf/out_of_process_instance.cc
@@ -23,7 +23,6 @@
 #include "base/memory/weak_ptr.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/notreached.h"
-#include "base/numerics/ranges.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
@@ -2263,19 +2262,6 @@
   pp::PDF::UserMetricsRecordAction(this, pp::Var(action));
 }
 
-gfx::PointF OutOfProcessInstance::BoundScrollPositionToDocument(
-    const gfx::PointF& scroll_position) {
-  float max_x = std::max(
-      document_size().width() * float{zoom()} - plugin_dip_size().width(),
-      0.0f);
-  float x = base::ClampToRange(scroll_position.x(), 0.0f, max_x);
-  float max_y = std::max(
-      document_size().height() * float{zoom()} - plugin_dip_size().height(),
-      0.0f);
-  float y = base::ClampToRange(scroll_position.y(), 0.0f, max_y);
-  return gfx::PointF(x, y);
-}
-
 bool OutOfProcessInstance::SendInputEventToEngine(const pp::InputEvent& event) {
   switch (event.GetType()) {
     case PP_INPUTEVENT_TYPE_MOUSEDOWN:
diff --git a/pdf/out_of_process_instance.h b/pdf/out_of_process_instance.h
index 11ae2e4b..1e18602 100644
--- a/pdf/out_of_process_instance.h
+++ b/pdf/out_of_process_instance.h
@@ -27,7 +27,6 @@
 #include "ppapi/cpp/private/find_private.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/gfx/geometry/point.h"
-#include "ui/gfx/geometry/point_f.h"
 #include "ui/gfx/geometry/rect.h"
 
 namespace gfx {
@@ -280,9 +279,6 @@
   // Sends the thumbnail image data.
   void SendThumbnail(const std::string& message_id, Thumbnail thumbnail);
 
-  // Bound the given scroll position to the document.
-  gfx::PointF BoundScrollPositionToDocument(const gfx::PointF& scroll_position);
-
   // These values are persisted to logs. Entries should not be renumbered and
   // numeric values should never be reused.
   enum class PdfHasAttachment {
diff --git a/pdf/pdf_view_plugin_base.cc b/pdf/pdf_view_plugin_base.cc
index 512bb693..987c455 100644
--- a/pdf/pdf_view_plugin_base.cc
+++ b/pdf/pdf_view_plugin_base.cc
@@ -20,6 +20,7 @@
 #include "base/feature_list.h"
 #include "base/memory/weak_ptr.h"
 #include "base/notreached.h"
+#include "base/numerics/ranges.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/optional.h"
 #include "base/strings/string_piece.h"
@@ -32,6 +33,7 @@
 #include "pdf/ppapi_migration/url_loader.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkColor.h"
+#include "ui/gfx/geometry/point_f.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/skia_util.h"
@@ -252,6 +254,17 @@
     background_parts_.push_back(part);
 }
 
+gfx::PointF PdfViewPluginBase::BoundScrollPositionToDocument(
+    const gfx::PointF& scroll_position) {
+  float max_x = std::max(
+      document_size_.width() * float{zoom_} - plugin_dip_size_.width(), 0.0f);
+  float x = base::ClampToRange(scroll_position.x(), 0.0f, max_x);
+  float max_y = std::max(
+      document_size_.height() * float{zoom_} - plugin_dip_size_.height(), 0.0f);
+  float y = base::ClampToRange(scroll_position.y(), 0.0f, max_y);
+  return gfx::PointF(x, y);
+}
+
 int PdfViewPluginBase::GetDocumentPixelWidth() const {
   return static_cast<int>(
       std::ceil(document_size_.width() * zoom() * device_scale()));
diff --git a/pdf/pdf_view_plugin_base.h b/pdf/pdf_view_plugin_base.h
index 07b3ad1f..881b5a8 100644
--- a/pdf/pdf_view_plugin_base.h
+++ b/pdf/pdf_view_plugin_base.h
@@ -17,6 +17,8 @@
 #include "pdf/pdfium/pdfium_form_filler.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkColor.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/point_f.h"
 #include "ui/gfx/geometry/rect.h"
 
 namespace base {
@@ -123,6 +125,9 @@
   // aren't painted by the PDF engine).
   void CalculateBackgroundParts();
 
+  // Bound the given scroll position to the document.
+  gfx::PointF BoundScrollPositionToDocument(const gfx::PointF& scroll_position);
+
   // Computes document width/height in device pixels, based on current zoom and
   // device scale
   int GetDocumentPixelWidth() const;
@@ -133,13 +138,10 @@
 
   const gfx::Rect& available_area() const { return available_area_; }
 
-  const gfx::Size& document_size() const { return document_size_; }
   void set_document_size(const gfx::Size& size) { document_size_ = size; }
 
   const gfx::Size& plugin_size() const { return plugin_size_; }
 
-  const gfx::Size& plugin_dip_size() const { return plugin_dip_size_; }
-
   const gfx::Point& plugin_offset() const { return plugin_offset_; }
 
   void SetBackgroundColor(SkColor background_color) {
diff --git a/remoting/host/installer/mac/BUILD.gn b/remoting/host/installer/mac/BUILD.gn
index fae0ad40..dee7a6c 100644
--- a/remoting/host/installer/mac/BUILD.gn
+++ b/remoting/host/installer/mac/BUILD.gn
@@ -10,6 +10,7 @@
 # TODO(crbug.com/1112471): Get this to run cleanly under Python 3.
 python2_action("remoting_me2me_host_archive") {
   _installer_mac_files = [
+    "app-entitlements.plist",
     "do_signing.sh",
     "do_signing.props",
     "ChromotingHost.pkgproj",
diff --git a/remoting/host/installer/mac/app-entitlements.plist b/remoting/host/installer/mac/app-entitlements.plist
new file mode 100644
index 0000000..b572d9c
--- /dev/null
+++ b/remoting/host/installer/mac/app-entitlements.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>com.apple.security.device.audio-input</key>
+	<true/>
+</dict>
+</plist>
diff --git a/remoting/host/installer/mac/do_signing.sh b/remoting/host/installer/mac/do_signing.sh
index 8377aa8..71f06d3 100755
--- a/remoting/host/installer/mac/do_signing.sh
+++ b/remoting/host/installer/mac/do_signing.sh
@@ -54,6 +54,9 @@
   PKGPROJ_HOST_SERVICE="ChromotingHostService.pkgproj"
   PKGPROJ_HOST_UNINSTALLER="ChromotingHostUninstaller.pkgproj"
 
+  # The app entitlements file.
+  APP_ENTITLEMENTS="app-entitlements.plist"
+
   # Final (user-visible) pkg name.
   PKG_FINAL="${HOST_PKG}.pkg"
 
@@ -146,6 +149,7 @@
   if linker_signed_arm64_needs_force "${name}"; then
       args+=(--force)
   fi
+  args+=(--entitlements "${input_dir}/${APP_ENTITLEMENTS}")
   args+=(--timestamp --options runtime "${name}")
   codesign "${args[@]}"
   codesign -v "${name}"
diff --git a/services/device/public/mojom/usb_device.mojom b/services/device/public/mojom/usb_device.mojom
index 89f39ea7..c14cf51 100644
--- a/services/device/public/mojom/usb_device.mojom
+++ b/services/device/public/mojom/usb_device.mojom
@@ -134,13 +134,13 @@
   uint16 index;
 
   // Unless the USB device was opened with |GetSecurityKeyDevice| then control
-  // transfers to attempt to configure an AOA[1] version with the following
+  // transfers to attempt to configure an AOA[1] model with the following
   // prefix will be rejected. These requests are blocked because they instruct
   // an Android phone to act as a security key and this should not be exposed
   // to, e.g., WebUSB.
   //
   // [1] https://source.android.com/devices/accessories/aoa
-  const string kSecurityKeyAOAVersion = "12eba9f901039b36";
+  const string kSecurityKeyAOAModel = "12eba9f901039b36";
 };
 
 // This enum is exposed through the chrome.usb extension API so existing values
diff --git a/services/device/usb/mojo/device_impl.cc b/services/device/usb/mojo/device_impl.cc
index c8fe750..dc4b9179 100644
--- a/services/device/usb/mojo/device_impl.cc
+++ b/services/device/usb/mojo/device_impl.cc
@@ -89,16 +89,16 @@
 bool IsAndroidSecurityKeyRequest(
     const mojom::UsbControlTransferParamsPtr& params,
     const std::vector<uint8_t>& data) {
-  // This matches a request to send an AOA version string:
+  // This matches a request to send an AOA model string:
   // https://source.android.com/devices/accessories/aoa#attempt-to-start-in-accessory-mode
   //
-  // The magic version is matched as a prefix because sending trailing NULs etc
+  // The magic model is matched as a prefix because sending trailing NULs etc
   // would be considered equivalent by Android but would not be caught by an
   // exact match here. Android is case-sensitive thus a byte-wise match is
   // suitable.
-  const char* magic = mojom::UsbControlTransferParams::kSecurityKeyAOAVersion;
+  const char* magic = mojom::UsbControlTransferParams::kSecurityKeyAOAModel;
   return params->type == mojom::UsbControlTransferType::VENDOR &&
-         params->request == 52 && params->index == 3 &&
+         params->request == 52 && params->index == 1 &&
          data.size() >= strlen(magic) &&
          memcmp(data.data(), magic, strlen(magic)) == 0;
 }
diff --git a/services/device/usb/mojo/device_impl_unittest.cc b/services/device/usb/mojo/device_impl_unittest.cc
index 70a18f13..2d12c25 100644
--- a/services/device/usb/mojo/device_impl_unittest.cc
+++ b/services/device/usb/mojo/device_impl_unittest.cc
@@ -1060,8 +1060,7 @@
     loop.Run();
   }
 
-  const char* data_str =
-      mojom::UsbControlTransferParams::kSecurityKeyAOAVersion;
+  const char* data_str = mojom::UsbControlTransferParams::kSecurityKeyAOAModel;
   const std::vector<uint8_t> data(
       reinterpret_cast<const uint8_t*>(data_str),
       reinterpret_cast<const uint8_t*>(data_str) + strlen(data_str));
@@ -1072,7 +1071,7 @@
                 ControlTransferInternal(UsbTransferDirection::OUTBOUND,
                                         UsbControlTransferType::VENDOR,
                                         UsbControlTransferRecipient::DEVICE, 52,
-                                        0, 3, _, 0, _));
+                                        0, 1, _, 0, _));
   }
 
   {
@@ -1085,7 +1084,7 @@
     params->recipient = UsbControlTransferRecipient::DEVICE;
     params->request = 52;
     params->value = 0;
-    params->index = 3;
+    params->index = 1;
     base::RunLoop loop;
     device->ControlTransferOut(
         std::move(params), data, 0,
diff --git a/services/network/BUILD.gn b/services/network/BUILD.gn
index 6a6aed1..2ca6d75 100644
--- a/services/network/BUILD.gn
+++ b/services/network/BUILD.gn
@@ -54,8 +54,6 @@
     "ignore_errors_cert_verifier.h",
     "keepalive_statistics_recorder.cc",
     "keepalive_statistics_recorder.h",
-    "legacy_tls_config_distributor.cc",
-    "legacy_tls_config_distributor.h",
     "mojo_host_resolver_impl.cc",
     "mojo_host_resolver_impl.h",
     "net_log_exporter.cc",
diff --git a/services/network/legacy_tls_config_distributor.cc b/services/network/legacy_tls_config_distributor.cc
deleted file mode 100644
index 5158b3a..0000000
--- a/services/network/legacy_tls_config_distributor.cc
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "services/network/legacy_tls_config_distributor.h"
-
-#include <memory>
-#include <string>
-
-#include "base/containers/span.h"
-#include "base/location.h"
-#include "base/memory/scoped_refptr.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/string_util.h"
-#include "base/task/post_task.h"
-#include "base/task/task_traits.h"
-#include "base/task/thread_pool.h"
-#include "crypto/sha2.h"
-#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
-
-namespace network {
-
-namespace {
-
-// Helper to guarantee |notify_callback| is run, even if |process_callback|
-// no-ops due to the worker pool doing the parsing outliving the
-// LegacyTLSConfigDistributor.
-void ProcessParsedLegacyTLSConfig(
-    base::OnceCallback<void(scoped_refptr<LegacyTLSExperimentConfig>)>
-        process_callback,
-    base::OnceClosure notify_callback,
-    scoped_refptr<LegacyTLSExperimentConfig> config) {
-  std::move(process_callback).Run(std::move(config));
-  std::move(notify_callback).Run();
-}
-
-}  // namespace
-
-LegacyTLSExperimentConfig::LegacyTLSExperimentConfig() = default;
-LegacyTLSExperimentConfig::~LegacyTLSExperimentConfig() = default;
-
-// static
-scoped_refptr<LegacyTLSExperimentConfig> LegacyTLSExperimentConfig::Parse(
-    const std::string& data) {
-  auto config = base::MakeRefCounted<LegacyTLSExperimentConfig>();
-  if (data.empty() || !config->proto_.ParseFromString(data))
-    return nullptr;
-  return config;
-}
-
-bool LegacyTLSExperimentConfig::ShouldSuppressLegacyTLSWarning(
-    const std::string& hostname) const {
-  // Match on eTLD+1 rather than full hostname (to account for subdomains and
-  // redirects). If no registrable domain is found, default to using the
-  // hostname as-is.
-  auto domain = net::registry_controlled_domains::GetDomainAndRegistry(
-      hostname, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
-  if (domain.empty())
-    domain = hostname;
-
-  // Convert bytes from crypto::SHA256 so we can compare to the proto contents.
-  std::string host_hash_bytes = crypto::SHA256HashString(domain);
-  std::string host_hash = base::ToLowerASCII(
-      base::HexEncode(host_hash_bytes.data(), host_hash_bytes.size()));
-  const auto& control_site_hashes = proto_.control_site_hashes();
-
-  // Perform binary search on the sorted list of control site hashes to check
-  // if the input URL's hostname is included.
-  auto lower = std::lower_bound(control_site_hashes.begin(),
-                                control_site_hashes.end(), host_hash);
-
-  return lower != control_site_hashes.end() && *lower == host_hash;
-}
-
-LegacyTLSConfigDistributor::LegacyTLSConfigDistributor() = default;
-LegacyTLSConfigDistributor::~LegacyTLSConfigDistributor() = default;
-
-void LegacyTLSConfigDistributor::AddObserver(Observer* observer) {
-  observers_.AddObserver(observer);
-}
-
-void LegacyTLSConfigDistributor::RemoveObserver(Observer* observer) {
-  observers_.RemoveObserver(observer);
-}
-
-void LegacyTLSConfigDistributor::OnNewLegacyTLSConfig(
-    base::span<const uint8_t> config,
-    base::OnceClosure callback) {
-  // Make a copy for the background task, since the underlying storage for the
-  // span will go away.
-  std::string config_string(reinterpret_cast<const char*>(config.data()),
-                            config.size());
-
-  base::ThreadPool::PostTaskAndReplyWithResult(
-      FROM_HERE, {base::TaskPriority::USER_VISIBLE},
-      base::BindOnce(&LegacyTLSExperimentConfig::Parse,
-                     std::move(config_string)),
-      base::BindOnce(
-          &ProcessParsedLegacyTLSConfig,
-          base::BindOnce(&LegacyTLSConfigDistributor::OnLegacyTLSConfigParsed,
-                         weak_factory_.GetWeakPtr()),
-          std::move(callback)));
-}
-
-void LegacyTLSConfigDistributor::OnLegacyTLSConfigParsed(
-    scoped_refptr<LegacyTLSExperimentConfig> config) {
-  if (!config)
-    return;  // Error parsing the config.
-
-  config_ = std::move(config);
-
-  for (auto& observer : observers_) {
-    observer.OnNewLegacyTLSConfig(config_);
-  }
-}
-
-}  // namespace network
diff --git a/services/network/legacy_tls_config_distributor.h b/services/network/legacy_tls_config_distributor.h
deleted file mode 100644
index 1b055700..0000000
--- a/services/network/legacy_tls_config_distributor.h
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SERVICES_NETWORK_LEGACY_TLS_CONFIG_DISTRIBUTOR_H_
-#define SERVICES_NETWORK_LEGACY_TLS_CONFIG_DISTRIBUTOR_H_
-
-#include <stdint.h>
-
-#include <algorithm>
-#include <memory>
-#include <string>
-
-#include "base/callback_forward.h"
-#include "base/component_export.h"
-#include "base/containers/span.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "base/observer_list.h"
-#include "base/observer_list_types.h"
-#include "services/network/public/proto/tls_deprecation_config.pb.h"
-
-namespace network {
-
-// A LegacyTLSExperimentConfig is a wrapper for a
-// chrome_browser_ssl::LegacyTLSExperimentConfig proto, which allows lookups of
-// whether legacy TLS warnings should be suppressed for a URL.
-class COMPONENT_EXPORT(NETWORK_SERVICE) LegacyTLSExperimentConfig
-    : public base::RefCountedThreadSafe<LegacyTLSExperimentConfig> {
- public:
-  LegacyTLSExperimentConfig();
-  LegacyTLSExperimentConfig(const LegacyTLSExperimentConfig&) = delete;
-  LegacyTLSExperimentConfig& operator=(const LegacyTLSExperimentConfig&) =
-      delete;
-
-  // Parses a binary proto in |data| into a LegacyTLSExperiment config. Returns
-  // nullptr if parsing fails.
-  static scoped_refptr<LegacyTLSExperimentConfig> Parse(
-      const std::string& data);
-
-  // Looks up whether |hostname| is in the experiment config.
-  bool ShouldSuppressLegacyTLSWarning(const std::string& hostname) const;
-
- private:
-  ~LegacyTLSExperimentConfig();
-
-  friend class base::RefCountedThreadSafe<LegacyTLSExperimentConfig>;
-
-  chrome_browser_ssl::LegacyTLSExperimentConfig proto_;
-};
-
-// LegacyTLSConfigDistributor is a helper class to handle fan-out distribution
-// of new legacy TLS configs. As new encoded configs are received (via
-// OnNewLegacyTLSConfig), they will be parsed and, if successful, dispatched to
-// LegacyTLSConfigDistributor::Observers' OnNewLegacyTLSConfig().
-class COMPONENT_EXPORT(NETWORK_SERVICE) LegacyTLSConfigDistributor {
- public:
-  class Observer : public base::CheckedObserver {
-   public:
-    Observer(const Observer&) = delete;
-    Observer& operator=(const Observer&) = delete;
-
-    // Called whenever a new Legacy TLS config has been received.
-    virtual void OnNewLegacyTLSConfig(
-        scoped_refptr<LegacyTLSExperimentConfig> config) = 0;
-
-   protected:
-    Observer() = default;
-    ~Observer() override = default;
-  };
-
-  LegacyTLSConfigDistributor();
-  ~LegacyTLSConfigDistributor();
-  LegacyTLSConfigDistributor(const LegacyTLSConfigDistributor&) = delete;
-  LegacyTLSConfigDistributor& operator=(const LegacyTLSConfigDistributor&) =
-      delete;
-
-  // Adds an observer to be notified when new LegacyTLSConfigs are available.
-  // Note: Newly-added observers are not notified on the current |config()|,
-  // only newly configured LegacyTLSConfigs after the AddObserver call.
-  void AddObserver(Observer* observer);
-  // Removes a previously registered observer.
-  void RemoveObserver(Observer* observer);
-
-  // Returns the currently configured LegacyTLSConfig, or nullptr if one has not
-  // yet been configured.
-  scoped_refptr<LegacyTLSExperimentConfig> config() const { return config_; }
-
-  // Notifies the distributor that a new encoded LegacyTLSConfig, |config|, has
-  // been received. If the LegacyTLSConfig successfully decodes and is newer
-  // than the current LegacyTLSConfig, all observers will be notified.
-  // |callback| will be notified once all observers have been notified.
-  // |callback| is guaranteed to run (e.g., even if this object is deleted prior
-  // to it being run).
-  void OnNewLegacyTLSConfig(base::span<const uint8_t> config,
-                            base::OnceClosure callback);
-
- private:
-  void OnLegacyTLSConfigParsed(scoped_refptr<LegacyTLSExperimentConfig> config);
-
-  base::ObserverList<Observer,
-                     /*check_empty=*/true,
-                     /*allow_reentrancy=*/false>
-      observers_;
-  scoped_refptr<LegacyTLSExperimentConfig> config_ = nullptr;
-
-  base::WeakPtrFactory<LegacyTLSConfigDistributor> weak_factory_{this};
-};
-
-}  // namespace network
-
-#endif  // SERVICES_NETWORK_LEGACY_TLS_CONFIG_DISTRIBUTOR_H_
\ No newline at end of file
diff --git a/services/network/network_context.cc b/services/network/network_context.cc
index dc1d135d..cbef8450 100644
--- a/services/network/network_context.cc
+++ b/services/network/network_context.cc
@@ -2062,8 +2062,7 @@
       std::make_unique<SSLConfigServiceMojo>(
           std::move(params_->initial_ssl_config),
           std::move(params_->ssl_config_client_receiver),
-          network_service_->crl_set_distributor(),
-          network_service_->legacy_tls_config_distributor());
+          network_service_->crl_set_distributor());
   SSLConfigServiceMojo* ssl_config_service_raw = ssl_config_service.get();
   builder.set_ssl_config_service(std::move(ssl_config_service));
 
diff --git a/services/network/network_service.cc b/services/network/network_service.cc
index 1049108..01e7a29 100644
--- a/services/network/network_service.cc
+++ b/services/network/network_service.cc
@@ -55,7 +55,6 @@
 #include "services/network/dns_config_change_manager.h"
 #include "services/network/first_party_sets/first_party_sets.h"
 #include "services/network/http_auth_cache_copier.h"
-#include "services/network/legacy_tls_config_distributor.h"
 #include "services/network/net_log_exporter.h"
 #include "services/network/net_log_proxy_sink.h"
 #include "services/network/network_context.h"
@@ -370,9 +369,6 @@
 
   crl_set_distributor_ = std::make_unique<CRLSetDistributor>();
 
-  legacy_tls_config_distributor_ =
-      std::make_unique<LegacyTLSConfigDistributor>();
-
   doh_probe_activator_ = std::make_unique<DelayedDohProbeActivator>(this);
 
   trust_token_key_commitments_ = std::make_unique<TrustTokenKeyCommitments>();
@@ -661,13 +657,6 @@
   crl_set_distributor_->OnNewCRLSet(crl_set, std::move(callback));
 }
 
-void NetworkService::UpdateLegacyTLSConfig(
-    base::span<const uint8_t> config,
-    mojom::NetworkService::UpdateLegacyTLSConfigCallback callback) {
-  legacy_tls_config_distributor_->OnNewLegacyTLSConfig(config,
-                                                       std::move(callback));
-}
-
 void NetworkService::OnCertDBChanged() {
   net::CertDatabase::GetInstance()->NotifyObserversCertDBChanged();
 }
diff --git a/services/network/network_service.h b/services/network/network_service.h
index b18994b3..0f3afa6 100644
--- a/services/network/network_service.h
+++ b/services/network/network_service.h
@@ -61,7 +61,6 @@
 class CRLSetDistributor;
 class DnsConfigChangeManager;
 class HttpAuthCacheCopier;
-class LegacyTLSConfigDistributor;
 class NetLogProxySink;
 class NetworkContext;
 class NetworkService;
@@ -193,9 +192,6 @@
   void UpdateCRLSet(
       base::span<const uint8_t> crl_set,
       mojom::NetworkService::UpdateCRLSetCallback callback) override;
-  void UpdateLegacyTLSConfig(
-      base::span<const uint8_t> config,
-      mojom::NetworkService::UpdateLegacyTLSConfigCallback callback) override;
   void OnCertDBChanged() override;
 #if defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
   void SetCryptConfig(mojom::CryptConfigPtr crypt_config) override;
@@ -274,10 +270,6 @@
     return crl_set_distributor_.get();
   }
 
-  LegacyTLSConfigDistributor* legacy_tls_config_distributor() {
-    return legacy_tls_config_distributor_.get();
-  }
-
   const FirstPartySets* first_party_sets() const {
     return first_party_sets_.get();
   }
@@ -408,8 +400,6 @@
 
   std::unique_ptr<CRLSetDistributor> crl_set_distributor_;
 
-  std::unique_ptr<LegacyTLSConfigDistributor> legacy_tls_config_distributor_;
-
   // A timer that periodically calls UpdateLoadInfo while there are pending
   // loads and not waiting on an ACK from the client for the last sent
   // LoadInfo callback.
diff --git a/services/network/public/mojom/network_service.mojom b/services/network/public/mojom/network_service.mojom
index b33b713b..2c1eddc 100644
--- a/services/network/public/mojom/network_service.mojom
+++ b/services/network/public/mojom/network_service.mojom
@@ -356,11 +356,6 @@
   // this call, will use the same CRLSet.
   UpdateCRLSet(mojo_base.mojom.ReadOnlyBuffer crl_set) => ();
 
-  // Updates the configuration used for determining if a site should have legacy
-  // TLS warnings suppressed. Configs that cannot be parsed as a
-  // LegacyTLSExperimentConfig (protobuf) will be ignored.
-  UpdateLegacyTLSConfig(mojo_base.mojom.ReadOnlyBuffer config) => ();
-
   // Notification that the certificate database has been modified.
   OnCertDBChanged();
 
diff --git a/services/network/public/proto/BUILD.gn b/services/network/public/proto/BUILD.gn
index 0ab2431..7de51ed6 100644
--- a/services/network/public/proto/BUILD.gn
+++ b/services/network/public/proto/BUILD.gn
@@ -6,17 +6,12 @@
 import("//third_party/protobuf/proto_library.gni")
 
 group("proto") {
-  public_deps = [ ":tls_deprecation_config_proto" ]
-
+  public_deps = []
   if (is_ct_supported) {
     public_deps += [ ":sct_audit_report_proto" ]
   }
 }
 
-proto_library("tls_deprecation_config_proto") {
-  sources = [ "tls_deprecation_config.proto" ]
-}
-
 if (is_ct_supported) {
   proto_library("sct_audit_report_proto") {
     sources = [ "sct_audit_report.proto" ]
diff --git a/services/network/public/proto/tls_deprecation_config.proto b/services/network/public/proto/tls_deprecation_config.proto
deleted file mode 100644
index a54d5d3..0000000
--- a/services/network/public/proto/tls_deprecation_config.proto
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-syntax = "proto2";
-
-package chrome_browser_ssl;
-
-option optimize_for = LITE_RUNTIME;
-
-// The set of sites to be used as a control group for Legacy TLS experiments.
-// Warning UI will not be shown on these sites.
-message LegacyTLSExperimentConfig {
-  optional uint32 version_id = 1;
-  // SHA-256 hash of the hostname of sites in the control group (e.g., for
-  // "https://test.example.com/foo" the hostname is "test.example.com"). This
-  // list must be in sorted order (alphanumeric by hash value).
-  repeated string control_site_hashes = 2;
-}
diff --git a/services/network/ssl_config_service_mojo.cc b/services/network/ssl_config_service_mojo.cc
index 247d8df..fe3d7ee 100644
--- a/services/network/ssl_config_service_mojo.cc
+++ b/services/network/ssl_config_service_mojo.cc
@@ -7,7 +7,6 @@
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "mojo/public/cpp/bindings/type_converter.h"
-#include "services/network/legacy_tls_config_distributor.h"
 #include "services/network/ssl_config_type_converter.h"
 
 namespace network {
@@ -35,10 +34,8 @@
 SSLConfigServiceMojo::SSLConfigServiceMojo(
     mojom::SSLConfigPtr initial_config,
     mojo::PendingReceiver<mojom::SSLConfigClient> ssl_config_client_receiver,
-    CRLSetDistributor* crl_set_distributor,
-    LegacyTLSConfigDistributor* legacy_tls_config_distributor)
+    CRLSetDistributor* crl_set_distributor)
     : crl_set_distributor_(crl_set_distributor),
-      legacy_tls_config_distributor_(legacy_tls_config_distributor),
       client_cert_pooling_policy_(
           initial_config ? initial_config->client_cert_pooling_policy
                          : std::vector<std::string>()) {
@@ -52,14 +49,10 @@
 
   crl_set_distributor_->AddObserver(this);
   cert_verifier_config_.crl_set = crl_set_distributor_->crl_set();
-
-  legacy_tls_config_distributor_->AddObserver(this);
-  legacy_tls_config_ = legacy_tls_config_distributor_->config();
 }
 
 SSLConfigServiceMojo::~SSLConfigServiceMojo() {
   crl_set_distributor_->RemoveObserver(this);
-  legacy_tls_config_distributor_->RemoveObserver(this);
 }
 
 void SSLConfigServiceMojo::SetCertVerifierForConfiguring(
@@ -118,24 +111,10 @@
   return false;
 }
 
-bool SSLConfigServiceMojo::ShouldSuppressLegacyTLSWarning(
-    const std::string& hostname) const {
-  // If the config is not yet loaded, we err on the side of not showing warnings
-  // for any sites.
-  if (!legacy_tls_config_)
-    return true;
-  return legacy_tls_config_->ShouldSuppressLegacyTLSWarning(hostname);
-}
-
 void SSLConfigServiceMojo::OnNewCRLSet(scoped_refptr<net::CRLSet> crl_set) {
   cert_verifier_config_.crl_set = crl_set;
   if (cert_verifier_)
     cert_verifier_->SetConfig(cert_verifier_config_);
 }
 
-void SSLConfigServiceMojo::OnNewLegacyTLSConfig(
-    scoped_refptr<network::LegacyTLSExperimentConfig> config) {
-  legacy_tls_config_ = config;
-}
-
 }  // namespace network
diff --git a/services/network/ssl_config_service_mojo.h b/services/network/ssl_config_service_mojo.h
index 4d97a1d..e961314 100644
--- a/services/network/ssl_config_service_mojo.h
+++ b/services/network/ssl_config_service_mojo.h
@@ -11,9 +11,7 @@
 #include "net/cert/cert_verifier.h"
 #include "net/ssl/ssl_config_service.h"
 #include "services/network/crl_set_distributor.h"
-#include "services/network/legacy_tls_config_distributor.h"
 #include "services/network/public/mojom/ssl_config.mojom.h"
-#include "services/network/public/proto/tls_deprecation_config.pb.h"
 
 namespace network {
 
@@ -22,8 +20,7 @@
 class COMPONENT_EXPORT(NETWORK_SERVICE) SSLConfigServiceMojo
     : public mojom::SSLConfigClient,
       public net::SSLConfigService,
-      public CRLSetDistributor::Observer,
-      public LegacyTLSConfigDistributor::Observer {
+      public CRLSetDistributor::Observer {
  public:
   // If |ssl_config_client_receiver| is not provided, just sticks with the
   // initial configuration.
@@ -31,8 +28,7 @@
   SSLConfigServiceMojo(
       mojom::SSLConfigPtr initial_config,
       mojo::PendingReceiver<mojom::SSLConfigClient> ssl_config_client_receiver,
-      CRLSetDistributor* crl_set_distributor,
-      LegacyTLSConfigDistributor* legacy_tls_config_distributor);
+      CRLSetDistributor* crl_set_distributor);
   ~SSLConfigServiceMojo() override;
 
   // Sets |cert_verifier| to be configured by certificate-related settings
@@ -48,16 +44,10 @@
   net::SSLContextConfig GetSSLContextConfig() override;
   bool CanShareConnectionWithClientCerts(
       const std::string& hostname) const override;
-  bool ShouldSuppressLegacyTLSWarning(
-      const std::string& hostname) const override;
 
   // CRLSetDistributor::Observer implementation:
   void OnNewCRLSet(scoped_refptr<net::CRLSet> crl_set) override;
 
-  // LegacyTLSConfigDistributor::Observer implementation:
-  void OnNewLegacyTLSConfig(
-      scoped_refptr<LegacyTLSExperimentConfig> config) override;
-
  private:
   mojo::Receiver<mojom::SSLConfigClient> receiver_{this};
 
@@ -67,11 +57,6 @@
   net::CertVerifier* cert_verifier_;
   CRLSetDistributor* crl_set_distributor_;
 
-  // Provides an optional LegacyTLSExperimentConfig structure that can be used
-  // check if legacy TLS warnings should apply based on the URL.
-  scoped_refptr<LegacyTLSExperimentConfig> legacy_tls_config_;
-  LegacyTLSConfigDistributor* legacy_tls_config_distributor_;
-
   // The list of domains and subdomains from enterprise policy where connection
   // coalescing is allowed when client certs are in use if the hosts being
   // coalesced match this list.
diff --git a/testing/buildbot/chrome.json b/testing/buildbot/chrome.json
index 55e4161..1852edca 100644
--- a/testing/buildbot/chrome.json
+++ b/testing/buildbot/chrome.json
@@ -691,9 +691,14 @@
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
+              "cipd_package": "infra/python/cpython/linux-amd64",
+              "location": "vpython_dir_linux_amd64",
+              "revision": "version:2.7.15.chromium14"
+            },
+            {
               "cipd_package": "infra/tools/luci/vpython/linux-amd64",
               "location": "vpython_dir_linux_amd64",
-              "revision": "git_revision:9a931a5307c46b16b1c12e01e8239d4a73830b89"
+              "revision": "git_revision:0f694cdc06ba054b9960aa1ae9766e45b53d02c1"
             }
           ],
           "dimension_sets": [
@@ -1739,9 +1744,14 @@
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
+              "cipd_package": "infra/python/cpython/linux-amd64",
+              "location": "vpython_dir_linux_amd64",
+              "revision": "version:2.7.15.chromium14"
+            },
+            {
               "cipd_package": "infra/tools/luci/vpython/linux-amd64",
               "location": "vpython_dir_linux_amd64",
-              "revision": "git_revision:9a931a5307c46b16b1c12e01e8239d4a73830b89"
+              "revision": "git_revision:0f694cdc06ba054b9960aa1ae9766e45b53d02c1"
             }
           ],
           "dimension_sets": [
@@ -2787,9 +2797,14 @@
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
+              "cipd_package": "infra/python/cpython/linux-amd64",
+              "location": "vpython_dir_linux_amd64",
+              "revision": "version:2.7.15.chromium14"
+            },
+            {
               "cipd_package": "infra/tools/luci/vpython/linux-amd64",
               "location": "vpython_dir_linux_amd64",
-              "revision": "git_revision:9a931a5307c46b16b1c12e01e8239d4a73830b89"
+              "revision": "git_revision:0f694cdc06ba054b9960aa1ae9766e45b53d02c1"
             }
           ],
           "dimension_sets": [
@@ -3835,9 +3850,14 @@
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
+              "cipd_package": "infra/python/cpython/linux-amd64",
+              "location": "vpython_dir_linux_amd64",
+              "revision": "version:2.7.15.chromium14"
+            },
+            {
               "cipd_package": "infra/tools/luci/vpython/linux-amd64",
               "location": "vpython_dir_linux_amd64",
-              "revision": "git_revision:9a931a5307c46b16b1c12e01e8239d4a73830b89"
+              "revision": "git_revision:0f694cdc06ba054b9960aa1ae9766e45b53d02c1"
             }
           ],
           "dimension_sets": [
@@ -4883,9 +4903,14 @@
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
+              "cipd_package": "infra/python/cpython/linux-amd64",
+              "location": "vpython_dir_linux_amd64",
+              "revision": "version:2.7.15.chromium14"
+            },
+            {
               "cipd_package": "infra/tools/luci/vpython/linux-amd64",
               "location": "vpython_dir_linux_amd64",
-              "revision": "git_revision:9a931a5307c46b16b1c12e01e8239d4a73830b89"
+              "revision": "git_revision:0f694cdc06ba054b9960aa1ae9766e45b53d02c1"
             }
           ],
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index bc030d8..115746a80 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -199,11 +199,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 88.0.4324.177"
+            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 88.0.4324.179"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 88.0.4324.177",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 88.0.4324.179",
         "resultdb": {
           "enable": true
         },
@@ -213,7 +213,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.177"
+              "revision": "version:88.0.4324.179"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -278,11 +278,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 89.0.4389.49"
+            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 89.0.4389.50"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 89.0.4389.49",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 89.0.4389.50",
         "resultdb": {
           "enable": true
         },
@@ -292,7 +292,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M89",
-              "revision": "version:89.0.4389.49"
+              "revision": "version:89.0.4389.50"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -436,11 +436,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 88.0.4324.177"
+            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 88.0.4324.179"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 88.0.4324.177",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 88.0.4324.179",
         "resultdb": {
           "enable": true
         },
@@ -450,7 +450,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.177"
+              "revision": "version:88.0.4324.179"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -515,11 +515,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 89.0.4389.49"
+            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 89.0.4389.50"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 89.0.4389.49",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 89.0.4389.50",
         "resultdb": {
           "enable": true
         },
@@ -529,7 +529,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M89",
-              "revision": "version:89.0.4389.49"
+              "revision": "version:89.0.4389.50"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -677,11 +677,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 88.0.4324.177"
+            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 88.0.4324.179"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 88.0.4324.177",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 88.0.4324.179",
         "resultdb": {
           "enable": true
         },
@@ -691,7 +691,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.177"
+              "revision": "version:88.0.4324.179"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -756,11 +756,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 89.0.4389.49"
+            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 89.0.4389.50"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 89.0.4389.49",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 89.0.4389.50",
         "resultdb": {
           "enable": true
         },
@@ -770,7 +770,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M89",
-              "revision": "version:89.0.4389.49"
+              "revision": "version:89.0.4389.50"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -914,11 +914,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 88.0.4324.177"
+            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 88.0.4324.179"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 88.0.4324.177",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 88.0.4324.179",
         "resultdb": {
           "enable": true
         },
@@ -928,7 +928,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.177"
+              "revision": "version:88.0.4324.179"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -993,11 +993,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 89.0.4389.49"
+            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 89.0.4389.50"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 89.0.4389.49",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 89.0.4389.50",
         "resultdb": {
           "enable": true
         },
@@ -1007,7 +1007,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M89",
-              "revision": "version:89.0.4389.49"
+              "revision": "version:89.0.4389.50"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1218,11 +1218,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 88.0.4324.177"
+            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 88.0.4324.179"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 88.0.4324.177",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 88.0.4324.179",
         "resultdb": {
           "enable": true
         },
@@ -1232,7 +1232,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.177"
+              "revision": "version:88.0.4324.179"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1297,11 +1297,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 89.0.4389.49"
+            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 89.0.4389.50"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 89.0.4389.49",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 89.0.4389.50",
         "resultdb": {
           "enable": true
         },
@@ -1311,7 +1311,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M89",
-              "revision": "version:89.0.4389.49"
+              "revision": "version:89.0.4389.50"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1455,11 +1455,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 88.0.4324.177"
+            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 88.0.4324.179"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 88.0.4324.177",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 88.0.4324.179",
         "resultdb": {
           "enable": true
         },
@@ -1469,7 +1469,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.177"
+              "revision": "version:88.0.4324.179"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1534,11 +1534,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 89.0.4389.49"
+            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 89.0.4389.50"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 89.0.4389.49",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 89.0.4389.50",
         "resultdb": {
           "enable": true
         },
@@ -1548,7 +1548,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M89",
-              "revision": "version:89.0.4389.49"
+              "revision": "version:89.0.4389.50"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1759,11 +1759,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 88.0.4324.177"
+            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 88.0.4324.179"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 88.0.4324.177",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 88.0.4324.179",
         "resultdb": {
           "enable": true
         },
@@ -1773,7 +1773,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.177"
+              "revision": "version:88.0.4324.179"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1838,11 +1838,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 89.0.4389.49"
+            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 89.0.4389.50"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 89.0.4389.49",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 89.0.4389.50",
         "resultdb": {
           "enable": true
         },
@@ -1852,7 +1852,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M89",
-              "revision": "version:89.0.4389.49"
+              "revision": "version:89.0.4389.50"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1996,11 +1996,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 88.0.4324.177"
+            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 88.0.4324.179"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 88.0.4324.177",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 88.0.4324.179",
         "resultdb": {
           "enable": true
         },
@@ -2010,7 +2010,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.177"
+              "revision": "version:88.0.4324.179"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -2075,11 +2075,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 89.0.4389.49"
+            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 89.0.4389.50"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 89.0.4389.49",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 89.0.4389.50",
         "resultdb": {
           "enable": true
         },
@@ -2089,7 +2089,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M89",
-              "revision": "version:89.0.4389.49"
+              "revision": "version:89.0.4389.50"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index f74c362..69879707 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -31497,6 +31497,77 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
+          "--avd-config=../../tools/android/avd/proto/generic_android23.textpb",
+          "--git-revision=${got_revision}",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator_m.chrome_public_test_apk.filter"
+        ],
+        "experiment_percentage": 50,
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "chrome_public_test_apk"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "resultdb": {
+          "enable": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "device_os": null,
+              "device_type": null,
+              "machine_type": "e2-standard-8",
+              "os": "Ubuntu-16.04",
+              "pool": "chromium.tests.avd"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "avd_generic_android23",
+              "path": ".android"
+            },
+            {
+              "name": "system_images_android_23_google_apis_x86",
+              "path": ".emulator_sdk"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "service_account": "chrome-gold@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 25
+        },
+        "test": "chrome_public_test_apk",
+        "test_id_prefix": "ninja://chrome/android:chrome_public_test_apk/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
           "--avd-config=../../tools/android/avd/proto/generic_android23.textpb"
         ],
         "merge": {
diff --git a/testing/buildbot/chromium.angle.json b/testing/buildbot/chromium.angle.json
index 2df0831..9400132 100644
--- a/testing/buildbot/chromium.angle.json
+++ b/testing/buildbot/chromium.angle.json
@@ -1048,7 +1048,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "containment_type": "AUTO",
@@ -1100,7 +1100,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "containment_type": "AUTO",
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index a651848..dc526c3 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -656,9 +656,14 @@
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
+              "cipd_package": "infra/python/cpython/linux-amd64",
+              "location": "vpython_dir_linux_amd64",
+              "revision": "version:2.7.15.chromium14"
+            },
+            {
               "cipd_package": "infra/tools/luci/vpython/linux-amd64",
               "location": "vpython_dir_linux_amd64",
-              "revision": "git_revision:9a931a5307c46b16b1c12e01e8239d4a73830b89"
+              "revision": "git_revision:0f694cdc06ba054b9960aa1ae9766e45b53d02c1"
             }
           ],
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.clang.json b/testing/buildbot/chromium.clang.json
index 23f8642..76764048 100644
--- a/testing/buildbot/chromium.clang.json
+++ b/testing/buildbot/chromium.clang.json
@@ -40952,7 +40952,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -40997,7 +40997,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -41042,7 +41042,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -41087,7 +41087,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -41132,7 +41132,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -41177,7 +41177,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -41222,7 +41222,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -41267,7 +41267,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -41312,7 +41312,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -41357,7 +41357,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -41402,7 +41402,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -41447,7 +41447,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -41492,7 +41492,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -41537,7 +41537,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -41582,7 +41582,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -41627,7 +41627,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -41672,7 +41672,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -41717,7 +41717,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -41782,7 +41782,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -41826,7 +41826,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -41870,7 +41870,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -41914,7 +41914,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -41958,7 +41958,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -42002,7 +42002,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -42046,7 +42046,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -42090,7 +42090,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -42134,7 +42134,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -42178,7 +42178,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -42222,7 +42222,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -42266,7 +42266,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -42310,7 +42310,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -42354,7 +42354,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -42398,7 +42398,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -42442,7 +42442,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -42486,7 +42486,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -42530,7 +42530,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 4bf15b9..31cde48a 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -18974,7 +18974,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -19019,7 +19019,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -19064,7 +19064,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -19109,7 +19109,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -19154,7 +19154,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -19199,7 +19199,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -19244,7 +19244,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -19289,7 +19289,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -19334,7 +19334,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -19379,7 +19379,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -19424,7 +19424,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -19469,7 +19469,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -19514,7 +19514,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -19559,7 +19559,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -19604,7 +19604,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -19649,7 +19649,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -19694,7 +19694,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -19739,7 +19739,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -19784,7 +19784,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -19829,7 +19829,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -19874,7 +19874,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -19919,7 +19919,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -19964,7 +19964,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -20009,7 +20009,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -20054,7 +20054,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -20099,7 +20099,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -20144,7 +20144,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -20189,7 +20189,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -20234,7 +20234,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -20279,7 +20279,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -20324,7 +20324,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -20369,7 +20369,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -20414,7 +20414,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -20459,7 +20459,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -20504,7 +20504,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -20549,7 +20549,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -20594,7 +20594,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -20639,7 +20639,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -20684,7 +20684,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -20729,7 +20729,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -20774,7 +20774,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -20819,7 +20819,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -20864,7 +20864,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -20909,7 +20909,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -20954,7 +20954,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -20999,7 +20999,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -21051,7 +21051,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -21099,7 +21099,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -21147,7 +21147,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -21195,7 +21195,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -21243,7 +21243,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -21291,7 +21291,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -21339,7 +21339,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -21387,7 +21387,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -21435,7 +21435,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -21483,7 +21483,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -21531,7 +21531,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -21579,7 +21579,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -21627,7 +21627,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -21675,7 +21675,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -21723,7 +21723,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -21771,7 +21771,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -21819,7 +21819,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -21867,7 +21867,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -21915,7 +21915,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -21963,7 +21963,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -22011,7 +22011,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -22059,7 +22059,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -22107,7 +22107,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -22155,7 +22155,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -22203,7 +22203,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -22251,7 +22251,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -22299,7 +22299,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -22347,7 +22347,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -22395,7 +22395,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -22443,7 +22443,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -22491,7 +22491,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -22539,7 +22539,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -22587,7 +22587,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -22635,7 +22635,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -22684,7 +22684,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -22733,7 +22733,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -22782,7 +22782,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -22831,7 +22831,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -22880,7 +22880,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -22929,7 +22929,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -22978,7 +22978,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -23028,7 +23028,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -23078,7 +23078,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -23128,7 +23128,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -23178,7 +23178,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -23228,7 +23228,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -23278,7 +23278,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -23328,7 +23328,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -23378,7 +23378,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -23428,7 +23428,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -23478,7 +23478,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -23528,7 +23528,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -23578,7 +23578,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -23627,7 +23627,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -23676,7 +23676,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -23725,7 +23725,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -23774,7 +23774,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -23823,7 +23823,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -23872,7 +23872,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -23921,7 +23921,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -23970,7 +23970,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -24019,7 +24019,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -24068,7 +24068,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -24117,7 +24117,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -24166,7 +24166,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -24216,7 +24216,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -24266,7 +24266,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -24316,7 +24316,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -24366,7 +24366,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -24416,7 +24416,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -24465,7 +24465,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -24513,7 +24513,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -24561,7 +24561,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -24609,7 +24609,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -24657,7 +24657,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -24705,7 +24705,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -24753,7 +24753,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -24801,7 +24801,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -24850,7 +24850,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -24899,7 +24899,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -24948,7 +24948,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -24997,7 +24997,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -25046,7 +25046,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -25095,7 +25095,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -25143,7 +25143,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -25191,7 +25191,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -25239,7 +25239,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -25287,7 +25287,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -25335,7 +25335,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -25383,7 +25383,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -25432,7 +25432,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -25481,7 +25481,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -25530,7 +25530,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -25579,7 +25579,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -25628,7 +25628,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -25677,7 +25677,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -25725,7 +25725,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -25773,7 +25773,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -25821,7 +25821,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -25869,7 +25869,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -25917,7 +25917,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -25965,7 +25965,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -26013,7 +26013,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -26061,7 +26061,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -26109,7 +26109,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -26157,7 +26157,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -26206,7 +26206,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -26255,7 +26255,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -26304,7 +26304,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -26353,7 +26353,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -26402,7 +26402,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -26451,7 +26451,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -26499,7 +26499,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -26547,7 +26547,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -26595,7 +26595,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -26643,7 +26643,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -26691,7 +26691,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -26739,7 +26739,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -26787,7 +26787,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -26835,7 +26835,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -26883,7 +26883,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -26931,7 +26931,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -26979,7 +26979,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -27027,7 +27027,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -27075,7 +27075,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -27123,7 +27123,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -27171,7 +27171,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -27219,7 +27219,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -27267,7 +27267,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -27315,7 +27315,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -27363,7 +27363,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -27411,7 +27411,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -27459,7 +27459,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -27507,7 +27507,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -27555,7 +27555,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -27603,7 +27603,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -27651,7 +27651,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -27699,7 +27699,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -27747,7 +27747,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -27795,7 +27795,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -27843,7 +27843,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -27891,7 +27891,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -27939,7 +27939,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -27987,7 +27987,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -28035,7 +28035,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -28083,7 +28083,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -28131,7 +28131,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -28179,7 +28179,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -28227,7 +28227,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -28275,7 +28275,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -28323,7 +28323,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -28371,7 +28371,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -28419,7 +28419,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -28467,7 +28467,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -28515,7 +28515,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -28563,7 +28563,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -28611,7 +28611,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -28659,7 +28659,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -28707,7 +28707,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -28755,7 +28755,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -28804,7 +28804,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -28849,7 +28849,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -28898,7 +28898,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -28943,7 +28943,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -28988,7 +28988,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -29033,7 +29033,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -29078,7 +29078,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -29123,7 +29123,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -29168,7 +29168,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -29213,7 +29213,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -29259,7 +29259,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -29305,7 +29305,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -29352,7 +29352,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -29399,7 +29399,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -29445,7 +29445,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -29491,7 +29491,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -29537,7 +29537,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -29583,7 +29583,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -29628,7 +29628,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -29673,7 +29673,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -29718,7 +29718,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -29764,7 +29764,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -29809,7 +29809,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -29854,7 +29854,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -29900,7 +29900,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -29945,7 +29945,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -29990,7 +29990,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -30035,7 +30035,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -30080,7 +30080,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -30125,7 +30125,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -30170,7 +30170,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -30215,7 +30215,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -30260,7 +30260,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -30305,7 +30305,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -30356,7 +30356,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -30403,7 +30403,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -30450,7 +30450,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -30497,7 +30497,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -30544,7 +30544,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -30591,7 +30591,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -30638,7 +30638,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -30685,7 +30685,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -30732,7 +30732,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -30779,7 +30779,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -30826,7 +30826,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -30873,7 +30873,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -30920,7 +30920,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -30967,7 +30967,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -31014,7 +31014,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -31061,7 +31061,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -31109,7 +31109,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -31157,7 +31157,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -31205,7 +31205,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -31254,7 +31254,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -31303,7 +31303,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -31352,7 +31352,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -31401,7 +31401,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -31449,7 +31449,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -31497,7 +31497,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -31545,7 +31545,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -31593,7 +31593,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -31642,7 +31642,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -31690,7 +31690,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -31737,7 +31737,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -31785,7 +31785,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -31833,7 +31833,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -31880,7 +31880,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -31927,7 +31927,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -31974,7 +31974,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -32021,7 +32021,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -32068,7 +32068,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -32115,7 +32115,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -32163,7 +32163,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -32211,7 +32211,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -32258,7 +32258,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -32305,7 +32305,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -32352,7 +32352,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -32399,7 +32399,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -32447,7 +32447,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -32495,7 +32495,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -32542,7 +32542,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -32589,7 +32589,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -32636,7 +32636,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -32683,7 +32683,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -32730,7 +32730,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -32777,7 +32777,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -32824,7 +32824,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -32871,7 +32871,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -32918,7 +32918,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -32965,7 +32965,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -33012,7 +33012,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -33059,7 +33059,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -33106,7 +33106,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -33153,7 +33153,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -33200,7 +33200,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -33247,7 +33247,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -33294,7 +33294,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -33341,7 +33341,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -33408,7 +33408,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -33453,7 +33453,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -33498,7 +33498,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -33543,7 +33543,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -33588,7 +33588,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -33633,7 +33633,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -33678,7 +33678,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -33723,7 +33723,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -33768,7 +33768,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -33813,7 +33813,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -33858,7 +33858,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -33903,7 +33903,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -33948,7 +33948,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -33993,7 +33993,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -34039,7 +34039,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -34085,7 +34085,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -34131,7 +34131,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -34177,7 +34177,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -34223,7 +34223,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -34270,7 +34270,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -34317,7 +34317,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -34364,7 +34364,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -34411,7 +34411,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -34458,7 +34458,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -34505,7 +34505,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -34552,7 +34552,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -34599,7 +34599,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -34645,7 +34645,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -34691,7 +34691,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -34737,7 +34737,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -34783,7 +34783,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -34829,7 +34829,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -34875,7 +34875,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -34921,7 +34921,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -34967,7 +34967,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -35014,7 +35014,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -35061,7 +35061,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -35108,7 +35108,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -35154,7 +35154,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -35199,7 +35199,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -35244,7 +35244,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -35290,7 +35290,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -35336,7 +35336,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -35382,7 +35382,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -35428,7 +35428,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -35473,7 +35473,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -35518,7 +35518,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -35563,7 +35563,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -35609,7 +35609,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -35655,7 +35655,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -35701,7 +35701,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -35747,7 +35747,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -35792,7 +35792,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -35837,7 +35837,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -35882,7 +35882,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -35927,7 +35927,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -35973,7 +35973,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -36019,7 +36019,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -36065,7 +36065,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -36111,7 +36111,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -36156,7 +36156,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -36201,7 +36201,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -36246,7 +36246,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -36291,7 +36291,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -36336,7 +36336,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -36381,7 +36381,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -36426,7 +36426,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -36471,7 +36471,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -36516,7 +36516,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -36561,7 +36561,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -36606,7 +36606,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -36651,7 +36651,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -36696,7 +36696,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -36741,7 +36741,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -36786,7 +36786,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -36831,7 +36831,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -36876,7 +36876,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -36921,7 +36921,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -36966,7 +36966,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -37018,7 +37018,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -37063,7 +37063,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -37108,7 +37108,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -37153,7 +37153,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -37198,7 +37198,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -37243,7 +37243,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -37288,7 +37288,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -37333,7 +37333,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -37378,7 +37378,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -37423,7 +37423,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -37468,7 +37468,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -37514,7 +37514,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -37560,7 +37560,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -37606,7 +37606,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -37652,7 +37652,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -37699,7 +37699,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -37746,7 +37746,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -37793,7 +37793,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -37840,7 +37840,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -37887,7 +37887,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -37934,7 +37934,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -37980,7 +37980,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -38026,7 +38026,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -38072,7 +38072,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -38118,7 +38118,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -38164,7 +38164,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -38210,7 +38210,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -38257,7 +38257,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -38304,7 +38304,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -38350,7 +38350,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -38395,7 +38395,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -38441,7 +38441,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -38487,7 +38487,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -38533,7 +38533,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -38578,7 +38578,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -38623,7 +38623,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -38668,7 +38668,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -38714,7 +38714,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -38760,7 +38760,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -38806,7 +38806,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -38851,7 +38851,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -38896,7 +38896,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -38941,7 +38941,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -38987,7 +38987,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -39033,7 +39033,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -39079,7 +39079,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -39124,7 +39124,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -39169,7 +39169,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -39214,7 +39214,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -39259,7 +39259,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -39304,7 +39304,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -39349,7 +39349,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -39394,7 +39394,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -39439,7 +39439,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -39484,7 +39484,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -39529,7 +39529,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -39574,7 +39574,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -39619,7 +39619,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -39664,7 +39664,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -39709,7 +39709,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.mac.json b/testing/buildbot/chromium.mac.json
index a3438e9..afbca32 100644
--- a/testing/buildbot/chromium.mac.json
+++ b/testing/buildbot/chromium.mac.json
@@ -10314,7 +10314,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -10360,7 +10360,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -10406,7 +10406,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -10452,7 +10452,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -10498,7 +10498,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -10544,7 +10544,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -10590,7 +10590,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -10636,7 +10636,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -10682,7 +10682,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -10728,7 +10728,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -10774,7 +10774,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -10820,7 +10820,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -10866,7 +10866,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -10912,7 +10912,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -10958,7 +10958,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -11004,7 +11004,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -11050,7 +11050,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -11096,7 +11096,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -11142,7 +11142,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -11188,7 +11188,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -11234,7 +11234,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -11280,7 +11280,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -11326,7 +11326,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -11372,7 +11372,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -11418,7 +11418,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -11464,7 +11464,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -11510,7 +11510,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -11556,7 +11556,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -11602,7 +11602,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -11648,7 +11648,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -11694,7 +11694,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -11740,7 +11740,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -11786,7 +11786,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -11832,7 +11832,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -11879,7 +11879,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -11927,7 +11927,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -11975,7 +11975,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -12022,7 +12022,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -12069,7 +12069,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -12117,7 +12117,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -12164,7 +12164,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -12210,7 +12210,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -12256,7 +12256,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -12302,7 +12302,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -12348,7 +12348,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -12394,7 +12394,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -12440,7 +12440,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -12486,7 +12486,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -12532,7 +12532,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -12578,7 +12578,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -12624,7 +12624,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -12670,7 +12670,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -12716,7 +12716,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -12762,7 +12762,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -12808,7 +12808,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -12854,7 +12854,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -12900,7 +12900,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -12946,7 +12946,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -12992,7 +12992,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -13038,7 +13038,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -13084,7 +13084,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -13130,7 +13130,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -13176,7 +13176,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -13222,7 +13222,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -13269,7 +13269,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -13316,7 +13316,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -13362,7 +13362,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -13408,7 +13408,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -13454,7 +13454,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -13500,7 +13500,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -13546,7 +13546,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -13592,7 +13592,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -13638,7 +13638,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -13684,7 +13684,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -13730,7 +13730,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -13776,7 +13776,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -13822,7 +13822,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -13868,7 +13868,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -13914,7 +13914,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -13960,7 +13960,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -14006,7 +14006,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -14052,7 +14052,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -14098,7 +14098,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -14144,7 +14144,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -14190,7 +14190,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -14236,7 +14236,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -14282,7 +14282,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -14328,7 +14328,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -14374,7 +14374,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -14420,7 +14420,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -14466,7 +14466,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -14512,7 +14512,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -14558,7 +14558,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -14604,7 +14604,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -14650,7 +14650,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -14696,7 +14696,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -14742,7 +14742,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -14788,7 +14788,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -14834,7 +14834,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -14880,7 +14880,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -14926,7 +14926,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -14972,7 +14972,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -15018,7 +15018,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -15064,7 +15064,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -15110,7 +15110,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -15156,7 +15156,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -15202,7 +15202,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -15248,7 +15248,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -15294,7 +15294,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -15340,7 +15340,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -15386,7 +15386,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -15432,7 +15432,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -15478,7 +15478,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -15524,7 +15524,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -15578,7 +15578,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -15625,7 +15625,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -15672,7 +15672,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -15719,7 +15719,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -15766,7 +15766,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -15813,7 +15813,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -15860,7 +15860,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -15908,7 +15908,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -15956,7 +15956,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -16004,7 +16004,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -16052,7 +16052,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -16100,7 +16100,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -16148,7 +16148,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -16196,7 +16196,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -16244,7 +16244,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -16292,7 +16292,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -16340,7 +16340,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -16387,7 +16387,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -16434,7 +16434,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -16481,7 +16481,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -16528,7 +16528,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -16575,7 +16575,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -16622,7 +16622,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -16669,7 +16669,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -16716,7 +16716,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -16763,7 +16763,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -16810,7 +16810,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -16858,7 +16858,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -16906,7 +16906,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -16954,7 +16954,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -17002,7 +17002,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -17049,7 +17049,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -17096,7 +17096,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -17143,7 +17143,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -17190,7 +17190,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -17237,7 +17237,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -17284,7 +17284,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -17331,7 +17331,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -17378,7 +17378,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -17425,7 +17425,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -17472,7 +17472,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -17519,7 +17519,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -17566,7 +17566,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -17613,7 +17613,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -17660,7 +17660,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -17707,7 +17707,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -17759,7 +17759,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -17804,7 +17804,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -17849,7 +17849,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -17894,7 +17894,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -17939,7 +17939,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -17985,7 +17985,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -18031,7 +18031,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -18077,7 +18077,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -18124,7 +18124,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -18171,7 +18171,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -18218,7 +18218,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -18265,7 +18265,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -18311,7 +18311,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -18357,7 +18357,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -18403,7 +18403,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -18449,7 +18449,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -18496,7 +18496,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -18543,7 +18543,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -18589,7 +18589,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -18634,7 +18634,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -18679,7 +18679,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -18724,7 +18724,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -18770,7 +18770,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -18816,7 +18816,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -18861,7 +18861,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -18907,7 +18907,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -18953,7 +18953,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -18998,7 +18998,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -19043,7 +19043,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -19088,7 +19088,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
@@ -19133,7 +19133,7 @@
             {
               "cipd_package": "infra/tools/mac_toolchain/${platform}",
               "location": ".",
-              "revision": "git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a"
+              "revision": "git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776"
             }
           ],
           "dimension_sets": [
diff --git a/testing/buildbot/internal.chromeos.fyi.json b/testing/buildbot/internal.chromeos.fyi.json
index 0801f8b..287f87fb 100644
--- a/testing/buildbot/internal.chromeos.fyi.json
+++ b/testing/buildbot/internal.chromeos.fyi.json
@@ -708,9 +708,14 @@
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
+              "cipd_package": "infra/python/cpython/linux-amd64",
+              "location": "vpython_dir_linux_amd64",
+              "revision": "version:2.7.15.chromium14"
+            },
+            {
               "cipd_package": "infra/tools/luci/vpython/linux-amd64",
               "location": "vpython_dir_linux_amd64",
-              "revision": "git_revision:9a931a5307c46b16b1c12e01e8239d4a73830b89"
+              "revision": "git_revision:0f694cdc06ba054b9960aa1ae9766e45b53d02c1"
             }
           ],
           "dimension_sets": [
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl
index b8e8af94..4606f0a 100644
--- a/testing/buildbot/mixins.pyl
+++ b/testing/buildbot/mixins.pyl
@@ -688,7 +688,7 @@
         {
           "cipd_package": 'infra/tools/mac_toolchain/${platform}',
           'location': '.',
-          'revision': 'git_revision:796d2b92cff93fc2059623ce0a66284373ceea0a',
+          'revision': 'git_revision:8811b82233df02ee372cae1e19e379ca8c6a2776',
         },
       ],
     },
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index ccce99b..059ec24 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -766,6 +766,23 @@
           'service_account': 'chrome-gold-dev@chops-service-accounts.iam.gserviceaccount.com'
         },
       },
+      'android-marshmallow-x86-rel': {
+        'args': [
+          '--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator_m.chrome_public_test_apk.filter',
+        ],
+        'swarming': {
+          # TODO(crbug.com/1127110): Revisit shards and machine_type if there
+          # are n2 machines available in the test pool.
+          'shards': 25,
+          'dimension_sets': [
+            {
+              'machine_type': 'e2-standard-8', # use 8-core to shorten runtime
+            },
+          ],
+        },
+        # TODD(crbug.com/1127110): Remove experimental once it works fine.
+        'experiment_percentage': 50,
+      },
       'android-marshmallow-x86-rel-non-cq': {
         'args': [
           '--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator_m.chrome_public_test_apk.filter',
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index a7bd0eb..0f2b085 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -103,6 +103,18 @@
     # MM emulator CQ builder currently has limited capacity so some composition
     # test suites cannot be fullly enabled at once.
     'android_marshmallow_emulator_limited_capacity_gtests': {
+      # Tests from android_specific_chromium_gtests
+      'chrome_public_test_apk': {
+        'swarming': {
+          'shards': 20,
+        },
+        'mixins': [
+          'chrome-gold-service-account',
+          'skia_gold_test',
+          'enable_resultdb',
+        ],
+      },
+
       # Tests from chromium_gtests_for_devices_with_graphical_output
       'unit_tests': {
         'android_swarming': {
@@ -625,9 +637,14 @@
         'swarming': {
           'cipd_packages': [
             {
+              "cipd_package": 'infra/python/cpython/linux-amd64',
+              'location': 'vpython_dir_linux_amd64',
+              'revision': 'version:2.7.15.chromium14',
+            },
+            {
               "cipd_package": 'infra/tools/luci/vpython/linux-amd64',
               'location': 'vpython_dir_linux_amd64',
-              'revision': 'git_revision:9a931a5307c46b16b1c12e01e8239d4a73830b89',
+              'revision': 'git_revision:0f694cdc06ba054b9960aa1ae9766e45b53d02c1',
             }
           ],
           'shards': 3,
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 6cf92db..e8dd506 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -320,13 +320,13 @@
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
       '--impl-version=89',
     ],
-    'identifier': 'Implementation Library Skew Tests For 89.0.4389.49',
+    'identifier': 'Implementation Library Skew Tests For 89.0.4389.50',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M89',
-          'revision': 'version:89.0.4389.49',
+          'revision': 'version:89.0.4389.50',
         }
       ],
     },
@@ -344,13 +344,13 @@
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
       '--impl-version=88',
     ],
-    'identifier': 'Implementation Library Skew Tests For 88.0.4324.177',
+    'identifier': 'Implementation Library Skew Tests For 88.0.4324.179',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M88',
-          'revision': 'version:88.0.4324.177',
+          'revision': 'version:88.0.4324.179',
         }
       ],
     },
@@ -392,13 +392,13 @@
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
       '--impl-version=89',
     ],
-    'identifier': 'Implementation Library Skew Tests For 89.0.4389.49',
+    'identifier': 'Implementation Library Skew Tests For 89.0.4389.50',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M89',
-          'revision': 'version:89.0.4389.49',
+          'revision': 'version:89.0.4389.50',
         }
       ],
     },
@@ -416,13 +416,13 @@
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
       '--impl-version=88',
     ],
-    'identifier': 'Implementation Library Skew Tests For 88.0.4324.177',
+    'identifier': 'Implementation Library Skew Tests For 88.0.4324.179',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M88',
-          'revision': 'version:88.0.4324.177',
+          'revision': 'version:88.0.4324.179',
         }
       ],
     },
@@ -464,13 +464,13 @@
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
       '--client-version=89',
     ],
-    'identifier': 'Client Library Skew Tests For 89.0.4389.49',
+    'identifier': 'Client Library Skew Tests For 89.0.4389.50',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M89',
-          'revision': 'version:89.0.4389.49',
+          'revision': 'version:89.0.4389.50',
         }
       ],
     },
@@ -488,13 +488,13 @@
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
       '--client-version=88',
     ],
-    'identifier': 'Client Library Skew Tests For 88.0.4324.177',
+    'identifier': 'Client Library Skew Tests For 88.0.4324.179',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M88',
-          'revision': 'version:88.0.4324.177',
+          'revision': 'version:88.0.4324.179',
         }
       ],
     },
diff --git a/testing/scripts/common.py b/testing/scripts/common.py
index 0691772..5d6e9ffd 100644
--- a/testing/scripts/common.py
+++ b/testing/scripts/common.py
@@ -74,9 +74,9 @@
 
 
 def run_command(argv, env=None, cwd=None):
-  print 'Running %r in %r (env: %r)' % (argv, cwd, env)
+  print('Running %r in %r (env: %r)' % (argv, cwd, env))
   rc = test_env.run_command(argv, env=env, cwd=cwd)
-  print 'Command %r returned exit code %d' % (argv, rc)
+  print('Command %r returned exit code %d' % (argv, rc))
   return rc
 
 
@@ -345,13 +345,13 @@
     valid = True
     try:
       env['CHROME_HEADLESS'] = '1'
-      print 'Running command: %s\nwith env: %r' % (
-          ' '.join(cmd), env)
+      print('Running command: %s\nwith env: %r' % (
+          ' '.join(cmd), env))
       if self.options.xvfb:
         exit_code = xvfb.run_executable(cmd, env)
       else:
         exit_code = test_env.run_command(cmd, env=env)
-      print 'Command returned exit code %d' % exit_code
+      print('Command returned exit code %d' % exit_code)
       self.do_post_test_run_tasks()
       return exit_code
     except Exception:
diff --git a/testing/xvfb.py b/testing/xvfb.py
index 043cfda9..c7849e6 100755
--- a/testing/xvfb.py
+++ b/testing/xvfb.py
@@ -6,6 +6,8 @@
 """Runs tests with Xvfb and Openbox or Weston on Linux and normally on other
    platforms."""
 
+from __future__ import print_function
+
 import os
 import os.path
 import psutil
@@ -39,13 +41,13 @@
 
   thread.join(timeout_in_seconds)
   if thread.is_alive():
-    print >> sys.stderr, '%s running after SIGTERM, trying SIGKILL.' % name
+    print('%s running after SIGTERM, trying SIGKILL.\n' % name, file=sys.stderr)
     proc.kill()
 
   thread.join(timeout_in_seconds)
   if thread.is_alive():
-    print >> sys.stderr, \
-      '%s running after SIGTERM and SIGKILL; good luck!' % name
+    print('%s running after SIGTERM and SIGKILL; good luck!\n' % name,
+          file=sys.stderr)
 
 
 def launch_dbus(env):
@@ -78,7 +80,7 @@
         env[m.group(1)] = m.group(2)
     return int(env['DBUS_SESSION_BUS_PID'])
   except (subprocess.CalledProcessError, OSError, KeyError, ValueError) as e:
-    print 'Exception while running dbus_launch: %s' % e
+    print('Exception while running dbus_launch: %s' % e)
 
 
 # TODO(crbug.com/949194): Encourage setting flags to False.
@@ -123,7 +125,7 @@
   use_weston = False
   if '--use-weston' in cmd:
     if use_xvfb:
-      print >> sys.stderr, 'Unable to use Weston with xvfb.'
+      print('Unable to use Weston with xvfb.\n', file=sys.stderr)
       return 1
     use_weston = True
     cmd.remove('--use-weston')
@@ -216,10 +218,10 @@
 
     return test_env.run_executable(cmd, env, stdoutfile)
   except OSError as e:
-    print >> sys.stderr, 'Failed to start Xvfb or Openbox: %s' % str(e)
+    print('Failed to start Xvfb or Openbox: %s\n' % str(e), file=sys.stderr)
     return 1
   except _XvfbProcessError as e:
-    print >> sys.stderr, 'Xvfb fail: %s' % str(e)
+    print('Xvfb fail: %s\n' % str(e), file=sys.stderr)
     return 1
   finally:
     kill(openbox_proc, 'openbox')
@@ -277,10 +279,10 @@
     env['WAYLAND_DISPLAY'] = weston_proc_display
     return test_env.run_executable(cmd, env, stdoutfile)
   except OSError as e:
-    print >> sys.stderr, 'Failed to start Weston: %s' % str(e)
+    print('Failed to start Weston: %s\n' % str(e), file=sys.stderr)
     return 1
   except _WestonProcessError as e:
-    print >> sys.stderr, 'Weston fail: %s' % str(e)
+    print('Weston fail: %s\n' % str(e), file=sys.stderr)
     return 1
   finally:
     kill(weston_proc, 'weston')
@@ -378,22 +380,22 @@
   if not runtime_dir:
     runtime_dir = '/tmp/xdg-tmp-dir/'
     if not os.path.exists(runtime_dir):
-      os.makedirs(runtime_dir, 0700)
+      os.makedirs(runtime_dir, 0o700)
     env['XDG_RUNTIME_DIR'] = runtime_dir
 
 
 def main():
   usage = 'Usage: xvfb.py [command [--no-xvfb or --use-weston] args...]'
   if len(sys.argv) < 2:
-    print >> sys.stderr, usage
+    print(usage + '\n', file=sys.stderr)
     return 2
 
   # If the user still thinks the first argument is the execution directory then
   # print a friendly error message and quit.
   if os.path.isdir(sys.argv[1]):
-    print >> sys.stderr, (
-        'Invalid command: \"%s\" is a directory' % sys.argv[1])
-    print >> sys.stderr, usage
+    print('Invalid command: \"%s\" is a directory\n' % sys.argv[1],
+          file=sys.stderr)
+    print(usage + '\n', file=sys.stderr)
     return 3
 
   return run_executable(sys.argv[1:], os.environ.copy())
diff --git a/third_party/.gitignore b/third_party/.gitignore
index fed5bb89..2a0ff0f 100644
--- a/third_party/.gitignore
+++ b/third_party/.gitignore
@@ -122,7 +122,7 @@
 /jacoco/lib/
 /javalang/src/
 /jdk/current
-/jdk/extras
+/jdk/extras/java_8
 /jsr-305/src
 /junit/src
 /khronos_glcts
diff --git a/third_party/blink/public/common/tokens/multi_token_internal.h b/third_party/blink/public/common/tokens/multi_token_internal.h
index 8c2f7f37..f0fd3a0 100644
--- a/third_party/blink/public/common/tokens/multi_token_internal.h
+++ b/third_party/blink/public/common/tokens/multi_token_internal.h
@@ -55,8 +55,9 @@
 };
 
 // Specialization for util::TokenType<>.
-template <typename TokenTypeTag>
-struct MultiTokenVariantIsTokenType<::util::TokenType<TokenTypeTag>> {
+template <typename TokenTypeTag, bool kAllowImplicitConversion>
+struct MultiTokenVariantIsTokenType<
+    ::util::TokenType<TokenTypeTag, kAllowImplicitConversion>> {
   static constexpr bool kValue = true;
 
   // We expect an identical layout, which allows us to reinterpret_cast between
diff --git a/third_party/blink/public/common/tokens/tokens.h b/third_party/blink/public/common/tokens/tokens.h
index fad917f..37884d5 100644
--- a/third_party/blink/public/common/tokens/tokens.h
+++ b/third_party/blink/public/common/tokens/tokens.h
@@ -26,17 +26,32 @@
 // Uniquely identifies a blink::LocalFrame / blink::WebLocalFrame /
 // content::RenderFrame in a renderer process, and its content::RenderFrameHost
 // counterpart in the browser.
-using LocalFrameToken = util::TokenType<class LocalFrameTokenTypeMarker>;
+// TODO(crbug.com/1096617): Remove this after the token type migration.
+using LocalFrameToken = util::TokenType<class LocalFrameTokenTypeMarker,
+                                        /* kAllowImplicitConversion = */ true>;
 
 // Uniquely identifies a blink::RemoteFrame / blink::WebRemoteFrame /
 // content::RenderFrameProxy in a renderer process, and its
 // content::RenderFrameProxyHost counterpart in the browser. There can be
 // multiple RemoteFrames corresponding to a single LocalFrame, and each token
 // will be distinct.
-using RemoteFrameToken = util::TokenType<class RemoteFrameTokenTypeMarker>;
+// TODO(crbug.com/1096617): Remove this after the token type migration.
+using RemoteFrameToken = util::TokenType<class RemoteFrameTokenTypeMarker,
+                                         /* kAllowImplicitConversion = */ true>;
 
 // Can represent either type of FrameToken.
-using FrameToken = MultiToken<LocalFrameToken, RemoteFrameToken>;
+// TODO(crbug.com/1096617): Remove the implicit casting support after the token
+// type migration.
+class FrameToken : public MultiToken<LocalFrameToken, RemoteFrameToken> {
+ public:
+  using MultiToken<LocalFrameToken, RemoteFrameToken>::MultiToken;
+
+  // Allow implicit casting down to base::UnguessableToken during the migration.
+  // NOLINTNEXTLINE(google-explicit-constructor)
+  constexpr operator const base::UnguessableToken&() const& {
+    return this->value();
+  }
+};
 
 ////////////////////////////////////////////////////////////////////////////////
 // WORKER TOKENS
diff --git a/third_party/blink/public/mojom/feature_policy/feature_policy_feature.mojom b/third_party/blink/public/mojom/feature_policy/feature_policy_feature.mojom
index bc52c94..3f9ab83 100644
--- a/third_party/blink/public/mojom/feature_policy/feature_policy_feature.mojom
+++ b/third_party/blink/public/mojom/feature_policy/feature_policy_feature.mojom
@@ -49,18 +49,6 @@
   kVerticalScroll = 27,
   // Controls access to Screen Wake Lock
   kScreenWakeLock = 31,
-  // These are the defined sandbox features implemented as policy-controlled
-  // features.
-  kTopNavigation = 33,
-  kFormSubmission = 34,
-  kScript = 35,
-  kPopups = 36,
-  kPointerLock = 37,
-  kModals = 38,
-  kOrientationLock = 39,
-  kPresentation = 40,
-  // End of sandbox features.
-
   // Sample Origin Trial enabled feature. This is used only for testing.
   kFrobulate = 41,
   // Controls access to Serial
@@ -69,10 +57,6 @@
   kHid = 43,
   // Controls access to Idle Detection
   kIdleDetection = 44,
-
-  // Implements sandbox flag: allow-downloads.
-  kDownloads = 49,
-
   // Allow execution while not in the viewport.
   kExecutionWhileOutOfViewport = 50,
   // Allow execution while not rendered.
diff --git a/third_party/blink/public/mojom/file_system_access/file_system_access_manager.mojom b/third_party/blink/public/mojom/file_system_access/file_system_access_manager.mojom
index f23b919..80f0c6e 100644
--- a/third_party/blink/public/mojom/file_system_access/file_system_access_manager.mojom
+++ b/third_party/blink/public/mojom/file_system_access/file_system_access_manager.mojom
@@ -51,6 +51,13 @@
   // Prompts the user to select a file from the local filesystem. Returns an
   // error code if something failed, or a list of the selected entries on
   // success.
+  // See the documentation for how the `starting_directory` fields interact:
+  // https://github.com/WICG/file-system-access/blob/main/SuggestedNameAndDir.md
+  // |starting_directory_id| allows for specification of the "purpose" of a file
+  // picker invocation. When an ID is specified, the picker will remember the
+  // picked directory. The next time the matching ID is specified, the picker
+  // will default to this directory. The ID cannot exceed 32 characters in
+  // length and may only contain alphanumeric characters, '-', and '_'.
   // |well_known_starting_directory| opens the file picker at a well-known
   // directory.
   // |starting_directory_token| is resolved into a file or directory by the
@@ -63,6 +70,7 @@
   // |include_accepts_all| is treated as if it was true.
   ChooseEntries(ChooseFileSystemEntryType type,
                 array<ChooseFileSystemEntryAcceptsOption> accepts,
+                string starting_directory_id,
                 WellKnownDirectory well_known_starting_directory,
                 pending_remote<FileSystemAccessTransferToken>? starting_directory_token,
                 string suggested_name,  // Only allowed when type == kSaveFile.
diff --git a/third_party/blink/public/mojom/web_feature/web_feature.mojom b/third_party/blink/public/mojom/web_feature/web_feature.mojom
index bb52e6d..0853a35 100644
--- a/third_party/blink/public/mojom/web_feature/web_feature.mojom
+++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -3123,6 +3123,7 @@
   kV8WasmExceptionHandling = 3800,
   kWasmModuleSharing = 3801,
   kCrossOriginWasmModuleSharing = 3802,
+  kOverflowClipAlongEitherAxis = 3803,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/public/web/web_frame.h b/third_party/blink/public/web/web_frame.h
index fa2ec51..5dea045 100644
--- a/third_party/blink/public/web/web_frame.h
+++ b/third_party/blink/public/web/web_frame.h
@@ -72,9 +72,11 @@
   // Returns the number of live WebFrame objects, used for leak checking.
   static int InstanceCount();
 
-  // TODO(crbug.com/1096617): Remove the UnguessableToken version of this.
-  static WebFrame* FromFrameToken(const base::UnguessableToken&);
+  // TODO(crbug.com/1096617): Remove all but the FrameToken variant of this.
   static WebFrame* FromFrameToken(const FrameToken&);
+  static WebFrame* FromFrameToken(const base::UnguessableToken&);
+  static WebFrame* FromFrameToken(const LocalFrameToken&);
+  static WebFrame* FromFrameToken(const RemoteFrameToken&);
 
   virtual bool IsWebLocalFrame() const = 0;
   virtual WebLocalFrame* ToWebLocalFrame() = 0;
diff --git a/third_party/blink/renderer/bindings/core/v8/to_v8_traits.h b/third_party/blink/renderer/bindings/core/v8/to_v8_traits.h
index 392d4118..02b696a6 100644
--- a/third_party/blink/renderer/bindings/core/v8/to_v8_traits.h
+++ b/third_party/blink/renderer/bindings/core/v8/to_v8_traits.h
@@ -167,19 +167,6 @@
     // if |value| is a null string, V8String() returns an empty string.
     return V8String(script_state->GetIsolate(), value);
   }
-
-  static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
-                                        const AtomicString& value)
-      WARN_UNUSED_RESULT {
-    // if |value| is a null string, V8String() returns an empty string.
-    return V8String(script_state->GetIsolate(), value);
-  }
-
-  static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
-                                        const char* value) WARN_UNUSED_RESULT {
-    // if |value| is a nullptr, V8String() returns an empty string.
-    return V8String(script_state->GetIsolate(), value);
-  }
 };
 
 // ScriptWrappable
diff --git a/third_party/blink/renderer/bindings/core/v8/to_v8_traits_test.cc b/third_party/blink/renderer/bindings/core/v8/to_v8_traits_test.cc
index 19b999c9..69b6d64 100644
--- a/third_party/blink/renderer/bindings/core/v8/to_v8_traits_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/to_v8_traits_test.cc
@@ -116,72 +116,52 @@
 TEST(ToV8TraitsTest, String) {
   const V8TestingScope scope;
   const String string("string");
-  const AtomicString atomic_string("atomicString");
-  const char* const charptr_string = "arrayString";
+  const char* const charptr_string = "charptrString";
   // ByteString
   TEST_TOV8_TRAITS(scope, IDLByteStringV2, "string", string);
-  TEST_TOV8_TRAITS(scope, IDLByteStringV2, "atomicString", atomic_string);
-  TEST_TOV8_TRAITS(scope, IDLByteStringV2, "arrayString", charptr_string);
+  TEST_TOV8_TRAITS(scope, IDLByteStringV2, "charptrString", charptr_string);
   // DOMString
   TEST_TOV8_TRAITS(scope, IDLStringV2, "string", string);
-  TEST_TOV8_TRAITS(scope, IDLStringV2, "atomicString", atomic_string);
-  TEST_TOV8_TRAITS(scope, IDLStringV2, "arrayString", charptr_string);
+  TEST_TOV8_TRAITS(scope, IDLStringV2, "charptrString", charptr_string);
   TEST_TOV8_TRAITS(scope, IDLStringTreatNullAsEmptyStringV2, "string", string);
-  TEST_TOV8_TRAITS(scope, IDLStringTreatNullAsEmptyStringV2, "atomicString",
-                   atomic_string);
-  TEST_TOV8_TRAITS(scope, IDLStringTreatNullAsEmptyStringV2, "arrayString",
+  TEST_TOV8_TRAITS(scope, IDLStringTreatNullAsEmptyStringV2, "charptrString",
                    charptr_string);
   // USVString
   TEST_TOV8_TRAITS(scope, IDLUSVStringV2, "string", string);
-  TEST_TOV8_TRAITS(scope, IDLUSVStringV2, "atomicString", atomic_string);
-  TEST_TOV8_TRAITS(scope, IDLUSVStringV2, "arrayString", charptr_string);
+  TEST_TOV8_TRAITS(scope, IDLUSVStringV2, "charptrString", charptr_string);
   // [StringContext=TrustedHTML] DOMString
   TEST_TOV8_TRAITS(scope, IDLStringStringContextTrustedHTMLV2, "string",
                    string);
-  TEST_TOV8_TRAITS(scope, IDLStringStringContextTrustedHTMLV2, "atomicString",
-                   atomic_string);
-  TEST_TOV8_TRAITS(scope, IDLStringStringContextTrustedHTMLV2, "arrayString",
+  TEST_TOV8_TRAITS(scope, IDLStringStringContextTrustedHTMLV2, "charptrString",
                    charptr_string);
   TEST_TOV8_TRAITS(scope,
                    IDLStringStringContextTrustedHTMLTreatNullAsEmptyStringV2,
                    "string", string);
   TEST_TOV8_TRAITS(scope,
                    IDLStringStringContextTrustedHTMLTreatNullAsEmptyStringV2,
-                   "atomicString", atomic_string);
-  TEST_TOV8_TRAITS(scope,
-                   IDLStringStringContextTrustedHTMLTreatNullAsEmptyStringV2,
-                   "arrayString", charptr_string);
+                   "charptrString", charptr_string);
   // [StringContext=TrustedScript] DOMString
   TEST_TOV8_TRAITS(scope, IDLStringStringContextTrustedScriptV2, "string",
                    string);
-  TEST_TOV8_TRAITS(scope, IDLStringStringContextTrustedScriptV2, "atomicString",
-                   atomic_string);
-  TEST_TOV8_TRAITS(scope, IDLStringStringContextTrustedScriptV2, "arrayString",
-                   charptr_string);
+  TEST_TOV8_TRAITS(scope, IDLStringStringContextTrustedScriptV2,
+                   "charptrString", charptr_string);
   TEST_TOV8_TRAITS(scope,
                    IDLStringStringContextTrustedScriptTreatNullAsEmptyStringV2,
                    "string", string);
   TEST_TOV8_TRAITS(scope,
                    IDLStringStringContextTrustedScriptTreatNullAsEmptyStringV2,
-                   "atomicString", atomic_string);
-  TEST_TOV8_TRAITS(scope,
-                   IDLStringStringContextTrustedScriptTreatNullAsEmptyStringV2,
-                   "arrayString", charptr_string);
+                   "charptrString", charptr_string);
   // [StringContext=TrustedScriptURL] USVString
   TEST_TOV8_TRAITS(scope, IDLUSVStringStringContextTrustedScriptURLV2, "string",
                    string);
   TEST_TOV8_TRAITS(scope, IDLUSVStringStringContextTrustedScriptURLV2,
-                   "atomicString", atomic_string);
-  TEST_TOV8_TRAITS(scope, IDLUSVStringStringContextTrustedScriptURLV2,
-                   "arrayString", charptr_string);
+                   "charptrString", charptr_string);
 }
 
 TEST(ToV8TraitsTest, EmptyString) {
   const V8TestingScope scope;
   const String empty_string("");
   TEST_TOV8_TRAITS(scope, IDLStringV2, "", empty_string);
-  const AtomicString empty_atomic_string("");
-  TEST_TOV8_TRAITS(scope, IDLStringV2, "", empty_atomic_string);
   const char* const empty = "";
   TEST_TOV8_TRAITS(scope, IDLStringV2, "", empty);
 }
@@ -190,8 +170,6 @@
   const V8TestingScope scope;
   const String null_string;
   TEST_TOV8_TRAITS(scope, IDLStringV2, "", null_string);
-  const AtomicString null_atomic_string;
-  TEST_TOV8_TRAITS(scope, IDLStringV2, "", null_atomic_string);
   const char* const null = nullptr;
   TEST_TOV8_TRAITS(scope, IDLStringV2, "", null);
 }
diff --git a/third_party/blink/renderer/bindings/generated_in_modules.gni b/third_party/blink/renderer/bindings/generated_in_modules.gni
index 6f0b62b..cc911a9 100644
--- a/third_party/blink/renderer/bindings/generated_in_modules.gni
+++ b/third_party/blink/renderer/bindings/generated_in_modules.gni
@@ -211,6 +211,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_close_event_init.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_collected_client_data.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_collected_client_data.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_color_select_event_init.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_color_select_event_init.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_constant_source_options.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_constant_source_options.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_constrain_boolean_parameters.cc",
@@ -1410,6 +1412,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_clipboard_item.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_close_event.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_close_event.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_color_select_event.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_color_select_event.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_compression_stream.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_compression_stream.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_constant_source_node.cc",
@@ -1530,6 +1534,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_extendable_event.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_extendable_message_event.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_extendable_message_event.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_eye_dropper.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_eye_dropper.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_face_detector.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_face_detector.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_federated_credential.cc",
diff --git a/third_party/blink/renderer/bindings/idl_in_modules.gni b/third_party/blink/renderer/bindings/idl_in_modules.gni
index a271375..5b73008 100644
--- a/third_party/blink/renderer/bindings/idl_in_modules.gni
+++ b/third_party/blink/renderer/bindings/idl_in_modules.gni
@@ -212,6 +212,9 @@
           "//third_party/blink/renderer/modules/encryptedmedia/navigator_request_media_key_system_access.idl",
           "//third_party/blink/renderer/modules/eventsource/event_source.idl",
           "//third_party/blink/renderer/modules/eventsource/event_source_init.idl",
+          "//third_party/blink/renderer/modules/eyedropper/color_select_event.idl",
+          "//third_party/blink/renderer/modules/eyedropper/color_select_event_init.idl",
+          "//third_party/blink/renderer/modules/eyedropper/eye_dropper.idl",
           "//third_party/blink/renderer/modules/file_system_access/data_transfer_item_file_system_access.idl",
           "//third_party/blink/renderer/modules/file_system_access/directory_picker_options.idl",
           "//third_party/blink/renderer/modules/file_system_access/file_picker_accept_type.idl",
@@ -403,6 +406,7 @@
           "//third_party/blink/renderer/modules/mediastream/media_stream_track_event_init.idl",
           "//third_party/blink/renderer/modules/mediastream/media_stream_track_generator.idl",
           "//third_party/blink/renderer/modules/mediastream/media_stream_track_processor.idl",
+          "//third_party/blink/renderer/modules/mediastream/media_stream_track_signal.idl",
           "//third_party/blink/renderer/modules/mediastream/media_track_capabilities.idl",
           "//third_party/blink/renderer/modules/mediastream/media_track_constraint_set.idl",
           "//third_party/blink/renderer/modules/mediastream/media_track_constraints.idl",
diff --git a/third_party/blink/renderer/core/css/css_gradient_value.cc b/third_party/blink/renderer/core/css/css_gradient_value.cc
index 5bba1337..27aea6a 100644
--- a/third_party/blink/renderer/core/css/css_gradient_value.cc
+++ b/third_party/blink/renderer/core/css/css_gradient_value.cc
@@ -390,8 +390,8 @@
 
   const float first_offset = stops.front().offset;
   const float last_offset = stops.back().offset;
-  const float span =
-      std::min(last_offset - first_offset, std::numeric_limits<float>::max());
+  const float span = std::min(std::max(last_offset - first_offset, 0.f),
+                              std::numeric_limits<float>::max());
 
   if (fabs(span) < std::numeric_limits<float>::epsilon()) {
     // All stops are coincident -> use a single clamped offset value.
@@ -418,8 +418,7 @@
     // stop offsets should be monotonically increasing in [0 , 1]
     DCHECK_GE(normalized_offset, 0);
     DCHECK_LE(normalized_offset, 1);
-    DCHECK(i == 0 ||
-           normalized_offset >= (stops[i - 1].offset - first_offset) / span);
+    DCHECK(i == 0 || normalized_offset >= desc.stops.back().stop);
 
     desc.stops.emplace_back(normalized_offset, stops[i].color);
   }
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 698809c..62030873 100644
--- a/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
@@ -420,7 +420,7 @@
   }
 }
 
-void StyleAdjuster::AdjustOverflow(ComputedStyle& style) {
+void StyleAdjuster::AdjustOverflow(ComputedStyle& style, Element* element) {
   DCHECK(style.OverflowX() != EOverflow::kVisible ||
          style.OverflowY() != EOverflow::kVisible);
 
@@ -457,6 +457,11 @@
     else if (style.OverflowY() == EOverflow::kClip)
       style.SetOverflowY(EOverflow::kHidden);
   }
+  if (element && (style.OverflowX() == EOverflow::kClip ||
+                  style.OverflowY() == EOverflow::kClip)) {
+    UseCounter::Count(element->GetDocument(),
+                      WebFeature::kOverflowClipAlongEitherAxis);
+  }
 }
 
 static void AdjustStyleForDisplay(ComputedStyle& style,
@@ -733,7 +738,7 @@
 
   if (style.OverflowX() != EOverflow::kVisible ||
       style.OverflowY() != EOverflow::kVisible)
-    AdjustOverflow(style);
+    AdjustOverflow(style, element);
 
   // overflow-clip-margin only applies if 'overflow: clip' is set along both
   // axis or 'contain: paint'.
diff --git a/third_party/blink/renderer/core/css/resolver/style_adjuster.h b/third_party/blink/renderer/core/css/resolver/style_adjuster.h
index f2314180..e32fe368 100644
--- a/third_party/blink/renderer/core/css/resolver/style_adjuster.h
+++ b/third_party/blink/renderer/core/css/resolver/style_adjuster.h
@@ -43,7 +43,7 @@
   static void AdjustStyleForEditing(ComputedStyle&);
 
  private:
-  static void AdjustOverflow(ComputedStyle& style);
+  static void AdjustOverflow(ComputedStyle& style, Element* element);
   static void AdjustForForcedColorsMode(ComputedStyle& style);
 };
 
diff --git a/third_party/blink/renderer/core/css/resolver/style_adjuster_test.cc b/third_party/blink/renderer/core/css/resolver/style_adjuster_test.cc
index f7dbe27b9..b83eb72 100644
--- a/third_party/blink/renderer/core/css/resolver/style_adjuster_test.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_adjuster_test.cc
@@ -255,4 +255,24 @@
             target->GetComputedStyle()->GetEffectiveTouchAction());
 }
 
+TEST_F(StyleAdjusterTest, OverflowClipUseCount) {
+  GetDocument().SetBaseURLOverride(KURL("http://test.com"));
+  SetBodyInnerHTML(R"HTML(
+    <div></div>
+    <div style='overflow: hidden'></div>
+    <div style='overflow: scroll'></div>
+    <div></div>
+  )HTML");
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_FALSE(
+      GetDocument().IsUseCounted(WebFeature::kOverflowClipAlongEitherAxis));
+
+  SetBodyInnerHTML(R"HTML(
+    <div style='overflow: clip'></div>
+  )HTML");
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_TRUE(
+      GetDocument().IsUseCounted(WebFeature::kOverflowClipAlongEitherAxis));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/style_rule_counter_style.h b/third_party/blink/renderer/core/css/style_rule_counter_style.h
index 89d490be..c82cbc01 100644
--- a/third_party/blink/renderer/core/css/style_rule_counter_style.h
+++ b/third_party/blink/renderer/core/css/style_rule_counter_style.h
@@ -80,7 +80,7 @@
   }
 
   bool HasFailedOrCanceledSubresources() const {
-    // TODO(crbug.com/687225): Implement.
+    // TODO(crbug.com/1176323): Handle image symbols when we implement it.
     return false;
   }
 
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index b576a68f..baa2e9b 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -39,6 +39,7 @@
 #include <utility>
 
 #include "base/auto_reset.h"
+#include "base/debug/dump_without_crashing.h"
 #include "base/macros.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/optional.h"
@@ -6927,7 +6928,14 @@
   if (!HaveRenderBlockingResourcesLoaded())
     return;
   font_preload_manager_->WillBeginRendering();
-  View()->BeginLifecycleUpdates();
+  // TODO(japhet): If IsActive() is true, View() should always be non-null.
+  // Speculative fix for https://crbug.com/1171891
+  if (auto* view = View()) {
+    view->BeginLifecycleUpdates();
+  } else {
+    NOTREACHED();
+    base::debug::DumpWithoutCrashing();
+  }
 }
 
 Vector<IconURL> Document::IconURLs(int icon_types_mask) {
diff --git a/third_party/blink/renderer/core/dom/node.cc b/third_party/blink/renderer/core/dom/node.cc
index 13caf93f..8e981c6 100644
--- a/third_party/blink/renderer/core/dom/node.cc
+++ b/third_party/blink/renderer/core/dom/node.cc
@@ -2856,6 +2856,7 @@
 void Node::HandleLocalEvents(Event& event) {
   if (IsDocumentNode() && GetDocument().PopupShowing() &&
       event.eventPhase() == Event::kCapturingPhase) {
+    DCHECK(RuntimeEnabledFeatures::HTMLPopupElementEnabled());
     // There is a popup visible - check if this event should "light dismiss"
     // one or more popups.
     const AtomicString& event_type = event.type();
@@ -2869,6 +2870,12 @@
         }
       }
       GetDocument().HideAllPopupsUntil(closest_popup_parent);
+    } else if (event_type == event_type_names::kKeydown) {
+      const KeyboardEvent* key_event = DynamicTo<KeyboardEvent>(event);
+      if (key_event && key_event->key() == "Escape") {
+        // Escape key just pops the topmost <popup> off the stack.
+        GetDocument().HideTopmostPopupElement();
+      }
     }
   }
 
diff --git a/third_party/blink/renderer/core/events/event_type_names.json5 b/third_party/blink/renderer/core/events/event_type_names.json5
index dcc00853d..3e44434 100644
--- a/third_party/blink/renderer/core/events/event_type_names.json5
+++ b/third_party/blink/renderer/core/events/event_type_names.json5
@@ -73,6 +73,7 @@
     "click",
     "close",
     "closing",
+    "colorselect",
     "complete",
     "compositionend",
     "compositionstart",
diff --git a/third_party/blink/renderer/core/events/pointer_event.h b/third_party/blink/renderer/core/events/pointer_event.h
index ce41f9a..de54b4e 100644
--- a/third_party/blink/renderer/core/events/pointer_event.h
+++ b/third_party/blink/renderer/core/events/pointer_event.h
@@ -13,7 +13,7 @@
 
 class PointerEventInit;
 
-class CORE_EXPORT PointerEvent final : public MouseEvent {
+class CORE_EXPORT PointerEvent : public MouseEvent {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
diff --git a/third_party/blink/renderer/core/frame/frame_serializer.cc b/third_party/blink/renderer/core/frame/frame_serializer.cc
index 0211a33..442bc666 100644
--- a/third_party/blink/renderer/core/frame/frame_serializer.cc
+++ b/third_party/blink/renderer/core/frame/frame_serializer.cc
@@ -520,7 +520,8 @@
       break;
 
     case CSSRule::kCounterStyleRule:
-      // TODO(crbug.com/687225): Implement
+      // TODO(crbug.com/1176323): Handle image symbols in @counter-style rules
+      // when we implement it.
       break;
 
     // Rules in which no external resources can be referenced
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
index 0513215..f05575b 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -621,14 +621,26 @@
 }
 
 // static
+WebFrame* WebFrame::FromFrameToken(const FrameToken& frame_token) {
+  auto* frame = Frame::ResolveFrame(frame_token);
+  return WebFrame::FromCoreFrame(frame);
+}
+
+// static
 WebFrame* WebFrame::FromFrameToken(const base::UnguessableToken& frame_token) {
   auto* frame = Frame::ResolveFrame(frame_token);
   return WebFrame::FromCoreFrame(frame);
 }
 
 // static
-WebFrame* WebFrame::FromFrameToken(const FrameToken& frame_token) {
-  auto* frame = Frame::ResolveFrame(frame_token);
+WebFrame* WebFrame::FromFrameToken(const LocalFrameToken& frame_token) {
+  auto* frame = Frame::ResolveFrame(FrameToken(frame_token));
+  return WebFrame::FromCoreFrame(frame);
+}
+
+// static
+WebFrame* WebFrame::FromFrameToken(const RemoteFrameToken& frame_token) {
+  auto* frame = Frame::ResolveFrame(FrameToken(frame_token));
   return WebFrame::FromCoreFrame(frame);
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_object.h b/third_party/blink/renderer/core/layout/layout_object.h
index 9d09cd6..1537d8f 100644
--- a/third_party/blink/renderer/core/layout/layout_object.h
+++ b/third_party/blink/renderer/core/layout/layout_object.h
@@ -606,23 +606,24 @@
     return ShouldApplyLayoutContainment(StyleRef());
   }
 
+  inline bool IsEligibleForSizeContainment() const {
+    NOT_DESTROYED();
+    return (!IsInline() || IsAtomicInlineLevel()) && !IsRubyText() &&
+           (!IsTablePart() || IsTableCaption()) && !IsTable();
+  }
   inline bool ShouldApplySizeContainment() const {
     NOT_DESTROYED();
-    return StyleRef().ContainsSize() &&
-           (!IsInline() || IsAtomicInlineLevel()) && !IsRubyText() &&
-           (!IsTablePart() || IsTableCaption()) && !IsTable();
+    return StyleRef().ContainsSize() && IsEligibleForSizeContainment();
   }
   inline bool ShouldApplyInlineSizeContainment() const {
     NOT_DESTROYED();
     return (StyleRef().ContainsInlineSize() || StyleRef().ContainsSize()) &&
-           (!IsInline() || IsAtomicInlineLevel()) && !IsRubyText() &&
-           (!IsTablePart() || IsTableCaption()) && !IsTable();
+           IsEligibleForSizeContainment();
   }
   inline bool ShouldApplyBlockSizeContainment() const {
     NOT_DESTROYED();
     return (StyleRef().ContainsBlockSize() || StyleRef().ContainsSize()) &&
-           (!IsInline() || IsAtomicInlineLevel()) && !IsRubyText() &&
-           (!IsTablePart() || IsTableCaption()) && !IsTable();
+           IsEligibleForSizeContainment();
   }
   inline bool ShouldApplyStyleContainment() const {
     NOT_DESTROYED();
@@ -639,7 +640,13 @@
   }
   inline bool IsContainerForContainerQueries() const {
     NOT_DESTROYED();
-    return ShouldApplyLayoutContainment() && ShouldApplySizeContainment();
+    // TODO(crbug.com/1146092): Determine from the container queries what kind
+    // of size containment we require. Right now we allow query matching as long
+    // as there's any size containment at all specified, but we need the axes in
+    // the queries and the size containment to match.
+    return ShouldApplyLayoutContainment() &&
+           (ShouldApplyInlineSizeContainment() ||
+            ShouldApplyBlockSizeContainment());
   }
 
   inline bool IsStackingContext() const {
diff --git a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.cc b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.cc
index c4474e9..41301a3 100644
--- a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.cc
@@ -369,6 +369,28 @@
   return LayoutRectOutsets();
 }
 
+// Effective column index is index of columns with mergeable
+// columns skipped. Used in a11y.
+unsigned LayoutNGTable::AbsoluteColumnToEffectiveColumn(
+    unsigned absolute_column_index) const {
+  NOT_DESTROYED();
+  if (!cached_table_columns_) {
+    NOTREACHED();
+    return absolute_column_index;
+  }
+  unsigned effective_column_index = 0;
+  unsigned column_count = cached_table_columns_.get()->data.size();
+  for (unsigned current_column_index = 0; current_column_index < column_count;
+       ++current_column_index) {
+    if (current_column_index != 0 &&
+        !cached_table_columns_.get()->data[current_column_index].is_mergeable)
+      ++effective_column_index;
+    if (current_column_index == absolute_column_index)
+      return effective_column_index;
+  }
+  return effective_column_index;
+}
+
 bool LayoutNGTable::IsFirstCell(const LayoutNGTableCellInterface& cell) const {
   NOT_DESTROYED();
   const LayoutNGTableRowInterface* row = cell.RowInterface();
diff --git a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.h b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.h
index 5c70f14..c3eb81b 100644
--- a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.h
+++ b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.h
@@ -172,14 +172,8 @@
     return ShouldCollapseBorders() ? 0 : StyleRef().VerticalBorderSpacing();
   }
 
-  // Legacy had a concept of colspan column compression. This is a legacy
-  // method to map between absolute and compressed columns.
-  // Because NG does not compress columns, absolute and effective are the same.
   unsigned AbsoluteColumnToEffectiveColumn(
-      unsigned absolute_column_index) const final {
-    NOT_DESTROYED();
-    return absolute_column_index;
-  }
+      unsigned absolute_column_index) const final;
 
   // NG does not need this method. Sections are not cached.
   void RecalcSectionsIfNeeded() const final {}
diff --git a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.cc b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.cc
index b94c476..e675fd2 100644
--- a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.cc
@@ -132,7 +132,12 @@
 // behaviour is correct. Consider removing these methods.
 unsigned LayoutNGTableSection::NumEffectiveColumns() const {
   NOT_DESTROYED();
-  return To<LayoutNGTable>(TableInterface()->ToLayoutObject())->ColumnCount();
+  const LayoutNGTable* table = Table();
+  DCHECK(table);
+  wtf_size_t column_count = table->ColumnCount();
+  if (column_count == 0)
+    return 0;
+  return table->AbsoluteColumnToEffectiveColumn(column_count - 1) + 1;
 }
 
 // TODO(crbug.com/1079133): Used by AXLayoutObject::IsDataTable/ColumnCount,
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
index 768b3733..4621a92cc 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
@@ -55,9 +55,17 @@
     const NGTableTypes::Columns& column_constraints,
     LayoutUnit inline_table_border_padding,
     LayoutUnit inline_border_spacing) {
-  unsigned inline_space_count = 2 + (column_constraints.data.size() > 1
-                                         ? column_constraints.data.size() - 1
-                                         : 0);
+  unsigned inline_space_count = 2;
+  bool is_first_column = true;
+  for (const NGTableTypes::Column& column : column_constraints.data) {
+    if (!column.is_mergeable) {
+      if (is_first_column)
+        is_first_column = false;
+      else
+        inline_space_count++;
+    }
+  }
+
   return inline_table_border_padding +
          inline_space_count * inline_border_spacing;
 }
@@ -146,7 +154,13 @@
     *has_collapsed_columns =
         *has_collapsed_columns || column_constraint.is_collapsed;
     column_location.offset = column_offset;
-    if (shrink_collapsed && column_constraint.is_collapsed) {
+    if (column_constraints.data[i].is_mergeable &&
+        (column_sizes[i] == kIndefiniteSize ||
+         column_sizes[i] == LayoutUnit())) {
+      // Empty mergeable columns are treated as collapsed.
+      column_location.size = LayoutUnit();
+      column_location.is_collapsed = true;
+    } else if (shrink_collapsed && column_constraint.is_collapsed) {
       column_location.is_collapsed = true;
       column_location.size = LayoutUnit();
     } else {
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc
index 98b84d70..d7e735e 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc
@@ -22,7 +22,6 @@
   unsigned percent_columns_count = 0;
   unsigned fixed_columns_count = 0;
   unsigned auto_columns_count = 0;
-
   // What guesses mean is described in table specification.
   // https://www.w3.org/TR/css-tables-3/#width-distribution-algorithm
   enum { kMinGuess, kPercentageGuess, kSpecifiedGuess, kMaxGuess, kAboveMax };
@@ -38,7 +37,10 @@
     all_columns_count++;
     DCHECK(column->min_inline_size);
     DCHECK(column->max_inline_size);
-    if (column->percent) {
+
+    if (column->is_mergeable) {
+      ;  // Mergeable columns are ignored.
+    } else if (column->percent) {
       percent_columns_count++;
       total_percent += *column->percent;
       LayoutUnit percent_inline_size =
@@ -92,6 +94,8 @@
       LayoutUnit* computed_size = computed_sizes.begin();
       for (const NGTableTypes::Column* column = start_column;
            column != end_column; ++column, ++computed_size) {
+        if (column->is_mergeable)
+          continue;
         *computed_size = column->min_inline_size.value_or(LayoutUnit());
       }
     } break;
@@ -106,6 +110,8 @@
       LayoutUnit* last_computed_size = nullptr;
       for (const NGTableTypes::Column* column = start_column;
            column != end_column; ++column, ++computed_size) {
+        if (column->is_mergeable)
+          continue;
         if (column->percent) {
           last_computed_size = computed_size;
           LayoutUnit percent_inline_size =
@@ -144,6 +150,8 @@
       LayoutUnit* computed_size = computed_sizes.begin();
       for (const NGTableTypes::Column* column = start_column;
            column != end_column; ++column, ++computed_size) {
+        if (column->is_mergeable)
+          continue;
         if (column->percent) {
           *computed_size = column->ResolvePercentInlineSize(target_inline_size);
         } else if (column->is_constrained) {
@@ -190,6 +198,8 @@
       LayoutUnit* computed_size = computed_sizes.begin();
       for (const NGTableTypes::Column* column = start_column;
            column != end_column; ++column, ++computed_size) {
+        if (column->is_mergeable)
+          continue;
         if (column->percent) {
           *computed_size = column->ResolvePercentInlineSize(target_inline_size);
         } else if (column->is_constrained || is_exact_match) {
@@ -226,6 +236,8 @@
         LayoutUnit* computed_size = computed_sizes.begin();
         for (const NGTableTypes::Column* column = start_column;
              column != end_column; ++column, ++computed_size) {
+          if (column->is_mergeable)
+            continue;
           if (column->percent) {
             *computed_size =
                 column->ResolvePercentInlineSize(target_inline_size);
@@ -256,6 +268,8 @@
         LayoutUnit* computed_size = computed_sizes.begin();
         for (const NGTableTypes::Column* column = start_column;
              column != end_column; ++column, ++computed_size) {
+          if (column->is_mergeable)
+            continue;
           if (column->percent) {
             *computed_size =
                 column->ResolvePercentInlineSize(target_inline_size);
@@ -286,6 +300,8 @@
         LayoutUnit* computed_size = computed_sizes.begin();
         for (const NGTableTypes::Column* column = start_column;
              column != end_column; ++column, ++computed_size) {
+          if (column->is_mergeable)
+            continue;
           DCHECK(column->percent);
           last_computed_size = computed_size;
           if (total_percent > 0.0f) {
@@ -447,18 +463,32 @@
   NGTableTypes::Column* end_column = start_column + colspan_cell.span;
   DCHECK_NE(start_column, end_column);
 
+  // Inline sizes for redistribution exclude border spacing.
+  LayoutUnit total_inner_border_spacing;
+  unsigned effective_span = 0;
+  bool is_first_column = true;
+  for (NGTableTypes::Column* column = start_column; column != end_column;
+       ++column) {
+    if (column->is_mergeable)
+      continue;
+    ++effective_span;
+    if (!is_first_column)
+      total_inner_border_spacing += inline_border_spacing;
+    else
+      is_first_column = false;
+  }
   LayoutUnit colspan_cell_min_inline_size;
   LayoutUnit colspan_cell_max_inline_size;
   // Colspanned cells only distribute min inline size if constrained.
   if (colspan_cell.cell_inline_constraint.is_constrained) {
     colspan_cell_min_inline_size =
         (colspan_cell.cell_inline_constraint.min_inline_size -
-         (colspan_cell.span - 1) * inline_border_spacing)
+         total_inner_border_spacing)
             .ClampNegativeToZero();
   }
   colspan_cell_max_inline_size =
       (colspan_cell.cell_inline_constraint.max_inline_size -
-       (colspan_cell.span - 1) * inline_border_spacing)
+       total_inner_border_spacing)
           .ClampNegativeToZero();
 
   // Distribute min/max/percentage evenly between all cells.
@@ -468,18 +498,19 @@
       colspan_cell.cell_inline_constraint.percent.value_or(0.0f);
 
   LayoutUnit new_min_size = LayoutUnit(colspan_cell_min_inline_size /
-                                       static_cast<float>(colspan_cell.span));
+                                       static_cast<float>(effective_span));
   LayoutUnit new_max_size = LayoutUnit(colspan_cell_max_inline_size /
-                                       static_cast<float>(colspan_cell.span));
+                                       static_cast<float>(effective_span));
   base::Optional<float> new_percent;
   if (colspan_cell.cell_inline_constraint.percent) {
-    new_percent =
-        *colspan_cell.cell_inline_constraint.percent / colspan_cell.span;
+    new_percent = *colspan_cell.cell_inline_constraint.percent / effective_span;
   }
 
   NGTableTypes::Column* last_column;
   for (NGTableTypes::Column* column = start_column; column < end_column;
        ++column) {
+    if (column->is_mergeable)
+      continue;
     last_column = column;
     rounding_error_min_inline_size -= new_min_size;
     rounding_error_max_inline_size -= new_max_size;
@@ -521,13 +552,25 @@
   NGTableTypes::Column* end_column = start_column + effective_span;
 
   // Inline sizes for redistribution exclude border spacing.
+  LayoutUnit total_inner_border_spacing;
+  bool is_first_column = true;
+  for (NGTableTypes::Column* column = start_column; column != end_column;
+       ++column) {
+    if (!column->is_mergeable) {
+      if (!is_first_column)
+        total_inner_border_spacing += inline_border_spacing;
+      else
+        is_first_column = false;
+    }
+  }
+
   LayoutUnit colspan_cell_min_inline_size =
       (colspan_cell.cell_inline_constraint.min_inline_size -
-       (effective_span - 1) * inline_border_spacing)
+       total_inner_border_spacing)
           .ClampNegativeToZero();
   LayoutUnit colspan_cell_max_inline_size =
       (colspan_cell.cell_inline_constraint.max_inline_size -
-       (effective_span - 1) * inline_border_spacing)
+       total_inner_border_spacing)
           .ClampNegativeToZero();
   base::Optional<float> colspan_cell_percent =
       colspan_cell.cell_inline_constraint.percent;
@@ -544,6 +587,8 @@
         column->max_inline_size = LayoutUnit();
       if (!column->min_inline_size)
         column->min_inline_size = LayoutUnit();
+      if (column->is_mergeable)
+        continue;
       all_columns_count++;
       if (column->percent) {
         percent_columns_count++;
@@ -559,7 +604,7 @@
       // max_inline_size.
       for (NGTableTypes::Column* column = start_column; column != end_column;
            ++column) {
-        if (column->percent)
+        if (column->percent || column->is_mergeable)
           continue;
         float column_percent;
         if (nonpercent_columns_max_inline_size != LayoutUnit()) {
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers_test.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers_test.cc
index ac8f540..cc9c95a17 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers_test.cc
@@ -21,7 +21,9 @@
                                 percent,
                                 /* border_padding */ LayoutUnit(),
                                 is_constrained,
-                                /* is_collapsed */ false};
+                                /* is_collapsed */ false,
+                                /* is_table_fixed */ false,
+                                /* is_mergeable */ false};
   }
 
   NGTableTypes::Row MakeRow(int block_size,
@@ -155,16 +157,16 @@
   column_constraints->data.Shrink(0);
   column_constraints->data.push_back(
       NGTableTypes::Column{LayoutUnit(0), column_widths[0], base::nullopt,
-                           LayoutUnit(), false, false});
+                           LayoutUnit(), false, false, false, false});
   column_constraints->data.push_back(
       NGTableTypes::Column{LayoutUnit(3.33333), column_widths[1], base::nullopt,
-                           LayoutUnit(), false, false});
+                           LayoutUnit(), false, false, false, false});
   column_constraints->data.push_back(
       NGTableTypes::Column{LayoutUnit(3.33333), column_widths[2], base::nullopt,
-                           LayoutUnit(), false, false});
+                           LayoutUnit(), false, false, false, false});
   column_constraints->data.push_back(
       NGTableTypes::Column{LayoutUnit(0), column_widths[3], base::nullopt,
-                           LayoutUnit(), false, false});
+                           LayoutUnit(), false, false, false, false});
 
   LayoutUnit assignable_table_inline_size =
       column_widths[0] + column_widths[1] + column_widths[2] + column_widths[3];
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc
index a3a0b4b..bf9f8b0d 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc
@@ -85,6 +85,7 @@
   InlineSizesFromStyle(style, /* inline_border_padding */ LayoutUnit(),
                        /* is_parallel */ true, &inline_size, &min_inline_size,
                        &max_inline_size, &percentage_inline_size);
+  bool is_mergeable;
   if (!inline_size)
     inline_size = default_inline_size;
   if (min_inline_size && inline_size)
@@ -93,13 +94,16 @@
   if (percentage_inline_size && *percentage_inline_size == 0.0f)
     percentage_inline_size.reset();
   bool is_collapsed = style.Visibility() == EVisibility::kCollapse;
-  return Column{min_inline_size.value_or(LayoutUnit()),
-                inline_size,
+  if (is_table_fixed) {
+    is_mergeable = false;
+  } else {
+    is_mergeable = (inline_size.value_or(LayoutUnit()) == LayoutUnit()) &&
+                   (percentage_inline_size.value_or(0.0f) == 0.0f);
+  }
+  return Column(min_inline_size.value_or(LayoutUnit()), inline_size,
                 percentage_inline_size,
-                LayoutUnit() /* percent_border_padding */,
-                is_constrained,
-                is_collapsed,
-                is_table_fixed};
+                LayoutUnit() /* percent_border_padding */, is_constrained,
+                is_collapsed, is_table_fixed, is_mergeable);
 }
 
 // Implements https://www.w3.org/TR/css-tables-3/#computing-cell-measures
@@ -286,7 +290,8 @@
   // Constrained columns in fixed tables take precedence over cells.
   if (is_constrained && is_table_fixed)
     return;
-
+  if (!is_table_fixed)
+    is_mergeable = false;
   if (min_inline_size) {
     if (min_inline_size < cell->min_inline_size) {
       min_inline_size = cell->min_inline_size;
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h
index 47e7177..b892c3f2 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h
@@ -57,6 +57,23 @@
   // Constraint for a column.
   struct Column {
     DISALLOW_NEW();
+    Column(const base::Optional<LayoutUnit>& min_inline_size,
+           const base::Optional<LayoutUnit>& max_inline_size,
+           const base::Optional<float>& percent,
+           LayoutUnit percent_border_padding,
+           bool is_constrained,
+           bool is_collapsed,
+           bool is_table_fixed,
+           bool is_mergeable)
+        : min_inline_size(min_inline_size),
+          max_inline_size(max_inline_size),
+          percent(percent),
+          percent_border_padding(percent_border_padding),
+          is_constrained(is_constrained),
+          is_collapsed(is_collapsed),
+          is_table_fixed(is_table_fixed),
+          is_mergeable(is_mergeable) {}
+    Column() = default;
     // These members are initialized from <col> and <colgroup>, then they
     // accumulate data from |CellInlineConstraint|s.
     base::Optional<LayoutUnit> min_inline_size;
@@ -68,6 +85,7 @@
     bool is_constrained = false;
     bool is_collapsed = false;
     bool is_table_fixed = false;
+    bool is_mergeable = false;
 
     void Encompass(const base::Optional<NGTableTypes::CellInlineConstraint>&);
     LayoutUnit ResolvePercentInlineSize(
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc
index d981861a..03c16f5 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc
@@ -24,6 +24,55 @@
 
 namespace {
 
+// Mergeable columns cannot be distributed to.
+// Make at least one spanned column is distributable.
+void EnsureDistributableColumnExists(
+    wtf_size_t start_column_index,
+    wtf_size_t span,
+    NGTableTypes::Columns* column_constraints) {
+  if (span == 0)
+    return;
+  DCHECK_LT(start_column_index, column_constraints->data.size());
+  wtf_size_t effective_span =
+      std::min(span, column_constraints->data.size() - start_column_index);
+  if (effective_span == 0)
+    return;
+  NGTableTypes::Column* start_column =
+      &column_constraints->data[start_column_index];
+  NGTableTypes::Column* end_column = start_column + effective_span;
+
+  NGTableTypes::Column* first_mergeable_column = nullptr;
+  for (NGTableTypes::Column* column = start_column; column != end_column;
+       ++column) {
+    if (!column->is_collapsed) {
+      if (!column->is_mergeable) {
+        // Found non-collapsed, non mergeable column, nothing to do.
+        return;
+      } else if (!first_mergeable_column) {
+        // Found first non-collapsed, mergeable column.
+        first_mergeable_column = column;
+      }
+    }
+  }
+  // The interesting problem being solved here is interaction between
+  // collapsed and mergeable columns.
+  // All columns that are created by colspanned cell are mergeable by
+  // default. Without collapsing, the first column would always be
+  // marked as !mergeable.
+  // What to do if the first column collapses? If that was the only
+  // non-mergeable column, the entire cell would merge into first column,
+  // and collapse.
+  // To prevent "whole cell hidden if 1st cell is collapsed",
+  // we try to make first non-collapsed column mergeable.
+  // If all columns collapse, first cell is marked as meargable.
+  if (first_mergeable_column) {
+    // Some columns were not collapsed, mark first as mergeable.
+    first_mergeable_column->is_mergeable = false;
+  } else {
+    start_column->is_mergeable = false;
+  }
+}
+
 // Applies cell/wide cell constraints to columns.
 // Guarantees columns min/max widths have non-empty values.
 void ApplyCellConstraintsToColumnConstraints(
@@ -32,8 +81,37 @@
     bool is_fixed_layout,
     NGTableTypes::ColspanCells* colspan_cell_constraints,
     NGTableTypes::Columns* column_constraints) {
-  if (column_constraints->data.size() < cell_constraints.size())
-    column_constraints->data.resize(cell_constraints.size());
+  // Satisfy prerequisites for cell merging:
+
+  if (column_constraints->data.size() < cell_constraints.size()) {
+    // Column constraint must exist for each cell.
+    NGTableTypes::Column default_column;
+    default_column.is_table_fixed = is_fixed_layout;
+    default_column.is_mergeable = !is_fixed_layout;
+    wtf_size_t column_count =
+        cell_constraints.size() - column_constraints->data.size();
+    // Must loop because WTF::Vector does not support resize with default value.
+    for (wtf_size_t i = 0; i < column_count; ++i)
+      column_constraints->data.push_back(default_column);
+    DCHECK_EQ(column_constraints->data.size(), cell_constraints.size());
+
+  } else if (column_constraints->data.size() > cell_constraints.size()) {
+    // Trim mergeable columns off the end.
+    wtf_size_t last_non_merged_column = column_constraints->data.size() - 1;
+    while (last_non_merged_column + 1 > cell_constraints.size() &&
+           column_constraints->data[last_non_merged_column].is_mergeable) {
+      --last_non_merged_column;
+    }
+    column_constraints->data.resize(last_non_merged_column + 1);
+    DCHECK_GE(column_constraints->data.size(), cell_constraints.size());
+  }
+  // Make sure there exists a non-mergeable column for each colspanned cell.
+  for (const NGTableTypes::ColspanCell& colspan_cell :
+       *colspan_cell_constraints) {
+    EnsureDistributableColumnExists(colspan_cell.start_column,
+                                    colspan_cell.span, column_constraints);
+  }
+
   // Distribute cell constraints to column constraints.
   for (wtf_size_t i = 0; i < cell_constraints.size(); ++i) {
     column_constraints->data[i].Encompass(cell_constraints[i]);
diff --git a/third_party/blink/renderer/core/paint/box_painter_base.cc b/third_party/blink/renderer/core/paint/box_painter_base.cc
index 5d357c6..2519088 100644
--- a/third_party/blink/renderer/core/paint/box_painter_base.cc
+++ b/third_party/blink/renderer/core/paint/box_painter_base.cc
@@ -578,7 +578,7 @@
                               GraphicsContext& context,
                               const Vector<Color>& animated_colors,
                               const Vector<double>& offsets) {
-  FloatRect src_rect = dest_rect.Rect();
+  FloatRect src_rect(FloatPoint(), dest_rect.Rect().Size());
   BackgroundColorPaintImageGenerator* generator =
       document->GetFrame()->GetBackgroundColorPaintImageGenerator();
   scoped_refptr<Image> paint_worklet_image =
diff --git a/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc b/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc
index 017de97..ed1f714c 100644
--- a/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc
+++ b/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc
@@ -307,8 +307,13 @@
 
 static bool ObjectTypeSupportsCompositedTransformAnimation(
     const LayoutObject& object) {
-  if (object.IsSVGChild())
-    return RuntimeEnabledFeatures::CompositeSVGEnabled();
+  if (object.IsSVGChild()) {
+    if (!RuntimeEnabledFeatures::CompositeSVGEnabled())
+      return false;
+    // Transforms are not supported on hidden containers, inlines, or text.
+    return !object.IsSVGHiddenContainer() && !object.IsLayoutInline() &&
+           !object.IsText();
+  }
   // Transforms don't apply on non-replaced inline elements.
   return object.IsBox();
 }
diff --git a/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder_test.cc b/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder_test.cc
index 72585f1..1629912 100644
--- a/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder_test.cc
+++ b/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder_test.cc
@@ -413,4 +413,46 @@
             CompositingReasonFinder::DirectReasonsForPaintProperties(*text));
 }
 
+TEST_F(CompositingReasonFinderTest, NotSupportedTransformAnimationsOnSVG) {
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      * { animation: transformKeyframes 1s infinite; }
+      @keyframes transformKeyframes {
+        0% { transform: rotate(-5deg); }
+        100% { transform: rotate(5deg); }
+      }
+    </style>
+    <svg>
+      <defs id="defs" />
+      <text id="text">text content
+        <tspan id="tspan">tspan content</tspan>
+      </text>
+    </svg>
+  )HTML");
+
+  auto* defs = GetLayoutObjectByElementId("defs");
+  EXPECT_EQ(CompositingReason::kNone,
+            CompositingReasonFinder::DirectReasonsForPaintProperties(*defs));
+
+  auto* text = GetLayoutObjectByElementId("text");
+  EXPECT_EQ(CompositingReason::kActiveTransformAnimation,
+            CompositingReasonFinder::DirectReasonsForPaintProperties(*text));
+
+  auto* text_content = text->SlowFirstChild();
+  ASSERT_TRUE(text_content->IsText());
+  EXPECT_EQ(
+      CompositingReason::kNone,
+      CompositingReasonFinder::DirectReasonsForPaintProperties(*text_content));
+
+  auto* tspan = GetLayoutObjectByElementId("tspan");
+  EXPECT_EQ(CompositingReason::kNone,
+            CompositingReasonFinder::DirectReasonsForPaintProperties(*tspan));
+
+  auto* tspan_content = tspan->SlowFirstChild();
+  ASSERT_TRUE(tspan_content->IsText());
+  EXPECT_EQ(
+      CompositingReason::kNone,
+      CompositingReasonFinder::DirectReasonsForPaintProperties(*tspan_content));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/ng/ng_table_painters.cc b/third_party/blink/renderer/core/paint/ng/ng_table_painters.cc
index 807fd94..7c96dcf5 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_table_painters.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_table_painters.cc
@@ -40,13 +40,14 @@
   NGTableCollapsedEdge(const NGTableCollapsedEdge& source, int offset)
       : borders_(source.borders_) {
     // If edge index would have been negative.
-    if (offset < 0 && edge_index_ < static_cast<wtf_size_t>(abs(offset))) {
+    if (offset < 0 &&
+        source.edge_index_ < static_cast<wtf_size_t>(std::abs(offset))) {
       edge_index_ = UINT_MAX;
-      return;
+    } else {
+      edge_index_ = source.edge_index_ + offset;
+      if (edge_index_ >= borders_.EdgeCount())
+        edge_index_ = UINT_MAX;
     }
-    edge_index_ = source.edge_index_ + offset;
-    if (edge_index_ >= borders_.EdgeCount())
-      edge_index_ = UINT_MAX;
     InitCachedProps();
   }
 
diff --git a/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.cc b/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.cc
index 15cd7d7..abb1c4b 100644
--- a/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.cc
+++ b/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.cc
@@ -46,6 +46,13 @@
     to_transfer = DOMArrayBuffer::Create(Content()->Data(), ByteLength());
   }
 
+  return to_transfer->TransferDetachable(isolate, result);
+}
+
+bool DOMArrayBuffer::TransferDetachable(v8::Isolate* isolate,
+                                        ArrayBufferContents& result) {
+  DCHECK(IsDetachable(isolate));
+
   if (IsDetached()) {
     result.Detach();
     return false;
@@ -61,7 +68,7 @@
 
   Vector<v8::Local<v8::ArrayBuffer>, 4> buffer_handles;
   v8::HandleScope handle_scope(isolate);
-  AccumulateArrayBuffersForAllWorlds(isolate, to_transfer, buffer_handles);
+  AccumulateArrayBuffersForAllWorlds(isolate, this, buffer_handles);
 
   for (const auto& buffer_handle : buffer_handles)
     buffer_handle->Detach();
diff --git a/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h b/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h
index 00ba385d..e9a85d3 100644
--- a/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h
+++ b/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h
@@ -78,6 +78,9 @@
 
   v8::Local<v8::Value> Wrap(v8::Isolate*,
                             v8::Local<v8::Object> creation_context) override;
+
+ private:
+  bool TransferDetachable(v8::Isolate*, ArrayBufferContents& result);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/BUILD.gn b/third_party/blink/renderer/modules/BUILD.gn
index 88417ec..ae3e67c 100644
--- a/third_party/blink/renderer/modules/BUILD.gn
+++ b/third_party/blink/renderer/modules/BUILD.gn
@@ -107,6 +107,7 @@
     "//third_party/blink/renderer/modules/encryptedmedia",
     "//third_party/blink/renderer/modules/eventsource",
     "//third_party/blink/renderer/modules/exported",
+    "//third_party/blink/renderer/modules/eyedropper",
     "//third_party/blink/renderer/modules/filesystem",
     "//third_party/blink/renderer/modules/file_system_access",
     "//third_party/blink/renderer/modules/font_access",
@@ -391,6 +392,7 @@
     "mediastream/pushable_media_stream_video_source_test.cc",
     "mediastream/user_media_client_test.cc",
     "mediastream/video_track_adapter_unittest.cc",
+    "mediastream/video_track_signal_underlying_sink_test.cc",
     "mediastream/webaudio_media_stream_audio_sink_test.cc",
     "mediastream/webmediaplayer_ms_test.cc",
     "nfc/nfc_proxy_test.cc",
diff --git a/third_party/blink/renderer/modules/event_target_modules_names.json5 b/third_party/blink/renderer/modules/event_target_modules_names.json5
index e0bcc950..2a634aa 100644
--- a/third_party/blink/renderer/modules/event_target_modules_names.json5
+++ b/third_party/blink/renderer/modules/event_target_modules_names.json5
@@ -15,6 +15,7 @@
     "BluetoothRemoteGATTCharacteristic",
     "CookieStore",
     "DelegatedInkTrail",
+    "EyeDropper",
     "DeviceService",
     "MediaKeySession",
     "FileWriter",
diff --git a/third_party/blink/renderer/modules/eyedropper/BUILD.gn b/third_party/blink/renderer/modules/eyedropper/BUILD.gn
new file mode 100644
index 0000000..9d70bb3
--- /dev/null
+++ b/third_party/blink/renderer/modules/eyedropper/BUILD.gn
@@ -0,0 +1,14 @@
+# Copyright 2021 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//third_party/blink/renderer/modules/modules.gni")
+
+blink_modules_sources("eyedropper") {
+  sources = [
+    "color_select_event.cc",
+    "color_select_event.h",
+    "eye_dropper.cc",
+    "eye_dropper.h",
+  ]
+}
diff --git a/third_party/blink/renderer/modules/eyedropper/OWNERS b/third_party/blink/renderer/modules/eyedropper/OWNERS
new file mode 100644
index 0000000..8a8afda
--- /dev/null
+++ b/third_party/blink/renderer/modules/eyedropper/OWNERS
@@ -0,0 +1 @@
+iopopesc@microsoft.com
diff --git a/third_party/blink/renderer/modules/eyedropper/color_select_event.cc b/third_party/blink/renderer/modules/eyedropper/color_select_event.cc
new file mode 100644
index 0000000..cffd6ea2
--- /dev/null
+++ b/third_party/blink/renderer/modules/eyedropper/color_select_event.cc
@@ -0,0 +1,42 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/eyedropper/color_select_event.h"
+
+#include "third_party/blink/renderer/modules/eyedropper/color_select_event_init.h"
+
+namespace blink {
+
+ColorSelectEvent::ColorSelectEvent(const AtomicString& type,
+                                   const ColorSelectEventInit* initializer,
+                                   base::TimeTicks platform_time_stamp,
+                                   SyntheticEventType synthetic_event_type,
+                                   WebMenuSourceType menu_source_type)
+    : PointerEvent(type,
+                   initializer,
+                   platform_time_stamp,
+                   synthetic_event_type,
+                   menu_source_type),
+      value_(initializer->value()) {}
+
+ColorSelectEvent* ColorSelectEvent::Create(
+    const AtomicString& type,
+    const ColorSelectEventInit* initializer,
+    base::TimeTicks platform_time_stamp,
+    SyntheticEventType synthetic_event_type,
+    WebMenuSourceType menu_source_type) {
+  return MakeGarbageCollected<ColorSelectEvent>(
+      type, initializer, platform_time_stamp, synthetic_event_type,
+      menu_source_type);
+}
+
+String ColorSelectEvent::value() const {
+  return value_;
+}
+
+void ColorSelectEvent::Trace(Visitor* visitor) const {
+  PointerEvent::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/eyedropper/color_select_event.h b/third_party/blink/renderer/modules/eyedropper/color_select_event.h
new file mode 100644
index 0000000..3b09874
--- /dev/null
+++ b/third_party/blink/renderer/modules/eyedropper/color_select_event.h
@@ -0,0 +1,41 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_EYEDROPPER_COLOR_SELECT_EVENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_EYEDROPPER_COLOR_SELECT_EVENT_H_
+
+#include "third_party/blink/renderer/bindings/modules/v8/v8_color_select_event_init.h"
+#include "third_party/blink/renderer/core/events/pointer_event.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+
+namespace blink {
+
+class ColorSelectEvent final : public PointerEvent {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  explicit ColorSelectEvent(const AtomicString& type,
+                            const ColorSelectEventInit* initializer,
+                            base::TimeTicks platform_time_stamp,
+                            SyntheticEventType synthetic_event_type,
+                            WebMenuSourceType menu_source_type);
+
+  static ColorSelectEvent* Create(
+      const AtomicString& type,
+      const ColorSelectEventInit* initializer,
+      base::TimeTicks platform_time_stamp = base::TimeTicks::Now(),
+      SyntheticEventType synthetic_event_type = kRealOrIndistinguishable,
+      WebMenuSourceType menu_source_type = kMenuSourceNone);
+
+  String value() const;
+
+  void Trace(Visitor*) const override;
+
+ private:
+  String value_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_EYEDROPPER_COLOR_SELECT_EVENT_H_
diff --git a/third_party/blink/renderer/modules/eyedropper/color_select_event.idl b/third_party/blink/renderer/modules/eyedropper/color_select_event.idl
new file mode 100644
index 0000000..97b2bfb
--- /dev/null
+++ b/third_party/blink/renderer/modules/eyedropper/color_select_event.idl
@@ -0,0 +1,12 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/EyeDropper/explainer.md
+
+[Exposed=Window, RuntimeEnabled=EyeDropperAPI]
+interface ColorSelectEvent : PointerEvent {
+  constructor(DOMString type, optional ColorSelectEventInit eventInitDict = {});
+
+  readonly attribute DOMString value;
+};
diff --git a/third_party/blink/renderer/modules/eyedropper/color_select_event_init.idl b/third_party/blink/renderer/modules/eyedropper/color_select_event_init.idl
new file mode 100644
index 0000000..0285a737
--- /dev/null
+++ b/third_party/blink/renderer/modules/eyedropper/color_select_event_init.idl
@@ -0,0 +1,9 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/EyeDropper/explainer.md
+
+dictionary ColorSelectEventInit : PointerEventInit {
+    DOMString value = "";
+};
diff --git a/third_party/blink/renderer/modules/eyedropper/eye_dropper.cc b/third_party/blink/renderer/modules/eyedropper/eye_dropper.cc
new file mode 100644
index 0000000..615ae23
--- /dev/null
+++ b/third_party/blink/renderer/modules/eyedropper/eye_dropper.cc
@@ -0,0 +1,48 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/eyedropper/eye_dropper.h"
+
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/modules/event_target_modules.h"
+
+namespace blink {
+
+EyeDropper::EyeDropper(ScriptState* script_state)
+    : ExecutionContextClient(ExecutionContext::From(script_state)),
+      opened_(false) {}
+
+EyeDropper* EyeDropper::Create(ScriptState* script_state) {
+  return MakeGarbageCollected<EyeDropper>(script_state);
+}
+
+ExecutionContext* EyeDropper::GetExecutionContext() const {
+  return ExecutionContextClient::GetExecutionContext();
+}
+
+const AtomicString& EyeDropper::InterfaceName() const {
+  return event_target_names::kEyeDropper;
+}
+
+EyeDropper::~EyeDropper() = default;
+
+bool EyeDropper::opened() const {
+  return opened_;
+}
+
+ScriptPromise EyeDropper::open() {
+  // TODO(iopopesc): Add support for open.
+  return ScriptPromise();
+}
+
+void EyeDropper::close() {
+  // TODO(iopopesc): Add support for close.
+}
+
+void EyeDropper::Trace(Visitor* visitor) const {
+  EventTargetWithInlineData::Trace(visitor);
+  ExecutionContextClient::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/eyedropper/eye_dropper.h b/third_party/blink/renderer/modules/eyedropper/eye_dropper.h
new file mode 100644
index 0000000..e131a0b2
--- /dev/null
+++ b/third_party/blink/renderer/modules/eyedropper/eye_dropper.h
@@ -0,0 +1,59 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_EYEDROPPER_EYE_DROPPER_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_EYEDROPPER_EYE_DROPPER_H_
+
+#include "base/macros.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
+#include "third_party/blink/renderer/core/dom/events/event_target.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+
+namespace blink {
+
+// The EyeDropper API enables developers to use a browser-supplied eyedropper
+// in their web applications. This feature is still
+// under development, and is not part of the standard. It can be enabled
+// by passing --enable-blink-features=EyeDropperAPI. See
+// https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/EyeDropper/explainer.md
+// for more details.
+class EyeDropper final : public EventTargetWithInlineData,
+                         public ExecutionContextClient {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  explicit EyeDropper(ScriptState*);
+  static EyeDropper* Create(ScriptState*);
+  EyeDropper(const EyeDropper&) = delete;
+  EyeDropper& operator=(const EyeDropper&) = delete;
+  ~EyeDropper() override;
+
+  // EventTarget:
+  ExecutionContext* GetExecutionContext() const override;
+  const AtomicString& InterfaceName() const override;
+
+  // Opens the eyedropper and replaces the cursor with a browser-defined preview
+  // and sets opened boolean to true.
+  ScriptPromise open();
+
+  // Exits the eyedropper mode and the cursor returns to its regular
+  // functionality. Sets opened boolean to false.
+  void close();
+
+  // States if the eyedropper is opened and in use.
+  bool opened() const;
+
+  DEFINE_ATTRIBUTE_EVENT_LISTENER(colorselect, kColorselect)
+  DEFINE_ATTRIBUTE_EVENT_LISTENER(close, kClose)
+
+  void Trace(Visitor*) const override;
+
+ private:
+  bool opened_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_EYEDROPPER_EYE_DROPPER_H_
diff --git a/third_party/blink/renderer/modules/eyedropper/eye_dropper.idl b/third_party/blink/renderer/modules/eyedropper/eye_dropper.idl
new file mode 100644
index 0000000..75278162
--- /dev/null
+++ b/third_party/blink/renderer/modules/eyedropper/eye_dropper.idl
@@ -0,0 +1,19 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/EyeDropper/explainer.md
+
+[Exposed=Window, RuntimeEnabled=EyeDropperAPI]
+interface EyeDropper : EventTarget {
+  [CallWith=ScriptState] constructor();
+
+  Promise<void> open();
+  void close();
+
+  readonly attribute boolean opened;
+
+  // Event handler attributes
+  attribute EventHandler oncolorselect;
+  attribute EventHandler onclose;
+};
diff --git a/third_party/blink/renderer/modules/eyedropper/idls.gni b/third_party/blink/renderer/modules/eyedropper/idls.gni
new file mode 100644
index 0000000..96c64c9
--- /dev/null
+++ b/third_party/blink/renderer/modules/eyedropper/idls.gni
@@ -0,0 +1,10 @@
+# Copyright 2021 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+modules_idl_files = [
+  "color_select_event.idl",
+  "eye_dropper.idl",
+]
+
+modules_dictionary_idl_files = [ "color_select_event_init.idl" ]
diff --git a/third_party/blink/renderer/modules/file_system_access/directory_picker_options.idl b/third_party/blink/renderer/modules/file_system_access/directory_picker_options.idl
index c6ab741..87d4c4be 100644
--- a/third_party/blink/renderer/modules/file_system_access/directory_picker_options.idl
+++ b/third_party/blink/renderer/modules/file_system_access/directory_picker_options.idl
@@ -4,5 +4,6 @@
 
 // https://wicg.github.io/file-system-access/#dictdef-directorypickeroptions
 dictionary DirectoryPickerOptions {
+  [RuntimeEnabled=FileSystemAccessAPIExperimental] USVString id;
   [RuntimeEnabled=FileSystemAccessAPIExperimental] (WellKnownDirectory or FileSystemHandle)? startIn;
 };
diff --git a/third_party/blink/renderer/modules/file_system_access/file_picker_options.idl b/third_party/blink/renderer/modules/file_system_access/file_picker_options.idl
index 5e1dfb34..37ab8a59 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_picker_options.idl
+++ b/third_party/blink/renderer/modules/file_system_access/file_picker_options.idl
@@ -15,5 +15,6 @@
 dictionary FilePickerOptions {
   sequence<FilePickerAcceptType> types;
   boolean excludeAcceptAllOption = false;
+  [RuntimeEnabled=FileSystemAccessAPIExperimental] USVString id;
   [RuntimeEnabled=FileSystemAccessAPIExperimental] (WellKnownDirectory or FileSystemHandle)? startIn;
 };
diff --git a/third_party/blink/renderer/modules/file_system_access/global_file_system_access.cc b/third_party/blink/renderer/modules/file_system_access/global_file_system_access.cc
index 45c55b4..baf21387 100644
--- a/third_party/blink/renderer/modules/file_system_access/global_file_system_access.cc
+++ b/third_party/blink/renderer/modules/file_system_access/global_file_system_access.cc
@@ -14,6 +14,7 @@
 #include "third_party/blink/public/mojom/file_system_access/file_system_access_manager.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/file_system_access/file_system_access_manager.mojom-blink.h"
 #include "third_party/blink/public/mojom/file_system_access/file_system_access_manager.mojom-shared.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_directory_picker_options.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_file_picker_accept_type.h"
@@ -33,7 +34,6 @@
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/network/http_parsers.h"
-#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
 #include "third_party/blink/renderer/platform/wtf/text/ascii_ctype.h"
@@ -42,6 +42,8 @@
 
 namespace {
 
+constexpr char kDefaultStartingDirectoryId[] = "";
+
 constexpr bool IsHTTPWhitespace(UChar chr) {
   return chr == ' ' || chr == '\n' || chr == '\t' || chr == '\r';
 }
@@ -50,6 +52,10 @@
   return IsASCIIAlphanumeric(chr) || chr == '+' || chr == '.';
 }
 
+bool IsValidIdCodePoint(UChar chr) {
+  return IsASCIIAlphanumeric(chr) || chr == '_' || chr == '-';
+}
+
 bool VerifyIsValidExtension(const String& extension,
                             ExceptionState& exception_state) {
   if (!extension.StartsWith(".")) {
@@ -76,6 +82,21 @@
   return true;
 }
 
+String VerifyIsValidId(const String& id, ExceptionState& exception_state) {
+  if (!id.IsAllSpecialCharacters<IsValidIdCodePoint>()) {
+    exception_state.ThrowTypeError("ID '" + id +
+                                   "' contains invalid characters.");
+    return String();
+  }
+  if (id.length() > 32) {
+    exception_state.ThrowTypeError("ID '" + id +
+                                   "' cannot be longer than 32 characters.");
+    return String();
+  }
+
+  return std::move(id);
+}
+
 bool AddExtension(const String& extension,
                   Vector<String>& extensions,
                   ExceptionState& exception_state) {
@@ -196,6 +217,7 @@
     LocalDOMWindow& window,
     mojom::blink::ChooseFileSystemEntryType chooser_type,
     Vector<mojom::blink::ChooseFileSystemEntryAcceptsOptionPtr> accepts,
+    const String& starting_directory_id,
     mojom::blink::WellKnownDirectory well_known_starting_directory,
     mojo::PendingRemote<mojom::blink::FileSystemAccessTransferToken>
         starting_directory_token,
@@ -215,12 +237,9 @@
 
   auto* raw_manager = manager.get();
   raw_manager->ChooseEntries(
-      chooser_type, std::move(accepts),
-      RuntimeEnabledFeatures::FileSystemAccessAPIExperimentalEnabled()
-          ? std::move(well_known_starting_directory)
-          : mojom::blink::WellKnownDirectory::kDefault,
-      std::move(starting_directory_token), std::move(suggested_name),
-      accept_all,
+      chooser_type, std::move(accepts), std::move(starting_directory_id),
+      well_known_starting_directory, std::move(starting_directory_token),
+      std::move(suggested_name), accept_all,
       WTF::Bind(
           [](ScriptPromiseResolver* resolver,
              mojo::Remote<mojom::blink::FileSystemAccessManager>,
@@ -288,6 +307,13 @@
     return ScriptPromise();
   }
 
+  String starting_directory_id = kDefaultStartingDirectoryId;
+  if (options->hasId()) {
+    starting_directory_id = VerifyIsValidId(options->id(), exception_state);
+    if (exception_state.HadException())
+      return ScriptPromise();
+  }
+
   auto well_known_starting_directory =
       mojom::blink::WellKnownDirectory::kDefault;
   mojo::PendingRemote<blink::mojom::blink::FileSystemAccessTransferToken> token;
@@ -313,9 +339,9 @@
       options->multiple()
           ? mojom::blink::ChooseFileSystemEntryType::kOpenMultipleFiles
           : mojom::blink::ChooseFileSystemEntryType::kOpenFile,
-      std::move(accepts), std::move(well_known_starting_directory),
-      std::move(token), /*suggested_name=*/"",
-      !options->excludeAcceptAllOption(),
+      std::move(accepts), std::move(starting_directory_id),
+      std::move(well_known_starting_directory), std::move(token),
+      /*suggested_name=*/"", !options->excludeAcceptAllOption(),
       /*return_as_sequence=*/true);
 }
 
@@ -338,6 +364,13 @@
     return ScriptPromise();
   }
 
+  String starting_directory_id = kDefaultStartingDirectoryId;
+  if (options->hasId()) {
+    starting_directory_id = VerifyIsValidId(options->id(), exception_state);
+    if (exception_state.HadException())
+      return ScriptPromise();
+  }
+
   auto well_known_starting_directory =
       mojom::blink::WellKnownDirectory::kDefault;
   mojo::PendingRemote<blink::mojom::blink::FileSystemAccessTransferToken> token;
@@ -360,12 +393,9 @@
 
   return ShowFilePickerImpl(
       script_state, window, mojom::blink::ChooseFileSystemEntryType::kSaveFile,
-      std::move(accepts), std::move(well_known_starting_directory),
-      std::move(token),
-      (options->hasSuggestedName() &&
-       RuntimeEnabledFeatures::FileSystemAccessAPIExperimentalEnabled())
-          ? options->suggestedName()
-          : "",
+      std::move(accepts), std::move(starting_directory_id),
+      std::move(well_known_starting_directory), std::move(token),
+      options->hasSuggestedName() ? options->suggestedName() : "",
       !options->excludeAcceptAllOption(),
       /*return_as_sequence=*/false);
 }
@@ -378,6 +408,13 @@
     ExceptionState& exception_state) {
   UseCounter::Count(window, WebFeature::kFileSystemPickerMethod);
 
+  String starting_directory_id = kDefaultStartingDirectoryId;
+  if (options->hasId()) {
+    starting_directory_id = VerifyIsValidId(options->id(), exception_state);
+    if (exception_state.HadException())
+      return ScriptPromise();
+  }
+
   auto well_known_starting_directory =
       mojom::blink::WellKnownDirectory::kDefault;
   mojo::PendingRemote<blink::mojom::blink::FileSystemAccessTransferToken> token;
@@ -401,9 +438,9 @@
   return ShowFilePickerImpl(
       script_state, window,
       mojom::blink::ChooseFileSystemEntryType::kOpenDirectory, {},
+      std::move(starting_directory_id),
       std::move(well_known_starting_directory), std::move(token),
-      /*suggested_name=*/"",
-      /*accept_all=*/true,
+      /*suggested_name=*/"", /*accept_all=*/true,
       /*return_as_sequence=*/false);
 }
 
diff --git a/third_party/blink/renderer/modules/file_system_access/global_file_system_access_test.cc b/third_party/blink/renderer/modules/file_system_access/global_file_system_access_test.cc
index 2ee36ed..8a431c0 100644
--- a/third_party/blink/renderer/modules/file_system_access/global_file_system_access_test.cc
+++ b/third_party/blink/renderer/modules/file_system_access/global_file_system_access_test.cc
@@ -60,6 +60,7 @@
   void ChooseEntries(
       mojom::ChooseFileSystemEntryType type,
       WTF::Vector<mojom::blink::ChooseFileSystemEntryAcceptsOptionPtr> accepts,
+      const WTF::String& starting_directory_id,
       mojom::WellKnownDirectory well_known_starting_directory,
       mojo::PendingRemote<mojom::blink::FileSystemAccessTransferToken> token,
       const WTF::String& suggested_name,
diff --git a/third_party/blink/renderer/modules/mediastream/BUILD.gn b/third_party/blink/renderer/modules/mediastream/BUILD.gn
index cfd79eb..ccb78a95 100644
--- a/third_party/blink/renderer/modules/mediastream/BUILD.gn
+++ b/third_party/blink/renderer/modules/mediastream/BUILD.gn
@@ -104,6 +104,8 @@
     "video_track_adapter.h",
     "video_track_adapter_settings.cc",
     "video_track_adapter_settings.h",
+    "video_track_signal_underlying_sink.cc",
+    "video_track_signal_underlying_sink.h",
     "web_media_stream_device_observer.cc",
     "web_media_stream_utils.cc",
     "webaudio_media_stream_audio_sink.cc",
diff --git a/third_party/blink/renderer/modules/mediastream/idls.gni b/third_party/blink/renderer/modules/mediastream/idls.gni
index 0e316f4..7f7baf3 100644
--- a/third_party/blink/renderer/modules/mediastream/idls.gni
+++ b/third_party/blink/renderer/modules/mediastream/idls.gni
@@ -25,6 +25,7 @@
   "media_stream_constraints.idl",
   "media_stream_event_init.idl",
   "media_stream_track_event_init.idl",
+  "media_stream_track_signal.idl",
   "media_track_capabilities.idl",
   "media_track_constraint_set.idl",
   "media_track_constraints.idl",
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_track_signal.idl b/third_party/blink/renderer/modules/mediastream/media_stream_track_signal.idl
new file mode 100644
index 0000000..905c908
--- /dev/null
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_track_signal.idl
@@ -0,0 +1,15 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// https://w3c.github.io/mediacapture-insertable-streams/#dictdef-mediastreamtracksignal
+
+enum MediaStreamTrackSignalType {
+  "request-frame",
+  "set-min-frame-rate"
+};
+
+dictionary MediaStreamTrackSignal {
+  required MediaStreamTrackSignalType signalType;
+  double frameRate;
+};
diff --git a/third_party/blink/renderer/modules/mediastream/mock_media_stream_video_source.cc b/third_party/blink/renderer/modules/mediastream/mock_media_stream_video_source.cc
index 4b802c0..b7f926bc 100644
--- a/third_party/blink/renderer/modules/mediastream/mock_media_stream_video_source.cc
+++ b/third_party/blink/renderer/modules/mediastream/mock_media_stream_video_source.cc
@@ -68,6 +68,11 @@
   is_suspended_ = !has_consumers;
 }
 
+VideoCaptureFeedbackCB MockMediaStreamVideoSource::GetFeedbackCallback() const {
+  return WTF::BindRepeating(&MockMediaStreamVideoSource::OnFrameFeedback,
+                            WTF::Unretained(this));
+}
+
 base::WeakPtr<MediaStreamVideoSource> MockMediaStreamVideoSource::GetWeakPtr()
     const {
   return weak_factory_.GetWeakPtr();
diff --git a/third_party/blink/renderer/modules/mediastream/mock_media_stream_video_source.h b/third_party/blink/renderer/modules/mediastream/mock_media_stream_video_source.h
index a65c77e6..1ffb2b7f5 100644
--- a/third_party/blink/renderer/modules/mediastream/mock_media_stream_video_source.h
+++ b/third_party/blink/renderer/modules/mediastream/mock_media_stream_video_source.h
@@ -27,6 +27,8 @@
   MOCK_METHOD0(OnRequestRefreshFrame, void());
   MOCK_METHOD1(OnCapturingLinkSecured, void(bool));
   MOCK_CONST_METHOD0(SupportsEncodedOutput, bool());
+  MOCK_METHOD1(OnFrameDropped, void(media::VideoCaptureFrameDropReason));
+  MOCK_CONST_METHOD1(OnFrameFeedback, void(const media::VideoFrameFeedback&));
 
   // Simulate that the underlying source start successfully.
   void StartMockedSource();
@@ -71,6 +73,7 @@
   base::Optional<media::VideoCaptureParams> GetCurrentCaptureParams()
       const override;
   void OnHasConsumers(bool has_consumers) override;
+  VideoCaptureFeedbackCB GetFeedbackCallback() const override;
   base::WeakPtr<MediaStreamVideoSource> GetWeakPtr() const override;
 
  protected:
diff --git a/third_party/blink/renderer/modules/mediastream/pushable_media_stream_video_source.cc b/third_party/blink/renderer/modules/mediastream/pushable_media_stream_video_source.cc
index 155ca55..9d48f9b3 100644
--- a/third_party/blink/renderer/modules/mediastream/pushable_media_stream_video_source.cc
+++ b/third_party/blink/renderer/modules/mediastream/pushable_media_stream_video_source.cc
@@ -10,6 +10,10 @@
 
 namespace blink {
 
+PushableMediaStreamVideoSource::PushableMediaStreamVideoSource(
+    const base::WeakPtr<MediaStreamVideoSource>& upstream_source)
+    : upstream_source_(upstream_source) {}
+
 void PushableMediaStreamVideoSource::PushFrame(
     scoped_refptr<media::VideoFrame> video_frame,
     base::TimeTicks estimated_capture_time) {
@@ -34,6 +38,38 @@
                           estimated_capture_time));
 }
 
+void PushableMediaStreamVideoSource::RequestRefreshFrame() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  if (upstream_source_)
+    upstream_source_->RequestRefreshFrame();
+}
+
+void PushableMediaStreamVideoSource::OnFrameDropped(
+    media::VideoCaptureFrameDropReason reason) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  if (upstream_source_)
+    upstream_source_->OnFrameDropped(reason);
+}
+
+VideoCaptureFeedbackCB PushableMediaStreamVideoSource::GetFeedbackCallback()
+    const {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  if (upstream_source_) {
+    return WTF::BindRepeating(
+        [](const base::WeakPtr<MediaStreamVideoSource>& source,
+           const media::VideoFrameFeedback& feedback) {
+          if (!source)
+            return;
+
+          PushableMediaStreamVideoSource* pushable_source =
+              static_cast<PushableMediaStreamVideoSource*>(source.get());
+          pushable_source->GetInternalFeedbackCallback().Run(feedback);
+        },
+        GetWeakPtr());
+  }
+  return VideoCaptureFeedbackCB();
+}
+
 void PushableMediaStreamVideoSource::StartSourceImpl(
     VideoCaptureDeliverFrameCB frame_callback,
     EncodedVideoFrameCB encoded_frame_callback) {
@@ -54,4 +90,13 @@
   return weak_factory_.GetWeakPtr();
 }
 
+VideoCaptureFeedbackCB
+PushableMediaStreamVideoSource::GetInternalFeedbackCallback() const {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  if (!upstream_source_)
+    return VideoCaptureFeedbackCB();
+
+  return upstream_source_->GetFeedbackCallback();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/mediastream/pushable_media_stream_video_source.h b/third_party/blink/renderer/modules/mediastream/pushable_media_stream_video_source.h
index ad35f9b3..8312f7ad 100644
--- a/third_party/blink/renderer/modules/mediastream/pushable_media_stream_video_source.h
+++ b/third_party/blink/renderer/modules/mediastream/pushable_media_stream_video_source.h
@@ -20,6 +20,9 @@
 class MODULES_EXPORT PushableMediaStreamVideoSource
     : public MediaStreamVideoSource {
  public:
+  PushableMediaStreamVideoSource() = default;
+  explicit PushableMediaStreamVideoSource(
+      const base::WeakPtr<MediaStreamVideoSource>& upstream_source);
   // See the definition of VideoCaptureDeliverFrameCB in
   // media/capture/video_capturer_source.h for the documentation
   // of |estimated_capture_time| and the difference with
@@ -29,15 +32,22 @@
   bool running() const { return running_; }
 
   // MediaStreamVideoSource
+  void RequestRefreshFrame() override;
+  void OnFrameDropped(media::VideoCaptureFrameDropReason reason) override;
+  VideoCaptureFeedbackCB GetFeedbackCallback() const override;
   void StartSourceImpl(VideoCaptureDeliverFrameCB frame_callback,
                        EncodedVideoFrameCB encoded_frame_callback) override;
   void StopSourceImpl() override;
   base::WeakPtr<MediaStreamVideoSource> GetWeakPtr() const override;
 
+  VideoCaptureFeedbackCB GetInternalFeedbackCallback() const;
+
  private:
   bool running_ = false;
   VideoCaptureDeliverFrameCB deliver_frame_cb_;
 
+  base::WeakPtr<MediaStreamVideoSource> upstream_source_;
+
   THREAD_CHECKER(thread_checker_);
   base::WeakPtrFactory<MediaStreamVideoSource> weak_factory_{this};
 };
diff --git a/third_party/blink/renderer/modules/mediastream/pushable_media_stream_video_source_test.cc b/third_party/blink/renderer/modules/mediastream/pushable_media_stream_video_source_test.cc
index 00ce5408..8cb17424 100644
--- a/third_party/blink/renderer/modules/mediastream/pushable_media_stream_video_source_test.cc
+++ b/third_party/blink/renderer/modules/mediastream/pushable_media_stream_video_source_test.cc
@@ -11,6 +11,7 @@
 #include "third_party/blink/public/web/modules/mediastream/media_stream_video_sink.h"
 #include "third_party/blink/public/web/web_heap.h"
 #include "third_party/blink/renderer/modules/mediastream/media_stream_video_track.h"
+#include "third_party/blink/renderer/modules/mediastream/mock_media_stream_video_source.h"
 #include "third_party/blink/renderer/modules/mediastream/video_track_adapter_settings.h"
 #include "third_party/blink/renderer/platform/mediastream/media_stream_source.h"
 #include "third_party/blink/renderer/platform/testing/io_task_runner_testing_platform_support.h"
@@ -58,16 +59,36 @@
   base::OnceClosure got_frame_cb_;
 };
 
+MediaStreamSource* CreateConnectedMediaStreamSource(
+    MediaStreamVideoSource* video_source) {
+  MediaStreamSource* media_stream_source =
+      MakeGarbageCollected<MediaStreamSource>(
+          "dummy_source_id", MediaStreamSource::kTypeVideo, "dummy_source_name",
+          false /* remote */);
+  media_stream_source->SetPlatformSource(base::WrapUnique(video_source));
+  return media_stream_source;
+}
+
+WebMediaStreamTrack StartVideoSource(MediaStreamVideoSource* video_source) {
+  return MediaStreamVideoTrack::CreateVideoTrack(
+      video_source, MediaStreamVideoSource::ConstraintsOnceCallback(),
+      /*enabled=*/true);
+}
+
+MediaStreamSource* CreateAndStartMediaStreamSource(
+    MediaStreamVideoSource* video_source) {
+  MediaStreamSource* source = CreateConnectedMediaStreamSource(video_source);
+  StartVideoSource(video_source);
+  return source;
+}
+
 }  // namespace
 
 class PushableMediaStreamVideoSourceTest : public testing::Test {
  public:
   PushableMediaStreamVideoSourceTest() {
     pushable_video_source_ = new PushableMediaStreamVideoSource();
-    stream_source_ = MakeGarbageCollected<MediaStreamSource>(
-        "dummy_source_id", MediaStreamSource::kTypeVideo, "dummy_source_name",
-        false /* remote */);
-    stream_source_->SetPlatformSource(base::WrapUnique(pushable_video_source_));
+    stream_source_ = CreateConnectedMediaStreamSource(pushable_video_source_);
   }
 
   void TearDown() override {
@@ -76,10 +97,7 @@
   }
 
   WebMediaStreamTrack StartSource() {
-    return MediaStreamVideoTrack::CreateVideoTrack(
-        pushable_video_source_,
-        MediaStreamVideoSource::ConstraintsOnceCallback(),
-        /*enabled=*/true);
+    return StartVideoSource(pushable_video_source_);
   }
 
  protected:
@@ -131,4 +149,24 @@
   EXPECT_EQ(natural_size.height(), 50);
 }
 
+TEST_F(PushableMediaStreamVideoSourceTest, ForwardToUpstream) {
+  MockMediaStreamVideoSource* mock_source = new MockMediaStreamVideoSource();
+  PushableMediaStreamVideoSource* pushable_video_source =
+      new PushableMediaStreamVideoSource(mock_source->GetWeakPtr());
+  CreateAndStartMediaStreamSource(mock_source);
+  CreateAndStartMediaStreamSource(pushable_video_source);
+
+  EXPECT_CALL(*mock_source, OnRequestRefreshFrame());
+  pushable_video_source->RequestRefreshFrame();
+
+  EXPECT_CALL(*mock_source,
+              OnFrameDropped(media::VideoCaptureFrameDropReason::
+                                 kResolutionAdapterFrameIsNotValid));
+  pushable_video_source->OnFrameDropped(
+      media::VideoCaptureFrameDropReason::kResolutionAdapterFrameIsNotValid);
+
+  EXPECT_CALL(*mock_source, OnFrameFeedback(media::VideoFrameFeedback()));
+  pushable_video_source->GetFeedbackCallback().Run(media::VideoFrameFeedback());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/mediastream/video_track_signal_underlying_sink.cc b/third_party/blink/renderer/modules/mediastream/video_track_signal_underlying_sink.cc
new file mode 100644
index 0000000..dfc1b83
--- /dev/null
+++ b/third_party/blink/renderer/modules/mediastream/video_track_signal_underlying_sink.cc
@@ -0,0 +1,106 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/mediastream/video_track_signal_underlying_sink.h"
+
+#include "third_party/blink/public/web/modules/mediastream/media_stream_video_source.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_media_stream_track_signal.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_media_stream_track_signal_type.h"
+#include "third_party/blink/renderer/modules/mediastream/media_stream_track.h"
+#include "third_party/blink/renderer/modules/mediastream/media_stream_track_signal.h"
+#include "third_party/blink/renderer/modules/mediastream/media_stream_video_track.h"
+#include "third_party/blink/renderer/modules/mediastream/pushable_media_stream_video_source.h"
+#include "third_party/blink/renderer/platform/bindings/exception_state.h"
+#include "third_party/blink/renderer/platform/mediastream/media_stream_component.h"
+
+namespace blink {
+
+VideoTrackSignalUnderlyingSink::VideoTrackSignalUnderlyingSink(
+    MediaStreamTrack* track) {
+  if (!MediaStreamVideoTrack::From(track->Component()))
+    return;
+  track_ = track;
+  MediaStreamVideoSource* video_source =
+      MediaStreamVideoSource::GetVideoSource(track->Component()->Source());
+  if (video_source)
+    source_ = video_source->GetWeakPtr();
+}
+
+ScriptPromise VideoTrackSignalUnderlyingSink::start(
+    ScriptState* script_state,
+    WritableStreamDefaultController* controller,
+    ExceptionState& exception_state) {
+  return ScriptPromise::CastUndefined(script_state);
+}
+
+ScriptPromise VideoTrackSignalUnderlyingSink::write(
+    ScriptState* script_state,
+    ScriptValue chunk,
+    WritableStreamDefaultController* controller,
+    ExceptionState& exception_state) {
+  MediaStreamTrackSignal* signal =
+      NativeValueTraits<MediaStreamTrackSignal>::NativeValue(
+          script_state->GetIsolate(), chunk.V8Value(), exception_state);
+  if (!signal) {
+    exception_state.ThrowTypeError("Null signal.");
+    return ScriptPromise();
+  }
+
+  if (!track_ || track_->Ended()) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      "No live track");
+    return ScriptPromise();
+  }
+
+  if (signal->signalType() == "request-frame") {
+    if (!source_ || !source_->IsRunning()) {
+      exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                        "No active source");
+      return ScriptPromise();
+    }
+    source_->RequestRefreshFrame();
+    return ScriptPromise::CastUndefined(script_state);
+  } else if (signal->signalType() == "set-min-frame-rate") {
+    if (!signal->hasFrameRate()) {
+      exception_state.ThrowTypeError(
+          "A non-negative frameRate is required for set-min-frame-rate.");
+      return ScriptPromise();
+    }
+    if (auto* video_track = MediaStreamVideoTrack::From(track_->Component())) {
+      video_track->SetMinimumFrameRate(signal->frameRate());
+      return ScriptPromise::CastUndefined(script_state);
+    } else {
+      exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                        "No active track");
+      return ScriptPromise();
+    }
+  }
+
+  exception_state.ThrowTypeError("Invalid signal.");
+  return ScriptPromise();
+}
+
+ScriptPromise VideoTrackSignalUnderlyingSink::abort(
+    ScriptState* script_state,
+    ScriptValue reason,
+    ExceptionState& exception_state) {
+  track_.Clear();
+  source_.reset();
+  return ScriptPromise::CastUndefined(script_state);
+}
+
+ScriptPromise VideoTrackSignalUnderlyingSink::close(
+    ScriptState* script_state,
+    ExceptionState& exception_state) {
+  track_.Clear();
+  source_.reset();
+  return ScriptPromise::CastUndefined(script_state);
+}
+
+void VideoTrackSignalUnderlyingSink::Trace(Visitor* visitor) const {
+  visitor->Trace(track_);
+  UnderlyingSinkBase::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/mediastream/video_track_signal_underlying_sink.h b/third_party/blink/renderer/modules/mediastream/video_track_signal_underlying_sink.h
new file mode 100644
index 0000000..6272da1a
--- /dev/null
+++ b/third_party/blink/renderer/modules/mediastream/video_track_signal_underlying_sink.h
@@ -0,0 +1,44 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASTREAM_VIDEO_TRACK_SIGNAL_UNDERLYING_SINK_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASTREAM_VIDEO_TRACK_SIGNAL_UNDERLYING_SINK_H_
+
+#include "third_party/blink/renderer/core/streams/underlying_sink_base.h"
+#include "third_party/blink/renderer/modules/modules_export.h"
+
+namespace blink {
+
+class MediaStreamTrack;
+class MediaStreamVideoSource;
+
+class MODULES_EXPORT VideoTrackSignalUnderlyingSink
+    : public UnderlyingSinkBase {
+ public:
+  // |source| must outlive this MediaStreamVideoTrackUnderlyingSink.
+  explicit VideoTrackSignalUnderlyingSink(MediaStreamTrack* track);
+
+  // UnderlyingSinkBase overrides.
+  ScriptPromise start(ScriptState* script_state,
+                      WritableStreamDefaultController* controller,
+                      ExceptionState& exception_state) override;
+  ScriptPromise write(ScriptState* script_state,
+                      ScriptValue chunk,
+                      WritableStreamDefaultController* controller,
+                      ExceptionState& exception_state) override;
+  ScriptPromise abort(ScriptState* script_state,
+                      ScriptValue reason,
+                      ExceptionState& exception_state) override;
+  ScriptPromise close(ScriptState* script_state,
+                      ExceptionState& exception_state) override;
+  void Trace(Visitor* visitor) const override;
+
+ private:
+  WeakMember<MediaStreamTrack> track_;
+  base::WeakPtr<MediaStreamVideoSource> source_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASTREAM_VIDEO_TRACK_SIGNAL_UNDERLYING_SINK_H_
diff --git a/third_party/blink/renderer/modules/mediastream/video_track_signal_underlying_sink_test.cc b/third_party/blink/renderer/modules/mediastream/video_track_signal_underlying_sink_test.cc
new file mode 100644
index 0000000..0d43bbc
--- /dev/null
+++ b/third_party/blink/renderer/modules/mediastream/video_track_signal_underlying_sink_test.cc
@@ -0,0 +1,244 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/mediastream/video_track_signal_underlying_sink.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/web/web_heap.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise_tester.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_media_stream_track_signal.h"
+#include "third_party/blink/renderer/core/streams/writable_stream.h"
+#include "third_party/blink/renderer/core/streams/writable_stream_default_writer.h"
+#include "third_party/blink/renderer/modules/mediastream/media_stream_track.h"
+#include "third_party/blink/renderer/modules/mediastream/media_stream_track_signal.h"
+#include "third_party/blink/renderer/modules/mediastream/media_stream_video_track.h"
+#include "third_party/blink/renderer/modules/mediastream/mock_media_stream_video_source.h"
+#include "third_party/blink/renderer/platform/mediastream/media_stream_component.h"
+#include "third_party/blink/renderer/platform/mediastream/media_stream_source.h"
+#include "third_party/blink/renderer/platform/testing/io_task_runner_testing_platform_support.h"
+
+using testing::_;
+
+namespace blink {
+
+class VideoTrackSignalUnderlyingSinkTest : public testing::Test {
+ public:
+  VideoTrackSignalUnderlyingSinkTest() {
+    mock_video_source_ = new MockMediaStreamVideoSource();
+    media_stream_source_ = MakeGarbageCollected<MediaStreamSource>(
+        "dummy_source_id", MediaStreamSource::kTypeVideo, "dummy_source_name",
+        /*remote=*/false);
+    media_stream_source_->SetPlatformSource(
+        base::WrapUnique(mock_video_source_));
+    web_track_ = MediaStreamVideoTrack::CreateVideoTrack(
+        mock_video_source_, MediaStreamVideoSource::ConstraintsOnceCallback(),
+        true);
+    mock_video_source_->StartMockedSource();
+  }
+
+  ~VideoTrackSignalUnderlyingSinkTest() override {
+    platform_->RunUntilIdle();
+    mock_video_source_->StopSource();
+    base::RunLoop run_loop;
+    platform_->GetIOTaskRunner()->PostTask(FROM_HERE, run_loop.QuitClosure());
+    run_loop.Run();
+    WebHeap::CollectAllGarbageForTesting();
+  }
+
+  MediaStreamTrack* CreateTrack(ExecutionContext* context) const {
+    return MakeGarbageCollected<MediaStreamTrack>(context, web_track_);
+  }
+  VideoTrackSignalUnderlyingSink* CreateUnderlyingSink(
+      MediaStreamTrack* track) {
+    return MakeGarbageCollected<VideoTrackSignalUnderlyingSink>(track);
+  }
+
+  ScriptValue CreateSignalChunk(ScriptState* script_state,
+                                const String& signal_name) {
+    MediaStreamTrackSignal* signal = MediaStreamTrackSignal::Create();
+    signal->setSignalType(signal_name);
+    return ScriptValue(script_state->GetIsolate(),
+                       ToV8(signal, script_state->GetContext()->Global(),
+                            script_state->GetIsolate()));
+  }
+
+  ScriptValue CreateRequestFrameChunk(ScriptState* script_state) {
+    return CreateSignalChunk(script_state, "request-frame");
+  }
+
+  ScriptValue CreateSetMinFrameRateChunk(
+      ScriptState* script_state,
+      const base::Optional<double>& frame_rate = 10.0) {
+    MediaStreamTrackSignal* signal = MediaStreamTrackSignal::Create();
+    signal->setSignalType("set-min-frame-rate");
+    if (frame_rate)
+      signal->setFrameRate(*frame_rate);
+    return ScriptValue(script_state->GetIsolate(),
+                       ToV8(signal, script_state->GetContext()->Global(),
+                            script_state->GetIsolate()));
+  }
+
+ protected:
+  Persistent<MediaStreamSource> media_stream_source_;
+  WebMediaStreamTrack web_track_;
+  MockMediaStreamVideoSource* mock_video_source_;
+  ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform_;
+};
+
+TEST_F(VideoTrackSignalUnderlyingSinkTest,
+       WriteRequestFrameToStreamForwardsToVideoSource) {
+  V8TestingScope v8_scope;
+  ScriptState* script_state = v8_scope.GetScriptState();
+  auto* track = CreateTrack(v8_scope.GetExecutionContext());
+  auto* underlying_sink = CreateUnderlyingSink(track);
+  auto* writable_stream = WritableStream::CreateWithCountQueueingStrategy(
+      script_state, underlying_sink, 1u);
+
+  NonThrowableExceptionState exception_state;
+  auto* writer = writable_stream->getWriter(script_state, exception_state);
+
+  auto request_frame_chunk = CreateRequestFrameChunk(script_state);
+  EXPECT_CALL(*mock_video_source_, OnRequestRefreshFrame());
+  ScriptPromiseTester write_tester(
+      script_state,
+      writer->write(script_state, request_frame_chunk, exception_state));
+  write_tester.WaitUntilSettled();
+
+  const double frame_rate = 14.8;
+  auto set_min_frame_rate_chunk =
+      CreateSetMinFrameRateChunk(script_state, frame_rate);
+  MediaStreamVideoTrack* video_track =
+      MediaStreamVideoTrack::From(track->Component());
+  EXPECT_FALSE(video_track->min_frame_rate().has_value());
+  ScriptPromiseTester write_tester2(
+      script_state,
+      writer->write(script_state, set_min_frame_rate_chunk, exception_state));
+  write_tester2.WaitUntilSettled();
+  EXPECT_TRUE(video_track->min_frame_rate().has_value());
+  EXPECT_EQ(video_track->min_frame_rate().value(), frame_rate);
+
+  writer->releaseLock(script_state);
+  ScriptPromiseTester close_tester(
+      script_state, writable_stream->close(script_state, exception_state));
+  close_tester.WaitUntilSettled();
+
+  MediaStreamTrack* clone = track->clone(script_state);
+  track->stopTrack(v8_scope.GetExecutionContext());
+
+  // Writing to the sink after the track closes should fail, even if the source
+  // is active.
+  EXPECT_TRUE(mock_video_source_->IsRunning());
+  DummyExceptionStateForTesting dummy_exception_state;
+  underlying_sink->write(script_state, CreateRequestFrameChunk(script_state),
+                         nullptr, dummy_exception_state);
+  EXPECT_TRUE(dummy_exception_state.HadException());
+  EXPECT_EQ(dummy_exception_state.Code(),
+            static_cast<ExceptionCode>(DOMExceptionCode::kInvalidStateError));
+
+  clone->stopTrack(v8_scope.GetExecutionContext());
+  EXPECT_FALSE(mock_video_source_->IsRunning());
+  // Writing to the sink after the source closes should fail.
+  dummy_exception_state.ClearException();
+  underlying_sink->write(script_state, CreateRequestFrameChunk(script_state),
+                         nullptr, dummy_exception_state);
+  EXPECT_TRUE(dummy_exception_state.HadException());
+  EXPECT_EQ(dummy_exception_state.Code(),
+            static_cast<ExceptionCode>(DOMExceptionCode::kInvalidStateError));
+}
+
+TEST_F(VideoTrackSignalUnderlyingSinkTest, WriteInvalidDataFails) {
+  V8TestingScope v8_scope;
+  ScriptState* script_state = v8_scope.GetScriptState();
+  auto* track = CreateTrack(v8_scope.GetExecutionContext());
+  auto* underlying_sink = CreateUnderlyingSink(track);
+
+  MediaStreamVideoTrack* video_track =
+      MediaStreamVideoTrack::From(track->Component());
+  EXPECT_FALSE(video_track->min_frame_rate().has_value());
+
+  DummyExceptionStateForTesting exception_state;
+  auto set_min_frame_rate_chunk =
+      CreateSetMinFrameRateChunk(script_state, base::nullopt);
+  underlying_sink->write(script_state, set_min_frame_rate_chunk, nullptr,
+                         exception_state);
+  EXPECT_TRUE(exception_state.HadException());
+  EXPECT_FALSE(video_track->min_frame_rate().has_value());
+
+  exception_state.ClearException();
+  EXPECT_FALSE(exception_state.HadException());
+  underlying_sink->write(script_state,
+                         CreateSignalChunk(script_state, "invalid-signal"),
+                         nullptr, exception_state);
+  EXPECT_TRUE(exception_state.HadException());
+
+  // Writing null fails
+  exception_state.ClearException();
+  EXPECT_FALSE(exception_state.HadException());
+  underlying_sink->write(script_state,
+                         ScriptValue::CreateNull(v8_scope.GetIsolate()),
+                         nullptr, exception_state);
+  EXPECT_TRUE(exception_state.HadException());
+
+  // Writing an intenger fails
+  exception_state.ClearException();
+  EXPECT_FALSE(exception_state.HadException());
+  underlying_sink->write(script_state, ScriptValue::From(script_state, 5),
+                         nullptr, exception_state);
+  EXPECT_TRUE(exception_state.HadException());
+
+  track->stopTrack(v8_scope.GetExecutionContext());
+}
+
+TEST_F(VideoTrackSignalUnderlyingSinkTest, WriteToClosedSinkFails) {
+  V8TestingScope v8_scope;
+  ScriptState* script_state = v8_scope.GetScriptState();
+  auto* track = CreateTrack(v8_scope.GetExecutionContext());
+  auto* underlying_sink = CreateUnderlyingSink(track);
+
+  auto* writable_stream = WritableStream::CreateWithCountQueueingStrategy(
+      script_state, underlying_sink, 1u);
+
+  NonThrowableExceptionState exception_state;
+  ScriptPromiseTester abort_tester(
+      script_state, writable_stream->close(script_state, exception_state));
+  abort_tester.WaitUntilSettled();
+
+  // Writing to the sink after the stream closes should fail.
+  DummyExceptionStateForTesting dummy_exception_state;
+  underlying_sink->write(script_state, CreateRequestFrameChunk(script_state),
+                         nullptr, dummy_exception_state);
+  EXPECT_TRUE(dummy_exception_state.HadException());
+  EXPECT_EQ(dummy_exception_state.Code(),
+            static_cast<ExceptionCode>(DOMExceptionCode::kInvalidStateError));
+
+  track->stopTrack(v8_scope.GetExecutionContext());
+}
+
+TEST_F(VideoTrackSignalUnderlyingSinkTest, WriteToAbortedSinkFails) {
+  V8TestingScope v8_scope;
+  ScriptState* script_state = v8_scope.GetScriptState();
+  auto* track = CreateTrack(v8_scope.GetExecutionContext());
+  auto* underlying_sink = CreateUnderlyingSink(track);
+
+  auto* writable_stream = WritableStream::CreateWithCountQueueingStrategy(
+      script_state, underlying_sink, 1u);
+
+  NonThrowableExceptionState exception_state;
+  ScriptPromiseTester abort_tester(
+      script_state, writable_stream->abort(script_state, exception_state));
+  abort_tester.WaitUntilSettled();
+
+  // Writing to the sink after the stream aborts should fail.
+  DummyExceptionStateForTesting dummy_exception_state;
+  underlying_sink->write(script_state, CreateRequestFrameChunk(script_state),
+                         nullptr, dummy_exception_state);
+  EXPECT_TRUE(dummy_exception_state.HadException());
+  EXPECT_EQ(dummy_exception_state.Code(),
+            static_cast<ExceptionCode>(DOMExceptionCode::kInvalidStateError));
+
+  track->stopTrack(v8_scope.GetExecutionContext());
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/modules_idl_files.gni b/third_party/blink/renderer/modules/modules_idl_files.gni
index d39536ba..2f3b9289 100644
--- a/third_party/blink/renderer/modules/modules_idl_files.gni
+++ b/third_party/blink/renderer/modules/modules_idl_files.gni
@@ -76,6 +76,7 @@
   "//third_party/blink/renderer/modules/encoding/idls.gni",
   "//third_party/blink/renderer/modules/encryptedmedia/idls.gni",
   "//third_party/blink/renderer/modules/eventsource/idls.gni",
+  "//third_party/blink/renderer/modules/eyedropper/idls.gni",
   "//third_party/blink/renderer/modules/filesystem/idls.gni",
   "//third_party/blink/renderer/modules/file_system_access/idls.gni",
   "//third_party/blink/renderer/modules/font_access/idls.gni",
diff --git a/third_party/blink/renderer/modules/webcodecs/DEPS b/third_party/blink/renderer/modules/webcodecs/DEPS
index 189c70a..e1eae306 100644
--- a/third_party/blink/renderer/modules/webcodecs/DEPS
+++ b/third_party/blink/renderer/modules/webcodecs/DEPS
@@ -6,6 +6,7 @@
 
     "+gpu/command_buffer/client/shared_image_interface.h",
     "+gpu/command_buffer/client/raster_interface.h",
+    "+gpu/config/gpu_feature_info.h",
     "+gpu/GLES2/gl2extchromium.h",
 
     "+media/audio",
diff --git a/third_party/blink/renderer/modules/webcodecs/video_frame.cc b/third_party/blink/renderer/modules/webcodecs/video_frame.cc
index 30618b66..9c32686 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_frame.cc
+++ b/third_party/blink/renderer/modules/webcodecs/video_frame.cc
@@ -11,6 +11,7 @@
 #include "components/viz/common/gpu/raster_context_provider.h"
 #include "components/viz/common/resources/single_release_callback.h"
 #include "gpu/command_buffer/client/shared_image_interface.h"
+#include "gpu/config/gpu_feature_info.h"
 #include "media/base/timestamp_constants.h"
 #include "media/base/video_frame.h"
 #include "media/base/video_frame_metadata.h"
@@ -202,24 +203,6 @@
 #endif
 }
 
-bool PreferAcceleratedImages(const media::VideoFrame& frame) {
-  if (frame.format() == media::PIXEL_FORMAT_I420A)
-    return false;
-
-  if (frame.HasTextures())
-    return true;
-
-  if (frame.format() == media::PIXEL_FORMAT_ARGB ||
-      frame.format() == media::PIXEL_FORMAT_XRGB ||
-      frame.format() == media::PIXEL_FORMAT_ABGR ||
-      frame.format() == media::PIXEL_FORMAT_XBGR) {
-    return false;
-  }
-
-  constexpr int kCpuEfficientFrameSize = 320u * 240u;
-  return frame.visible_rect().size().GetArea() > kCpuEfficientFrameSize;
-}
-
 scoped_refptr<StaticBitmapImage> CreateImage(
     scoped_refptr<media::VideoFrame> frame) {
   // TODO(sandersd): Do we need to be able to handle limited-range RGB? It
@@ -256,102 +239,47 @@
         Thread::Current()->GetTaskRunner(), std::move(release_callback));
   }
 
-  const bool is_mappable =
-      frame->IsMappable() && (frame->format() == media::PIXEL_FORMAT_I420 ||
-                              frame->format() == media::PIXEL_FORMAT_I420A);
-  const bool is_texturable =
-      frame->HasTextures() && (frame->format() == media::PIXEL_FORMAT_I420 ||
-                               frame->format() == media::PIXEL_FORMAT_I420A ||
-                               frame->format() == media::PIXEL_FORMAT_NV12);
-  const bool is_rgb = frame->format() == media::PIXEL_FORMAT_ARGB ||
-                      frame->format() == media::PIXEL_FORMAT_XRGB ||
-                      frame->format() == media::PIXEL_FORMAT_ABGR ||
-                      frame->format() == media::PIXEL_FORMAT_XBGR;
-
-  if (!is_mappable && !is_texturable && !is_rgb) {
-    DLOG(ERROR) << "Unsupported VideoFrame: " << frame->AsHumanReadableString();
-    return nullptr;
-  }
-
-  if (!PreferAcceleratedImages(*frame)) {
-    auto info = SkImageInfo::Make(
-        frame->visible_rect().width(), frame->visible_rect().height(),
-        kN32_SkColorType, kUnpremul_SkAlphaType, std::move(sk_color_space));
-
-    sk_sp<SkData> image_pixels = TryAllocateSkData(info.computeMinByteSize());
-    if (!image_pixels)
-      return nullptr;
-
-    media::PaintCanvasVideoRenderer::ConvertVideoFrameToRGBPixels(
-        frame.get(), image_pixels->writable_data(), info.minRowBytes());
-    return UnacceleratedStaticBitmapImage::Create(SkImage::MakeRasterData(
-        info, std::move(image_pixels), info.minRowBytes()));
-  }
-
   auto raster_context_provider = GetRasterContextProvider();
-  if (!raster_context_provider) {
-    DLOG(ERROR) << "Graphics context unavailable.";
-    return nullptr;
-  }
 
-  auto* ri = raster_context_provider->RasterInterface();
-  auto* shared_image_interface =
-      raster_context_provider->SharedImageInterface();
-  uint32_t usage =
-      gpu::SHARED_IMAGE_USAGE_GLES2 | gpu::SHARED_IMAGE_USAGE_DISPLAY;
-  if (raster_context_provider->ContextCapabilities().supports_oop_raster) {
-    usage |= gpu::SHARED_IMAGE_USAGE_RASTER |
-             gpu::SHARED_IMAGE_USAGE_OOP_RASTERIZATION;
-  }
-
-  gpu::MailboxHolder dest_holder;
-  // Use coded_size() to comply with media::ConvertFromVideoFrameYUV.
-  dest_holder.mailbox = shared_image_interface->CreateSharedImage(
-      viz::ResourceFormat::RGBA_8888, frame->coded_size(), gfx::ColorSpace(),
-      kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, usage,
-      gpu::kNullSurfaceHandle);
-  dest_holder.sync_token = shared_image_interface->GenUnverifiedSyncToken();
-  dest_holder.texture_target = GL_TEXTURE_2D;
-
-  if (frame->NumTextures() == 1) {
-    ri->WaitSyncTokenCHROMIUM(dest_holder.sync_token.GetConstData());
-    ri->CopySubTexture(frame->mailbox_holder(0).mailbox, dest_holder.mailbox,
-                       GL_TEXTURE_2D, 0, 0, 0, 0, frame->coded_size().width(),
-                       frame->coded_size().height(), GL_FALSE, GL_FALSE);
-  } else {
-    media::VideoFrameYUVConverter::ConvertYUVVideoFrameNoCaching(
-        frame.get(), raster_context_provider.get(), dest_holder);
-  }
-
-  gpu::SyncToken sync_token;
-  ri->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
-
-  auto release_callback = viz::SingleReleaseCallback::Create(base::BindOnce(
-      [](scoped_refptr<viz::RasterContextProvider> provider,
-         gpu::Mailbox mailbox, const gpu::SyncToken& sync_token, bool is_lost) {
-        provider->SharedImageInterface()->DestroySharedImage(sync_token,
-                                                             mailbox);
-      },
-      raster_context_provider, dest_holder.mailbox));
-
-  const auto sk_image_info = SkImageInfo::Make(
-      frame->coded_size().width(), frame->coded_size().height(),
-      kN32_SkColorType, kUnpremul_SkAlphaType, std::move(sk_color_space));
-
-  auto image = AcceleratedStaticBitmapImage::CreateFromCanvasMailbox(
-      dest_holder.mailbox, sync_token, 0u, sk_image_info,
-      dest_holder.texture_target, true,
-      SharedGpuContext::ContextProviderWrapper(),
-      base::PlatformThread::CurrentRef(), Thread::Current()->GetTaskRunner(),
-      std::move(release_callback));
-
+  std::unique_ptr<CanvasResourceProvider> resource_provider;
   if (frame->HasTextures()) {
-    // Attach a new sync token to |frame|, so it's not destroyed
-    // before |image| is fully created.
-    media::WaitAndReplaceSyncTokenClient client(ri);
-    frame->UpdateReleaseSyncToken(&client);
+    if (!raster_context_provider)
+      return nullptr;  // Unable to get/create a shared main thread context.
+    if (!raster_context_provider->GrContext() &&
+        !raster_context_provider->ContextCapabilities().supports_oop_raster) {
+      return nullptr;  // The context has been lost.
+    }
   }
-  return image;
+
+  if (!raster_context_provider ||
+      raster_context_provider->GetGpuFeatureInfo().IsWorkaroundEnabled(
+          DISABLE_IMAGEBITMAP_FROM_VIDEO_USING_GPU) ||
+      !SharedGpuContext::IsGpuCompositingEnabled()) {
+    resource_provider = CanvasResourceProvider::CreateBitmapProvider(
+        IntSize(frame->visible_rect().size()), kLow_SkFilterQuality,
+        CanvasResourceParams(),
+        CanvasResourceProvider::ShouldInitialize::kCallClear);
+  } else {
+    resource_provider = CanvasResourceProvider::CreateSharedImageProvider(
+        IntSize(frame->visible_rect().size()), kLow_SkFilterQuality,
+        CanvasResourceParams(CanvasColorSpace::kSRGB, kN32_SkColorType,
+                             kPremul_SkAlphaType),  // Default canvas settings,
+        CanvasResourceProvider::ShouldInitialize::kCallClear,
+        SharedGpuContext::ContextProviderWrapper(), RasterMode::kGPU,
+        false,  // Origin of GL texture is bottom left on screen
+        gpu::SHARED_IMAGE_USAGE_DISPLAY);
+  }
+
+  cc::PaintFlags media_flags;
+  media_flags.setAlpha(0xFF);
+  media_flags.setFilterQuality(kLow_SkFilterQuality);
+  media_flags.setBlendMode(SkBlendMode::kSrc);
+
+  media::PaintCanvasVideoRenderer().Paint(
+      frame.get(), resource_provider->Canvas(),
+      gfx::RectF(frame->visible_rect()), media_flags, media::kNoTransformation,
+      raster_context_provider.get());
+  return resource_provider->Snapshot();
 }
 
 bool IsSupportedPlanarFormat(const media::VideoFrame& frame) {
diff --git a/third_party/blink/renderer/modules/webcodecs/video_track_reader.cc b/third_party/blink/renderer/modules/webcodecs/video_track_reader.cc
index 54aacbb..6f459ff 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_track_reader.cc
+++ b/third_party/blink/renderer/modules/webcodecs/video_track_reader.cc
@@ -8,6 +8,7 @@
 #include "media/base/video_frame.h"
 #include "third_party/blink/public/mojom/web_feature/web_feature.mojom-blink.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/core/inspector/console_message.h"
 #include "third_party/blink/renderer/modules/mediastream/media_stream_video_track.h"
 #include "third_party/blink/renderer/modules/webcodecs/video_frame.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
@@ -28,6 +29,13 @@
       track_(track) {
   UseCounter::Count(ExecutionContext::From(script_state),
                     WebFeature::kWebCodecs);
+
+  ExecutionContext::From(script_state)
+      ->AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
+          mojom::blink::ConsoleMessageSource::kDeprecation,
+          mojom::blink::ConsoleMessageLevel::kWarning,
+          "VideoTrackReader is deprecated; use MediaStreamTrackProcessor "
+          "instead."));
 }
 
 void VideoTrackReader::start(V8VideoFrameOutputCallback* callback,
diff --git a/third_party/blink/renderer/modules/webcodecs/video_track_reader.h b/third_party/blink/renderer/modules/webcodecs/video_track_reader.h
index 081ac1c4..15c22ace 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_track_reader.h
+++ b/third_party/blink/renderer/modules/webcodecs/video_track_reader.h
@@ -16,6 +16,8 @@
 
 class ScriptState;
 
+// Note: This class is deprecated. Use MediaStreamTrackProcessor instead.
+// TODO(crbug.com/1157610): remove this class.
 class MODULES_EXPORT VideoTrackReader final
     : public ScriptWrappable,
       public ExecutionContextLifecycleObserver,
diff --git a/third_party/blink/renderer/modules/webcodecs/video_track_reader.idl b/third_party/blink/renderer/modules/webcodecs/video_track_reader.idl
index b1d83ed..fecfe4d 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_track_reader.idl
+++ b/third_party/blink/renderer/modules/webcodecs/video_track_reader.idl
@@ -8,6 +8,8 @@
     Exposed=Window,
     RuntimeEnabled=WebCodecs
 ] interface VideoTrackReader {
+  // DEPRECATED: use MediaStreamTrackProcessor instead.
+  // TODO(https://crbug.com/1157610): remove this idl.
   [CallWith=ScriptState, RaisesException, MeasureAs=WebCodecsVideoTrackReader]
   constructor(MediaStreamTrack track);
 
diff --git a/third_party/blink/renderer/modules/xr/xr_cpu_depth_information.cc b/third_party/blink/renderer/modules/xr/xr_cpu_depth_information.cc
index c12edb7..4d8d69b6 100644
--- a/third_party/blink/renderer/modules/xr/xr_cpu_depth_information.cc
+++ b/third_party/blink/renderer/modules/xr/xr_cpu_depth_information.cc
@@ -78,11 +78,9 @@
   depth_coordinates.Scale(size_.width(), size_.height(), 1.0);
 
   uint32_t column = base::ClampToRange<uint32_t>(
-      static_cast<uint32_t>(std::round(depth_coordinates.x())), 0,
-      size_.width() - 1);
+      static_cast<uint32_t>(depth_coordinates.x()), 0, size_.width() - 1);
   uint32_t row = base::ClampToRange<uint32_t>(
-      static_cast<uint32_t>(std::round(depth_coordinates.y())), 0,
-      size_.height() - 1);
+      static_cast<uint32_t>(depth_coordinates.y()), 0, size_.height() - 1);
 
   auto checked_index =
       base::CheckAdd(column, base::CheckMul(row, size_.width()));
diff --git a/third_party/blink/renderer/platform/geometry/calculation_value.cc b/third_party/blink/renderer/platform/geometry/calculation_value.cc
index 6afa82b..797e3409 100644
--- a/third_party/blink/renderer/platform/geometry/calculation_value.cc
+++ b/third_party/blink/renderer/platform/geometry/calculation_value.cc
@@ -45,8 +45,9 @@
 }
 
 float CalculationValue::Evaluate(float max_value) const {
-  float value = is_expression_ ? data_.expression->Evaluate(max_value)
-                               : Pixels() + Percent() / 100 * max_value;
+  float value =
+      clampTo<float>(is_expression_ ? data_.expression->Evaluate(max_value)
+                                    : Pixels() + Percent() / 100 * max_value);
   return (IsNonNegative() && value < 0) ? 0 : value;
 }
 
diff --git a/third_party/blink/renderer/platform/heap/v8_wrapper/thread_state.cc b/third_party/blink/renderer/platform/heap/v8_wrapper/thread_state.cc
index a476463..e0d95c2 100644
--- a/third_party/blink/renderer/platform/heap/v8_wrapper/thread_state.cc
+++ b/third_party/blink/renderer/platform/heap/v8_wrapper/thread_state.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/platform/heap/v8_wrapper/thread_state.h"
 
 #include "gin/public/v8_platform.h"
+#include "third_party/blink/renderer/platform/bindings/wrapper_type_info.h"
 #include "third_party/blink/renderer/platform/heap/v8_wrapper/custom_spaces.h"
 #include "third_party/blink/renderer/platform/wtf/hash_set.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
@@ -65,8 +66,12 @@
 }  // namespace
 
 ThreadState::ThreadState()
-    : cpp_heap_(
-          v8::CppHeap::Create(gin::V8Platform::Get(), {CreateCustomSpaces()})),
+    : cpp_heap_(v8::CppHeap::Create(
+          gin::V8Platform::Get(),
+          {CreateCustomSpaces(),
+           v8::WrapperDescriptor(kV8DOMWrapperTypeIndex,
+                                 kV8DOMWrapperObjectIndex,
+                                 gin::GinEmbedder::kEmbedderBlink)})),
       thread_id_(CurrentThread()) {
   allocation_handle_ = &cpp_heap_->GetAllocationHandle();
   *(thread_specific_.Get()) = this;
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index c9a0c65e..d3fdb574 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -891,6 +891,10 @@
       name: "ExtraWebGLVideoTextureMetadata",
     },
     {
+      name: "EyeDropperAPI",
+      status: "test",
+    },
+    {
       name: "FaceDetector",
       status: "experimental",
     },
diff --git a/third_party/blink/tools/blinkpy/common/net/network_transaction.py b/third_party/blink/tools/blinkpy/common/net/network_transaction.py
index 5236119..569863ca 100644
--- a/third_party/blink/tools/blinkpy/common/net/network_transaction.py
+++ b/third_party/blink/tools/blinkpy/common/net/network_transaction.py
@@ -28,7 +28,7 @@
 
 import logging
 import time
-import urllib2
+from six.moves import urllib
 
 _log = logging.getLogger(__name__)
 
@@ -57,7 +57,7 @@
         while True:
             try:
                 return request()
-            except urllib2.HTTPError as error:
+            except urllib.error.HTTPError as error:
                 if self._return_none_on_404 and error.code == 404:
                     return None
                 self._check_for_timeout()
diff --git a/third_party/blink/tools/blinkpy/common/net/web.py b/third_party/blink/tools/blinkpy/common/net/web.py
index ff76cf6..02af1e4 100644
--- a/third_party/blink/tools/blinkpy/common/net/web.py
+++ b/third_party/blink/tools/blinkpy/common/net/web.py
@@ -26,13 +26,13 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-import urllib2
+from six.moves import urllib
 
 from blinkpy.common.net.network_transaction import NetworkTransaction
 
 
 class Web(object):
-    class _HTTPRedirectHandler2(urllib2.HTTPRedirectHandler):  # pylint:disable=no-init
+    class _HTTPRedirectHandler2(urllib.request.HTTPRedirectHandler):  # pylint:disable=no-init
         """A subclass of HTTPRedirectHandler to support 308 Permanent Redirect."""
 
         def http_error_308(self, req, fp, code, msg, headers):  # pylint:disable=unused-argument
@@ -45,8 +45,8 @@
             lambda: self.request('GET', url).read())
 
     def request(self, method, url, data=None, headers=None):
-        opener = urllib2.build_opener(Web._HTTPRedirectHandler2)
-        request = urllib2.Request(url=url, data=data)
+        opener = urllib.request.build_opener(Web._HTTPRedirectHandler2)
+        request = urllib.request.Request(url=url, data=data)
 
         request.get_method = lambda: method
 
diff --git a/third_party/blink/tools/blinkpy/common/system/executive.py b/third_party/blink/tools/blinkpy/common/system/executive.py
index e11599d8..6c81bf2 100644
--- a/third_party/blink/tools/blinkpy/common/system/executive.py
+++ b/third_party/blink/tools/blinkpy/common/system/executive.py
@@ -39,6 +39,8 @@
 import threading
 import time
 
+import six
+
 _log = logging.getLogger(__name__)
 
 
@@ -72,7 +74,7 @@
         self.cwd = cwd
 
     def message_with_output(self):
-        return unicode(self)
+        return six.text_type(self)
 
     def command_name(self):
         command_path = self.script_args
@@ -298,7 +300,7 @@
         # See https://bugs.webkit.org/show_bug.cgi?id=37528
         # for an example of a regression caused by passing a unicode string directly.
         # FIXME: We may need to encode differently on different platforms.
-        if isinstance(user_input, unicode):
+        if isinstance(user_input, six.text_type):
             user_input = user_input.encode(self._child_process_encoding())
         return (self.PIPE, user_input)
 
@@ -309,7 +311,7 @@
         args = self._stringify_args(args)
         escaped_args = []
         for arg in args:
-            if isinstance(arg, unicode):
+            if isinstance(arg, six.text_type):
                 # Escape any non-ascii characters for easy copy/paste
                 arg = arg.encode('unicode_escape')
             # FIXME: Do we need to fix quotes here?
@@ -447,7 +449,7 @@
 
     def _stringify_args(self, args):
         # Popen will throw an exception if args are non-strings (like int())
-        string_args = map(unicode, args)
+        string_args = map(six.text_type, args)
         # The Windows implementation of Popen cannot handle unicode strings. :(
         return map(self._encode_argument_if_needed, string_args)
 
diff --git a/third_party/blink/tools/blinkpy/common/system/filesystem.py b/third_party/blink/tools/blinkpy/common/system/filesystem.py
index d4642cc8..9fd2e39 100644
--- a/third_party/blink/tools/blinkpy/common/system/filesystem.py
+++ b/third_party/blink/tools/blinkpy/common/system/filesystem.py
@@ -30,10 +30,10 @@
 A FileSystem object can be used to represent dependency on the
 filesystem, and can be replaced with a MockFileSystem in tests.
 """
+from __future__ import unicode_literals
 
 import codecs
 import errno
-import exceptions
 import glob
 import hashlib
 import logging
@@ -45,6 +45,12 @@
 import tempfile
 import time
 
+try:
+    import exceptions
+except ImportError:
+    # In py3, exceptions were moved into builtins
+    import builtins as exceptions
+
 _log = logging.getLogger(__name__)
 
 
@@ -75,7 +81,7 @@
         """
         if sys.platform == 'win32' and len(path) >= self.WINDOWS_MAX_PATH:
             assert not path.startswith(r'\\'), "must not already be UNC"
-            return ur'\\?\%s' % (self.abspath(path), )
+            return r'\\?\%s' % (self.abspath(path), )
         return path
 
     def abspath(self, path):
diff --git a/third_party/blink/tools/blinkpy/common/system/profiler.py b/third_party/blink/tools/blinkpy/common/system/profiler.py
index fa1580b..1c22d4cd 100644
--- a/third_party/blink/tools/blinkpy/common/system/profiler.py
+++ b/third_party/blink/tools/blinkpy/common/system/profiler.py
@@ -143,7 +143,8 @@
     def profile_after_exit(self):
         # google-pprof doesn't check its arguments, so we have to.
         if not self._host.filesystem.exists(self._output_path):
-            print 'Failed to gather profile, %s does not exist.' % self._output_path
+            print('Failed to gather profile, %s does not exist.' %
+                  self._output_path)
             return
 
         pprof_args = [
@@ -151,13 +152,16 @@
             self._output_path
         ]
         profile_text = self._host.executive.run_command(pprof_args)
-        print 'First 10 lines of pprof --text:'
-        print self._first_ten_lines_of_profile(profile_text)
-        print 'http://google-perftools.googlecode.com/svn/trunk/doc/cpuprofile.html documents output.'
-        print
-        print 'To interact with the the full profile, including produce graphs:'
-        print ' '.join(
-            [self._pprof_path(), self._executable_path, self._output_path])
+        print('First 10 lines of pprof --text:')
+        print(self._first_ten_lines_of_profile(profile_text))
+        print(
+            'http://google-perftools.googlecode.com/svn/trunk/doc/cpuprofile.html documents output.'
+        )
+        print()
+        print(
+            'To interact with the the full profile, including produce graphs:')
+        print(' '.join(
+            [self._pprof_path(), self._executable_path, self._output_path]))
 
 
 class Perf(SingleFileOutputProfiler):
@@ -199,22 +203,24 @@
         perf_exitcode = self._perf_process.wait()
         # The exit code should always be -2, as we're always interrupting perf.
         if perf_exitcode not in (0, -2):
-            print "'perf record' failed (exit code: %i), can't process results:" % perf_exitcode
+            print(
+                "'perf record' failed (exit code: %i), can't process results:"
+                % perf_exitcode)
             return
 
         perf_args = [
             self._perf_path(), 'report', '--call-graph', 'none', '--input',
             self._output_path
         ]
-        print "First 10 lines of 'perf report --call-graph=none':"
+        print("First 10 lines of 'perf report --call-graph=none':")
 
-        print ' '.join(perf_args)
+        print(' '.join(perf_args))
         perf_output = self._host.executive.run_command(perf_args)
-        print self._first_ten_lines_of_profile(perf_output)
+        print(self._first_ten_lines_of_profile(perf_output))
 
-        print 'To view the full profile, run:'
-        print ' '.join([self._perf_path(), 'report', '-i', self._output_path])
-        print  # An extra line between tests looks nicer.
+        print('To view the full profile, run:')
+        print(' '.join([self._perf_path(), 'report', '-i', self._output_path]))
+        print()  # An extra line between tests looks nicer.
 
 
 class Sample(SingleFileOutputProfiler):
diff --git a/third_party/blink/tools/blinkpy/common/system/user.py b/third_party/blink/tools/blinkpy/common/system/user.py
index e6cd7f9..cbc1615 100644
--- a/third_party/blink/tools/blinkpy/common/system/user.py
+++ b/third_party/blink/tools/blinkpy/common/system/user.py
@@ -33,6 +33,8 @@
 import sys
 import webbrowser
 
+from six.moves import input
+
 from blinkpy.common.system.executive import Executive
 from blinkpy.common.system.filesystem import FileSystem
 from blinkpy.common.system.platform_info import PlatformInfo
@@ -52,7 +54,7 @@
 
     # FIXME: These are @classmethods because bugzilla.py doesn't have a Tool object (thus no User instance).
     @classmethod
-    def prompt(cls, message, repeat=1, input_func=raw_input):
+    def prompt(cls, message, repeat=1, input_func=input):
         response = None
         while repeat and not response:
             repeat -= 1
@@ -96,16 +98,16 @@
                          list_title,
                          list_items,
                          can_choose_multiple=False,
-                         input_func=raw_input):
-        print list_title
+                         input_func=input):
+        print(list_title)
         i = 0
         for item in list_items:
             i += 1
-            print '%2d. %s' % (i, item)
+            print('%2d. %s' % (i, item))
         return cls._wait_on_list_response(list_items, can_choose_multiple,
                                           input_func)
 
-    def confirm(self, message=None, default=DEFAULT_YES, input_func=raw_input):
+    def confirm(self, message=None, default=DEFAULT_YES, input_func=input):
         if not message:
             message = 'Continue?'
         choice = {'y': 'Y/n', 'n': 'y/N'}[default]
diff --git a/third_party/blink/tools/blinkpy/w3c/wpt_manifest.py b/third_party/blink/tools/blinkpy/w3c/wpt_manifest.py
index 865ece4..d45ced9e 100644
--- a/third_party/blink/tools/blinkpy/w3c/wpt_manifest.py
+++ b/third_party/blink/tools/blinkpy/w3c/wpt_manifest.py
@@ -163,7 +163,7 @@
         for test_type in self.test_types:
             if test_type not in items:
                 continue
-            for filename, records in items[test_type].iteritems():
+            for filename, records in items[test_type].items():
                 for item in filter(self._is_not_jsshell, records):
                     url_for_item = self._get_url_from_item(item)
                     url_items[url_for_item] = item
diff --git a/third_party/blink/tools/blinkpy/web_tests/breakpad/dump_reader_multipart.py b/third_party/blink/tools/blinkpy/web_tests/breakpad/dump_reader_multipart.py
index 79f415a7..59162de 100644
--- a/third_party/blink/tools/blinkpy/web_tests/breakpad/dump_reader_multipart.py
+++ b/third_party/blink/tools/blinkpy/web_tests/breakpad/dump_reader_multipart.py
@@ -29,7 +29,8 @@
 import cgi
 import logging
 import threading
-import Queue
+
+from six.moves import queue as Queue
 
 from blinkpy.common.path_finder import PathFinder
 from blinkpy.web_tests.breakpad.dump_reader import DumpReader
diff --git a/third_party/blink/tools/blinkpy/web_tests/layout_package/bot_test_expectations.py b/third_party/blink/tools/blinkpy/web_tests/layout_package/bot_test_expectations.py
index 1bd7b32..fa29bdb 100644
--- a/third_party/blink/tools/blinkpy/web_tests/layout_package/bot_test_expectations.py
+++ b/third_party/blink/tools/blinkpy/web_tests/layout_package/bot_test_expectations.py
@@ -33,7 +33,8 @@
 import logging
 import os.path
 import urllib
-import urllib2
+
+from six.moves import urllib
 
 from blinkpy.web_tests.models.typ_types import Expectation, ResultType
 
@@ -162,9 +163,9 @@
     def _results_url_for_builder(self, builder, use_try_step=False):
         test_type = (self.STEP_NAME_TRY if use_try_step else self.STEP_NAME)
         return self.RESULTS_URL_FORMAT % (
-            urllib.quote(test_type),
-            urllib.quote(self.builders.master_for_builder(builder)),
-            urllib.quote(builder))
+            urllib.parse.quote(test_type),
+            urllib.parse.quote(self.builders.master_for_builder(builder)),
+            urllib.parse.quote(builder))
 
     def _results_json_for_builder(self, builder):
         results_url = self._results_url_for_builder(
@@ -172,9 +173,8 @@
         try:
             _log.debug('Fetching flakiness data from appengine: %s',
                        results_url)
-            return ResultsJSON(builder, json.load(
-                urllib2.urlopen(results_url)))
-        except urllib2.URLError as error:
+            return ResultsJSON(builder, json.load(urllib.urlopen(results_url)))
+        except urllib.error.URLError as error:
             _log.warning(
                 'Could not retrieve flakiness data from the bot.  url: %s',
                 results_url)
@@ -185,9 +185,9 @@
         try:
             _log.debug('Fetching flakiness data from appengine: %s',
                        results_url)
-            return ResultsFilter(builder, json.load(
-                urllib2.urlopen(results_url)))
-        except urllib2.URLError as error:
+            return ResultsFilter(builder,
+                                 json.load(urllib.urlopen(results_url)))
+        except urllib.URLError as error:
             _log.warning(
                 'Could not retrieve flakiness data from the bot.  url: %s',
                 results_url)
diff --git a/third_party/blink/tools/blinkpy/web_tests/models/test_expectations.py b/third_party/blink/tools/blinkpy/web_tests/models/test_expectations.py
index 665c735..2dd29ce 100644
--- a/third_party/blink/tools/blinkpy/web_tests/models/test_expectations.py
+++ b/third_party/blink/tools/blinkpy/web_tests/models/test_expectations.py
@@ -186,7 +186,7 @@
         args:
             path: Absolute path of expectations file."""
         content = self._expectations_dict[path]
-        idx = self._expectations_dict.keys().index(path)
+        idx = list(self._expectations_dict.keys()).index(path)
         typ_expectations = self._expectations[idx]
         lines = []
 
diff --git a/third_party/blink/tools/blinkpy/web_tests/models/test_failures.py b/third_party/blink/tools/blinkpy/web_tests/models/test_failures.py
index 8726f99..e49e5553 100644
--- a/third_party/blink/tools/blinkpy/web_tests/models/test_failures.py
+++ b/third_party/blink/tools/blinkpy/web_tests/models/test_failures.py
@@ -26,7 +26,7 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-import cPickle
+from six.moves import cPickle
 
 from blinkpy.web_tests.controllers import repaint_overlay
 from blinkpy.web_tests.models.typ_types import ResultType
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/base.py b/third_party/blink/tools/blinkpy/web_tests/port/base.py
index 87c2c33..2cc0a10 100644
--- a/third_party/blink/tools/blinkpy/web_tests/port/base.py
+++ b/third_party/blink/tools/blinkpy/web_tests/port/base.py
@@ -33,7 +33,6 @@
 
 import time
 import collections
-import itertools
 import json
 import logging
 import optparse
@@ -41,6 +40,8 @@
 import sys
 import tempfile
 
+from six.moves import zip_longest
+
 from blinkpy.common import exit_codes
 from blinkpy.common import find_files
 from blinkpy.common import read_checksum_from_png
@@ -2045,8 +2046,8 @@
         # This walks through the set of paths where we should look for tests.
         # For each path, a map can be provided that we replace 'path' with in
         # the result.
-        for filter_path, virtual_prefix in itertools.izip_longest(
-                filter_paths, virtual_prefixes):
+        for filter_path, virtual_prefix in zip_longest(filter_paths,
+                                                       virtual_prefixes):
             # This is to make sure "external[\\/]?" can also match to
             # external/wpt.
             # TODO(robertma): Remove this special case when external/wpt is
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/factory.py b/third_party/blink/tools/blinkpy/web_tests/port/factory.py
index 40159d4..2f85644 100644
--- a/third_party/blink/tools/blinkpy/web_tests/port/factory.py
+++ b/third_party/blink/tools/blinkpy/web_tests/port/factory.py
@@ -86,14 +86,26 @@
         """Returns a Port subclass and its name for the given port_name."""
         if 'browser_test' in port_name:
             module_name, class_name = port_name.rsplit('.', 1)
-            module = __import__(module_name, globals(), locals(), [], -1)
+            try:
+                module = __import__(module_name, globals(), locals(), [], -1)
+            except ValueError:
+                # Python3 doesn't allow the level param to be -1. Setting it to
+                # 1 searches for modules in 1 parent directory.
+                module = __import__(module_name, globals(), locals(), [], 1)
             port_class_name = module.get_port_class_name(class_name)
             if port_class_name is not None:
                 return module.__dict__[port_class_name], class_name
         else:
             for port_class in cls.PORT_CLASSES:
                 module_name, class_name = port_class.rsplit('.', 1)
-                module = __import__(module_name, globals(), locals(), [], -1)
+                try:
+                    module = __import__(module_name, globals(), locals(), [],
+                                        -1)
+                except ValueError:
+                    # Python3 doesn't allow the level param to be -1. Setting it
+                    # to 1 searches for modules in 1 parent directory.
+                    module = __import__(module_name, globals(), locals(), [],
+                                        1)
                 port_class = module.__dict__[class_name]
                 if port_name.startswith(port_class.port_name):
                     return port_class, class_name
diff --git a/third_party/blink/web_tests/FlagExpectations/composite-after-paint b/third_party/blink/web_tests/FlagExpectations/composite-after-paint
index 497e9837..97944fb 100644
--- a/third_party/blink/web_tests/FlagExpectations/composite-after-paint
+++ b/third_party/blink/web_tests/FlagExpectations/composite-after-paint
@@ -64,13 +64,6 @@
 
 crbug.com/1124979 compositing/video/video-controls-layer-creation.html [ Pass Failure ]
 
-crbug.com/1150468 virtual/composite-bgcolor-animation/external/wpt/css/css-backgrounds/animations/one-element-animation.html [ Crash ]
-crbug.com/1150468 virtual/composite-bgcolor-animation/external/wpt/css/css-backgrounds/animations/one-element-three-keyframes-animation1.html [ Crash ]
-crbug.com/1150468 virtual/composite-bgcolor-animation/external/wpt/css/css-backgrounds/animations/one-element-three-keyframes-animation2.html [ Crash ]
-crbug.com/1150468 virtual/composite-bgcolor-animation/external/wpt/css/css-backgrounds/animations/one-element-three-keyframes-animation3.html [ Crash ]
-crbug.com/1150468 virtual/composite-bgcolor-animation/external/wpt/css/css-backgrounds/animations/one-element-transition.html [ Crash ]
-crbug.com/1150468 virtual/composite-bgcolor-animation/external/wpt/css/css-backgrounds/animations/animation-fallback-replace.html [ Crash ]
-
 crbug.com/1157199 external/wpt/css/css-paint-api/column-count-crash.https.html [ Crash ]
 crbug.com/1157199 virtual/off-main-thread-css-paint/external/wpt/css/css-paint-api/column-count-crash.https.html [ Crash ]
 
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
index 1307d12..1d7c7c2c0 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
+++ b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
@@ -791,7 +791,6 @@
 
 ### Pass in Legacy, fail in NG
 crbug.com/958381 external/wpt/css/css-tables/visibility-collapse-rowspan-005.html [ Pass ]
-crbug.com/958381 fast/table/large-col-span-crash.html [ Pass ]
 crbug.com/958381 fast/table/table-all-rowspans-height-distribution-in-rows-except-overlapped.html [ Pass ]
 crbug.com/958381 fast/table/table-all-rowspans-height-distribution-in-rows.html [ Pass ]
 crbug.com/958381 fast/table/table-rowspan-height-distribution-in-rows-1.html [ Pass ]
@@ -919,6 +918,13 @@
 crbug.com/958381 fast/css/empty-cell-baseline.html [ Failure ]
 crbug.com/958381 fast/css/inline-table-first-row-empty-cell-non-auto.html [ Failure ]
 
+# New failures after TablesNG landed, most likely rebaselined tests
+crbug.com/958381 accessibility/table-column-track-merging.html [ Failure ]
+crbug.com/958381 fast/table/colspanMinWidth.html [ Failure ]
+crbug.com/958381 fast/table/colspanMinWidth-vertical.html [ Failure ]
+crbug.com/958381 fast/table/large-col-span-crash.html [ Failure ]
+crbug.com/958381 external/wpt/css/css-tables/column-track-merging.html [ Failure ]
+
 # TablesNG end
 
 ### compositing/filters/
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 0163c9df..d255fac9 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1069,9 +1069,6 @@
 
 ### TablesNG
 # crbug.com/958381
-# fails because TablesNG does not coalesce columns
-crbug.com/958381 accessibility/table-cells-with-colspan.html [ Failure ]
-crbug.com/958381 fast/table/large-col-span-crash.html [ Failure ]
 
 # TODO fails because cell size with only input element is 18px, not 15. line-height: 0px fixes it.
 crbug.com/1171616 external/wpt/css/css-tables/height-distribution/percentage-sizing-of-table-cell-children.html [ Failure ]
@@ -1104,9 +1101,6 @@
 crbug.com/958381 [ Mac ] virtual/layout_ng_block_frag/fragmentation/fragmented-table-cell.html [ Failure ]
 crbug.com/958381 [ Mac ] fragmentation/single-line-cells-paginated-with-text.html [ Failure ]
 
-# Asan underinvalidation failure. Skip while investigating
-crbug.com/958381 paint/tables/huge-table-composited-scroll-collapsed-borders.html [ Skip ]
-
 # TablesNG ends
 
 # ====== LayoutNG-only failures until here ======
@@ -5693,3 +5687,7 @@
 crbug.com/1132413 external/wpt/html/semantics/scripting-1/the-script-element/json-module/non-object.tentative.any.sharedworker.html [ Pass Timeout ]
 crbug.com/1132413 external/wpt/html/semantics/scripting-1/the-script-element/json-module/utf8.tentative.html [ Pass Timeout ]
 crbug.com/1132413 external/wpt/html/semantics/scripting-1/the-script-element/json-module/valid-content-type.tentative.html [ Pass Timeout ]
+
+# Sheriff 2021-02-11
+crbug.com/1177573 http/tests/inspector-protocol/animation/animation-pause.js [ Failure Pass ]
+crbug.com/1177573 http/tests/inspector-protocol/animation/animation-pause-infinite.js [ Failure Pass ]
diff --git a/third_party/blink/web_tests/accessibility/table-column-track-merging.html b/third_party/blink/web_tests/accessibility/table-column-track-merging.html
new file mode 100644
index 0000000..08e43f3e
--- /dev/null
+++ b/third_party/blink/web_tests/accessibility/table-column-track-merging.html
@@ -0,0 +1,124 @@
+<!doctype html>
+<title>Column track merging accesibilty tests</title>
+<script src='../resources/testharness.js'></script>
+<script src='../resources/testharnessreport.js'></script>
+<link rel="author" title="Aleks Totic" href="atotic@chromium.org" />
+<link rel="help" href="https://www.w3.org/TR/css-tables-3/#dimensioning-the-row-column-grid--step2" />
+<style>
+
+main table {
+  border: 10px solid gray;
+  border-spacing: 20px;
+}
+
+main td {
+  width: 50px;
+  height:50px;
+  padding: 0;
+  background:linear-gradient(to right, yellow, orange);
+}
+main caption {
+  background: #EEE;
+}
+main .desc {
+  margin-top: 20px;
+  color: rgb(50,0,0);
+}
+main pre {
+  white-space: pre-wrap;
+
+}
+</style>
+<main>
+<p>Checks accessibility table properties when tracks are merged. a11y part of wpt/css/css-tables/column-track-merging.html</p>
+
+<table id="td_auto">
+<caption>auto</caption>
+<tr>
+  <td colspan=10></td>
+  <td></td>
+</tr>
+<tr>
+  <td colspan=10></td>
+  <td></td>
+</tr>
+</table>
+
+<table id="td_auto_width" style="width:400px">
+<caption>auto 400px</caption>
+<tr>
+  <td colspan=10></td>
+  <td></td>
+</tr>
+<tr>
+  <td colspan=10></td>
+  <td></td>
+</tr>
+</table>
+
+<table id="td_fixed" style="table-layout:fixed; width: 400px">
+<caption>fixed 400px</caption>
+<tr>
+  <td colspan=10></td>
+  <td></td>
+</tr>
+<tr>
+  <td colspan=10></td>
+  <td></td>
+</tr>
+</table>
+
+<table id="col_fixed_130" style="table-layout:fixed; width: 130px">
+<col span=10>
+<caption>col fixed 130px</caption>
+<tr>
+  <td></td>
+  <td></td>
+</tr>
+<tr>
+  <td></td>
+  <td></td>
+</tr>
+</table>
+
+</main>
+<script>
+
+  test(function() {
+    assert_true(!!window.accessibilityController, "accessibilityController exists");
+  }, "accessibilityController exists");
+
+  // a11y tests
+
+  test(function() {
+    let table = accessibilityController.accessibleElementById("td_auto");
+    assert_equals(table.columnCount, 2, "has merged td columns");
+    let cell = table.cellForColumnAndRow(0,0);
+    assert_equals(cell.columnIndexRange(), "{0, 1}", "cell.columnIndexRange");
+  }, "td_auto table props");
+
+  test(function() {
+    let table = accessibilityController.accessibleElementById("td_auto_width");
+    assert_equals(table.columnCount, 2, "has merged td columns");
+    let cell = table.cellForColumnAndRow(0,0);
+    assert_equals(cell.columnIndexRange(), "{0, 1}", "cell.columnIndexRange");
+  }, "td_auto_width table props");
+
+  test(function() {
+    let table = accessibilityController.accessibleElementById("td_fixed");
+    assert_equals(table.columnCount, 11, "has not merged td columns");
+    let cell = table.cellForColumnAndRow(0,0);
+    assert_equals(cell.columnIndexRange(), "{0, 10}", "cell.columnIndexRange");
+  }, "td_fixed table props");
+
+  test(function() {
+    let table = accessibilityController.accessibleElementById("col_fixed_130");
+    assert_equals(table.columnCount, 10, "has not merged td columns");
+    let cell = table.cellForColumnAndRow(0,0);
+    assert_equals(cell.columnIndexRange(), "{0, 1}", "cell.columnIndexRange");
+  }, "col_fixed table props");
+
+ </script>
+
+</body>
+</html>
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 635bf72..58cb80f 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
@@ -70,6 +70,15 @@
       ]
      ]
     },
+    "css-backgrounds": {
+     "linear-gradient-calc-crash.html": [
+      "5ae6104a7941b836ab700ca50b5dbe20914afcf6",
+      [
+       null,
+       {}
+      ]
+     ]
+    },
     "css-break": {
      "break-before-with-no-fragmentation-crash.html": [
       "fb80ec45bceec093481fa54513c606c5952628b1",
@@ -254,6 +263,15 @@
       ]
      }
     },
+    "css-images": {
+     "gradient-nan-crash.html": [
+      "8c4b647042531c50ec4fd441c9c26c302c75cae5",
+      [
+       null,
+       {}
+      ]
+     ]
+    },
     "css-layout-api": {
      "inside-multicol-crash.https.html": [
       "61223bfcdccfaa965d684e287da71bee00664fc4",
@@ -62217,6 +62235,255 @@
        ]
       ]
      },
+     "cssom": {
+      "cssom-additive-symbols-setter-invalid.html": [
+       "fd382553df86d6b093d5d580f97b6791dab8b77b",
+       [
+        null,
+        [
+         [
+          "/css/css-counter-styles/cssom/cssom-additive-symbols-setter-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "cssom-additive-symbols-setter.html": [
+       "1ff6b424464a6a637116d81e99f0098bfcc5ec32",
+       [
+        null,
+        [
+         [
+          "/css/css-counter-styles/cssom/cssom-additive-symbols-setter-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "cssom-fallback-setter-invalid.html": [
+       "c5c43a32237647209945e65fab10c97d24debd50",
+       [
+        null,
+        [
+         [
+          "/css/css-counter-styles/cssom/cssom-fallback-setter-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "cssom-fallback-setter.html": [
+       "399463f3f18e67cfbf52b936bed71ef975b0f3c4",
+       [
+        null,
+        [
+         [
+          "/css/css-counter-styles/cssom/cssom-fallback-setter-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "cssom-name-setter-invalid.html": [
+       "01edc415e90e6f7ce6ab5df98fec7def8e7c4f06",
+       [
+        null,
+        [
+         [
+          "/css/css-counter-styles/cssom/cssom-name-setter-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "cssom-name-setter.html": [
+       "4cb926dd1291e3e513875e45e21a4fa2e3ca2506",
+       [
+        null,
+        [
+         [
+          "/css/css-counter-styles/cssom/cssom-name-setter-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "cssom-negative-setter-invalid.html": [
+       "e15447ba4ddf52d2764f2080abc1e4966ca85f26",
+       [
+        null,
+        [
+         [
+          "/css/css-counter-styles/cssom/cssom-negative-setter-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "cssom-negative-setter.html": [
+       "06238841ec5d558ec4a97123480e4548a150b49a",
+       [
+        null,
+        [
+         [
+          "/css/css-counter-styles/cssom/cssom-negative-setter-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "cssom-pad-setter-invalid.html": [
+       "c263a1bb5fa8e94de60800100339abc819d6836e",
+       [
+        null,
+        [
+         [
+          "/css/css-counter-styles/cssom/cssom-pad-setter-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "cssom-pad-setter.html": [
+       "df1732dae6290fd27bdebb303fe56ed989a5e080",
+       [
+        null,
+        [
+         [
+          "/css/css-counter-styles/cssom/cssom-pad-setter-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "cssom-prefix-suffix-setter-invalid.html": [
+       "7aba3a0a5b915356ae95bf96345d9f3d54ba0712",
+       [
+        null,
+        [
+         [
+          "/css/css-counter-styles/cssom/cssom-prefix-suffix-setter-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "cssom-prefix-suffix-setter.html": [
+       "899caa26ba7f47eb9d2013172be1270334a1d541",
+       [
+        null,
+        [
+         [
+          "/css/css-counter-styles/cssom/cssom-prefix-suffix-setter-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "cssom-range-setter-invalid.html": [
+       "2fc459551f084c319b787114c3b5076d92c0eb08",
+       [
+        null,
+        [
+         [
+          "/css/css-counter-styles/cssom/cssom-range-setter-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "cssom-range-setter.html": [
+       "10d94f0cdb39c44b90accf2e18238ea2401184ee",
+       [
+        null,
+        [
+         [
+          "/css/css-counter-styles/cssom/cssom-range-setter-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "cssom-symbols-setter-invalid.html": [
+       "3b40b0d4a3bb7d5d99757c0316b6ad2d07fc4f1e",
+       [
+        null,
+        [
+         [
+          "/css/css-counter-styles/cssom/cssom-symbols-setter-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "cssom-symbols-setter.html": [
+       "cd9f66d23860120cab626a3be309d50839201704",
+       [
+        null,
+        [
+         [
+          "/css/css-counter-styles/cssom/cssom-symbols-setter-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "cssom-system-setter-1.html": [
+       "a616a60e0b08759e8c07c01f643cd0f5e4005731",
+       [
+        null,
+        [
+         [
+          "/css/css-counter-styles/cssom/cssom-system-setter-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "cssom-system-setter-2.html": [
+       "f1cc65d7fccf0f704d73c4919acab2a2df9de5f6",
+       [
+        null,
+        [
+         [
+          "/css/css-counter-styles/cssom/cssom-system-setter-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "cssom-system-setter-invalid.html": [
+       "e56ec1a23e4388d4daaaea11f3e887bc9ba8070f",
+       [
+        null,
+        [
+         [
+          "/css/css-counter-styles/cssom/cssom-system-setter-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ]
+     },
      "devanagari": {
       "css3-counter-styles-119.html": [
        "c736f7f5a323eb73857048cbd81289900a40ef04",
@@ -192112,6 +192379,44 @@
        []
       ]
      },
+     "cssom": {
+      "cssom-additive-symbols-setter-ref.html": [
+       "a09788e369a58a35dd92370cc1f25ef2561c1814",
+       []
+      ],
+      "cssom-fallback-setter-ref.html": [
+       "da4bb598206dfac3df58802e92e7ce3ffcb0a8a0",
+       []
+      ],
+      "cssom-name-setter-ref.html": [
+       "91251ad84369620ceb76005d872cb236a219e0f8",
+       []
+      ],
+      "cssom-negative-setter-ref.html": [
+       "7d465a3335e907207c26fbbb4ec159e816a84927",
+       []
+      ],
+      "cssom-pad-setter-ref.html": [
+       "6184686f0ee4830f0cebb39eba076fcb5673678a",
+       []
+      ],
+      "cssom-prefix-suffix-setter-ref.html": [
+       "bf52d54adbc550d65671ccb2cbe3c1a52b4c4d2f",
+       []
+      ],
+      "cssom-range-setter-ref.html": [
+       "0129b467c8381bf3bae38258f493fcbf31ab5cbc",
+       []
+      ],
+      "cssom-symbols-setter-ref.html": [
+       "64967db5ab92fa03d42f0a13caf02ab0562e8ae5",
+       []
+      ],
+      "cssom-system-setter-ref.html": [
+       "98bd994659ffa6286c386fb65181eb76c845dca3",
+       []
+      ]
+     },
      "devanagari": {
       "css3-counter-styles-119-ref.html": [
        "06a8bf9b30e4cb1a7a22f206bb1bdf7d5900ca19",
@@ -224125,35 +224430,35 @@
       []
      ],
      "typing-around-link-element-at-collapsed-selection.tentative_target=DesignMode-expected.txt": [
-      "a6b2d0a12de9ead154afaeccff8c4faeaa058bdd",
+      "c5eb3419080252735676e73f0955d412227ac6f6",
       []
      ],
      "typing-around-link-element-at-collapsed-selection.tentative_target=DesignMode_child=b-expected.txt": [
-      "6af2bc68af845c4dff50f24d7f906d9ca07f6236",
+      "3506214b7204700c68a1328d2e9809efa4aba070",
       []
      ],
      "typing-around-link-element-at-collapsed-selection.tentative_target=DesignMode_parent=b-expected.txt": [
-      "f51ea1257a6609c9c1a18aca3d369d8db48bed59",
+      "4075ee24059027dc1615370636c093d030933df1",
       []
      ],
      "typing-around-link-element-at-collapsed-selection.tentative_target=DesignMode_parent=b_child=i-expected.txt": [
-      "e8d34315723200c6f9f75f6d82cbbb51553c7897",
+      "f717a6282341b086bf61dbcaf98fa5f93fa9cd91",
       []
      ],
      "typing-around-link-element-at-non-collapsed-selection.tentative_target=DesignMode-expected.txt": [
-      "fb4c86105abd437d83fd2fdd97bcaf5d97f3a336",
+      "2774722af7cec7ffbd7d5634fe047c7cf6a3e044",
       []
      ],
      "typing-around-link-element-at-non-collapsed-selection.tentative_target=DesignMode_child=b-expected.txt": [
-      "2584e79549405bfcfdd69f5f6e327deeb65a5a87",
+      "7a6fc54479d51e4cc46c520f4fd4c0d792335a95",
       []
      ],
      "typing-around-link-element-at-non-collapsed-selection.tentative_target=DesignMode_parent=b-expected.txt": [
-      "739ba50c81524fc8c424880738e41661f0e30a2e",
+      "9b425c4d0519ea4749cd598459343ce30e274155",
       []
      ],
      "typing-around-link-element-at-non-collapsed-selection.tentative_target=DesignMode_parent=b_child=i-expected.txt": [
-      "9d0b49b6fba1ff29feb07331d74397d141fecc80",
+      "2cd75e33ab0d7c88da6bd362285873e7cf727fb9",
       []
      ]
     },
@@ -232872,7 +233177,7 @@
          []
         ],
         "transformations.yaml": [
-         "aa56c85f2aec3d6161a901823b814c6db83c74f0",
+         "65346ca1f800f36ad35afbc4510fad56b62a3bf6",
          []
         ],
         "video.yaml": [
@@ -238349,7 +238654,13 @@
        "object_border-ref.xhtml": [
         "6eaaa0ba1412e3ba834f33226534685dcf177e17",
         []
-       ]
+       ],
+       "resources": {
+        "aspect-ratio.js": [
+         "53226c38f5c9ac661c31a73ae360a5940acdbb33",
+         []
+        ]
+       }
       },
       "embedded-content": {
        "cross-domain-iframe.sub-ref.html": [
@@ -240689,6 +241000,18 @@
         []
        ],
        "css-module": {
+        "css-module-worker-test-expected.txt": [
+         "8421503f80c538b1df72a55b590035057c5ac694",
+         []
+        ],
+        "import-css-module-basic-expected.txt": [
+         "13524d3a9cb3d138c9aab9b61e2de2f0fe215f54",
+         []
+        ],
+        "import-css-module-dynamic-expected.txt": [
+         "cf4efe318705b810e64b173ce1f367fda0bd3639",
+         []
+        ],
         "resources": {
          "bad-import.css": [
           "796446b525ca03bf287e2abbdbbdae593b658bac",
@@ -240703,19 +241026,23 @@
           []
          ],
          "css-module-at-import-iframe.html": [
-          "86e7af7d51db98fd7e22c01954ced16683c4f0e7",
+          "cce9e2163d76dc8af5081f80b6f06e51a54f647a",
           []
          ],
          "css-module-basic-iframe.html": [
-          "3a555c392716c0ee915f1aa65cd5a1dae42f0c52",
+          "e093d39898f7bb8b8085be5e70c1d10635c40df6",
           []
          ],
          "css-module-basic-large-iframe.html": [
-          "cc5b660e4cbd0bcfbf20923f8c979cc40672891a",
+          "0cf11e9139f513f4f93286bd73f387d92743710f",
+          []
+         ],
+         "css-module-without-assertion-iframe.html": [
+          "3d1be841cee24e90d10be47fb821f9f55937fbfe",
           []
          ],
          "malformed-iframe.html": [
-          "471edd680cf656661bcc713656aaa23e49386ba7",
+          "f5c64f6b59e3e200c9d1015cf2e4e11efbd13388",
           []
          ],
          "malformed.css": [
@@ -240727,14 +241054,18 @@
           []
          ],
          "worker-dynamic-import.js": [
-          "9a3b0bb105b3ddc9d4e416ecfc3606c2442ba6ab",
+          "6f6852ce5501cf05255f9b341cdd3dc12f8792a7",
           []
          ],
          "worker.js": [
-          "397a12c3b53c60634417a67bef839af9aef07e6e",
+          "c97d9652d3583f555f64fd2eccda3ba677f2fca1",
           []
          ]
-        }
+        },
+        "utf8.tentative-expected.txt": [
+         "3fa83c0675cc11459f3d61548f0bf93f12d180c3",
+         []
+        ]
        },
        "defer.js": [
         "c4449ca7c8a598e3712c8b24eb9e424775c19102",
@@ -240984,7 +241315,7 @@
          []
         ],
         "json-module-service-worker-test.https.tentative-expected.txt": [
-         "df94598def63e2666aa51cf1012baa410a40e5eb",
+         "0e811cbc24651bc58e09aecc979e59539ce16edc",
          []
         ],
         "module.json": [
@@ -240996,11 +241327,11 @@
          []
         ],
         "non-object.tentative.any-expected.txt": [
-         "77c1f1875bc0b7eb15e1f8c59ad6851e9fdf6aa0",
+         "cf4efe318705b810e64b173ce1f367fda0bd3639",
          []
         ],
         "non-object.tentative.any.serviceworker-expected.txt": [
-         "25ca5d904d39e41d639846334c840117510cc58f",
+         "8b4bc1580850f03a1163ee1548fa81ffe8e62c7e",
          []
         ],
         "non-object.tentative.any.sharedworker-expected.txt": [
@@ -241008,7 +241339,7 @@
          []
         ],
         "non-object.tentative.any.worker-expected.txt": [
-         "77c1f1875bc0b7eb15e1f8c59ad6851e9fdf6aa0",
+         "cf4efe318705b810e64b173ce1f367fda0bd3639",
          []
         ],
         "null.json": [
@@ -241020,11 +241351,11 @@
          []
         ],
         "serviceworker-dynamic-import.js": [
-         "3c7d29488db927695d386ee8f6049e01899eb8aa",
+         "9466c6fbe409a790ee3eab5405b98b86b7842a40",
          []
         ],
         "serviceworker.js": [
-         "6d71f0175c7e47370bb3151ff7dcdff3a1794076",
+         "3f0a4d1664020f6d041ad77bccf956e2be22ce0f",
          []
         ],
         "string.json": [
@@ -248612,6 +248943,14 @@
       "4ba512140dae5d9df8d1b1bd19178a34ff5b40c0",
       []
      ],
+     "opaque-origin-history1.sub.https.html": [
+      "cb1f214f53c67667d918f2aca67936a5ccf31428",
+      []
+     ],
+     "opaque-origin-history2.https.html": [
+      "20e63cf48b485a19953f78e64ee90d3845aea396",
+      []
+     ],
      "opaque-origin1.sub.https.html": [
       "f8a8c9d1adf3ed99789d101607eeb75093977430",
       []
@@ -248769,18 +249108,10 @@
     ],
     "compat": {
      "pointerevent_mouseevent_key_pressed-expected.txt": [
-      "3be14d38d8192ac1be3e40ef320f7b1ae497b4c0",
+      "3d8bfb80d1134af4f5394daa0f5b624cabada1a5",
       []
      ]
     },
-    "idlharness.window-expected.txt": [
-     "22bb34a054ec5d0f1ed837b8b23d544110d78912",
-     []
-    ],
-    "idlharness.window.js.ini": [
-     "2c8f43716403bfd4a8bd8a7bdecaf888a2bcd697",
-     []
-    ],
     "pointerevent_coalesced_events_attributes-expected.txt": [
      "e9ecdaa4bfe35a4e711c10c960d3d7124c389537",
      []
@@ -251025,6 +251356,10 @@
     }
    },
    "resource-timing": {
+    "CodingConventions.md": [
+     "461cae421e66c34c17171f33f15933b4f99afe6d",
+     []
+    ],
     "DIR_METADATA": [
      "931e51bc45c9d2b60007e45aad0bb2bb63b82762",
      []
@@ -251471,7 +251806,7 @@
       []
      ],
      "webxr-test.js": [
-      "e3b21fb720af82c479d8b1547a5c420eceeb004d",
+      "fe9dc3f92ec90255e20822ba1b0c32e83f20f6a7",
       []
      ],
      "webxr-test.js.headers": [
@@ -259598,7 +259933,7 @@
      []
     ],
     "RTCPeerConnection-addIceCandidate-expected.txt": [
-     "74fd4978530c3133786c306d894b6beee582503d",
+     "a6fc09fb3d3fcbc4c5e276646fd371bfc68b7c28",
      []
     ],
     "RTCPeerConnection-createAnswer-expected.txt": [
@@ -259626,7 +259961,7 @@
      []
     ],
     "RTCPeerConnection-helper.js": [
-     "5aef0a8e21e42fafe58719c0fb246a919d1b15d1",
+     "cdfe63e792c6f7bcaa3d23a987d2fa7a8e87db97",
      []
     ],
     "RTCPeerConnection-mandatory-getStats.https-expected.txt": [
@@ -259752,7 +260087,7 @@
      []
     ],
     "idlharness.https.window-expected.txt": [
-     "b94b06ac527900ac825ed0f003ab4a325da38e4c",
+     "7b9055e098fd5fe5310aaaf2015d917db8222dca",
      []
     ],
     "legacy": {
@@ -345583,7 +345918,7 @@
      ]
     ],
     "showPicker-errors.https.window.js": [
-     "2310c323d9f37d317a65e2bb6cdcc001ebfa3568",
+     "189f47344cabfa2a6423b1cda6ae9fbfc7f2ad52",
      [
       "file-system-access/showPicker-errors.https.window.html",
       {
@@ -355719,6 +356054,13 @@
          {}
         ]
        ],
+       "2d.transformation.perspective.html": [
+        "6aa4a224d4bb1aba901c4594c1899e8ab5513f31",
+        [
+         null,
+         {}
+        ]
+       ],
        "2d.transformation.rotate.direction.html": [
         "4c1b48f7480f29d3e0d079040216b0eb7c98cf6b",
         [
@@ -355762,35 +356104,35 @@
         ]
        ],
        "2d.transformation.rotate3d.X.html": [
-        "c55a9bf25870387a0a070d13a3539e8c0c256025",
+        "42a4e3c45a0f3a1fa5656c57b269e01c133e37da",
         [
          null,
          {}
         ]
        ],
        "2d.transformation.rotate3d.Y.html": [
-        "12bf5078ae0bc87f703198b62d87828e4facc69f",
+        "5006769fa400b65d8263e25bf55f1aca1c8ec1fa",
         [
          null,
          {}
         ]
        ],
        "2d.transformation.rotate3d.Z.html": [
-        "8a264661c75865ed455d9814c7dabc2e8f15eb72",
+        "71e113dfe5ba707a43fc1a1ab33256ebd38614f4",
         [
          null,
          {}
         ]
        ],
        "2d.transformation.rotate3d.html": [
-        "80812bde220ac1b516a44ac66533f5cfca7c263c",
+        "104e0870f27c5464ef107e978740364e0c11c523",
         [
          null,
          {}
         ]
        ],
        "2d.transformation.rotateAxis.html": [
-        "47258dffb70d5ff0e1300049fcf10200c40e293b",
+        "be0785a7d3606dbc5f0e1a77cb176d996ce375f3",
         [
          null,
          {}
@@ -371384,7 +371726,7 @@
         ]
        ],
        "canvas-aspect-ratio.html": [
-        "91fdc6c86c55595c2484a88a4e026827c3608581",
+        "816d84e44478166f3f971501ecf5bbb4f79c08e8",
         [
          null,
          {}
@@ -371419,7 +371761,7 @@
         ]
        ],
        "img-aspect-ratio.html": [
-        "af4542e55fde52c4efd316795d1500b35099d3f6",
+        "79a9becca69c27bcd564301e29a9c8482363a49b",
         [
          null,
          {}
@@ -371454,7 +371796,7 @@
         ]
        ],
        "video-aspect-ratio.html": [
-        "c81b70dbf4ea6484643f534fecfccaf023f828ec",
+        "bfa91081e26ac8dc755262ecee5e87a876461dce",
         [
          null,
          {}
@@ -378192,6 +378534,15 @@
          {}
         ]
        ],
+       "popup-light-dismiss.tentative.html": [
+        "eaa7dfd0d054110646404e39c2ba93349842942a",
+        [
+         null,
+         {
+          "testdriver": true
+         }
+        ]
+       ],
        "popup-shadow-dom.tentative.html": [
         "eef0309a4db21cade0e0a24eff733b273b11457d",
         [
@@ -378543,14 +378894,21 @@
          ]
         ],
         "import-css-module-basic.html": [
-         "4ca2bb70899b1070030359ee1738075fed993567",
+         "207d553c69e2016770171a1e572ec724cd835561",
+         [
+          null,
+          {}
+         ]
+        ],
+        "import-css-module-dynamic.html": [
+         "4fbc11180fa0925f1e397bd42e78ab22ae177b29",
          [
           null,
           {}
          ]
         ],
         "utf8.tentative.html": [
-         "f71339b4d591853e5abb19b694b880395bfc9f81",
+         "6adcd716328b11bf29539f24c763dbeecff33d5e",
          [
           null,
           {}
@@ -379832,21 +380190,21 @@
          ]
         ],
         "json-module-service-worker-test.https.tentative.html": [
-         "2e1f9d8179cc2f396af8bfcd866188f917a3d76f",
+         "cc47da1499f13a1233ab71e461b77e7a7b53be26",
          [
           null,
           {}
          ]
         ],
         "module.tentative.html": [
-         "93243853226806c86ac360766fe3e86d30325c06",
+         "a495d4ac186260b6705c56b75d2b1ff1de7722a1",
          [
           null,
           {}
          ]
         ],
         "non-object.tentative.any.js": [
-         "dcbe60f2c2f9dc8478711a1cb4ed2e82defe7107",
+         "6d507177e1ec5f45ed39fe5f861f2c4490555114",
          [
           "html/semantics/scripting-1/the-script-element/json-module/non-object.tentative.any.html",
           {
@@ -379900,14 +380258,14 @@
          ]
         ],
         "utf8.tentative.html": [
-         "1c0360b17e3f9e5d1a5e8d0c65eb9e4a13d144ae",
+         "24a6f109e1c586fac8a00d0f2896e91ad8b5acfd",
          [
           null,
           {}
          ]
         ],
         "valid-content-type.tentative.html": [
-         "78e8b1d23fed229e800a4efef9d18e43697c023f",
+         "ff5953cb700ef360f45eb309f0e93fdabcb8e854",
          [
           null,
           {}
@@ -386741,7 +387099,7 @@
     "testdriver": {
      "actions": {
       "actionsWithKeyPressed.html": [
-       "b977f0c28735682e83729cb8a76cb258e123c26f",
+       "3e0795b14a7b3122d71cf5f4c5d5478867a479d3",
        [
         null,
         {
@@ -398000,6 +398358,13 @@
       }
      ]
     ],
+    "permissions-policy-opaque-origin-history.https.html": [
+     "969ca369e1bcbd211b31e19519a20398c1c32e6b",
+     [
+      null,
+      {}
+     ]
+    ],
     "permissions-policy-opaque-origin.https.html": [
      "edb6e11a95799447359c161175ed1002ed948902",
      [
@@ -398583,7 +398948,7 @@
      ]
     },
     "idlharness.window.js": [
-     "b41a65f3ed0e58ab0bdfab55302c0d49cfb7df24",
+     "e6e84fa9c72ff8ac639607d8a4dad8c0816e7ac6",
      [
       "pointerevents/idlharness.window.html",
       {
@@ -425190,6 +425555,13 @@
        {}
       ]
      ],
+     "repeatcount-numeric-limit.tentative.svg": [
+      "aa0432559bf916c1172c3d30b3bc105cbfb25fdd",
+      [
+       null,
+       {}
+      ]
+     ],
      "repeatn-remove-add-animation.html": [
       "8098a8853568a10370bf35f444c6de5d842f07fe",
       [
@@ -438717,7 +439089,7 @@
      ]
     ],
     "audio-encoder.any.js": [
-     "8b918dc0ba0a686cda04b3bdf05f77fba1a931b0",
+     "9ce1d0d7b0aaa441a554c47144e55f02e0ed1c71",
      [
       "webcodecs/audio-encoder.any.html",
       {
@@ -440065,7 +440437,7 @@
      ]
     ],
     "RTCPeerConnection-addIceCandidate.html": [
-     "5fcb6e864a8a223d5062b22b7a8f5e89fbf9d4cb",
+     "d8e24d608baee6c60e6d2b3ae246974fe6936ec2",
      [
       null,
       {}
@@ -447104,6 +447476,57 @@
       ]
      ]
     },
+    "light-estimation": {
+     "xrFrame_getLightEstimate_oldSession.https.html": [
+      "7a896aa9ff7657733a95ffa8011489a9d29f2764",
+      [
+       null,
+       {}
+      ]
+     ],
+     "xrFrame_getLightEstimate_staleFrame.https.html": [
+      "499a30d56116d49d456636d3070797907fde1f32",
+      [
+       null,
+       {}
+      ]
+     ],
+     "xrFrame_getLightEstimate_valid.https.html": [
+      "68c5d841fcb3724a1674b5b5c77814385d73b7fe",
+      [
+       null,
+       {}
+      ]
+     ],
+     "xrSession_getLightProbe_ended.https.html": [
+      "cc046499f9748048303c8afff916ad66be3b2a0e",
+      [
+       null,
+       {}
+      ]
+     ],
+     "xrSession_getLightProbe_notEnabled.https.html": [
+      "23fe1c6ec5cddae336f64764f391d2e790c9261b",
+      [
+       null,
+       {}
+      ]
+     ],
+     "xrSession_getLightProbe_valid.https.html": [
+      "074c7fd1dcfc2edaf3616cc23c13a16ba869e806",
+      [
+       null,
+       {}
+      ]
+     ],
+     "xrWebGLBinding_getReflectionCubeMap.https.html": [
+      "b46f44881d5e852aa549c1395b70be8cc110e9b1",
+      [
+       null,
+       {}
+      ]
+     ]
+    },
     "navigator_xr_sameObject.https.html": [
      "2c3ea541a97301d4dfabe93bc71c8c6d3c50b5d1",
      [
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/linear-gradient-calc-crash.html b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/linear-gradient-calc-crash.html
new file mode 100644
index 0000000..5ae6104
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/linear-gradient-calc-crash.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<title>CSS Backgrounds and Borders Test: Chrome linear-gradient crash test with large percentage calc()</title>
+<link rel="help" href="https://crbug.com/1174046">
+<div style="background-image: linear-gradient(to left, black, red calc(1e39% + 0px), green);">Should not crash</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-images/gradient-nan-crash.html b/third_party/blink/web_tests/external/wpt/css/css-images/gradient-nan-crash.html
new file mode 100644
index 0000000..8c4b647
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/gradient-nan-crash.html
@@ -0,0 +1,2 @@
+<!doctype html>
+<style>body { background: linear-gradient(black calc(0% * (1e39 - 1e39)), black 0%); }</style>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-tables/column-track-merging.html b/third_party/blink/web_tests/external/wpt/css/css-tables/column-track-merging.html
new file mode 100644
index 0000000..6dba9e6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-tables/column-track-merging.html
@@ -0,0 +1,278 @@
+<!doctype html>
+<title>Column track merging</title>
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<script src="/resources/check-layout-th.js"></script>
+<link rel="author" title="Aleks Totic" href="atotic@chromium.org" />
+<link rel="help" href="https://www.w3.org/TR/css-tables-3/#dimensioning-the-row-column-grid--step2" />
+<style>
+
+main table {
+  border: 10px solid gray;
+  border-spacing: 20px;
+}
+
+main td {
+  width: 50px;
+  height:50px;
+  padding: 0;
+  background:linear-gradient(to right, yellow, orange);
+}
+main caption {
+  background: #EEE;
+}
+main .desc {
+  margin-top: 20px;
+  color: rgb(50,0,0);
+}
+main pre {
+  white-space: pre-wrap;
+
+}
+</style>
+<h3>Column merging investigation</h3>
+<o>Empty columns is a column that has no originating cells</o>
+<p><a href="https://www.w3.org/TR/css-tables-3/#dimensioning-the-row-column-grid--step2">Table standard</a> discusses this under "track merging".</p>
+<ul>
+  <li>Do empty columns get coalesced?</li>
+  <li>How does this interact with table-layout:fixed, table width</li>
+  <li>Is there a difference between columns defined by COL, vs TD.colspan? Yes!</li>
+  <li>Do COLs with specified width get merged?</li>
+</ul>
+<p>Compatibility</p>
+<li>Edge17 has a bug where width of a colspanned cell always includes cell width + width of border spacing. It should be max(cell width, border spacing)</li>
+<li>Safari matches Chrome Legacy. TD-originated columns are always merged.</li>
+<li>Firefox follows the standard, but has a few bugs.</li>
+<main>
+
+<h3>TD merging</h3>
+
+<pre class="desc">Auto table, and TD.colspan=10
+  FF/Chrome Legacy/Safari: Standard. Tracks merge.
+  Edge17: Tracks do not merge. Wide cell is 180px (9 * border spacing)
+</pre>
+<table id="td_auto" data-expected-width=180>
+<caption>auto</caption>
+<tr>
+  <td colspan=10 data-expected-width=50></td>
+  <td></td>
+</tr>
+<tr>
+  <td colspan=10></td>
+  <td></td>
+</tr>
+</table>
+
+<pre class="desc">Auto table(400px), and TD.colspan=10
+  FF/Chrome Legacy/Safari/Edge17: Standard. Tracks merge. Colspan cell grows because it is unconstrained.
+</pre>
+<table id="td_auto_width" style="width:400px" data-expected-width=400>
+<caption>auto 400px</caption>
+<tr>
+  <td colspan=10 data-expected-width=270></td>
+  <td></td>
+</tr>
+<tr>
+  <td colspan=10></td>
+  <td></td>
+</tr>
+</table>
+
+<pre class="desc">Auto table(130px), and TD.colspan=10
+  FF/Chrome Legacy/Safari: Standard. Tracks merge. Colspan cell shrinks to min width becuase it is unconstrained.
+  Edge17: Non-compliant, buggy. Wide cell too wide, narrow cell disappears.
+</pre>
+<table id="td_auto_width_130" style="width:130px" data-expected-width=130>
+<caption>auto 130px</caption>
+<tr>
+  <td colspan=10 data-expected-width=10><div style="width:10px"></div></td>
+  <td></td>
+</tr>
+<tr>
+  <td colspan=10></td>
+  <td></td>
+</tr>
+</table>
+
+<pre class="td_fixed">Fixed(400px) table, and TD.colspan=10
+  Chrome/Safari: Non-compliant. Tracks merge. Cells are the same size, fixed algo distributes extra width evenly.
+  Firefox: Standard.
+  Edge17: Standard, buggy. Wide cell too wide. Edge's bug is that it computes max width as (width + border_spacing) instead of max(width, border_spacing).
+</pre>
+<table id="td_fixed" style="table-layout:fixed; width: 400px" data-expected-width=400>
+<caption>fixed 400px</caption>
+<tr>
+  <td colspan=10 data-expected-width=180></td>
+  <td></td>
+</tr>
+<tr>
+  <td colspan=10></td>
+  <td></td>
+</tr>
+</table>
+
+<pre class="td_fixed">Fixed(130px) table, and TD.colspan=10
+  Chrome/Safari: Non-compliant.Tracks merge, cells same size.
+  Firefox: Standard + buggy. Table does not grow.
+  Edge17: Standard + buggy. Wide cell too wide.
+</pre>
+<table id="td_fixed" style="table-layout:fixed; width: 130px" data-expected-width=310>
+<caption>fixed 130px</caption>
+<tr>
+  <td colspan=10 data-expected-width=180></td>
+  <td></td>
+</tr>
+<tr>
+  <td colspan=10></td>
+  <td></td>
+</tr>
+</table>
+
+<h3>COL merging. Same tests with COL span=10 replacing TD</h3>
+
+<pre class="desc">Auto table
+  FF/Chrome Legacy/Safari, Edge17: Standard. wide cell is 50px, tracks do merge.
+</pre>
+<table id="col_auto" data-expected-width=180>
+<caption>auto</caption>
+<col span=10>
+<tr>
+  <td data-expected-width=50></td>
+  <td></td>
+</tr>
+<tr>
+  <td></td>
+  <td></td>
+</tr>
+</table>
+
+<pre class="desc">Auto table(400px)
+  FF/Chrome Legacy/Safari, Edge17: Standard. Both cells grow the same because unconstrained.
+</pre>
+<table id="col_auto_width" style="width:400px" data-expected-width=400>
+<caption>auto 400px</caption>
+<col span=10>
+<tr>
+  <td data-expected-width=160></td>
+  <td></td>
+</tr>
+<tr>
+  <td ></td>
+  <td></td>
+</tr>
+</table>
+
+<pre class="desc">Auto table(130px)
+  FF/Chrome Legacy/Safari, Edge17: Standard. Both cells shrink.
+</pre>
+<table id="col_auto_width_130" style="width:130px" data-expected-width=130>
+<caption>auto 130px</caption>
+<col span=10>
+<tr>
+  <td data-expected-width=28><div style="width:10px"></div></td>
+  <td></td>
+</tr>
+<tr>
+  <td></td>
+  <td></td>
+</tr>
+</table>
+
+<pre class="desc">Fixed(400px) table
+  Chrome/Safari,Firefox: Standard.
+  Edge17: Buggy. Fixed cells grow to fill table.
+</pre>
+<table id="col_fixed" style="table-layout:fixed; width: 400px" data-expected-width=400>
+<caption>fixed 400px</caption>
+<col span=10>
+<tr>
+  <td data-expected-width=50></td>
+  <td></td>
+</tr>
+<tr>
+  <td></td>
+  <td></td>
+</tr>
+</table>
+
+<pre class="td_fixed">Fixed(130px) table
+  Chrome/Safari: Standard, very buggy. Non-collapsed columns shrink to single border spacing.
+  Firefox: Standard.
+  Edge17: Non-compliant, collapses columns.
+</pre>
+<table id="col_fixed_130" style="table-layout:fixed; width: 130px" data-expected-width=340>
+<col span=10>
+<caption>fixed 130px</caption>
+<tr>
+  <td data-expected-width=50></td>
+  <td></td>
+</tr>
+<tr>
+  <td></td>
+  <td></td>
+</tr>
+</table>
+
+<h3>COL merging when COL has specified width.</h3>
+
+<ul><li>Chrome Legacy/Edge17/Safari: non-compliant, merge COLs with specified widths.
+ <li>Firefox: Standard, unless COL width is 0px. Buggy, does not include border-spacing around columns.</ul>
+<pre class="desc">Auto table, COL width 30px.
+  Chrome Legacy/Edge17/Safari: non-compliant, merge.
+  Firefox: Standard, buggy. does not include border-spacing around columns.
+</pre>
+<table id="col_auto" data-expected-width=580>
+<caption>auto col 30px</caption>
+<col span=10 style="width:30px">
+<tr>
+  <td data-expected-width=50></td>
+  <td></td>
+</tr>
+<tr>
+  <td></td>
+  <td></td>
+</tr>
+</table>
+
+<pre class="desc">Auto table, COL width 5%.
+  Chrome Legacy/Edge17/Safari: non-compliant, merge.
+  Firefox: Standard, buggy. does not include border-spacing around columns.
+</pre>
+<table id="col_auto" data-expected-width=640>
+<caption>auto col 10%</caption>
+<col span=5 style="width:10%">
+<tr>
+  <td data-expected-width=100></td>
+  <td></td>
+</tr>
+<tr>
+  <td></td>
+  <td></td>
+</tr>
+</table>
+
+<pre class="desc">Auto table, COL width 0px.
+  Everyone: merges COL
+</pre>
+<table id="col_auto" data-expected-width=180>
+<caption>auto col 0px</caption>
+<col span=10 style="width:0px">
+<tr>
+  <td data-expected-width=50></td>
+  <td></td>
+</tr>
+<tr>
+  <td></td>
+  <td></td>
+</tr>
+</table>
+
+
+</main>
+<script>
+  checkLayout("main table");
+</script>
+
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-tables/tentative/table-width-redistribution-fixed.html b/third_party/blink/web_tests/external/wpt/css/css-tables/tentative/table-width-redistribution-fixed.html
new file mode 100644
index 0000000..e8f5ae8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-tables/tentative/table-width-redistribution-fixed.html
@@ -0,0 +1,320 @@
+<!doctype html>
+<title>Fixed table final assignable  distribution</title>
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<script src="/resources/check-layout-th.js"></script>
+<link rel="stylesheet" type="text/css" href="./support/table-tentative.css">
+<link rel="author" title="Aleks Totic" href="atotic@chromium.org" />
+<link rel="help" href="https://www.w3.org/TR/css-tables-3/#distributing-width-to-columns" />
+<style>
+  main table {
+    background: gray;
+    border-spacing: 8px 8px;
+    table-layout: fixed;
+  }
+  main table:hover { table-layout: auto; } /* useful for comparisons */
+  main td {
+    background: #BFB;
+    font-size: 10px;
+  }
+  main td > div {
+    display: inline-block;
+    background: rgba(56,162,56,0.3);
+    height:10px;
+  }
+</style>
+<main>
+<h1>Fixed tables: Compute column computed widths from assignable table width</h1>
+<ul>
+  <li>auto columns have a min width of 0. Max width still gets computed.</li>
+  <li>percent columns have a min width of 0.</li>
+  <li>fixed column.min_width is css width. It never changes.</li>
+  <li>fixed column.max_width is max(cells.max_width, css width).</li>
+  <li>colspan header cells distribute
+    <ul>
+      <li>max_width evenly between columns.</li>
+      <li>do not distribute min width</li>
+      <li>percentage evenly between columns</li>
+    </ul>
+  </li>
+</ul>
+
+<h2>Is table treated as fixed?</h2>
+<p class="testdesc">table width:auto is not treated as fixed.</p>
+<table style="table-layout:fixed; width:auto" data-expected-width=324>
+  <tr>
+    <td style="width:200px">200</td>
+    <td><div style="width:100px">min</div></td>
+  </tr>
+</table>
+<p class="testdesc">table width:px is treated as fixed.</p>
+<table style="table-layout:fixed; width:224px" data-expected-width=224>
+  <tr>
+    <td style="width:200px">200</td>
+    <td><div style="width:100px">min</div></td>
+  </tr>
+</table>
+<p class="testdesc">table width:min-content is treated as fixed.</p>
+<table style="table-layout:fixed; width:min-content" data-expected-width=224>
+  <tr>
+    <td style="width:200px">200</td>
+    <td><div style="width:100px">min</div></td>
+  </tr>
+</table>
+
+<h2>Fixed only</h2>
+
+<p class="testdesc">Table: 50px; C0:100/50/100 C1:100/50/75
+When table.css_width is &lt; columns.css_width, how is the conflict resolved?
+columns.css_width wins</p>
+<table style="width:50px" data-expected-width=224>
+  <tr>
+    <td style="width:100px" data-expected-width=100>
+      <div style="width:50px">50</div><div style="width:50px">50</div></td>
+    <td style="width:100px" data-expected-width=100>
+      <div style="width:50px">50</div><div style="width:25px">25</div></td>
+  </tr>
+</table>
+
+<p class="testdesc">Table: 300px; C0:100/100/200 C1:100/90/115
+When table.css_width is > columns.css_width , how is the conflict resolved?
+table.css_width wins</p>
+<table style="width:300px" data-expected-width=300>
+  <tr>
+    <td style="width:100px" data-expected-width=138>
+      <div style="width:100px">100</div><div style="width:100px">100</div></td>
+    <td style="width:100px" data-expected-width=138>
+      <div style="width:90px">90</div><div style="width:25px">25</div></td>
+  </tr>
+</table>
+
+<p class="testdesc">Table: 300px; C0:100/50/50 C1:100/100/100
+Fixed cells must grow, but their min widths differ.
+Fixed cells grow in proportion to their css width.
+<table style="width:calc(300px + 24px)" data-expected-width=324>
+  <tr>
+    <td style="width:100px" data-expected-width=150>
+      <div style="width:50px">50</div></td>
+    <td style="width:100px" data-expected-width=150>
+      <div style="width:100px">100</div></td>
+  </tr>
+</table>
+
+<p class="testdesc">Table: 50px; C0:100/50/50 C1:100/100/100
+What happens when column.min_width > column.css_width
+column.css_width wins over column.min_width.
+<table style="width:100px" data-expected-width=224>
+  <tr>
+    <td style="width:100px" data-expected-width=100>
+      <div style="width:200px"></div></td>
+    <td style="width:100px" data-expected-width=100>
+      <div style="width:200px"></div></td>
+  </tr>
+</table>
+
+<p class="testdesc">Table: 1px.
+What happens to min_width when multiple cells specify css_width of the same column?
+1st cell wins.
+<table style="width:1px" data-expected-width=116>
+  <tr>
+    <td style="width:100px" data-expected-width=100>
+      <div style="width:200px">200</div></td>
+  </tr>
+      <td style="width:150px" data-expected-width=100>
+        <div style="width:150px">150</div></td>
+  </tr>
+</table>
+
+<h2>Auto only</h2>
+
+<p class="testdesc">Width is distributed evenly
+</p>
+<table style="width:548px">
+  <tr>
+    <td data-expected-width=100><div style="width:10px;height:30px"></div></td>
+    <td data-expected-width=100><div style="width:20px;height:30px"></div></td>
+    <td data-expected-width=100><div style="width:30px;height:30px"></div></td>
+    <td data-expected-width=100><div style="width:40px;height:30px"></div></td>
+    <td data-expected-width=100><div style="width:120px;height:30px"></div></td>
+  </tr>
+</table>
+
+<h2>Colspan distribution</h2>
+
+<p class="testdesc">Table: 1px
+Does column.min_width change with colspan distribution from later rows to first row?
+No
+<table style="width:1px" data-expected-width=74>
+  <tr>
+    <td data-expected-width=0>
+      <div style="width:50px"></div></td>
+    <td style="width:50px" data-expected-width=50>
+      <div style="width:50px"></div></td>
+  </tr>
+  <tr>
+    <td colspan=2 style="width:200px" data-expected-width=58>
+      <div style="width:200px"></div></td>
+  </tr>
+</table>
+
+<p class="testdesc">Table: 632px
+Does column.percent change with colspan distribution?
+No.
+<table style="width:632px" data-expected-width=632>
+  <tr>
+    <td data-expected-width=360>
+      <div style="width:50px"></div></td>
+    <td style="width:20%"  data-expected-width=120>
+      <div style="width:50px"></div></td>
+    <td style="width:20%" data-expected-width=120></td>
+  </tr>
+  <tr>
+    <td colspan="2" style="width:90%">
+      <div style="width:100px"></div></td>
+    <td>auto</td>
+  </tr>
+</table>
+
+<h2>Colspan header cells</h2>
+<section>
+<ol>
+  <li>Fixed/percentage colspan cells get distributed evenly.</li>
+  <li>Auto cells</li>
+</ol>
+
+<p class="testdesc">Assignable: 400px
+Fixed header cells with colspan.
+Columns divded evenly</p>
+<p class="error">Legacy Chrome is slightly off, something about spacing and wide cells.</p>
+<table style="width:calc(600px + 40px)" data-expected-width=640>
+  <tr>
+    <td colspan=2 style="width:108px" data-expected-width=208>108</td>
+    <td colspan=2 style="width:208px" data-expected-width=408>208</td>
+  </tr>
+  <tr>
+    <td data-expected-width=100>1</td>
+    <td>1</td>
+    <td data-expected-width=200>1</td>
+    <td>1</td>
+  </tr>
+</table>
+
+<p class="testdesc">Assignable: 400px, C0:40% C1:20% C2:40%
+Percentage header cells with colspan
+C0 splits into C0.0 and C0.1, 16px each with 20%
+C1 splits into C1.0 and C1.1, 6px each with 10%
+Assignable width is 400, everyone gets according to percent.
+80/80/40/40/160.</p>
+<p class="error">Firefox is slightly off, with C2 taking 6px more. Unknown what math is used to get this answer.</p>
+<table style="width:448px" data-expected-width=448>
+  <tr>
+    <td colspan=2 style="width:40%" data-expected-width=168><div style="width:40px"></div></td>
+    <td colspan=2 style="width:20%" data-expected-width=88><div style="width:160px"></div></td>
+    <td style="width:40%" data-expected-width=160><div style="width:40px"></div></td>
+  </tr>
+  <tr>
+    <td data-expected-width=80>Auto</td>
+    <td data-expected-width=80>Auto</td>
+    <td data-expected-width=40>Auto</td>
+    <td data-expected-width=40>Auto</td>
+    <td data-expected-width=160>Auto</td>
+  </tr>
+</table>
+
+<p class="testdesc">Assignable: 1px, C0 Auto/100 colspan=2 , C1 100/Auto
+Auto header cells with colspan, table is min width
+min_width does not get redistributed.
+</p>
+<table style="width:1px" data-expected-width=132>
+  <tr>
+    <td colspan=2 data-expected-width=8>
+      <div style="width:100px">100</div></td>
+    <td style="width:100px" data-expected-width=100>100</td>
+  </tr>
+  <tr>
+    <td data-expected-width=0>x</td>
+    <td data-expected-width=0>x</td>
+    <td data-expected-width=100>x</td>
+  </tr>
+</table>
+
+<p class="testdesc">Assignable: 200; C0: colspan:2 Auto C1:colspan 8 Auto
+Auto colspan cells, and nothing else. Tricky because this means that internally
+table has to represent 8 cells, and wide cells that span beyond table width
+are usually truncated.
+C0: 20*2+8=48, C1: 20*8 + 7*8=216</p>
+<table style="width:calc(200px + 88px)" data-expected-width=288>
+  <tr>
+    <td colspan=2 style="height:20px" data-expected-width=48></td>
+    <td colspan=8 style="height:20px" data-expected-width=216></td>
+  </tr>
+</table>
+
+<h2>Percentage only</h2>
+
+<p class="testdesc">Assignable: 100px;columns add to 100%, auto width
+Columns are exact percentage size.
+<table style="width:calc(100px + 32px)" data-expected-width=132>
+  <tr>
+    <td style="width:50%" data-expected-width=50>50%</td>
+    <td style="width:30%" data-expected-width=30>30%</td>
+    <td style="width:20%" data-expected-width=20>20%</td>
+  </tr>
+</table>
+
+<p class="testdesc">Assignable: 100px;columns add to 50%, auto width
+Columns grow proportional to percent.
+<table style="width:calc(100px + 32px)" data-expected-width=132>
+  <tr>
+    <td style="width:25%" data-expected-width=50>25%</td>
+    <td style="width:15%" data-expected-width=30>15%</td>
+    <td style="width:10%" data-expected-width=20>10%</td>
+  </tr>
+</table>
+
+
+<p class="testdesc">Assignable: 100px;columns add to 50%, with min width
+Min width is ignored.
+<table style="width:calc(100px + 32px)" data-expected-width=132>
+  <tr>
+    <td style="width:50%" data-expected-width=50><div style="width:50px">50</div></td>
+    <td style="width:30%" data-expected-width=30><div style="width:50px">50</div></td>
+    <td style="width:20%" data-expected-width=20><div style="width:50px">50</div></td>
+  </tr>
+</table>
+
+<h2>Percentage/auto/fixed mix</h2>
+
+<p class="testdesc">Assignable: 100px;C0:50% C1:100px C2: Auto
+C0: 50% becomes
+<table style="width:calc(100px + 32px)" data-expected-width=132>
+  <tr>
+    <td style="width:50%" data-expected-width=50>50%</td>
+    <td style="width:30px" data-expected-width=30>30px</td>
+    <td data-expected-width=20></td>
+  </tr>
+</table>
+
+<p class="testdesc">Assignable: 100px;C0:50% C1:50px
+Clean split
+<table style="width:calc(100px + 24px)" data-expected-width=124>
+  <tr>
+    <td style="width:50%" data-expected-width=50>50%</td>
+    <td style="width:50px" data-expected-width=50>50px</td>
+  </tr>
+</table>
+
+<p class="testdesc">Assignable: 100px;C0:20% C1:60% C2:60px
+Overconstrained: widths add up to 140.
+Fixed widths get distributed first, percentage takes the rest.
+<table style="width:calc(100px + 32px)" data-expected-width=132>
+  <tr>
+    <td style="width:20%" data-expected-width=10>20%</td>
+    <td style="width:60%" data-expected-width=30>60%</td>
+    <td style="width:60px" data-expected-width=60>60px</td>
+  </tr>
+</table>
+</main>
+<script>
+  checkLayout("table");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/file-system-access/showPicker-errors.https.window.js b/third_party/blink/web_tests/external/wpt/file-system-access/showPicker-errors.https.window.js
index 2310c323..189f473 100644
--- a/third_party/blink/web_tests/external/wpt/file-system-access/showPicker-errors.https.window.js
+++ b/third_party/blink/web_tests/external/wpt/file-system-access/showPicker-errors.https.window.js
@@ -86,6 +86,18 @@
     }));
   }, showPickerMethod + ': unknown well-known starting directory.');
 
+  promise_test(async t => {
+    await promise_rejects_js(t, TypeError, self[showPickerMethod]({
+      id: "inv*l:d\\ chara<ters",
+    }));
+  }, showPickerMethod + ': starting directory ID contains invalid characters.');
+
+  promise_test(async t => {
+    await promise_rejects_js(t, TypeError, self[showPickerMethod]({
+      id: "id-length-cannot-exceed-32-characters",
+    }));
+  }, showPickerMethod + ': starting directory ID cannot exceed 32 characters.');
+
   const invalid_extensions = {
     '.extensiontoolong': 'extension length more than 16.',
     '.txt.': 'extenstion ends with "."',
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-light-dismiss.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-light-dismiss.tentative.html
index 1781afb..eaa7dfd 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-light-dismiss.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-light-dismiss.tentative.html
@@ -92,6 +92,22 @@
       popup1.hide();
     },'Clicking inside a parent popup should close child popup');
 
+    // TODO(crbug.com/893480) This test is disabled, until keyDown
+    // and keyUp are supported in Chromium.
+    // popup1.show();
+    // popup2.show();
+    // const escape_key = "\uE00C";
+    // await pressKey(escape_key);
+    // test(t => {
+    //   assert_true(popup1.open);
+    //   assert_false(popup2.open);
+    // },'Escape key should close top level popup');
+    // await pressKey(escape_key);
+    // test(t => {
+    //   assert_false(popup1.open);
+    //   assert_false(popup2.open);
+    // },'Escape key should close top level popup (part 2)');
+
     done();
   })();
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/permissions-policy/permissions-policy-opaque-origin-history.https.html b/third_party/blink/web_tests/external/wpt/permissions-policy/permissions-policy-opaque-origin-history.https.html
new file mode 100644
index 0000000..969ca369
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/permissions-policy/permissions-policy-opaque-origin-history.https.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script>
+
+    function get_response() {
+      return new Promise(resolve => {
+        window.addEventListener('message', e => {
+          resolve(e.data);
+        }, { once: true });
+      });
+    }
+
+    promise_test(async () => {
+      // - opaque-origin-history1.html navigates itself to opaque-origin-history2.html.
+      // - opaque-origin-history2.html call window.history.back() to navigate
+      // back to opaque-origin-history1.html
+      // - opaque-origin-history1.html should still be able to access fullscreen
+      // feature after the history.back() navigation.
+      const iframe = document.createElement('iframe');
+      // sandbox iframe so that it has opaque origin.
+      iframe.sandbox = 'allow-scripts';
+      iframe.src = 'resources/opaque-origin-history1.sub.https.html';
+      iframe.allow = "fullscreen 'src'";
+      document.body.appendChild(iframe);
+
+
+      assert_equals(
+        await get_response(),
+        'fullscreen enabled in opaque-origin-history1.html',
+        'iframe should be able to access fullscreen.'
+      );
+
+      iframe.contentWindow.postMessage('redirect', '*');
+
+      assert_equals(
+        await get_response(),
+        'fullscreen enabled in opaque-origin-history1.html',
+        'iframe should still be able to access fullscreen after history.back() navigation.'
+      );
+    });
+  </script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/permissions-policy/resources/opaque-origin-history1.sub.https.html b/third_party/blink/web_tests/external/wpt/permissions-policy/resources/opaque-origin-history1.sub.https.html
new file mode 100644
index 0000000..cb1f214f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/permissions-policy/resources/opaque-origin-history1.sub.https.html
@@ -0,0 +1,15 @@
+<script>
+  window.addEventListener('message', e => {
+    if (e.data == 'redirect') {
+      location.assign(
+        "https://{{domains[]}}:{{location[port]}}/permissions-policy/resources/opaque-origin-history2.https.html");
+    }
+  });
+
+  parent.postMessage(
+    document.fullscreenEnabled ?
+    'fullscreen enabled in opaque-origin-history1.html' :
+    'fullscreen disabled in opaque-origin-history1.html',
+    '*'
+  );
+</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/permissions-policy/resources/opaque-origin-history2.https.html b/third_party/blink/web_tests/external/wpt/permissions-policy/resources/opaque-origin-history2.https.html
new file mode 100644
index 0000000..20e63cf
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/permissions-policy/resources/opaque-origin-history2.https.html
@@ -0,0 +1,3 @@
+<script>
+  history.back();
+</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/fast/table/large-col-span-crash.html b/third_party/blink/web_tests/fast/table/large-col-span-crash.html
index b440470..6fc7362 100644
--- a/third_party/blink/web_tests/fast/table/large-col-span-crash.html
+++ b/third_party/blink/web_tests/fast/table/large-col-span-crash.html
@@ -10,7 +10,7 @@
 <script src="../../resources/testharness.js"></script>
 <script src="../../resources/testharnessreport.js"></script>
 <script src="../../resources/check-layout-th.js"></script>
-<table data-expected-width=100 data-expected-height=100>
+<table data-expected-width=2004 data-expected-height=100>
   <col span="4294967295">
   <col>
 </table>
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/external/wpt/css/css-tables/tentative/table-width-redistribution-fixed-expected.txt b/third_party/blink/web_tests/flag-specific/disable-layout-ng/external/wpt/css/css-tables/tentative/table-width-redistribution-fixed-expected.txt
new file mode 100644
index 0000000..f58de563
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/external/wpt/css/css-tables/tentative/table-width-redistribution-fixed-expected.txt
@@ -0,0 +1,37 @@
+This is a testharness.js-based test.
+PASS table 1
+PASS table 2
+PASS table 3
+PASS table 4
+PASS table 5
+PASS table 6
+PASS table 7
+PASS table 8
+PASS table 9
+PASS table 10
+PASS table 11
+FAIL table 12 assert_equals: 
+<table style="width:calc(600px + 40px)" data-expected-width="640">
+  <tbody><tr>
+    <td colspan="2" style="width:108px" data-expected-width="208">108</td>
+    <td colspan="2" style="width:208px" data-expected-width="408">208</td>
+  </tr>
+  <tr>
+    <td data-expected-width="100">1</td>
+    <td>1</td>
+    <td data-expected-width="200">1</td>
+    <td>1</td>
+  </tr>
+</tbody></table>
+width expected 208 but got 214
+PASS table 13
+PASS table 14
+PASS table 15
+PASS table 16
+PASS table 17
+PASS table 18
+PASS table 19
+PASS table 20
+PASS table 21
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/linux/fast/table/colspanMinWidth-expected.png b/third_party/blink/web_tests/platform/linux/fast/table/colspanMinWidth-expected.png
index a4a09724..91f25c2 100644
--- a/third_party/blink/web_tests/platform/linux/fast/table/colspanMinWidth-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/table/colspanMinWidth-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/table/colspanMinWidth-vertical-expected.png b/third_party/blink/web_tests/platform/linux/fast/table/colspanMinWidth-vertical-expected.png
index b5eb3d5..05f2896 100644
--- a/third_party/blink/web_tests/platform/linux/fast/table/colspanMinWidth-vertical-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/table/colspanMinWidth-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/tables/mozilla_expected_failures/bugs/bug17826-expected.png b/third_party/blink/web_tests/platform/linux/tables/mozilla_expected_failures/bugs/bug17826-expected.png
index 26238bd..a47cc3cd 100644
--- a/third_party/blink/web_tests/platform/linux/tables/mozilla_expected_failures/bugs/bug17826-expected.png
+++ b/third_party/blink/web_tests/platform/linux/tables/mozilla_expected_failures/bugs/bug17826-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/tables/mozilla_expected_failures/bugs/bug47163-expected.png b/third_party/blink/web_tests/platform/linux/tables/mozilla_expected_failures/bugs/bug47163-expected.png
index 54aa47f..90fc151 100644
--- a/third_party/blink/web_tests/platform/linux/tables/mozilla_expected_failures/bugs/bug47163-expected.png
+++ b/third_party/blink/web_tests/platform/linux/tables/mozilla_expected_failures/bugs/bug47163-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/tables/mozilla_expected_failures/bugs/bug7121-2-expected.png b/third_party/blink/web_tests/platform/linux/tables/mozilla_expected_failures/bugs/bug7121-2-expected.png
index b8e6363d..2bd0151d 100644
--- a/third_party/blink/web_tests/platform/linux/tables/mozilla_expected_failures/bugs/bug7121-2-expected.png
+++ b/third_party/blink/web_tests/platform/linux/tables/mozilla_expected_failures/bugs/bug7121-2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-expected.png b/third_party/blink/web_tests/platform/linux/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-expected.png
new file mode 100644
index 0000000..91f25c2
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-vertical-expected.png b/third_party/blink/web_tests/platform/linux/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-vertical-expected.png
new file mode 100644
index 0000000..05f2896
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/layout_ng_fragment_traversal/fast/table/rowspan-paint-order-expected.png b/third_party/blink/web_tests/platform/linux/virtual/layout_ng_fragment_traversal/fast/table/rowspan-paint-order-expected.png
new file mode 100644
index 0000000..b5353a78
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/layout_ng_fragment_traversal/fast/table/rowspan-paint-order-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac-arm11.0/external/wpt/css/css-tables/html5-table-formatting-1-expected.txt b/third_party/blink/web_tests/platform/mac-mac-arm11.0/external/wpt/css/css-tables/html5-table-formatting-1-expected.txt
new file mode 100644
index 0000000..8910d66
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac-arm11.0/external/wpt/css/css-tables/html5-table-formatting-1-expected.txt
@@ -0,0 +1,9 @@
+This is a testharness.js-based test.
+PASS Empty tables can still get a lsyout
+FAIL Empty tables do not take table-columns into account assert_equals: expected 50 but got 200
+FAIL Empty tables do not take table-rows into account assert_equals: expected 50 but got 100
+PASS Table-columns are taken into account after missing cells are generated (empty line)
+PASS Table-columns are taken into account after missing cells are generated (partially empty line)
+PASS Table-columns are taken into account after missing cells are generated (non-empty line)
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-expected.png b/third_party/blink/web_tests/platform/mac-mac-arm11.0/fast/table/colspanMinWidth-expected.png
similarity index 100%
copy from third_party/blink/web_tests/platform/mac/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-expected.png
copy to third_party/blink/web_tests/platform/mac-mac-arm11.0/fast/table/colspanMinWidth-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-vertical-expected.png b/third_party/blink/web_tests/platform/mac-mac-arm11.0/fast/table/colspanMinWidth-vertical-expected.png
similarity index 100%
copy from third_party/blink/web_tests/platform/mac/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-vertical-expected.png
copy to third_party/blink/web_tests/platform/mac-mac-arm11.0/fast/table/colspanMinWidth-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac-arm11.0/tables/mozilla_expected_failures/bugs/bug47163-expected.png b/third_party/blink/web_tests/platform/mac-mac-arm11.0/tables/mozilla_expected_failures/bugs/bug47163-expected.png
new file mode 100644
index 0000000..b23aafb
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac-arm11.0/tables/mozilla_expected_failures/bugs/bug47163-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-expected.png b/third_party/blink/web_tests/platform/mac-mac-arm11.0/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-expected.png
similarity index 100%
rename from third_party/blink/web_tests/platform/mac/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-expected.png
rename to third_party/blink/web_tests/platform/mac-mac-arm11.0/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-vertical-expected.png b/third_party/blink/web_tests/platform/mac-mac-arm11.0/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-vertical-expected.png
similarity index 100%
rename from third_party/blink/web_tests/platform/mac/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-vertical-expected.png
rename to third_party/blink/web_tests/platform/mac-mac-arm11.0/virtual/layout_ng_fragment_traversal/fast/table/colspanMinWidth-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/table/colspanMinWidth-expected.png b/third_party/blink/web_tests/platform/mac/fast/table/colspanMinWidth-expected.png
index 2c4cd186..0aacb4b 100644
--- a/third_party/blink/web_tests/platform/mac/fast/table/colspanMinWidth-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/table/colspanMinWidth-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/table/colspanMinWidth-vertical-expected.png b/third_party/blink/web_tests/platform/mac/fast/table/colspanMinWidth-vertical-expected.png
index 0518096..aabc7f20a 100644
--- a/third_party/blink/web_tests/platform/mac/fast/table/colspanMinWidth-vertical-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/table/colspanMinWidth-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/tables/mozilla/bugs/bug1302-expected.png b/third_party/blink/web_tests/platform/mac/tables/mozilla/bugs/bug1302-expected.png
index efa6ca86..44cde41 100644
--- a/third_party/blink/web_tests/platform/mac/tables/mozilla/bugs/bug1302-expected.png
+++ b/third_party/blink/web_tests/platform/mac/tables/mozilla/bugs/bug1302-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/tables/mozilla_expected_failures/bugs/bug17826-expected.png b/third_party/blink/web_tests/platform/mac/tables/mozilla_expected_failures/bugs/bug17826-expected.png
index ed37f5f..bdbf39c 100644
--- a/third_party/blink/web_tests/platform/mac/tables/mozilla_expected_failures/bugs/bug17826-expected.png
+++ b/third_party/blink/web_tests/platform/mac/tables/mozilla_expected_failures/bugs/bug17826-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/tables/mozilla_expected_failures/bugs/bug47163-expected.png b/third_party/blink/web_tests/platform/mac/tables/mozilla_expected_failures/bugs/bug47163-expected.png
index b23aafb..15d23402 100644
--- a/third_party/blink/web_tests/platform/mac/tables/mozilla_expected_failures/bugs/bug47163-expected.png
+++ b/third_party/blink/web_tests/platform/mac/tables/mozilla_expected_failures/bugs/bug47163-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/tables/mozilla_expected_failures/bugs/bug7121-2-expected.png b/third_party/blink/web_tests/platform/mac/tables/mozilla_expected_failures/bugs/bug7121-2-expected.png
index 0d51927..bd778a6 100644
--- a/third_party/blink/web_tests/platform/mac/tables/mozilla_expected_failures/bugs/bug7121-2-expected.png
+++ b/third_party/blink/web_tests/platform/mac/tables/mozilla_expected_failures/bugs/bug7121-2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/table/colspanMinWidth-expected.png b/third_party/blink/web_tests/platform/win/fast/table/colspanMinWidth-expected.png
index 6b8125f..d7b36f9 100644
--- a/third_party/blink/web_tests/platform/win/fast/table/colspanMinWidth-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/table/colspanMinWidth-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/table/colspanMinWidth-vertical-expected.png b/third_party/blink/web_tests/platform/win/fast/table/colspanMinWidth-vertical-expected.png
index 80e7e7e1..e1fe026 100644
--- a/third_party/blink/web_tests/platform/win/fast/table/colspanMinWidth-vertical-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/table/colspanMinWidth-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/table/rowspan-paint-order-expected.png b/third_party/blink/web_tests/platform/win/fast/table/rowspan-paint-order-expected.png
index c581137..2d6d7ae 100644
--- a/third_party/blink/web_tests/platform/win/fast/table/rowspan-paint-order-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/table/rowspan-paint-order-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/tables/mozilla_expected_failures/bugs/bug17826-expected.png b/third_party/blink/web_tests/platform/win/tables/mozilla_expected_failures/bugs/bug17826-expected.png
index 374c7a5..7e7b317 100644
--- a/third_party/blink/web_tests/platform/win/tables/mozilla_expected_failures/bugs/bug17826-expected.png
+++ b/third_party/blink/web_tests/platform/win/tables/mozilla_expected_failures/bugs/bug17826-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/tables/mozilla_expected_failures/bugs/bug47163-expected.png b/third_party/blink/web_tests/platform/win/tables/mozilla_expected_failures/bugs/bug47163-expected.png
index 5e3dca2..3b7028f 100644
--- a/third_party/blink/web_tests/platform/win/tables/mozilla_expected_failures/bugs/bug47163-expected.png
+++ b/third_party/blink/web_tests/platform/win/tables/mozilla_expected_failures/bugs/bug47163-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/tables/mozilla_expected_failures/bugs/bug7121-2-expected.png b/third_party/blink/web_tests/platform/win/tables/mozilla_expected_failures/bugs/bug7121-2-expected.png
index 8018881..7341ad8 100644
--- a/third_party/blink/web_tests/platform/win/tables/mozilla_expected_failures/bugs/bug7121-2-expected.png
+++ b/third_party/blink/web_tests/platform/win/tables/mozilla_expected_failures/bugs/bug7121-2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/container-queries/wpt_internal/css/css-conditional/container-queries/inline-size-query-block-size-containment-expected.txt b/third_party/blink/web_tests/virtual/container-queries/wpt_internal/css/css-conditional/container-queries/inline-size-query-block-size-containment-expected.txt
new file mode 100644
index 0000000..56d84dad
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/container-queries/wpt_internal/css/css-conditional/container-queries/inline-size-query-block-size-containment-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Axis mismatch between query and size containment assert_equals: expected 50 but got 400
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index c220371..e138244 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -1138,6 +1138,10 @@
     getter reason
     getter wasClean
     method constructor
+interface ColorSelectEvent : PointerEvent
+    attribute @@toStringTag
+    getter value
+    method constructor
 interface Comment : CharacterData
     attribute @@toStringTag
     method constructor
@@ -2457,6 +2461,16 @@
     method AddSearchProvider
     method IsSearchProviderInstalled
     method constructor
+interface EyeDropper : EventTarget
+    attribute @@toStringTag
+    getter onclose
+    getter oncolorselect
+    getter opened
+    method close
+    method constructor
+    method open
+    setter onclose
+    setter oncolorselect
 interface FaceDetector
     attribute @@toStringTag
     method constructor
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-conditional/container-queries/block-size-containment.html b/third_party/blink/web_tests/wpt_internal/css/css-conditional/container-queries/block-size-containment.html
new file mode 100644
index 0000000..a8b3e5a
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-conditional/container-queries/block-size-containment.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<style>
+  #ancestry { display: flex; }
+  #keg { contain: block-size layout; display: flex; width: fit-content; }
+  @container (max-height: 200px) {
+    #target { width: 400px; }
+  }
+  @container (min-height: 400px) {
+    #target { width: 20px; }
+  }
+</style>
+<div id="ancestry">
+  <div id="keg">
+    <div id="target">
+      <div style="width:50px;"></div>
+    </div>
+  </div>
+</div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+  test(()=> {
+      ancestry.style.height = "100px";
+      assert_equals(keg.offsetWidth, 400);
+
+      ancestry.style.height = "300px";
+      assert_equals(keg.offsetWidth, 50);
+
+      ancestry.style.height = "500px";
+      assert_equals(keg.offsetWidth, 20);
+  }, "block-size containment only");
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-conditional/container-queries/inline-size-containment-vertical-rl.html b/third_party/blink/web_tests/wpt_internal/css/css-conditional/container-queries/inline-size-containment-vertical-rl.html
new file mode 100644
index 0000000..e5395d9
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-conditional/container-queries/inline-size-containment-vertical-rl.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<style>
+  #ancestry { writing-mode: vertical-rl; }
+  #keg { contain: inline-size layout; }
+  @container (max-height: 200px) {
+    #target { width: 400px; }
+  }
+  @container (min-height: 400px) {
+    #target { width: 20px; }
+  }
+</style>
+<div id="ancestry">
+  <div id="keg">
+    <div id="target">
+      <div style="width:50px;"></div>
+    </div>
+  </div>
+</div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+  test(()=> {
+      ancestry.style.height = "100px";
+      assert_equals(keg.offsetWidth, 400);
+
+      ancestry.style.height = "300px";
+      assert_equals(keg.offsetWidth, 50);
+
+      ancestry.style.height = "500px";
+      assert_equals(keg.offsetWidth, 20);
+  }, "inline-size containment only");
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-conditional/container-queries/inline-size-containment.html b/third_party/blink/web_tests/wpt_internal/css/css-conditional/container-queries/inline-size-containment.html
new file mode 100644
index 0000000..173d1bb
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-conditional/container-queries/inline-size-containment.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<style>
+  #keg { contain: inline-size layout; }
+  @container (max-width: 200px) {
+    #target { height: 400px; }
+  }
+  @container (min-width: 400px) {
+    #target { height: 20px; }
+  }
+</style>
+<div id="ancestry">
+  <div id="keg">
+    <div id="target">
+      <div style="height:50px;"></div>
+    </div>
+  </div>
+</div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+  test(()=> {
+      ancestry.style.width = "100px";
+      assert_equals(keg.offsetHeight, 400);
+
+      ancestry.style.width = "300px";
+      assert_equals(keg.offsetHeight, 50);
+
+      ancestry.style.width = "500px";
+      assert_equals(keg.offsetHeight, 20);
+  }, "inline-size containment only");
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-conditional/container-queries/inline-size-query-block-size-containment.html b/third_party/blink/web_tests/wpt_internal/css/css-conditional/container-queries/inline-size-query-block-size-containment.html
new file mode 100644
index 0000000..a1e9dcbc
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-conditional/container-queries/inline-size-query-block-size-containment.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<style>
+  #keg { contain: block-size layout; }
+  @container (max-width: 200px) {
+    #target { height: 400px; }
+  }
+  @container (min-width: 400px) {
+    #target { height: 20px; }
+  }
+</style>
+<div id="ancestry">
+  <div id="keg">
+    <div id="target">
+      <div style="height:50px;"></div>
+    </div>
+  </div>
+</div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+  test(()=> {
+      // Since the axis queried isn't size-contained, the query should never
+      // match.
+
+      ancestry.style.width = "100px";
+      assert_equals(target.offsetHeight, 50);
+
+      ancestry.style.width = "300px";
+      assert_equals(target.offsetHeight, 50);
+
+      ancestry.style.width = "500px";
+      assert_equals(target.offsetHeight, 50);
+  }, "Axis mismatch between query and size containment");
+</script>
diff --git a/third_party/closure_compiler/externs/accessibility_private.js b/third_party/closure_compiler/externs/accessibility_private.js
index f3bb791..d22548e 100644
--- a/third_party/closure_compiler/externs/accessibility_private.js
+++ b/third_party/closure_compiler/externs/accessibility_private.js
@@ -114,6 +114,7 @@
   DICTATION: 'dictation',
   END_TEXT_SELECTION: 'endTextSelection',
   INCREMENT: 'increment',
+  ITEM_SCAN: 'itemScan',
   JUMP_TO_BEGINNING_OF_TEXT: 'jumpToBeginningOfText',
   JUMP_TO_END_OF_TEXT: 'jumpToEndOfText',
   KEYBOARD: 'keyboard',
diff --git a/third_party/jdk/3pp/3pp.pb b/third_party/jdk/3pp/3pp.pb
new file mode 100644
index 0000000..0f9b166
--- /dev/null
+++ b/third_party/jdk/3pp/3pp.pb
@@ -0,0 +1,22 @@
+# Copyright 2021 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+create {
+  source {
+    url {
+      # See the link "[Binaries]" in
+      # https://wiki.openjdk.java.net/display/JDKUpdates/JDK11u#JDK11u-Releases
+      download_url: "https://github.com/AdoptOpenJDK/openjdk11-upstream-binaries/releases/download/jdk-11.0.4%2B11/OpenJDK11U-jdk_x64_linux_11.0.4_11.tar.gz"
+      version: "11.0.4+11"
+    }
+    patch_version: 'cr0'
+    unpack_archive: true
+    subdir: 'current'
+  }
+}
+
+upload {
+  pkg_prefix: "chromium/third_party"
+  universal: true
+}
diff --git a/third_party/jdk/extras/3pp/3pp.pb b/third_party/jdk/extras/3pp/3pp.pb
new file mode 100644
index 0000000..29f104f
--- /dev/null
+++ b/third_party/jdk/extras/3pp/3pp.pb
@@ -0,0 +1,22 @@
+# Copyright 2021 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+create {
+  source {
+    url {
+      # See the link "[Binaries]" in
+      # https://wiki.openjdk.java.net/display/jdk8u#Main-Releases
+      download_url: "https://github.com/AdoptOpenJDK/openjdk8-upstream-binaries/releases/download/jdk8u275-b01/OpenJDK8U-jdk_x64_linux_8u275b01.tar.gz"
+      version: "8u275-b01"
+    }
+    patch_version: 'cr0'
+    unpack_archive: true
+  }
+  build {}
+}
+
+upload {
+  pkg_prefix: "chromium/third_party/jdk"
+  universal: true
+}
diff --git a/third_party/jdk/extras/3pp/install.sh b/third_party/jdk/extras/3pp/install.sh
new file mode 100755
index 0000000..6e58ee7
--- /dev/null
+++ b/third_party/jdk/extras/3pp/install.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+# Copyright 2021 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+set -e
+set -x
+set -o pipefail
+
+PREFIX="$1"
+SUB_DIR="java_8/jre/lib"
+
+# Prepare the directory
+mkdir -p "$PREFIX/$SUB_DIR"
+# Copy the rt.jar file to $SUB_DIR
+cp ./jre/lib/rt.jar "$PREFIX/$SUB_DIR"
diff --git a/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-depth-gpu.html b/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-depth-gpu.html
index 731d12f..abc926e8 100644
--- a/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-depth-gpu.html
+++ b/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-depth-gpu.html
@@ -219,6 +219,7 @@
               depthTexture: gl.getUniformLocation(shaderProgram, 'uDepthTexture'),
               uvTransform: gl.getUniformLocation(shaderProgram, 'uUvTransform'),
               rawValueToMeters: gl.getUniformLocation(shaderProgram, 'uRawValueToMeters'),
+              alpha: gl.getUniformLocation(shaderProgram, 'uAlpha'),
             },
         };
 
@@ -373,6 +374,9 @@
         gl.uniform1f(programInfo.uniformLocations.rawValueToMeters,
                      depthData.rawValueToMeters);
 
+        gl.uniform1f(programInfo.uniformLocations.alpha,
+                     0.75);
+
         gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
       }
 
diff --git a/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-depth.html b/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-depth.html
index 4d530ea..62ab853f 100644
--- a/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-depth.html
+++ b/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-depth.html
@@ -70,10 +70,12 @@
       attribute vec2 aVertexPosition;
       attribute float aDepthDistance;
 
+      uniform float uPointSize;
+
       varying float vDepthDistance;
 
       void main(void) {
-        gl_PointSize = 15.0;
+        gl_PointSize = uPointSize;
         gl_Position = vec4(aVertexPosition, -1.0, 1.0);
         vDepthDistance = aDepthDistance;
       }
@@ -206,6 +208,8 @@
               depthDistance: gl.getAttribLocation(shaderProgram, 'aDepthDistance'),
             },
             uniformLocations: {
+              pointSize: gl.getUniformLocation(shaderProgram, 'uPointSize'),
+              alpha: gl.getUniformLocation(shaderProgram, 'uAlpha'),
             },
         };
 
@@ -423,6 +427,47 @@
         return vertices_data;
       }
 
+      function calculateVerticesFromViewCoordinatesSupersampled(depthData, viewport) {
+        // For verification only.
+
+        const smaller_depth_dim = Math.min(depthData.width, depthData.height);
+        const larger_depth_dim = Math.max(depthData.width, depthData.height);
+
+        const X_RANGE = 0.1;
+        const Y_RANGE = 0.1;
+
+        const NUM_SAMPLES_X = Math.trunc(4 * smaller_depth_dim);
+        const NUM_SAMPLES_Y = Math.trunc(4 * larger_depth_dim);
+
+        const vertices_data = [];
+
+        for(let x = 0; x <= X_RANGE; x += 1/NUM_SAMPLES_X) {
+          for(let y = 0; y <= X_RANGE; y += 1/NUM_SAMPLES_Y ) {
+            const distance = depthData.getDepthInMeters(x, y);
+
+            // We need to convert normalized view coordinates to normalized device coordinates,
+            // with the origin at the center of a cube with side length = 2 and Y growing upward.
+            const depth_coords_ndc = vec3.fromValues(x, y, 0.0);
+
+            // First, fix up the Y axis:
+            depth_coords_ndc[1] = 1 - depth_coords_ndc[1];
+
+            // Then, convert to range [-1, 1]:
+            depth_coords_ndc[0] = (2.0 * depth_coords_ndc[0]) - 1;
+            depth_coords_ndc[1] = (2.0 * depth_coords_ndc[1]) - 1;
+
+            if(depth_coords_ndc[0] > 1 || depth_coords_ndc[0] < -1 ||
+               depth_coords_ndc[1] > 1 || depth_coords_ndc[1] < -1) {
+              continue;
+            }
+
+            vertices_data.push(depth_coords_ndc[0], depth_coords_ndc[1], distance);
+          }
+        }
+
+        return vertices_data;
+      }
+
       function renderDepthInformationCPU(depthData, view, viewport) {
         const vertices_data = calculateVerticesFromViewCoordinates(depthData, viewport);
 
@@ -455,6 +500,12 @@
           programInfo.attribLocations.depthDistance
         );
 
+        gl.uniform1f(programInfo.uniformLocations.pointSize,
+                     15.0);
+
+        gl.uniform1f(programInfo.uniformLocations.alpha,
+                     0.9);
+
         gl.drawArrays(gl.POINTS, 0, vertices_data.length / 3);
       }
 
diff --git a/third_party/webxr_test_pages/webxr-samples/shaders/depth-api-cpu.frag b/third_party/webxr_test_pages/webxr-samples/shaders/depth-api-cpu.frag
index 58d1fb1..f14fa94 100644
--- a/third_party/webxr_test_pages/webxr-samples/shaders/depth-api-cpu.frag
+++ b/third_party/webxr_test_pages/webxr-samples/shaders/depth-api-cpu.frag
@@ -1,5 +1,7 @@
 precision mediump float;
 
+uniform float uAlpha;
+
 varying float vDepthDistance;
 
 const highp float kMaxDepthInMeters = 8.0; // In meters.
@@ -18,7 +20,7 @@
 
 void main(void) {
   highp float normalized_depth = clamp(vDepthDistance / kMaxDepthInMeters, 0.0, 1.0);
-  gl_FragColor = vec4(DepthGetColorVisualization(normalized_depth), 0.75);
+  gl_FragColor = vec4(DepthGetColorVisualization(normalized_depth), uAlpha);
 }
 
 // Insert turbo.glsl here.
diff --git a/third_party/webxr_test_pages/webxr-samples/shaders/depth-api-gpu.frag b/third_party/webxr_test_pages/webxr-samples/shaders/depth-api-gpu.frag
index b265757..eb5716c 100644
--- a/third_party/webxr_test_pages/webxr-samples/shaders/depth-api-gpu.frag
+++ b/third_party/webxr_test_pages/webxr-samples/shaders/depth-api-gpu.frag
@@ -3,6 +3,7 @@
 uniform sampler2D uDepthTexture;
 uniform mat4 uUvTransform;
 uniform float uRawValueToMeters;
+uniform float uAlpha;
 
 varying vec2 vTexCoord;
 
@@ -33,7 +34,7 @@
 
   highp float normalized_depth = clamp(
     DepthGetMeters(uDepthTexture, texCoord.xy) / kMaxDepthInMeters, 0.0, 1.0);
-  gl_FragColor = vec4(DepthGetColorVisualization(normalized_depth), 0.75);
+  gl_FragColor = vec4(DepthGetColorVisualization(normalized_depth), uAlpha);
 }
 
 // Insert turbo.glsl here.
diff --git a/third_party/wpt_tools/README.chromium b/third_party/wpt_tools/README.chromium
index 615d239..d65f78a5 100644
--- a/third_party/wpt_tools/README.chromium
+++ b/third_party/wpt_tools/README.chromium
@@ -12,3 +12,5 @@
     for more details on maintenance.
 Local Modifications:
 - Removed all files except for those listed in wpt/WPTIncludeList.
+- Cherry-picked df9dc69c2340d79fbd56ee2adfa85ac8d4af0b93 temporarily to fix a
+  lint error blocking the importer.
diff --git a/third_party/wpt_tools/wpt/tools/lint/lint.py b/third_party/wpt_tools/wpt/tools/lint/lint.py
index 1872e62..f936055 100644
--- a/third_party/wpt_tools/wpt/tools/lint/lint.py
+++ b/third_party/wpt_tools/wpt/tools/lint/lint.py
@@ -507,6 +507,7 @@
         if (source_file.type != "support" and
             not source_file.name_is_reference and
             not source_file.name_is_tentative and
+            not source_file.name_is_crashtest and
             not source_file.spec_links):
             return [rules.MissingLink.error(path)]
 
diff --git a/tools/clang/scripts/build.py b/tools/clang/scripts/build.py
index 3b10d7a..bd71a527 100755
--- a/tools/clang/scripts/build.py
+++ b/tools/clang/scripts/build.py
@@ -537,11 +537,6 @@
 
   projects = 'clang;compiler-rt;lld;chrometools;clang-tools-extra'
 
-  if sys.platform == 'darwin':
-    # clang needs libc++, else -stdlib=libc++ won't find includes
-    # (this is needed for bootstrap builds and for building the fuchsia runtime)
-    projects += ';libcxx'
-
   base_cmake_args = [
       '-GNinja',
       '-DCMAKE_BUILD_TYPE=Release',
@@ -564,6 +559,16 @@
       '-DLLVM_ENABLE_DIA_SDK=OFF',
   ]
 
+  if sys.platform == 'darwin':
+    # clang needs libc++, else -stdlib=libc++ won't find includes
+    # (this is needed for bootstrap builds and for building the fuchsia runtime)
+    # We don't use the libcxx headers in the chrome build, they're just there
+    # for building small test programs using `clang` manually. Given that, and
+    # given that the libcxx test suite is very slow, omit libcxx's tests from
+    # check-all.
+    projects += ';libcxx'
+    base_cmake_args += [ '-DLIBCXX_INCLUDE_TESTS=OFF' ]
+
   if args.gcc_toolchain:
     # Use the specified gcc installation for building.
     cc = os.path.join(args.gcc_toolchain, 'bin', 'gcc')
diff --git a/tools/grit/grit/format/resource_map.py b/tools/grit/grit/format/resource_map.py
index 98de2eaf..cd6ca78 100644
--- a/tools/grit/grit/format/resource_map.py
+++ b/tools/grit/grit/format/resource_map.py
@@ -57,14 +57,16 @@
 #ifndef GRIT_RESOURCE_MAP_STRUCT_
 #define GRIT_RESOURCE_MAP_STRUCT_
 struct GritResourceMap {
-  const char* const name;
-  int value;
+  const char* const path;
+  int id;
 };
 #endif // GRIT_RESOURCE_MAP_STRUCT_
 
 extern const GritResourceMap %(map_name)s[];
 extern const size_t %(map_name)sSize;
-''' % { 'map_name': GetMapName(root) }
+''' % {
+      'map_name': GetMapName(root)
+  }
 
 
 def _FormatSourceHeader(root, output_dir):
diff --git a/tools/grit/grit/format/resource_map_unittest.py b/tools/grit/grit/format/resource_map_unittest.py
index b56e7e1..38cd31fc 100755
--- a/tools/grit/grit/format/resource_map_unittest.py
+++ b/tools/grit/grit/format/resource_map_unittest.py
@@ -51,13 +51,14 @@
        </release>''', run_gatherers=True)
     output = util.StripBlankLinesAndComments(''.join(
         resource_map.GetFormatter('resource_map_header')(grd, 'en', '.')))
-    self.assertEqual('''\
+    self.assertEqual(
+        '''\
 #include <stddef.h>
 #ifndef GRIT_RESOURCE_MAP_STRUCT_
 #define GRIT_RESOURCE_MAP_STRUCT_
 struct GritResourceMap {
-  const char* const name;
-  int value;
+  const char* const path;
+  int id;
 };
 #endif // GRIT_RESOURCE_MAP_STRUCT_
 extern const GritResourceMap kTheRcHeader[];
@@ -161,13 +162,14 @@
         </release>''', run_gatherers=True)
     output = util.StripBlankLinesAndComments(''.join(
         resource_map.GetFormatter('resource_map_header')(grd, 'en', '.')))
-    self.assertEqual('''\
+    self.assertEqual(
+        '''\
 #include <stddef.h>
 #ifndef GRIT_RESOURCE_MAP_STRUCT_
 #define GRIT_RESOURCE_MAP_STRUCT_
 struct GritResourceMap {
-  const char* const name;
-  int value;
+  const char* const path;
+  int id;
 };
 #endif // GRIT_RESOURCE_MAP_STRUCT_
 extern const GritResourceMap kTheRcHeader[];
@@ -240,13 +242,14 @@
         </release>''', run_gatherers=True)
     output = util.StripBlankLinesAndComments(''.join(
         resource_map.GetFormatter('resource_map_header')(grd, 'en', '.')))
-    self.assertEqual('''\
+    self.assertEqual(
+        '''\
 #include <stddef.h>
 #ifndef GRIT_RESOURCE_MAP_STRUCT_
 #define GRIT_RESOURCE_MAP_STRUCT_
 struct GritResourceMap {
-  const char* const name;
-  int value;
+  const char* const path;
+  int id;
 };
 #endif // GRIT_RESOURCE_MAP_STRUCT_
 extern const GritResourceMap kTheRcHeader[];
@@ -313,13 +316,14 @@
     grd.InitializeIds()
     output = util.StripBlankLinesAndComments(''.join(
         resource_map.GetFormatter('resource_map_header')(grd, 'en', '.')))
-    self.assertEqual('''\
+    self.assertEqual(
+        '''\
 #include <stddef.h>
 #ifndef GRIT_RESOURCE_MAP_STRUCT_
 #define GRIT_RESOURCE_MAP_STRUCT_
 struct GritResourceMap {
-  const char* const name;
-  int value;
+  const char* const path;
+  int id;
 };
 #endif // GRIT_RESOURCE_MAP_STRUCT_
 extern const GritResourceMap kTheRcHeader[];
diff --git a/tools/gritsettings/resource_ids.spec b/tools/gritsettings/resource_ids.spec
index 6d19ae8a..f93adda 100644
--- a/tools/gritsettings/resource_ids.spec
+++ b/tools/gritsettings/resource_ids.spec
@@ -118,8 +118,9 @@
     "META": {"sizes": {"includes": [35]}},
     "includes": [1370],
   },
-  "chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_resources.grd": {
-    "structures": [1400],
+  "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_resources.grd": {
+    "META": {"sizes": {"includes": [10]}},
+    "includes": [1380],
   },
   "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/commander/commander_resources.grd": {
     "META": {"sizes": {"includes": [15]}},
@@ -340,9 +341,9 @@
     "META": {"align": 100},
     "messages": [2500],
   },
-  "<(SHARED_INTERMEDIATE_DIR)/chromeos/components/camera_app_ui/chromeos_camera_app_resources.grd": {
-    "META": {"sizes": {"includes": [300],}},
+  "chromeos/components/camera_app_ui/resources/camera_app_resources.grd": {
     "includes": [2505],
+    "structures": [2510],
   },
   "chromeos/components/camera_app_ui/resources/strings/camera_strings.grd": {
     "messages": [2515],
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 3b48caf3..00a3168 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -779,6 +779,7 @@
       'android-pie-arm64-wpt-rel-non-cq': 'android_release_trybot_arm64_webview_google',
       'android-pie-x86-rel': 'android_release_trybot_x86_fastbuild_webview_google',
       'android-weblayer-pie-x86-fyi-rel': 'android_release_trybot_x86_fastbuild_webview_google',
+      'android-weblayer-pie-x86-wpt-fyi-rel': 'android_release_trybot_x86_fastbuild_webview_google',
       'android-webview-pie-arm64-fyi-rel': 'android_release_trybot_arm64_webview_google',
       'android-10-arm64-rel': 'android_release_trybot_arm64_fastbuild_webview_google',
       'android-11-x86-fyi-rel': 'android_release_trybot_x86_fastbuild_webview_google',
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.android.json b/tools/mb/mb_config_expectations/tryserver.chromium.android.json
index 02dc4ca3..eee5b574 100644
--- a/tools/mb/mb_config_expectations/tryserver.chromium.android.json
+++ b/tools/mb/mb_config_expectations/tryserver.chromium.android.json
@@ -386,6 +386,23 @@
       "use_goma": true
     }
   },
+  "android-weblayer-pie-x86-wpt-fyi-rel": {
+    "gn_args": {
+      "dcheck_always_on": true,
+      "disable_android_lint": true,
+      "ffmpeg_branding": "Chrome",
+      "is_component_build": false,
+      "is_debug": false,
+      "proprietary_codecs": true,
+      "strip_debug_info": true,
+      "symbol_level": 1,
+      "system_webview_package_name": "com.google.android.webview",
+      "target_cpu": "x86",
+      "target_os": "android",
+      "use_errorprone_java_compiler": false,
+      "use_goma": true
+    }
+  },
   "android-webview-marshmallow-arm64-dbg": {
     "gn_args": {
       "dcheck_always_on": true,
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 12cc43c5..62e2584b 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -12743,7 +12743,7 @@
   <int value="0" label="Cookies setting"/>
   <int value="1" label="Images setting"/>
   <int value="2" label="JavaScript setting"/>
-  <int value="3" label="Plugins setting"/>
+  <int value="3" label="Plugins setting [removed in M90]"/>
   <int value="4" label="Popups setting"/>
   <int value="5" label="Location setting"/>
   <int value="6" label="Notifications setting"/>
@@ -31390,6 +31390,7 @@
   <int value="3800" label="V8WasmExceptionHandling"/>
   <int value="3801" label="WasmModuleSharing"/>
   <int value="3802" label="CrossOriginWasmModuleSharing"/>
+  <int value="3803" label="OverflowClipAlongEitherAxis"/>
 </enum>
 
 <enum name="FeaturePolicyAllowlistType">
@@ -43692,8 +43693,6 @@
   <int value="-1416184931" label="TranslateRankerEnforcement:enabled"/>
   <int value="-1412230070" label="query-tiles-instant-background-task"/>
   <int value="-1411733990" label="OmniboxDedupeGoogleDriveURLs:disabled"/>
-  <int value="-1411219910"
-      label="enable-experimental-accessibility-chromevox-annotations"/>
   <int value="-1411003295" label="disable-encrypted-media"/>
   <int value="-1410394131" label="EvDetailsInPageInfo:enabled"/>
   <int value="-1410001116"
diff --git a/tools/metrics/histograms/histograms_xml/ash/histograms.xml b/tools/metrics/histograms/histograms_xml/ash/histograms.xml
index 0fe28529..b2b3bc5 100644
--- a/tools/metrics/histograms/histograms_xml/ash/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/ash/histograms.xml
@@ -163,6 +163,18 @@
   </summary>
 </histogram>
 
+<histogram name="Ash.AppNotificationBadgingPref" enum="Boolean"
+    expires_after="2021-08-11">
+  <owner>mmourgos@chromium.org</owner>
+  <owner>gzadina@google.com</owner>
+  <summary>
+    For each user, records whether they have the app notification badging user
+    preference enabled or disabled. This metric is only logged when an active
+    user session has been started. This metric is logged periodically every 30
+    minutes.
+  </summary>
+</histogram>
+
 <histogram base="true" name="Ash.Assistant.AnimationSmoothness" units="%"
     expires_after="2021-07-20">
 <!-- Name completed by histogram_suffixes name="AshAssistantAnimationSmoothness" -->
diff --git a/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml b/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
index ca07314b..9c79c9e 100644
--- a/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
+++ b/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
@@ -6008,29 +6008,65 @@
 </histogram_suffixes>
 
 <histogram_suffixes name="EventLatencyBreakdowns" separator=".">
-  <suffix name="Activation" label="The duration of the activation stage."/>
+  <suffix name="Activation" label="The duration of the activation stage.">
+    <obsolete>
+      Obsolete as of M90. Breakdowns are being tracked in UKM.
+    </obsolete>
+  </suffix>
   <suffix name="BeginImplFrameToSendBeginMainFrame"
       label="The time from when the compositor impl frame is started to when
-             BeginMainFrame is sent."/>
+             BeginMainFrame is sent.">
+    <obsolete>
+      Obsolete as of M90. Breakdowns are being tracked in UKM.
+    </obsolete>
+  </suffix>
   <suffix name="BrowserToRendererCompositor"
       label="The time from when the event is generated until the first
-             compositor stage after the event arrives in the renderer."/>
-  <suffix name="Commit" label="The duration of the commit stage."/>
+             compositor stage after the event arrives in the renderer.">
+    <obsolete>
+      Obsolete as of M90. Breakdowns are being tracked in UKM.
+    </obsolete>
+  </suffix>
+  <suffix name="Commit" label="The duration of the commit stage.">
+    <obsolete>
+      Obsolete as of M90. Breakdowns are being tracked in UKM.
+    </obsolete>
+  </suffix>
   <suffix name="EndActivateToSubmitCompositorFrame"
       label="The time from when an activation is complete to the next
-             SubmitCompositorFrame."/>
+             SubmitCompositorFrame.">
+    <obsolete>
+      Obsolete as of M90. Breakdowns are being tracked in UKM.
+    </obsolete>
+  </suffix>
   <suffix name="EndCommitToActivation"
       label="The time from when a commit is complete to the beginning of the
-             next activation."/>
+             next activation.">
+    <obsolete>
+      Obsolete as of M90. Breakdowns are being tracked in UKM.
+    </obsolete>
+  </suffix>
   <suffix name="SendBeginMainFrameToCommit"
-      label="The time from when the BeginMainFrame is sent to the beginning
-             of the commit."/>
+      label="The time from when the BeginMainFrame is sent to the beginning of
+             the commit.">
+    <obsolete>
+      Obsolete as of M90. Breakdowns are being tracked in UKM.
+    </obsolete>
+  </suffix>
   <suffix name="SendBeginMainFrameToCommit.Animate"
       label="The time portion of SendBeginMainFrameToCommit spent on
-             animations."/>
+             animations.">
+    <obsolete>
+      Obsolete as of M90. Breakdowns are being tracked in UKM.
+    </obsolete>
+  </suffix>
   <suffix name="SendBeginMainFrameToCommit.BeginMainSentToStarted"
       label="The time portion of SendBeginMainFrameToCommit spent before
-             starting main thread work."/>
+             starting main thread work.">
+    <obsolete>
+      Obsolete as of M90. Breakdowns are being tracked in UKM.
+    </obsolete>
+  </suffix>
   <suffix name="SendBeginMainFrameToCommit.Composite"
       label="The time portion of SendBeginMainFrameToCommit spent on
              compositing.">
@@ -6039,25 +6075,52 @@
     </obsolete>
   </suffix>
   <suffix name="SendBeginMainFrameToCommit.CompositeCommit"
-      label="The time portion of SendBeginMainFrameToCommit spent on
-             composite commit."/>
+      label="The time portion of SendBeginMainFrameToCommit spent on composite
+             commit.">
+    <obsolete>
+      Obsolete as of M90. Breakdowns are being tracked in UKM.
+    </obsolete>
+  </suffix>
   <suffix name="SendBeginMainFrameToCommit.CompositingAssignments"
       label="The time portion of SendBeginMainFrameToCommit spent on updating
-             compositing assignments."/>
+             compositing assignments.">
+    <obsolete>
+      Obsolete as of M90. Breakdowns are being tracked in UKM.
+    </obsolete>
+  </suffix>
   <suffix name="SendBeginMainFrameToCommit.CompositingInputs"
       label="The time portion of SendBeginMainFrameToCommit spent on updating
-             compositing inputs."/>
+             compositing inputs.">
+    <obsolete>
+      Obsolete as of M90. Breakdowns are being tracked in UKM.
+    </obsolete>
+  </suffix>
   <suffix name="SendBeginMainFrameToCommit.HandleInputEvents"
       label="The time portion of SendBeginMainFrameToCommit spent on handling
-             imput events."/>
+             imput events.">
+    <obsolete>
+      Obsolete as of M90. Breakdowns are being tracked in UKM.
+    </obsolete>
+  </suffix>
   <suffix name="SendBeginMainFrameToCommit.LayoutUpdate"
       label="The time portion of SendBeginMainFrameToCommit spent on layout
-             update."/>
+             update.">
+    <obsolete>
+      Obsolete as of M90. Breakdowns are being tracked in UKM.
+    </obsolete>
+  </suffix>
   <suffix name="SendBeginMainFrameToCommit.Paint"
-      label="The time portion of SendBeginMainFrameToCommit spent on paint."/>
+      label="The time portion of SendBeginMainFrameToCommit spent on paint.">
+    <obsolete>
+      Obsolete as of M90. Breakdowns are being tracked in UKM.
+    </obsolete>
+  </suffix>
   <suffix name="SendBeginMainFrameToCommit.Prepaint"
-      label="The time portion of SendBeginMainFrameToCommit spent on
-             prepaint."/>
+      label="The time portion of SendBeginMainFrameToCommit spent on prepaint.">
+    <obsolete>
+      Obsolete as of M90. Breakdowns are being tracked in UKM.
+    </obsolete>
+  </suffix>
   <suffix name="SendBeginMainFrameToCommit.ScrollingCoordinator"
       label="The time portion of SendBeginMainFrameToCommit spent on scrolling
              coordinator.">
@@ -6068,33 +6131,65 @@
   </suffix>
   <suffix name="SendBeginMainFrameToCommit.StyleUpdate"
       label="The time portion of SendBeginMainFrameToCommit spent on style
-             update."/>
+             update.">
+    <obsolete>
+      Obsolete as of M90. Breakdowns are being tracked in UKM.
+    </obsolete>
+  </suffix>
   <suffix name="SendBeginMainFrameToCommit.UpdateLayers"
       label="The time portion of SendBeginMainFrameToCommit spent on updating
-             layers."/>
+             layers.">
+    <obsolete>
+      Obsolete as of M90. Breakdowns are being tracked in UKM.
+    </obsolete>
+  </suffix>
   <suffix name="SubmitCompositorFrameToPresentationCompositorFrame"
       label="The time from when the compositor frame is submitted to the
-             display compositor to when it is presented."/>
+             display compositor to when it is presented.">
+    <obsolete>
+      Obsolete as of M90. Breakdowns are being tracked in UKM.
+    </obsolete>
+  </suffix>
   <suffix
       name="SubmitCompositorFrameToPresentationCompositorFrame.ReceivedCompositorFrameToStartDraw"
       label="The time from when the compositor frame is received to when it
-             starts to draw."/>
+             starts to draw.">
+    <obsolete>
+      Obsolete as of M90. Breakdowns are being tracked in UKM.
+    </obsolete>
+  </suffix>
   <suffix
       name="SubmitCompositorFrameToPresentationCompositorFrame.StartDrawToSwapStart"
       label="The time from when the compositor frame is started to draw to
-             when it starts swap."/>
+             when it starts swap.">
+    <obsolete>
+      Obsolete as of M90. Breakdowns are being tracked in UKM.
+    </obsolete>
+  </suffix>
   <suffix
       name="SubmitCompositorFrameToPresentationCompositorFrame.SubmitToReceiveCompositorFrame"
       label="The time from when the compositor frame is submitted to when it
-             is received."/>
+             is received.">
+    <obsolete>
+      Obsolete as of M90. Breakdowns are being tracked in UKM.
+    </obsolete>
+  </suffix>
   <suffix
       name="SubmitCompositorFrameToPresentationCompositorFrame.SwapEndToPresentationCompositorFrame"
       label="The time from when the compositor frame ends swap to when it is
-             presented."/>
+             presented.">
+    <obsolete>
+      Obsolete as of M90. Breakdowns are being tracked in UKM.
+    </obsolete>
+  </suffix>
   <suffix
       name="SubmitCompositorFrameToPresentationCompositorFrame.SwapStartToSwapEnd"
-      label="The time from when the compositor frame starts to swap to when
-             it ends swap."/>
+      label="The time from when the compositor frame starts to swap to when it
+             ends swap.">
+    <obsolete>
+      Obsolete as of M90. Breakdowns are being tracked in UKM.
+    </obsolete>
+  </suffix>
   <suffix name="TotalLatency"
       label="Total latency from when the event is generated until the frame
              is presented on screen."/>
@@ -12184,6 +12279,7 @@
 
 <histogram_suffixes name="OptimizationGuide_OptimizationTargets" separator=".">
   <suffix name="LanguageDetection" label="Language detection"/>
+  <suffix name="PageTopics" label="Page topics"/>
   <suffix name="PainfulPageLoad" label="Painful page load"/>
   <affected-histogram name="OptimizationGuide.IsPredictionModelValid"/>
   <affected-histogram
diff --git a/tools/metrics/histograms/histograms_xml/others/histograms.xml b/tools/metrics/histograms/histograms_xml/others/histograms.xml
index d778aa0..195b7c0 100644
--- a/tools/metrics/histograms/histograms_xml/others/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/others/histograms.xml
@@ -7756,6 +7756,7 @@
 <histogram name="KeyboardAccessory.AccessoryActionImpression"
     enum="AccessoryAction" expires_after="2021-07-27">
   <owner>fhorschig@chromium.org</owner>
+  <owner>ioanap@chromium.org</owner>
   <summary>
     Android only. Records whenever users faces an action in the accessory bar or
     one of its sheets.
@@ -7765,6 +7766,7 @@
 <histogram name="KeyboardAccessory.AccessoryActionSelected"
     enum="AccessoryAction" expires_after="2021-07-27">
   <owner>fhorschig@chromium.org</owner>
+  <owner>ioanap@chromium.org</owner>
   <summary>
     Android only. Records whenever users select an action in the accessory bar
     or one of its sheets.
@@ -7785,6 +7787,7 @@
 <histogram name="KeyboardAccessory.AccessorySheetSuggestionCount" units="count"
     expires_after="2021-07-27">
   <owner>fhorschig@chromium.org</owner>
+  <owner>ioanap@chromium.org</owner>
   <summary>
     Android only. Records how many suggestions a user faced when opening a
     sheet. The base histogram counts impressions across all sheets.
@@ -7792,8 +7795,9 @@
 </histogram>
 
 <histogram name="KeyboardAccessory.AccessorySheetSuggestionsSelected"
-    enum="AccessorySuggestionType" expires_after="2021-03-21">
+    enum="AccessorySuggestionType" expires_after="2021-07-27">
   <owner>fhorschig@chromium.org</owner>
+  <owner>ioanap@chromium.org</owner>
   <summary>
     Android only. Records which type of suggestion was selected from an open
     sheet.
@@ -7801,8 +7805,9 @@
 </histogram>
 
 <histogram name="KeyboardAccessory.AccessorySheetTriggered"
-    enum="AccessorySheetTrigger" expires_after="2021-07-18">
+    enum="AccessorySheetTrigger" expires_after="2021-07-27">
   <owner>fhorschig@chromium.org</owner>
+  <owner>ioanap@chromium.org</owner>
   <summary>
     Android only. Records how often the bottom sheet was opened or closed by a
     user and the overall count of closures. Closing buckets may be logged up to
@@ -7811,7 +7816,7 @@
 </histogram>
 
 <histogram name="KeyboardAccessory.AccessoryToggleClicked"
-    enum="AccessoryToggleType" expires_after="2021-06-20">
+    enum="AccessoryToggleType" expires_after="2021-07-27">
   <owner>ioanap@chromium.org</owner>
   <owner>fhorschig@chromium.org</owner>
   <summary>
@@ -7822,7 +7827,7 @@
 </histogram>
 
 <histogram name="KeyboardAccessory.AccessoryToggleImpression"
-    enum="AccessoryToggleType" expires_after="2021-06-20">
+    enum="AccessoryToggleType" expires_after="2021-07-27">
   <owner>ioanap@chromium.org</owner>
   <owner>fhorschig@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/safe_browsing/histograms.xml b/tools/metrics/histograms/histograms_xml/safe_browsing/histograms.xml
index 4931119..80da52be 100644
--- a/tools/metrics/histograms/histograms_xml/safe_browsing/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/safe_browsing/histograms.xml
@@ -675,7 +675,8 @@
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Records the last bypass event type before a user disables Enhanced Safe
-    Browsing. Logged each time a user disables Enhanced Safe Browsing.
+    Browsing. Logged each time a user disables Enhanced Safe Browsing. Logged at
+    most three times a week.
   </summary>
 </histogram>
 
diff --git a/tools/metrics/histograms/histograms_xml/stability/histograms.xml b/tools/metrics/histograms/histograms_xml/stability/histograms.xml
index 58b3b24..e130d0f 100644
--- a/tools/metrics/histograms/histograms_xml/stability/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/stability/histograms.xml
@@ -264,6 +264,16 @@
   </summary>
 </histogram>
 
+<histogram name="Stability.Experimental.BrowserCrash" enum="Boolean"
+    expires_after="2021-06-15">
+  <owner>asvitkine@chromium.org</owner>
+  <owner>chrome-metrics-team@google.com</owner>
+  <summary>
+    Logged at the same time as the kBrowserCrash bucket of Stability.Counts2,
+    for debugging crbug.com/1176977.
+  </summary>
+</histogram>
+
 <histogram name="Stability.Experimental.Counts" enum="StabilityEventType"
     expires_after="2021-01-15">
   <obsolete>
@@ -277,6 +287,16 @@
   </summary>
 </histogram>
 
+<histogram name="Stability.Experimental.Counts2" enum="StabilityEventType"
+    expires_after="2021-06-15">
+  <owner>asvitkine@chromium.org</owner>
+  <owner>chrome-metrics-team@google.com</owner>
+  <summary>
+    Like Stability.Counts2, but logged as a sparse histogram for debugging
+    crbug.com/1176977.
+  </summary>
+</histogram>
+
 <histogram name="Stability.Experimental.PageLoads" enum="StabilityPageLoadType"
     expires_after="2021-07-11">
   <owner>fdoray@chromium.org</owner>
diff --git a/tools/perf/cli_tools/tbmv3/validate_tbmv3_metric.py b/tools/perf/cli_tools/tbmv3/validate_tbmv3_metric.py
index 62e0e26..58be588 100644
--- a/tools/perf/cli_tools/tbmv3/validate_tbmv3_metric.py
+++ b/tools/perf/cli_tools/tbmv3/validate_tbmv3_metric.py
@@ -7,6 +7,7 @@
 import ast
 import argparse
 import importlib
+import json
 import logging
 import os
 import pprint
@@ -94,6 +95,10 @@
                       default=None,
                       help=('Path to trace_processor shell. '
                             'Default: Binary downloaded from cloud storage.'))
+  parser.add_argument('--force-recompute-tbmv2',
+                      action='store_true',
+                      help=('Recompute TBMv2 Metrics. Otherwise it will use '
+                            'a cached result when available.'))
   parser.add_argument('-v', '--verbose', action='store_true')
   args = parser.parse_args()
   return args
@@ -121,6 +126,7 @@
                       args.trace_processor_path)
 
     self.traces_dir = args.traces_dir
+    self.force_recompute_tbmv2 = args.force_recompute_tbmv2
 
 
 class TraceInfo(object):
@@ -177,9 +183,25 @@
   return TraceInfo(json_trace, proto_trace)
 
 
-def RunTBMv2Metric(tbmv2_metric, json_trace):
+def GetV2CachedResultPath(tbmv2_metric, json_trace):
+  dirname = os.path.dirname(json_trace)
+  basename = os.path.basename(json_trace) + '.' + tbmv2_metric + '.json'
+  return os.path.join(dirname, basename)
+
+
+def RunTBMv2Metric(tbmv2_metric, json_trace, force_recompute=False):
   message = 'Running TBMv2 Metric...'
   PrintNoLn(message)
+  hset = histogram_set.HistogramSet()
+
+  cached_results = GetV2CachedResultPath(tbmv2_metric, json_trace)
+
+  if not force_recompute and os.path.exists(cached_results):
+    with open(cached_results) as f:
+      hset.ImportDicts(json.load(f))
+    CursorErase(len(message))
+    return hset
+
   metrics = [tbmv2_metric]
   TEN_MINUTES = 60 * 10
   trace_abspath = os.path.abspath(json_trace)
@@ -193,8 +215,10 @@
     raise Exception("Metric %s is empty for trace %s" %
                     (tbmv2_metric, json_trace))
   histograms = mre_result.pairs['histograms']
-  hset = histogram_set.HistogramSet()
   hset.ImportDicts(histograms)
+  with open(cached_results, 'w') as f:
+    json.dump(histograms, f)
+
   CursorErase(len(message))
   return hset
 
@@ -215,7 +239,9 @@
         self.simple_config = ctx.simple_config
 
     def RunTBMv2(self, metric):
-      return RunTBMv2Metric(metric, trace_info.json_trace)
+      return RunTBMv2Metric(metric,
+                            trace_info.json_trace,
+                            force_recompute=ctx.force_recompute_tbmv2)
 
     def RunTBMv3(self, metric):
       return RunTBMv3Metric(ctx.trace_processor_path, metric,
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 2fdf9ee..91a482a 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -6,7 +6,7 @@
         },
         "mac": {
             "hash": "b983f5d7f044777facbf785367a560e7d8b07c6c",
-            "remote_path": "perfetto_binaries/trace_processor_shell/mac/c0182a50034c0c17a444c40aec96ee54239bc269/trace_processor_shell"
+            "remote_path": "perfetto_binaries/trace_processor_shell/mac/d5c3eb6a5fe1b920ce5833cd8511d2b7810882fc/trace_processor_shell"
         },
         "linux": {
             "hash": "ecf01a77a074b6ac612cb19909eba5ea93d8cf75",
diff --git a/ui/accessibility/accessibility_switches.cc b/ui/accessibility/accessibility_switches.cc
index d71cf27..be3f94f 100644
--- a/ui/accessibility/accessibility_switches.cc
+++ b/ui/accessibility/accessibility_switches.cc
@@ -40,10 +40,6 @@
 const char kEnableExperimentalAccessibilitySwitchAccessText[] =
     "enable-experimental-accessibility-switch-access-text";
 
-// Enables annotations feature that hasn't launched yet.
-const char kEnableExperimentalAccessibilityChromeVoxAnnotations[] =
-    "enable-experimental-accessibility-chromevox-annotations";
-
 // Enables Switch Access point scanning. This feature hasn't launched yet.
 const char kEnableSwitchAccessPointScanning[] =
     "enable-switch-access-point-scanning";
diff --git a/ui/accessibility/accessibility_switches.h b/ui/accessibility/accessibility_switches.h
index 56c8203..6a5280c 100644
--- a/ui/accessibility/accessibility_switches.h
+++ b/ui/accessibility/accessibility_switches.h
@@ -24,8 +24,6 @@
     kEnableExperimentalAccessibilityLanguageDetectionDynamic[];
 AX_BASE_EXPORT extern const char
     kEnableExperimentalAccessibilitySwitchAccessText[];
-AX_BASE_EXPORT extern const char
-    kEnableExperimentalAccessibilityChromeVoxAnnotations[];
 AX_BASE_EXPORT extern const char kEnableSwitchAccessPointScanning[];
 AX_BASE_EXPORT extern const char
     kEnableExperimentalAccessibilitySwitchAccessSetupGuide[];
diff --git a/ui/accessibility/ax_assistant_structure.cc b/ui/accessibility/ax_assistant_structure.cc
index de239c5e..b48828d 100644
--- a/ui/accessibility/ax_assistant_structure.cc
+++ b/ui/accessibility/ax_assistant_structure.cc
@@ -34,15 +34,6 @@
   return false;
 }
 
-bool HasOnlyTextChildren(const AXNode* node) {
-  for (size_t i = 0; i < node->GetUnignoredChildCount(); ++i) {
-    AXNode* child = node->GetUnignoredChildAtIndex(i);
-    if (!child->IsText())
-      return false;
-  }
-  return true;
-}
-
 // TODO(muyuanli): share with BrowserAccessibility.
 bool IsSimpleTextControl(const AXNode* node, uint32_t state) {
   return (node->data().role == ax::mojom::Role::kTextField ||
@@ -131,27 +122,6 @@
   return value;
 }
 
-bool HasOnlyTextAndImageChildren(const AXNode* node) {
-  for (size_t i = 0; i < node->GetUnignoredChildCount(); ++i) {
-    AXNode* child = node->GetUnignoredChildAtIndex(i);
-    if (!child->IsText() && !ui::IsImage(child->data().role)) {
-      return false;
-    }
-  }
-  return true;
-}
-
-bool IsFocusable(const AXNode* node) {
-  if (node->data().role == ax::mojom::Role::kIframe ||
-      node->data().role == ax::mojom::Role::kIframePresentational ||
-      ((node->data().role == ax::mojom::Role::kRootWebArea ||
-        node->data().role == ax::mojom::Role::kPdfRoot) &&
-       node->GetUnignoredParent())) {
-    return node->data().HasStringAttribute(ax::mojom::StringAttribute::kName);
-  }
-  return node->data().HasState(ax::mojom::State::kFocusable);
-}
-
 base::string16 GetText(const AXNode* node, bool show_password) {
   if (node->data().role == ax::mojom::Role::kPdfRoot ||
       node->data().role == ax::mojom::Role::kIframe ||
@@ -159,12 +129,10 @@
     return base::string16();
   }
 
-  ax::mojom::NameFrom name_from = static_cast<ax::mojom::NameFrom>(
-      node->data().GetIntAttribute(ax::mojom::IntAttribute::kNameFrom));
-  if (ui::IsListItem(node->data().role) &&
-      name_from == ax::mojom::NameFrom::kContents) {
-    if (!node->children().empty() && !HasOnlyTextChildren(node))
-      return base::string16();
+  ax::mojom::NameFrom name_from = node->data().GetNameFrom();
+
+  if (!ui::IsLeaf(node) && name_from == ax::mojom::NameFrom::kContents) {
+    return base::string16();
   }
 
   base::string16 value = GetValue(node, show_password);
@@ -212,9 +180,7 @@
     return text;
   }
 
-  if (text.empty() &&
-      (HasOnlyTextChildren(node) ||
-       (IsFocusable(node) && HasOnlyTextAndImageChildren(node)))) {
+  if (text.empty() && IsLeaf(node)) {
     for (size_t i = 0; i < node->GetUnignoredChildCount(); ++i) {
       AXNode* child = node->GetUnignoredChildAtIndex(i);
       text += GetText(child, show_password);
@@ -227,6 +193,7 @@
         node->data().GetString16Attribute(ax::mojom::StringAttribute::kUrl);
     text = AXUrlBaseText(url);
   }
+
   return text;
 }
 
diff --git a/ui/accessibility/ax_node.cc b/ui/accessibility/ax_node.cc
index 5227b8a..6fdf734 100644
--- a/ui/accessibility/ax_node.cc
+++ b/ui/accessibility/ax_node.cc
@@ -500,8 +500,6 @@
 
 base::string16 AXNode::GetHypertext() const {
   DCHECK(!tree_->GetTreeUpdateInProgressState());
-  if (IsIgnoredForTextNavigation())
-    return base::string16();
 
   // Hypertext is not exposed for descendants of leaf nodes. For such nodes,
   // their inner text is equivalent to their hypertext. Otherwise, we would
@@ -546,10 +544,6 @@
 
 std::string AXNode::GetInnerText() const {
   DCHECK(!tree_->GetTreeUpdateInProgressState());
-  // The inner text computed should exclude the elements not exposed to text
-  // navigation.
-  if (IsIgnoredForTextNavigation())
-    return std::string();
 
   // If a text field has no descendants, then we compute its inner text from its
   // value or its placeholder. Otherwise we prefer to look at its descendant
diff --git a/ui/accessibility/ax_node_position_unittest.cc b/ui/accessibility/ax_node_position_unittest.cc
index ba7d20e9..cf1bf5c 100644
--- a/ui/accessibility/ax_node_position_unittest.cc
+++ b/ui/accessibility/ax_node_position_unittest.cc
@@ -9630,6 +9630,168 @@
   ASSERT_TRUE(*text_position_two == *text_position_one);
 }
 
+TEST_F(AXPositionTest, OperatorEqualsTextPositionsInTextField) {
+  g_ax_embedded_object_behavior = AXEmbeddedObjectBehavior::kExposeCharacter;
+
+  // ++1 kRootWebArea
+  // ++++2 kTextField editable
+  // ++++++3 kGenericContainer editable
+  // ++++++++4 kStaticText editable "Hello"
+  // ++++++++++5 kInlineTextBox "Hello"
+  AXNodeData root_1;
+  AXNodeData text_field_2;
+  AXNodeData generic_container_3;
+  AXNodeData static_text_4;
+  AXNodeData inline_box_5;
+
+  root_1.id = 1;
+  text_field_2.id = 2;
+  generic_container_3.id = 3;
+  static_text_4.id = 4;
+  inline_box_5.id = 5;
+
+  root_1.role = ax::mojom::Role::kRootWebArea;
+  root_1.child_ids = {text_field_2.id};
+
+  text_field_2.role = ax::mojom::Role::kTextField;
+  text_field_2.AddState(ax::mojom::State::kEditable);
+  text_field_2.AddBoolAttribute(ax::mojom::BoolAttribute::kEditableRoot, true);
+  text_field_2.child_ids = {generic_container_3.id};
+
+  generic_container_3.role = ax::mojom::Role::kGenericContainer;
+  generic_container_3.AddState(ax::mojom::State::kEditable);
+  generic_container_3.child_ids = {static_text_4.id};
+
+  static_text_4.role = ax::mojom::Role::kStaticText;
+  static_text_4.SetName("Hello");
+  static_text_4.child_ids = {inline_box_5.id};
+
+  inline_box_5.role = ax::mojom::Role::kInlineTextBox;
+  inline_box_5.SetName("Hello");
+
+  SetTree(CreateAXTree({root_1, text_field_2, generic_container_3,
+                        static_text_4, inline_box_5}));
+
+  // TextPosition anchor_id=5 anchor_role=inlineTextBox text_offset=4
+  // annotated_text=hell<o>
+  TestPositionType inline_text_position = AXNodePosition::CreateTextPosition(
+      GetTreeID(), inline_box_5.id, 4 /* text_offset */,
+      ax::mojom::TextAffinity::kDownstream);
+  ASSERT_NE(nullptr, inline_text_position);
+
+  // TextPosition anchor_id=2 anchor_role=textField text_offset=4
+  // annotated_text=hell<o>
+  TestPositionType text_field_position = AXNodePosition::CreateTextPosition(
+      GetTreeID(), text_field_2.id, 4 /* text_offset */,
+      ax::mojom::TextAffinity::kDownstream);
+  ASSERT_NE(nullptr, text_field_position);
+
+  // Validate that two positions in the text field with the same text offsets
+  // but different anchors are logically equal.
+  EXPECT_EQ(*inline_text_position, *text_field_position);
+  EXPECT_EQ(*text_field_position, *inline_text_position);
+}
+
+TEST_F(AXPositionTest, OperatorEqualsTextPositionsInSearchBox) {
+  g_ax_embedded_object_behavior = AXEmbeddedObjectBehavior::kExposeCharacter;
+
+  // ++1 kRootWebArea
+  // ++++2 kSearchBox editable editableRoot=true
+  // ++++++3 kGenericContainer
+  // ++++++++4 kGenericContainer editable
+  // ++++++++++5 kStaticText editable "Hello"
+  // ++++++++++++6 kInlineTextBox "Hello"
+  // ++++7 kButton
+  // ++++++8 kStaticText "X"
+  // ++++++++9 kInlineTextBox "X"
+  AXNodeData root_1;
+  AXNodeData search_box_2;
+  AXNodeData generic_container_3;
+  AXNodeData generic_container_4;
+  AXNodeData static_text_5;
+  AXNodeData inline_box_6;
+  AXNodeData button_7;
+  AXNodeData static_text_8;
+  AXNodeData inline_box_9;
+
+  root_1.id = 1;
+  search_box_2.id = 2;
+  generic_container_3.id = 3;
+  generic_container_4.id = 4;
+  static_text_5.id = 5;
+  inline_box_6.id = 6;
+  button_7.id = 7;
+  static_text_8.id = 8;
+  inline_box_9.id = 9;
+
+  root_1.role = ax::mojom::Role::kRootWebArea;
+  root_1.child_ids = {search_box_2.id, button_7.id};
+
+  search_box_2.role = ax::mojom::Role::kSearchBox;
+  search_box_2.AddState(ax::mojom::State::kEditable);
+  search_box_2.AddBoolAttribute(ax::mojom::BoolAttribute::kEditableRoot, true);
+  search_box_2.child_ids = {generic_container_3.id};
+
+  generic_container_3.role = ax::mojom::Role::kGenericContainer;
+  generic_container_3.child_ids = {generic_container_4.id};
+
+  generic_container_4.role = ax::mojom::Role::kGenericContainer;
+  generic_container_4.AddState(ax::mojom::State::kEditable);
+  generic_container_4.child_ids = {static_text_5.id};
+
+  static_text_5.role = ax::mojom::Role::kStaticText;
+  static_text_5.SetName("Hello");
+  static_text_5.child_ids = {inline_box_6.id};
+
+  inline_box_6.role = ax::mojom::Role::kInlineTextBox;
+  inline_box_6.SetName("Hello");
+
+  button_7.role = ax::mojom::Role::kButton;
+  button_7.child_ids = {static_text_8.id};
+
+  static_text_8.role = ax::mojom::Role::kStaticText;
+  static_text_8.SetName("X");
+  static_text_8.child_ids = {inline_box_9.id};
+
+  inline_box_9.role = ax::mojom::Role::kInlineTextBox;
+  inline_box_9.SetName("X");
+
+  SetTree(CreateAXTree({root_1, search_box_2, generic_container_3,
+                        generic_container_4, static_text_5, inline_box_6,
+                        button_7, static_text_8, inline_box_9}));
+
+  // TextPosition anchor_role=inlineTextBox_6 text_offset=5
+  // annotated_text=hello<>
+  TestPositionType inline_text_position = AXNodePosition::CreateTextPosition(
+      GetTreeID(), inline_box_6.id, 5 /* text_offset */,
+      ax::mojom::TextAffinity::kDownstream);
+  ASSERT_NE(nullptr, inline_text_position);
+
+  // TextPosition anchor_role=search_box_2 text_offset=5 annotated_text=hello<>
+  TestPositionType search_box_position = AXNodePosition::CreateTextPosition(
+      GetTreeID(), search_box_2.id, 5 /* text_offset */,
+      ax::mojom::TextAffinity::kDownstream);
+  ASSERT_NE(nullptr, search_box_position);
+
+  EXPECT_EQ(*search_box_position, *inline_text_position);
+  EXPECT_EQ(*inline_text_position, *search_box_position);
+
+  // TextPosition anchor_role=static_text_8 text_offset=0 annotated_text=<X>
+  TestPositionType static_text_position = AXNodePosition::CreateTextPosition(
+      GetTreeID(), static_text_8.id, 0 /* text_offset */,
+      ax::mojom::TextAffinity::kDownstream);
+  ASSERT_NE(nullptr, static_text_position);
+
+  // TextPosition anchor_role=button_7 text_offset=0 annotated_text=<X>
+  TestPositionType button_position = AXNodePosition::CreateTextPosition(
+      GetTreeID(), button_7.id, 0 /* text_offset */,
+      ax::mojom::TextAffinity::kDownstream);
+  ASSERT_NE(nullptr, button_position);
+
+  EXPECT_EQ(*button_position, *static_text_position);
+  EXPECT_EQ(*static_text_position, *button_position);
+}
+
 TEST_F(AXPositionTest, OperatorsTreePositionsAroundEmbeddedCharacter) {
   g_ax_embedded_object_behavior = AXEmbeddedObjectBehavior::kExposeCharacter;
 
@@ -10645,7 +10807,6 @@
   // ++++13 kStaticText
   // ++++14 kButton
   // ++++++15 kGenericContainer ignored
-  // ++++16 kSplitter
   AXNodeData root_1;
   AXNodeData static_text_2;
   AXNodeData inline_box_3;
@@ -10661,7 +10822,6 @@
   AXNodeData static_text_13;
   AXNodeData button_14;
   AXNodeData generic_container_15;
-  AXNodeData splitter_16;
 
   root_1.id = 1;
   static_text_2.id = 2;
@@ -10678,14 +10838,12 @@
   static_text_13.id = 13;
   button_14.id = 14;
   generic_container_15.id = 15;
-  splitter_16.id = 16;
 
   root_1.role = ax::mojom::Role::kRootWebArea;
   root_1.child_ids = {static_text_2.id,        text_field_4.id,
                       static_text_6.id,        heading_8.id,
                       generic_container_11.id, generic_container_12.id,
-                      static_text_13.id,       button_14.id,
-                      splitter_16.id};
+                      static_text_13.id,       button_14.id};
 
   static_text_2.role = ax::mojom::Role::kStaticText;
   static_text_2.SetName("Hello ");
@@ -10744,15 +10902,11 @@
   generic_container_15.role = ax::mojom::Role::kGenericContainer;
   generic_container_15.AddState(ax::mojom::State::kIgnored);
 
-  splitter_16.role = ax::mojom::Role::kSplitter;
-  splitter_16.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
-                               true);
-
-  SetTree(CreateAXTree(
-      {root_1, static_text_2, inline_box_3, text_field_4, generic_container_5,
-       static_text_6, inline_box_7, heading_8, static_text_9, inline_box_10,
-       generic_container_11, generic_container_12, static_text_13, button_14,
-       generic_container_15, splitter_16}));
+  SetTree(CreateAXTree({root_1, static_text_2, inline_box_3, text_field_4,
+                        generic_container_5, static_text_6, inline_box_7,
+                        heading_8, static_text_9, inline_box_10,
+                        generic_container_11, generic_container_12,
+                        static_text_13, button_14, generic_container_15}));
 
   // CreateNextWordStartPosition tests.
   TestPositionType position = AXNodePosition::CreateTextPosition(
@@ -10868,10 +11022,10 @@
       base::StrCat({STRING16_LITERAL("Hello "), AXNode::kEmbeddedCharacter,
                     STRING16_LITERAL(" world"), AXNode::kEmbeddedCharacter,
                     AXNode::kEmbeddedCharacter, STRING16_LITERAL("hey"),
-                    AXNode::kEmbeddedCharacter, AXNode::kEmbeddedCharacter});
+                    AXNode::kEmbeddedCharacter});
   EXPECT_EQ(expected_text, position->GetText());
 
-  // A position on an empty object that has been replaced by an "object
+  // A position on an empty object that has been replaced by an "embedded object
   // replacement character".
   position = AXNodePosition::CreateTextPosition(
       GetTreeID(), text_field_4.id, 0 /* text_offset */,
@@ -10880,18 +11034,18 @@
       << *position;
 
   position = position->CreateParentPosition();
-  // Hello <embedded> world<embedded><embedded>hey<embedded><embedded>
-  EXPECT_EQ(20, position->MaxTextOffset()) << *position;
+  // Hello <embedded> world<embedded><embedded>hey<embedded>
+  EXPECT_EQ(19, position->MaxTextOffset()) << *position;
 
   // `AXPosition::MaxTextOffset()` on a node which is the parent of a set of
   // text nodes and non-text nodes, the latter represented by "embedded object
   // replacement characters".
   //
-  // Hello <embedded> world<embedded><embedded>hey<embedded><embedded>
+  // Hello <embedded> world<embedded><embedded>hey<embedded>
   position = AXNodePosition::CreateTextPosition(
       GetTreeID(), root_1.id, 0 /* text_offset */,
       ax::mojom::TextAffinity::kDownstream);
-  EXPECT_EQ(20, position->MaxTextOffset()) << *position;
+  EXPECT_EQ(19, position->MaxTextOffset()) << *position;
 
   // The following is to test a specific edge case with heading navigation,
   // occurring in `AXPosition::CreatePreviousFormatStartPosition`.
@@ -10934,23 +11088,6 @@
   EXPECT_EQ(button_14.id, text_position->anchor_id());
   EXPECT_EQ(1, text_position->text_offset());
   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, text_position->affinity());
-
-  // The following is to test that an element with the kSplitter role is not
-  // exposed to the accessibility tree's text representation, e.g. UIA's text
-  // pattern.
-  text_position = AXNodePosition::CreateTextPosition(
-      GetTreeID(), button_14.id, 1 /* text_offset */,
-      ax::mojom::TextAffinity::kDownstream);
-  ASSERT_NE(nullptr, text_position);
-
-  // The |text_position| shouldn't change.
-  text_position = text_position->CreateNextParagraphStartPosition(
-      AXBoundaryBehavior::StopAtLastAnchorBoundary);
-  ASSERT_NE(nullptr, text_position);
-  EXPECT_TRUE(text_position->IsLeafTextPosition());
-  EXPECT_EQ(button_14.id, text_position->anchor_id());
-  EXPECT_EQ(1, text_position->text_offset());
-  EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, text_position->affinity());
 }
 
 TEST_F(AXPositionTest, EmptyObjectReplacedByCharacterEmbedObject) {
@@ -11206,148 +11343,6 @@
   }
 }
 
-TEST_F(AXPositionTest, TextPositionComparisonTextField) {
-  g_ax_embedded_object_behavior = AXEmbeddedObjectBehavior::kExposeCharacter;
-
-  // ++1 kRootWebArea
-  // ++++2 kTextField editable
-  // ++++++3 kGenericContainer editable
-  // ++++++++4 kStaticText editable "Hello"
-  // ++++++++++5 kInlineTextBox "Hello"
-  AXNodeData root_1;
-  AXNodeData text_field_2;
-  AXNodeData generic_container_3;
-  AXNodeData static_text_4;
-  AXNodeData inline_box_5;
-
-  root_1.id = 1;
-  text_field_2.id = 2;
-  generic_container_3.id = 3;
-  static_text_4.id = 4;
-  inline_box_5.id = 5;
-
-  root_1.role = ax::mojom::Role::kRootWebArea;
-  root_1.child_ids = {text_field_2.id};
-
-  text_field_2.role = ax::mojom::Role::kTextField;
-  text_field_2.AddState(ax::mojom::State::kEditable);
-  text_field_2.AddBoolAttribute(ax::mojom::BoolAttribute::kEditableRoot, true);
-  text_field_2.child_ids = {generic_container_3.id};
-
-  generic_container_3.role = ax::mojom::Role::kGenericContainer;
-  generic_container_3.AddState(ax::mojom::State::kEditable);
-  generic_container_3.child_ids = {static_text_4.id};
-
-  static_text_4.role = ax::mojom::Role::kStaticText;
-  static_text_4.SetName("Hello");
-  static_text_4.child_ids = {inline_box_5.id};
-
-  inline_box_5.role = ax::mojom::Role::kInlineTextBox;
-  inline_box_5.SetName("Hello");
-
-  SetTree(CreateAXTree({root_1, text_field_2, generic_container_3,
-                        static_text_4, inline_box_5}));
-
-  // TextPosition anchor_id=5 anchor_role=inlineTextBox text_offset=4
-  // annotated_text=hell<o>
-  TestPositionType inline_text_position = AXNodePosition::CreateTextPosition(
-      GetTreeID(), inline_box_5.id, 4, ax::mojom::TextAffinity::kDownstream);
-  ASSERT_NE(nullptr, inline_text_position);
-
-  // TextPosition anchor_id=2 anchor_role=textField text_offset=4
-  // annotated_text=hell<o>
-  TestPositionType text_field_position = AXNodePosition::CreateTextPosition(
-      GetTreeID(), text_field_2.id, 4, ax::mojom::TextAffinity::kDownstream);
-  ASSERT_NE(nullptr, text_field_position);
-
-  // Validate that two positions in the text field with the same text offsets
-  // but different anchors are logically equal.
-  EXPECT_EQ(*inline_text_position, *text_field_position);
-  EXPECT_EQ(*text_field_position, *inline_text_position);
-}
-
-TEST_F(AXPositionTest, TextPositionComparisonSearchBox) {
-  g_ax_embedded_object_behavior = AXEmbeddedObjectBehavior::kExposeCharacter;
-
-  // ++1 kRootWebArea
-  // ++++2 kSearchBox editable editableRoot=true
-  // ++++++3 kGenericContainer
-  // ++++++++4 kGenericContainer editable
-  // ++++++++++5 kStaticText editable "Hello"
-  // ++++++++++++6 kInlineTextBox "Hello"
-  // ++++7 kButton
-  // ++++++8 kStaticText "X"
-  // ++++++++9 kInlineTextBox "X"
-  AXNodeData root_1;
-  AXNodeData search_box_2;
-  AXNodeData generic_container_3;
-  AXNodeData generic_container_4;
-  AXNodeData static_text_5;
-  AXNodeData inline_box_6;
-  AXNodeData button_7;
-  AXNodeData static_text_8;
-  AXNodeData inline_box_9;
-
-  root_1.id = 1;
-  search_box_2.id = 2;
-  generic_container_3.id = 3;
-  generic_container_4.id = 4;
-  static_text_5.id = 5;
-  inline_box_6.id = 6;
-  button_7.id = 7;
-  static_text_8.id = 8;
-  inline_box_9.id = 9;
-
-  root_1.role = ax::mojom::Role::kRootWebArea;
-  root_1.child_ids = {search_box_2.id, button_7.id};
-
-  search_box_2.role = ax::mojom::Role::kSearchBox;
-  search_box_2.AddState(ax::mojom::State::kEditable);
-  search_box_2.AddBoolAttribute(ax::mojom::BoolAttribute::kEditableRoot, true);
-  search_box_2.child_ids = {generic_container_3.id};
-
-  generic_container_3.role = ax::mojom::Role::kGenericContainer;
-  generic_container_3.child_ids = {generic_container_4.id};
-
-  generic_container_4.role = ax::mojom::Role::kGenericContainer;
-  generic_container_4.AddState(ax::mojom::State::kEditable);
-  generic_container_4.child_ids = {static_text_5.id};
-
-  static_text_5.role = ax::mojom::Role::kStaticText;
-  static_text_5.SetName("Hello");
-  static_text_5.child_ids = {inline_box_6.id};
-
-  inline_box_6.role = ax::mojom::Role::kInlineTextBox;
-  inline_box_6.SetName("Hello");
-
-  button_7.role = ax::mojom::Role::kButton;
-  button_7.child_ids = {static_text_8.id};
-
-  static_text_8.role = ax::mojom::Role::kStaticText;
-  static_text_8.SetName("X");
-  static_text_8.child_ids = {inline_box_9.id};
-
-  inline_box_9.role = ax::mojom::Role::kInlineTextBox;
-  inline_box_9.SetName("X");
-
-  SetTree(CreateAXTree({root_1, search_box_2, generic_container_3,
-                        generic_container_4, static_text_5, inline_box_6,
-                        button_7, static_text_8, inline_box_9}));
-
-  // TextPosition anchor_role=inlineTextBox text_offset=5 annotated_text=hello<>
-  TestPositionType inline_text_position = AXNodePosition::CreateTextPosition(
-      GetTreeID(), inline_box_6.id, 5, ax::mojom::TextAffinity::kDownstream);
-  ASSERT_NE(nullptr, inline_text_position);
-
-  // TextPosition anchor_role=searchBox text_offset=5 annotated_text=hello<>
-  TestPositionType search_box_position = AXNodePosition::CreateTextPosition(
-      GetTreeID(), search_box_2.id, 5, ax::mojom::TextAffinity::kDownstream);
-  ASSERT_NE(nullptr, search_box_position);
-
-  EXPECT_EQ(*search_box_position, *inline_text_position);
-  EXPECT_EQ(*inline_text_position, *search_box_position);
-}
-
 //
 // Instantiations of parameterized tests.
 //
diff --git a/ui/accessibility/ax_position.h b/ui/accessibility/ax_position.h
index 9d3d8bab..bf79d986 100644
--- a/ui/accessibility/ax_position.h
+++ b/ui/accessibility/ax_position.h
@@ -351,7 +351,7 @@
     return GetNodeInTree(tree_id_, anchor_id_);
   }
 
-  virtual int GetAnchorSiblingCount() const {
+  int GetAnchorSiblingCount() const {
     if (IsNullPosition())
       return 0;
 
@@ -375,10 +375,7 @@
     DCHECK(GetAnchor());
     // If this position is anchored to an ignored node, then consider this
     // position to be ignored.
-    //
-    // TODO(nektar): Ensure that all methods skip over empty text nodes and
-    // remove the IsIgnoredForTextNavigation call.
-    if (GetAnchor()->IsIgnored() || GetAnchor()->IsIgnoredForTextNavigation())
+    if (GetAnchor()->IsIgnored())
       return true;
 
     switch (kind_) {
@@ -3733,7 +3730,6 @@
     // unignored child, making this a leaf tree or text position, or a leaf's
     // descendant.
     return !GetAnchor()->IsIgnored() &&
-           !GetAnchor()->IsIgnoredForTextNavigation() &&
            !IsPlatformDocument(GetAnchorRole()) && !IsInTextObject() &&
            !IsIframe(GetAnchorRole());
   }
@@ -3809,10 +3805,10 @@
 
   // Abstract methods.
 
-  // Returns the text that is present inside the anchor node, including any text
-  // found in descendant text nodes, based on the platform's text
-  // representation. Some platforms use an embedded object replacement character
-  // that replaces the text coming from each child node.
+  // Returns the text (in UTF16 format) that is present inside the anchor node,
+  // including any text found in descendant text nodes, based on the platform's
+  // text representation. Some platforms use an embedded object replacement
+  // character that replaces the text coming from most child nodes.
   virtual base::string16 GetText() const = 0;
 
   // Determines if the anchor containing this position is a <br> or a text
@@ -3830,15 +3826,11 @@
   // Returns the length of the text that is present inside the anchor node,
   // including any text found in descendant text nodes. This is based on the
   // platform's text representation. Some platforms use an embedded object
-  // character that replaces the text coming from each child node.
+  // character that replaces the text coming from most child nodes.
   //
   // Similar to "text_offset_", the length of the text is in UTF16 code units,
   // not in grapheme clusters.
-  virtual int MaxTextOffset() const {
-    if (IsNullPosition())
-      return INVALID_OFFSET;
-    return int{GetText().length()};
-  }
+  virtual int MaxTextOffset() const = 0;
 
   // Returns the accessibility role of this position's anchor node. If this is a
   // "null position", returns `ax::mojom::Role::kNone`.
@@ -3998,8 +3990,9 @@
   virtual AXNodeTextStyles GetTextStyles() const = 0;
   virtual std::vector<int32_t> GetWordStartOffsets() const = 0;
   virtual std::vector<int32_t> GetWordEndOffsets() const = 0;
-  virtual int32_t GetNextOnLineID(int32_t node_id) const = 0;
-  virtual int32_t GetPreviousOnLineID(int32_t node_id) const = 0;
+  virtual AXNodeData::AXID GetNextOnLineID(AXNodeData::AXID node_id) const = 0;
+  virtual AXNodeData::AXID GetPreviousOnLineID(
+      AXNodeData::AXID node_id) const = 0;
 
  private:
   // Defines the relationship between positions during traversal.
diff --git a/ui/accessibility/ax_range_unittest.cc b/ui/accessibility/ax_range_unittest.cc
index 73c897c..48ac880e 100644
--- a/ui/accessibility/ax_range_unittest.cc
+++ b/ui/accessibility/ax_range_unittest.cc
@@ -170,8 +170,9 @@
 
   button_.role = ax::mojom::Role::kButton;
   button_.SetHasPopup(ax::mojom::HasPopup::kMenu);
-  button_.SetName(BUTTON);
+  button_.SetName("Button");
   button_.SetNameFrom(ax::mojom::NameFrom::kValue);
+  button_.SetValue("Button");
   button_.relative_bounds.bounds = gfx::RectF(20, 20, 100, 30);
   button_.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
                           check_box1_.id);
diff --git a/ui/accessibility/platform/ax_platform_node_base.cc b/ui/accessibility/platform/ax_platform_node_base.cc
index 60511e02..0dae24d 100644
--- a/ui/accessibility/platform/ax_platform_node_base.cc
+++ b/ui/accessibility/platform/ax_platform_node_base.cc
@@ -1328,6 +1328,7 @@
 AXHypertext::AXHypertext(const AXHypertext& other) = default;
 AXHypertext& AXHypertext::operator=(const AXHypertext& other) = default;
 
+// TODO(nektar): To be able to use AXNode in Views, move this logic to AXNode.
 void AXPlatformNodeBase::UpdateComputedHypertext() const {
   if (!delegate_)
     return;
@@ -1340,14 +1341,14 @@
   }
 
   // Construct the hypertext for this node, which contains the concatenation
-  // of all of the static text and widespace of this node's children and an
+  // of all of the static text and whitespace from this node's children, and an
   // embedded object character for all the other children. Build up a map from
   // the character index of each embedded object character to the id of the
   // child object it points to.
   base::string16 hypertext;
   for (AXPlatformNodeChildIterator child_iter = AXPlatformNodeChildrenBegin();
        child_iter != AXPlatformNodeChildrenEnd(); ++child_iter) {
-    // Similar to Firefox, we don't expose text-only objects in IA2 and ATK
+    // Similar to Firefox, we don't expose text nodes in IAccessible2 and ATK
     // hypertext with the embedded object character. We copy all of their text
     // instead.
     if (child_iter->IsText()) {
diff --git a/ui/accessibility/platform/ax_platform_node_base.h b/ui/accessibility/platform/ax_platform_node_base.h
index 69e20d56..f8e7dccd 100644
--- a/ui/accessibility/platform/ax_platform_node_base.h
+++ b/ui/accessibility/platform/ax_platform_node_base.h
@@ -28,6 +28,8 @@
 
 struct AXNodeData;
 
+// TODO(nektar): Move this struct over to AXNode so that it can be accessed by
+// AXPosition.
 struct AX_EXPORT AXHypertext {
   AXHypertext();
   ~AXHypertext();
@@ -274,14 +276,14 @@
   bool HasFocus();
 
   // If this node is a leaf, returns the visible accessible name of this node.
-  // Otherwise represents every non-leaf child node with a special "embedded
-  // object character", and every leaf child node with its visible accessible
+  // Otherwise represents every non-textual child node with a special "embedded
+  // object character", and every textual child node with its visible accessible
   // name. This is how displayed text and embedded objects are represented in
   // ATK and IA2 APIs.
   base::string16 GetHypertext() const;
 
-  // Returns the text of this node and all descendant nodes; including text
-  // found in embedded objects.
+  // Returns the text that is found inside this node and all its descendants;
+  // including text found in embedded objects.
   //
   // Only text displayed on screen is included. Text from ARIA and HTML
   // attributes that is either not displayed on screen, or outside this node,
diff --git a/ui/accessibility/platform/ax_platform_node_textprovider_win.h b/ui/accessibility/platform/ax_platform_node_textprovider_win.h
index 92af711..d216815b 100644
--- a/ui/accessibility/platform/ax_platform_node_textprovider_win.h
+++ b/ui/accessibility/platform/ax_platform_node_textprovider_win.h
@@ -12,6 +12,7 @@
 #include "ui/accessibility/platform/ax_platform_node_win.h"
 
 namespace ui {
+
 class __declspec(uuid("3e1c192b-4348-45ac-8eb6-4b58eeb3dcca"))
     AXPlatformNodeTextProviderWin
     : public CComObjectRootEx<CComMultiThreadModel>,
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
index a2ca025..b5d734e 100644
--- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
@@ -1197,21 +1197,30 @@
   AXPositionInstance current_endpoint = endpoint->AsLeafTextPosition();
 
   for (int iteration = 0; iteration < std::abs(count); ++iteration) {
-    AXPositionInstance next_endpoint = GetNextTextBoundaryPosition(
-        current_endpoint, boundary_type,
-        AXBoundaryBehavior::StopAtLastAnchorBoundary, boundary_direction);
-    DCHECK(next_endpoint->IsLeafTextPosition());
+    do {
+      AXPositionInstance next_endpoint = GetNextTextBoundaryPosition(
+          current_endpoint, boundary_type,
+          AXBoundaryBehavior::StopAtLastAnchorBoundary, boundary_direction);
+      DCHECK(next_endpoint->IsLeafTextPosition());
 
-    // Since AXBoundaryBehavior::StopAtLastAnchorBoundary forces the next text
-    // boundary position to be different than the input position, the only case
-    // where these are equal is when they're already located at the last anchor
-    // boundary. In such case, there is no next position to move to.
-    if (next_endpoint->GetAnchor() == current_endpoint->GetAnchor() &&
-        *next_endpoint == *current_endpoint) {
-      *units_moved = (count > 0) ? iteration : -iteration;
-      return current_endpoint;
-    }
-    current_endpoint = std::move(next_endpoint);
+      // Since AXBoundaryBehavior::StopAtLastAnchorBoundary forces the next text
+      // boundary position to be different than the input position, the only
+      // case where these are equal is when they're already located at the last
+      // anchor boundary. In such case, there is no next position to move to.
+      if (next_endpoint->GetAnchor() == current_endpoint->GetAnchor() &&
+          *next_endpoint == *current_endpoint) {
+        *units_moved = (count > 0) ? iteration : -iteration;
+        return current_endpoint;
+      }
+      current_endpoint = std::move(next_endpoint);
+      // Loop until we're not on a position that is ignored for text navigation.
+      // There is one exception for character navigation - since the ignored
+      // anchor is represented by an embedded object character, we allow
+      // navigation by character for consistency (i.e. you should be able to
+      // move by character the same number of characters that are represented by
+      // the ranges flat string buffer).
+    } while (boundary_type != ax::mojom::TextBoundary::kCharacter &&
+             current_endpoint->GetAnchor()->IsIgnoredForTextNavigation());
   }
 
   *units_moved = count;
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
index 3db246b..465553e 100644
--- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
@@ -1602,6 +1602,73 @@
 }
 
 TEST_F(AXPlatformNodeTextRangeProviderTest,
+       TestITextRangeProviderIgnoredForTextNavigation) {
+  // ++1 kRootWebArea
+  // ++++2 kStaticText
+  // ++++++3 kInlineTextBox foo
+  // ++++4 kSplitter
+  // ++++5 kStaticText
+  // ++++++6 kInlineTextBox bar
+  ui::AXNodeData root_1;
+  ui::AXNodeData static_text_2;
+  ui::AXNodeData inline_box_3;
+  ui::AXNodeData splitter_4;
+  ui::AXNodeData static_text_5;
+  ui::AXNodeData inline_box_6;
+
+  root_1.id = 1;
+  static_text_2.id = 2;
+  inline_box_3.id = 3;
+  splitter_4.id = 4;
+  static_text_5.id = 5;
+  inline_box_6.id = 6;
+
+  root_1.role = ax::mojom::Role::kRootWebArea;
+  root_1.child_ids = {static_text_2.id, splitter_4.id, static_text_5.id};
+
+  static_text_2.role = ax::mojom::Role::kStaticText;
+  static_text_2.child_ids = {inline_box_3.id};
+  static_text_2.SetName("foo");
+
+  inline_box_3.role = ax::mojom::Role::kInlineTextBox;
+  inline_box_3.SetName("foo");
+
+  splitter_4.role = ax::mojom::Role::kSplitter;
+  splitter_4.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
+                              true);
+
+  static_text_5.role = ax::mojom::Role::kStaticText;
+  static_text_5.child_ids = {inline_box_6.id};
+  static_text_5.SetName("bar");
+
+  inline_box_6.role = ax::mojom::Role::kInlineTextBox;
+  inline_box_6.SetName("bar");
+
+  ui::AXTreeUpdate update;
+  ui::AXTreeData tree_data;
+  tree_data.tree_id = ui::AXTreeID::CreateNewAXTreeID();
+  update.tree_data = tree_data;
+  update.has_tree_data = true;
+  update.root_id = root_1.id;
+  update.nodes = {root_1,     static_text_2, inline_box_3,
+                  splitter_4, static_text_5, inline_box_6};
+
+  Init(update);
+
+  AXNode* root_node = GetRootAsAXNode();
+  ComPtr<ITextRangeProvider> text_range_provider;
+  GetTextRangeProviderFromTextNode(text_range_provider, root_node);
+
+  EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"foo\n\xFFFC\nbar");
+
+  int count;
+  ASSERT_HRESULT_SUCCEEDED(text_range_provider->MoveEndpointByUnit(
+      TextPatternRangeEndpoint_Start, TextUnit_Paragraph, /*count*/ 1, &count));
+  ASSERT_EQ(1, count);
+  EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"bar");
+}
+
+TEST_F(AXPlatformNodeTextRangeProviderTest,
        TestITextRangeProviderInvalidCalls) {
   // Test for when a text range provider is invalid. Because no ax tree is
   // available, the anchor is invalid, so the text range provider fails the
diff --git a/ui/aura/env.cc b/ui/aura/env.cc
index 1aa181e..8f2a19b4 100644
--- a/ui/aura/env.cc
+++ b/ui/aura/env.cc
@@ -8,7 +8,6 @@
 #include "base/lazy_instance.h"
 #include "base/memory/ptr_util.h"
 #include "base/observer_list_types.h"
-#include "build/build_config.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/env_input_state_controller.h"
 #include "ui/aura/env_observer.h"
@@ -22,19 +21,11 @@
 #include "ui/events/gestures/gesture_recognizer_impl.h"
 #include "ui/events/platform/platform_event_source.h"
 
-#if defined(OS_WIN)
-#include "ui/base/cursor/win/win_cursor_factory.h"
-#endif
-
 #if defined(USE_OZONE)
 #include "ui/ozone/public/ozone_platform.h"
 #endif
 
 #if defined(USE_X11)
-#include "ui/base/x/x11_cursor_factory.h"
-#endif
-
-#if defined(OS_WIN) || defined(USE_X11)
 #include "ui/gfx/switches.h"
 #endif
 
@@ -219,17 +210,12 @@
     : env_controller_(std::make_unique<EnvInputStateController>(this)),
       gesture_recognizer_(std::make_unique<ui::GestureRecognizerImpl>()),
       input_state_lookup_(InputStateLookup::Create()) {
-#if defined(OS_WIN) || defined(USE_X11)
-  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kHeadless)) {
 #if defined(USE_X11)
-    // In Ozone/X11, the cursor factory is initialized by the platform
-    // initialization code.
-    if (!features::IsUsingOzonePlatform())
-      cursor_factory_ = std::make_unique<ui::X11CursorFactory>();
-#else
-    cursor_factory_ = std::make_unique<ui::WinCursorFactory>();
-#endif
-  }
+  // In Ozone/X11, the cursor factory is initialized by the platform
+  // initialization code.
+  if (!features::IsUsingOzonePlatform() &&
+      !base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kHeadless))
+    cursor_factory_ = std::make_unique<ui::X11CursorFactory>();
 #endif
 }
 
diff --git a/ui/aura/env.h b/ui/aura/env.h
index 2baf5a54..bacd5053 100644
--- a/ui/aura/env.h
+++ b/ui/aura/env.h
@@ -19,15 +19,15 @@
 #include "ui/events/types/event_type.h"
 #include "ui/gfx/geometry/point.h"
 
+#if defined(USE_X11)
+#include "ui/base/x/x11_cursor_factory.h"  // nogncheck
+#endif
+
 namespace ui {
 class ContextFactory;
 class EventObserver;
 class GestureRecognizer;
 class PlatformEventSource;
-
-#if defined(OS_WIN) || defined(USE_X11)
-class CursorFactory;
-#endif
 }  // namespace ui
 
 namespace aura {
@@ -182,8 +182,8 @@
 
   std::unique_ptr<ui::GestureRecognizer> gesture_recognizer_;
 
-#if defined(OS_WIN) || defined(USE_X11)
-  std::unique_ptr<ui::CursorFactory> cursor_factory_;
+#if defined(USE_X11)
+  std::unique_ptr<ui::X11CursorFactory> cursor_factory_;
 #endif
 
   std::unique_ptr<InputStateLookup> input_state_lookup_;
diff --git a/ui/base/cursor/BUILD.gn b/ui/base/cursor/BUILD.gn
index 9e6049e..947ff85 100644
--- a/ui/base/cursor/BUILD.gn
+++ b/ui/base/cursor/BUILD.gn
@@ -24,8 +24,12 @@
   ]
   deps = [ "//ui/gfx:geometry_skia" ]
 
-  if (use_aura) {
-    sources += [ "cursor_aura.cc" ]
+  if (is_win) {
+    sources += [ "cursor_win.cc" ]
+  }
+
+  if (use_x11 || use_ozone) {
+    sources += [ "cursor_ozone.cc" ]
   }
 }
 
@@ -73,10 +77,6 @@
       sources += [
         "cursor_loader_win.cc",
         "cursor_loader_win.h",
-        "win/win_cursor.cc",
-        "win/win_cursor.h",
-        "win/win_cursor_factory.cc",
-        "win/win_cursor_factory.h",
       ]
       deps += [ "//ui/resources:ui_unscaled_resources_grd" ]
     }
diff --git a/ui/base/cursor/cursor.h b/ui/base/cursor/cursor.h
index e5ffd67..1c54756 100644
--- a/ui/base/cursor/cursor.h
+++ b/ui/base/cursor/cursor.h
@@ -17,10 +17,13 @@
 
 namespace ui {
 
+#if defined(OS_WIN)
+typedef ::HCURSOR PlatformCursor;
+#else
 // NOTE: On Ozone platforms, the type is chosen at runtime, and is either
 // X11Cursor* or BitmapCursorOzone*.
-// On Windows, it's WinCursor*.
-using PlatformCursor = void*;
+typedef void* PlatformCursor;
+#endif
 
 // Ref-counted cursor that supports both default and custom cursors.
 class COMPONENT_EXPORT(UI_BASE_CURSOR_BASE) Cursor {
diff --git a/ui/base/cursor/cursor_factory.cc b/ui/base/cursor/cursor_factory.cc
index 65afeca..523aea8 100644
--- a/ui/base/cursor/cursor_factory.cc
+++ b/ui/base/cursor/cursor_factory.cc
@@ -174,7 +174,7 @@
       return {"dnd-link", "hand2"};
     case mojom::CursorType::kCustom:
       // kCustom is for custom image cursors. The platform cursor will be set
-      // at WebCursor::GetNativeCursor().
+      // at WebCursor::GetPlatformCursor().
       NOTREACHED();
       FALLTHROUGH;
     case mojom::CursorType::kNull:
diff --git a/ui/base/cursor/cursor_loader_ozone.cc b/ui/base/cursor/cursor_loader_ozone.cc
index 0aa0453..6d38667 100644
--- a/ui/base/cursor/cursor_loader_ozone.cc
+++ b/ui/base/cursor/cursor_loader_ozone.cc
@@ -43,7 +43,7 @@
 void CursorLoaderOzone::SetPlatformCursor(gfx::NativeCursor* cursor) {
   DCHECK(cursor);
 
-  // The platform cursor was already set via WebCursor::GetNativeCursor.
+  // The platform cursor was already set via WebCursor::GetPlatformCursor.
   if (cursor->type() == mojom::CursorType::kCustom)
     return;
   cursor->set_image_scale_factor(scale());
diff --git a/ui/base/cursor/cursor_loader_unittest.cc b/ui/base/cursor/cursor_loader_unittest.cc
index be3cd023..67b4c2b 100644
--- a/ui/base/cursor/cursor_loader_unittest.cc
+++ b/ui/base/cursor/cursor_loader_unittest.cc
@@ -4,17 +4,10 @@
 
 #include "ui/base/cursor/cursor_loader.h"
 
-#include "base/memory/scoped_refptr.h"
-#include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/cursor/cursor.h"
 #include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
 
-#if defined(OS_WIN)
-#include "ui/base/cursor/win/win_cursor.h"
-#include "ui/base/cursor/win/win_cursor_factory.h"
-#endif
-
 #if defined(USE_OZONE)
 #include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h"
 #endif
@@ -38,24 +31,17 @@
 
 }  // namespace
 
-#if defined(OS_WIN)
-TEST(CursorLoaderTest, InvisibleCursor) {
-  WinCursorFactory cursor_factory;
-  auto* invisible_cursor = static_cast<WinCursor*>(LoadInvisibleCursor());
-  ASSERT_NE(invisible_cursor, nullptr);
-  EXPECT_EQ(invisible_cursor->hcursor(), nullptr);
-}
-#endif
-
-#if defined(USE_OZONE) && !defined(USE_X11)
-TEST(CursorLoaderTest, InvisibleCursor) {
+#if !defined(USE_X11)
+TEST(CursorLoaderTest, InvisibleCursorOnNotX11) {
+#if defined(USE_OZONE)
   BitmapCursorFactoryOzone cursor_factory;
+#endif
   EXPECT_EQ(LoadInvisibleCursor(), nullptr);
 }
 #endif
 
 #if defined(USE_X11)
-TEST(CursorLoaderTest, InvisibleCursor) {
+TEST(CursorLoaderTest, InvisibleCursorOnX11) {
   X11CursorFactory cursor_factory;
   // Building an image cursor with an invalid SkBitmap should return the
   // invisible cursor in X11.
diff --git a/ui/base/cursor/cursor_loader_win.cc b/ui/base/cursor/cursor_loader_win.cc
index cece66e..c19fdb02 100644
--- a/ui/base/cursor/cursor_loader_win.cc
+++ b/ui/base/cursor/cursor_loader_win.cc
@@ -4,16 +4,126 @@
 
 #include "ui/base/cursor/cursor_loader_win.h"
 
+#include <windows.h>
+
 #include <memory>
 
-#include "base/logging.h"
-#include "base/optional.h"
+#include "base/lazy_instance.h"
+#include "base/notreached.h"
 #include "ui/base/cursor/cursor.h"
-#include "ui/base/cursor/cursor_factory.h"
 #include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
+#include "ui/resources/grit/ui_unscaled_resources.h"
 
 namespace ui {
 
+namespace {
+
+base::LazyInstance<std::wstring>::DestructorAtExit
+    g_cursor_resource_module_name;
+
+const wchar_t* GetCursorId(gfx::NativeCursor native_cursor) {
+  switch (native_cursor.type()) {
+    case mojom::CursorType::kNull:
+      return IDC_ARROW;
+    case mojom::CursorType::kPointer:
+      return IDC_ARROW;
+    case mojom::CursorType::kCross:
+      return IDC_CROSS;
+    case mojom::CursorType::kHand:
+      return IDC_HAND;
+    case mojom::CursorType::kIBeam:
+      return IDC_IBEAM;
+    case mojom::CursorType::kWait:
+      return IDC_WAIT;
+    case mojom::CursorType::kHelp:
+      return IDC_HELP;
+    case mojom::CursorType::kEastResize:
+      return IDC_SIZEWE;
+    case mojom::CursorType::kNorthResize:
+      return IDC_SIZENS;
+    case mojom::CursorType::kNorthEastResize:
+      return IDC_SIZENESW;
+    case mojom::CursorType::kNorthWestResize:
+      return IDC_SIZENWSE;
+    case mojom::CursorType::kSouthResize:
+      return IDC_SIZENS;
+    case mojom::CursorType::kSouthEastResize:
+      return IDC_SIZENWSE;
+    case mojom::CursorType::kSouthWestResize:
+      return IDC_SIZENESW;
+    case mojom::CursorType::kWestResize:
+      return IDC_SIZEWE;
+    case mojom::CursorType::kNorthSouthResize:
+      return IDC_SIZENS;
+    case mojom::CursorType::kEastWestResize:
+      return IDC_SIZEWE;
+    case mojom::CursorType::kNorthEastSouthWestResize:
+      return IDC_SIZENESW;
+    case mojom::CursorType::kNorthWestSouthEastResize:
+      return IDC_SIZENWSE;
+    case mojom::CursorType::kMove:
+      return IDC_SIZEALL;
+    case mojom::CursorType::kProgress:
+      return IDC_APPSTARTING;
+    case mojom::CursorType::kNoDrop:
+      return IDC_NO;
+    case mojom::CursorType::kNotAllowed:
+      return IDC_NO;
+    case mojom::CursorType::kColumnResize:
+      return MAKEINTRESOURCE(IDC_COLRESIZE);
+    case mojom::CursorType::kRowResize:
+      return MAKEINTRESOURCE(IDC_ROWRESIZE);
+    case mojom::CursorType::kMiddlePanning:
+      return MAKEINTRESOURCE(IDC_PAN_MIDDLE);
+    case mojom::CursorType::kMiddlePanningVertical:
+      return MAKEINTRESOURCE(IDC_PAN_MIDDLE_VERTICAL);
+    case mojom::CursorType::kMiddlePanningHorizontal:
+      return MAKEINTRESOURCE(IDC_PAN_MIDDLE_HORIZONTAL);
+    case mojom::CursorType::kEastPanning:
+      return MAKEINTRESOURCE(IDC_PAN_EAST);
+    case mojom::CursorType::kNorthPanning:
+      return MAKEINTRESOURCE(IDC_PAN_NORTH);
+    case mojom::CursorType::kNorthEastPanning:
+      return MAKEINTRESOURCE(IDC_PAN_NORTH_EAST);
+    case mojom::CursorType::kNorthWestPanning:
+      return MAKEINTRESOURCE(IDC_PAN_NORTH_WEST);
+    case mojom::CursorType::kSouthPanning:
+      return MAKEINTRESOURCE(IDC_PAN_SOUTH);
+    case mojom::CursorType::kSouthEastPanning:
+      return MAKEINTRESOURCE(IDC_PAN_SOUTH_EAST);
+    case mojom::CursorType::kSouthWestPanning:
+      return MAKEINTRESOURCE(IDC_PAN_SOUTH_WEST);
+    case mojom::CursorType::kWestPanning:
+      return MAKEINTRESOURCE(IDC_PAN_WEST);
+    case mojom::CursorType::kVerticalText:
+      return MAKEINTRESOURCE(IDC_VERTICALTEXT);
+    case mojom::CursorType::kCell:
+      return MAKEINTRESOURCE(IDC_CELL);
+    case mojom::CursorType::kZoomIn:
+      return MAKEINTRESOURCE(IDC_ZOOMIN);
+    case mojom::CursorType::kZoomOut:
+      return MAKEINTRESOURCE(IDC_ZOOMOUT);
+    case mojom::CursorType::kGrab:
+      return MAKEINTRESOURCE(IDC_HAND_GRAB);
+    case mojom::CursorType::kGrabbing:
+      return MAKEINTRESOURCE(IDC_HAND_GRABBING);
+    case mojom::CursorType::kCopy:
+      return MAKEINTRESOURCE(IDC_COPYCUR);
+    case mojom::CursorType::kAlias:
+      return MAKEINTRESOURCE(IDC_ALIAS);
+    case mojom::CursorType::kContextMenu:
+    case mojom::CursorType::kCustom:
+    case mojom::CursorType::kNone:
+      NOTIMPLEMENTED();
+      return IDC_ARROW;
+    default:
+      NOTREACHED();
+      return IDC_ARROW;
+  }
+}
+
+}  // namespace
+
 std::unique_ptr<CursorLoader> CursorLoader::Create(bool use_platform_cursors) {
   return std::make_unique<CursorLoaderWin>();
 }
@@ -32,15 +142,31 @@
   if (cursor->type() == mojom::CursorType::kCustom)
     return;
 
+  // Using a dark 1x1 bit bmp kNone cursor may still cause DWM to do composition
+  // work unnecessarily. Better to totally remove it from the screen.
+  // crbug.com/1069698
+  if (cursor->type() == mojom::CursorType::kNone) {
+    cursor->SetPlatformCursor(nullptr);
+    return;
+  }
+
   if (cursor->platform()) {
     cursor->SetPlatformCursor(cursor->platform());
   } else {
-    base::Optional<PlatformCursor> default_cursor =
-        CursorFactory::GetInstance()->GetDefaultCursor(cursor->type());
-    LOG_IF(ERROR, !default_cursor)
-        << "Failed to load cursor " << cursor->type();
-    cursor->SetPlatformCursor(default_cursor.value_or(nullptr));
+    const wchar_t* cursor_id = GetCursorId(*cursor);
+    PlatformCursor platform_cursor = LoadCursor(nullptr, cursor_id);
+    if (!platform_cursor && !g_cursor_resource_module_name.Get().empty()) {
+      platform_cursor = LoadCursor(
+          GetModuleHandle(g_cursor_resource_module_name.Get().c_str()),
+          cursor_id);
+    }
+    cursor->SetPlatformCursor(platform_cursor);
   }
 }
 
+// static
+void CursorLoaderWin::SetCursorResourceModule(const std::wstring& module_name) {
+  g_cursor_resource_module_name.Get() = module_name;
+}
+
 }  // namespace ui
diff --git a/ui/base/cursor/cursor_aura.cc b/ui/base/cursor/cursor_ozone.cc
similarity index 100%
rename from ui/base/cursor/cursor_aura.cc
rename to ui/base/cursor/cursor_ozone.cc
diff --git a/ui/base/cursor/cursor_win.cc b/ui/base/cursor/cursor_win.cc
new file mode 100644
index 0000000..2d4aafea
--- /dev/null
+++ b/ui/base/cursor/cursor_win.cc
@@ -0,0 +1,17 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/base/cursor/cursor.h"
+
+namespace ui {
+
+void Cursor::RefCustomCursor() {
+  // TODO(winguru):
+}
+
+void Cursor::UnrefCustomCursor() {
+  // TODO(winguru):
+}
+
+}  // namespace ui
diff --git a/ui/base/cursor/cursors_aura.cc b/ui/base/cursor/cursors_aura.cc
index 99e90cf..c527ac4d 100644
--- a/ui/base/cursor/cursors_aura.cc
+++ b/ui/base/cursor/cursors_aura.cc
@@ -19,7 +19,6 @@
 
 #if defined(OS_WIN)
 #include "ui/base/cursor/cursor_loader_win.h"
-#include "ui/base/cursor/win/win_cursor.h"
 #include "ui/gfx/icon_util.h"
 #endif
 
@@ -300,8 +299,7 @@
   Cursor cursor_copy = cursor;
   ui::CursorLoaderWin cursor_loader;
   cursor_loader.SetPlatformCursor(&cursor_copy);
-  return IconUtil::CreateSkBitmapFromHICON(
-      static_cast<WinCursor*>(cursor_copy.platform())->hcursor());
+  return IconUtil::CreateSkBitmapFromHICON(cursor_copy.platform());
 #else
   int resource_id;
   gfx::Point hotspot;
@@ -320,8 +318,7 @@
   Cursor cursor_copy = cursor;
   ui::CursorLoaderWin cursor_loader;
   cursor_loader.SetPlatformCursor(&cursor_copy);
-  return IconUtil::GetHotSpotFromHICON(
-      static_cast<WinCursor*>(cursor_copy.platform())->hcursor());
+  return IconUtil::GetHotSpotFromHICON(cursor_copy.platform());
 #else
   int resource_id;
   gfx::Point hotspot;
diff --git a/ui/base/cursor/win/win_cursor.cc b/ui/base/cursor/win/win_cursor.cc
deleted file mode 100644
index afa707f..0000000
--- a/ui/base/cursor/win/win_cursor.cc
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/base/cursor/win/win_cursor.h"
-
-#include <windows.h>
-
-namespace ui {
-
-WinCursor::WinCursor(HCURSOR hcursor) {
-  hcursor_ = hcursor;
-}
-
-WinCursor::~WinCursor() {
-  DestroyIcon(hcursor_);
-}
-
-}  // namespace ui
diff --git a/ui/base/cursor/win/win_cursor.h b/ui/base/cursor/win/win_cursor.h
deleted file mode 100644
index e1f787f..0000000
--- a/ui/base/cursor/win/win_cursor.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_BASE_CURSOR_WIN_WIN_CURSOR_H_
-#define UI_BASE_CURSOR_WIN_WIN_CURSOR_H_
-
-#include "base/component_export.h"
-#include "base/memory/ref_counted.h"
-#include "base/win/windows_types.h"
-
-namespace ui {
-
-// Ref counted class to hold a Windows cursor, i.e. an HCURSOR.  Clears the
-// resources on destruction.
-class COMPONENT_EXPORT(UI_BASE_CURSOR) WinCursor
-    : public base::RefCounted<WinCursor> {
- public:
-  explicit WinCursor(HCURSOR hcursor = nullptr);
-  WinCursor(const WinCursor&) = delete;
-  WinCursor& operator=(const WinCursor&) = delete;
-
-  HCURSOR hcursor() const { return hcursor_; }
-
- private:
-  friend class base::RefCounted<WinCursor>;
-
-  ~WinCursor();
-
-  HCURSOR hcursor_;
-};
-
-}  // namespace ui
-
-#endif  // UI_BASE_CURSOR_WIN_WIN_CURSOR_H_
diff --git a/ui/base/cursor/win/win_cursor_factory.cc b/ui/base/cursor/win/win_cursor_factory.cc
deleted file mode 100644
index 4d22caa..0000000
--- a/ui/base/cursor/win/win_cursor_factory.cc
+++ /dev/null
@@ -1,183 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/base/cursor/win/win_cursor_factory.h"
-
-#include <windows.h>
-
-#include <string>
-
-#include "base/lazy_instance.h"
-#include "base/memory/scoped_refptr.h"
-#include "base/notreached.h"
-#include "base/optional.h"
-#include "base/win/scoped_gdi_object.h"
-#include "base/win/windows_types.h"
-#include "ui/base/cursor/cursor.h"
-#include "ui/base/cursor/mojom/cursor_type.mojom.h"
-#include "ui/gfx/icon_util.h"
-#include "ui/resources/grit/ui_unscaled_resources.h"
-
-namespace ui {
-namespace {
-
-base::LazyInstance<std::wstring>::DestructorAtExit
-    g_cursor_resource_module_name;
-
-WinCursor* ToWinCursor(PlatformCursor cursor) {
-  return static_cast<WinCursor*>(cursor);
-}
-
-PlatformCursor ToPlatformCursor(WinCursor* cursor) {
-  return static_cast<PlatformCursor>(cursor);
-}
-
-const wchar_t* GetCursorId(mojom::CursorType type) {
-  switch (type) {
-    case mojom::CursorType::kNull:
-    case mojom::CursorType::kPointer:
-      return IDC_ARROW;
-    case mojom::CursorType::kCross:
-      return IDC_CROSS;
-    case mojom::CursorType::kHand:
-      return IDC_HAND;
-    case mojom::CursorType::kIBeam:
-      return IDC_IBEAM;
-    case mojom::CursorType::kWait:
-      return IDC_WAIT;
-    case mojom::CursorType::kHelp:
-      return IDC_HELP;
-    case mojom::CursorType::kEastResize:
-    case mojom::CursorType::kWestResize:
-    case mojom::CursorType::kEastWestResize:
-      return IDC_SIZEWE;
-    case mojom::CursorType::kNorthResize:
-    case mojom::CursorType::kSouthResize:
-    case mojom::CursorType::kNorthSouthResize:
-      return IDC_SIZENS;
-    case mojom::CursorType::kNorthEastResize:
-    case mojom::CursorType::kSouthWestResize:
-    case mojom::CursorType::kNorthEastSouthWestResize:
-      return IDC_SIZENESW;
-    case mojom::CursorType::kNorthWestResize:
-    case mojom::CursorType::kSouthEastResize:
-    case mojom::CursorType::kNorthWestSouthEastResize:
-      return IDC_SIZENWSE;
-    case mojom::CursorType::kMove:
-      return IDC_SIZEALL;
-    case mojom::CursorType::kProgress:
-      return IDC_APPSTARTING;
-    case mojom::CursorType::kNoDrop:
-    case mojom::CursorType::kNotAllowed:
-      return IDC_NO;
-    case mojom::CursorType::kColumnResize:
-      return MAKEINTRESOURCE(IDC_COLRESIZE);
-    case mojom::CursorType::kRowResize:
-      return MAKEINTRESOURCE(IDC_ROWRESIZE);
-    case mojom::CursorType::kMiddlePanning:
-      return MAKEINTRESOURCE(IDC_PAN_MIDDLE);
-    case mojom::CursorType::kMiddlePanningVertical:
-      return MAKEINTRESOURCE(IDC_PAN_MIDDLE_VERTICAL);
-    case mojom::CursorType::kMiddlePanningHorizontal:
-      return MAKEINTRESOURCE(IDC_PAN_MIDDLE_HORIZONTAL);
-    case mojom::CursorType::kEastPanning:
-      return MAKEINTRESOURCE(IDC_PAN_EAST);
-    case mojom::CursorType::kNorthPanning:
-      return MAKEINTRESOURCE(IDC_PAN_NORTH);
-    case mojom::CursorType::kNorthEastPanning:
-      return MAKEINTRESOURCE(IDC_PAN_NORTH_EAST);
-    case mojom::CursorType::kNorthWestPanning:
-      return MAKEINTRESOURCE(IDC_PAN_NORTH_WEST);
-    case mojom::CursorType::kSouthPanning:
-      return MAKEINTRESOURCE(IDC_PAN_SOUTH);
-    case mojom::CursorType::kSouthEastPanning:
-      return MAKEINTRESOURCE(IDC_PAN_SOUTH_EAST);
-    case mojom::CursorType::kSouthWestPanning:
-      return MAKEINTRESOURCE(IDC_PAN_SOUTH_WEST);
-    case mojom::CursorType::kWestPanning:
-      return MAKEINTRESOURCE(IDC_PAN_WEST);
-    case mojom::CursorType::kVerticalText:
-      return MAKEINTRESOURCE(IDC_VERTICALTEXT);
-    case mojom::CursorType::kCell:
-      return MAKEINTRESOURCE(IDC_CELL);
-    case mojom::CursorType::kZoomIn:
-      return MAKEINTRESOURCE(IDC_ZOOMIN);
-    case mojom::CursorType::kZoomOut:
-      return MAKEINTRESOURCE(IDC_ZOOMOUT);
-    case mojom::CursorType::kGrab:
-      return MAKEINTRESOURCE(IDC_HAND_GRAB);
-    case mojom::CursorType::kGrabbing:
-      return MAKEINTRESOURCE(IDC_HAND_GRABBING);
-    case mojom::CursorType::kCopy:
-      return MAKEINTRESOURCE(IDC_COPYCUR);
-    case mojom::CursorType::kAlias:
-      return MAKEINTRESOURCE(IDC_ALIAS);
-    case mojom::CursorType::kDndCopy:
-    case mojom::CursorType::kDndLink:
-    case mojom::CursorType::kDndMove:
-    case mojom::CursorType::kDndNone:
-    case mojom::CursorType::kContextMenu:
-      NOTIMPLEMENTED();
-      return IDC_ARROW;
-    case mojom::CursorType::kNone:
-    case mojom::CursorType::kCustom:
-      NOTREACHED();
-      return IDC_ARROW;
-  }
-  NOTREACHED();
-  return IDC_ARROW;
-}
-
-}  // namespace
-
-WinCursorFactory::WinCursorFactory() = default;
-
-WinCursorFactory::~WinCursorFactory() = default;
-
-base::Optional<PlatformCursor> WinCursorFactory::GetDefaultCursor(
-    mojom::CursorType type) {
-  if (!default_cursors_.count(type)) {
-    // Using a dark 1x1 bit bmp for the kNone cursor may still cause DWM to do
-    // composition work unnecessarily. Better to totally remove it from the
-    // screen. crbug.com/1069698
-    HCURSOR hcursor = nullptr;
-    if (type != mojom::CursorType::kNone) {
-      const wchar_t* id = GetCursorId(type);
-      hcursor = LoadCursor(nullptr, id);
-      if (!hcursor && !g_cursor_resource_module_name.Get().empty()) {
-        hcursor = LoadCursor(
-            GetModuleHandle(g_cursor_resource_module_name.Get().c_str()), id);
-      }
-      if (!hcursor)
-        return base::nullopt;
-    }
-    default_cursors_[type] = base::MakeRefCounted<WinCursor>(hcursor);
-  }
-
-  auto cursor = default_cursors_[type];
-  return ToPlatformCursor(cursor.get());
-}
-
-PlatformCursor WinCursorFactory::CreateImageCursor(mojom::CursorType type,
-                                                   const SkBitmap& bitmap,
-                                                   const gfx::Point& hotspot) {
-  auto cursor = base::MakeRefCounted<WinCursor>(
-      IconUtil::CreateCursorFromSkBitmap(bitmap, hotspot).release());
-  cursor->AddRef();
-  return ToPlatformCursor(cursor.get());
-}
-
-void WinCursorFactory::RefImageCursor(PlatformCursor cursor) {
-  ToWinCursor(cursor)->AddRef();
-}
-
-void WinCursorFactory::UnrefImageCursor(PlatformCursor cursor) {
-  ToWinCursor(cursor)->Release();
-}
-
-void SetCursorResourceModule(const std::wstring& module_name) {
-  g_cursor_resource_module_name.Get() = module_name;
-}
-
-}  // namespace ui
diff --git a/ui/base/cursor/win/win_cursor_factory.h b/ui/base/cursor/win/win_cursor_factory.h
deleted file mode 100644
index 908d3a3e..0000000
--- a/ui/base/cursor/win/win_cursor_factory.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_BASE_CURSOR_WIN_WIN_CURSOR_FACTORY_H_
-#define UI_BASE_CURSOR_WIN_WIN_CURSOR_FACTORY_H_
-
-#include <map>
-
-#include "base/component_export.h"
-#include "base/memory/scoped_refptr.h"
-#include "ui/base/cursor/cursor_factory.h"
-#include "ui/base/cursor/mojom/cursor_type.mojom-forward.h"
-#include "ui/base/cursor/win/win_cursor.h"
-
-class SkBitmap;
-
-namespace gfx {
-class Point;
-}
-
-namespace ui {
-
-class COMPONENT_EXPORT(UI_BASE_CURSOR) WinCursorFactory : public CursorFactory {
- public:
-  WinCursorFactory();
-  WinCursorFactory(const WinCursorFactory&) = delete;
-  WinCursorFactory& operator=(const WinCursorFactory&) = delete;
-  ~WinCursorFactory() override;
-
-  // CursorFactory:
-  base::Optional<PlatformCursor> GetDefaultCursor(
-      mojom::CursorType type) override;
-  PlatformCursor CreateImageCursor(mojom::CursorType type,
-                                   const SkBitmap& bitmap,
-                                   const gfx::Point& hotspot) override;
-  void RefImageCursor(PlatformCursor cursor) override;
-  void UnrefImageCursor(PlatformCursor cursor) override;
-
- private:
-  std::map<mojom::CursorType, scoped_refptr<WinCursor>> default_cursors_;
-};
-
-// Used to pass the cursor resource module name to the factory. This is used to
-// load non system cursors.
-COMPONENT_EXPORT(UI_BASE_CURSOR)
-void SetCursorResourceModule(const std::wstring& module_name);
-
-}  // namespace ui
-
-#endif  // UI_BASE_CURSOR_WIN_WIN_CURSOR_FACTORY_H_
diff --git a/ui/base/ui_base_features.cc b/ui/base/ui_base_features.cc
index 41c788a0..e2befcf 100644
--- a/ui/base/ui_base_features.cc
+++ b/ui/base/ui_base_features.cc
@@ -47,8 +47,19 @@
                                            base::FEATURE_DISABLED_BY_DEFAULT};
 
 bool IsNewShortcutMappingEnabled() {
-  return base::FeatureList::IsEnabled(kNewShortcutMapping);
+  // kImprovedKeyboardShortcuts supercedes kNewShortcutMapping.
+  return !IsImprovedKeyboardShortcutsEnabled() &&
+         base::FeatureList::IsEnabled(kNewShortcutMapping);
 }
+
+// This feature supercedes kNewShortcutMapping.
+const base::Feature kImprovedKeyboardShortcuts = {
+    "ImprovedKeyboardShortcuts", base::FEATURE_DISABLED_BY_DEFAULT};
+
+bool IsImprovedKeyboardShortcutsEnabled() {
+  return base::FeatureList::IsEnabled(kImprovedKeyboardShortcuts);
+}
+
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 // Update of the virtual keyboard settings UI as described in
diff --git a/ui/base/ui_base_features.h b/ui/base/ui_base_features.h
index 9b01e45..8824753e 100644
--- a/ui/base/ui_base_features.h
+++ b/ui/base/ui_base_features.h
@@ -102,6 +102,14 @@
 
 COMPONENT_EXPORT(UI_BASE_FEATURES)
 bool IsNewShortcutMappingEnabled();
+
+// This flag is intended to supercede kNewShortcutMapping above.
+COMPONENT_EXPORT(UI_BASE_FEATURES)
+extern const base::Feature kImprovedKeyboardShortcuts;
+
+COMPONENT_EXPORT(UI_BASE_FEATURES)
+bool IsImprovedKeyboardShortcutsEnabled();
+
 #endif
 
 // Indicates whether DrmOverlayManager should used the synchronous API to
diff --git a/ui/base/x/x11_shm_image_pool.cc b/ui/base/x/x11_shm_image_pool.cc
index f7850f7..c00f6468 100644
--- a/ui/base/x/x11_shm_image_pool.cc
+++ b/ui/base/x/x11_shm_image_pool.cc
@@ -211,12 +211,15 @@
     }
   }
 
+  const auto* visual_info = connection_->GetVisualInfoFromId(visual_);
+  if (!visual_info)
+    return false;
+  size_t row_bytes = RowBytesForVisualWidth(*visual_info, pixel_size.width());
+
   for (FrameState& state : frame_states_) {
     state.bitmap = SkBitmap();
-    if (!state.bitmap.installPixels(image_info, state.shmaddr,
-                                    image_info.minRowBytes())) {
+    if (!state.bitmap.installPixels(image_info, state.shmaddr, row_bytes))
       return false;
-    }
     state.canvas = std::make_unique<SkCanvas>(state.bitmap);
   }
 
diff --git a/ui/base/x/x11_util.cc b/ui/base/x/x11_util.cc
index 0eab65b7..8a1edf9 100644
--- a/ui/base/x/x11_util.cc
+++ b/ui/base/x/x11_util.cc
@@ -238,6 +238,15 @@
       .Sync();
 }
 
+size_t RowBytesForVisualWidth(const x11::Connection::VisualInfo& visual_info,
+                              int width) {
+  auto bpp = visual_info.format->bits_per_pixel;
+  auto align = visual_info.format->scanline_pad;
+  size_t row_bits = bpp * width;
+  row_bits += (align - (row_bits % align)) % align;
+  return (row_bits + 7) / 8;
+}
+
 void DrawPixmap(x11::Connection* connection,
                 x11::VisualId visual,
                 x11::Drawable drawable,
@@ -257,11 +266,7 @@
   if (!visual_info)
     return;
 
-  auto bpp = visual_info->format->bits_per_pixel;
-  auto align = visual_info->format->scanline_pad;
-  size_t row_bits = bpp * width;
-  row_bits += (align - (row_bits % align)) % align;
-  size_t row_bytes = (row_bits + 7) / 8;
+  size_t row_bytes = RowBytesForVisualWidth(*visual_info, width);
 
   auto color_type = ColorTypeForVisual(visual);
   if (color_type == kUnknown_SkColorType) {
diff --git a/ui/base/x/x11_util.h b/ui/base/x/x11_util.h
index b817500..450525f4 100644
--- a/ui/base/x/x11_util.h
+++ b/ui/base/x/x11_util.h
@@ -148,6 +148,10 @@
 COMPONENT_EXPORT(UI_BASE_X)
 void DefineCursor(x11::Window window, x11::Cursor cursor);
 
+COMPONENT_EXPORT(UI_BASE_X)
+size_t RowBytesForVisualWidth(const x11::Connection::VisualInfo& visual_info,
+                              int width);
+
 // Draws an SkPixmap on |drawable| using the given |gc|, converting to the
 // server side visual as needed.
 COMPONENT_EXPORT(UI_BASE_X)
diff --git a/ui/platform_window/win/BUILD.gn b/ui/platform_window/win/BUILD.gn
index bf545a4..519c51c 100644
--- a/ui/platform_window/win/BUILD.gn
+++ b/ui/platform_window/win/BUILD.gn
@@ -9,7 +9,6 @@
     "//base",
     "//skia",
     "//ui/base",
-    "//ui/base/cursor",
     "//ui/events",
     "//ui/gfx",
     "//ui/gfx/geometry",
diff --git a/ui/platform_window/win/win_window.cc b/ui/platform_window/win/win_window.cc
index cf00aaeb..264f59b 100644
--- a/ui/platform_window/win/win_window.cc
+++ b/ui/platform_window/win/win_window.cc
@@ -9,7 +9,6 @@
 
 #include "base/strings/string16.h"
 #include "base/strings/string_util_win.h"
-#include "ui/base/cursor/win/win_cursor.h"
 #include "ui/base/win/shell.h"
 #include "ui/events/event.h"
 #include "ui/events/event_utils.h"
@@ -147,8 +146,7 @@
 }
 
 void WinWindow::SetCursor(PlatformCursor cursor) {
-  DCHECK(cursor);
-  ::SetCursor(static_cast<WinCursor*>(cursor)->hcursor());
+  ::SetCursor(cursor);
 }
 
 void WinWindow::MoveCursorTo(const gfx::Point& location) {
diff --git a/ui/views/controls/progress_bar.cc b/ui/views/controls/progress_bar.cc
index 09e80a9..96e9ce5 100644
--- a/ui/views/controls/progress_bar.cc
+++ b/ui/views/controls/progress_bar.cc
@@ -31,13 +31,20 @@
 // In DP, the amount to round the corners of the progress bar (both bg and
 // fg, aka slice).
 constexpr int kCornerRadius = 3;
+constexpr int kSmallCornerRadius = 1;
 
-// Adds a rectangle to the path. The corners will be rounded if there is room.
+// Adds a rectangle to the path. The corners will be rounded with regular corner
+// radius if the progress bar height is larger than the regular corner radius.
+// Otherwise the corners will be rounded with the small corner radius if there
+// is room for it.
 void AddPossiblyRoundRectToPath(const gfx::Rect& rectangle,
                                 bool allow_round_corner,
                                 SkPath* path) {
-  if (!allow_round_corner || rectangle.height() < kCornerRadius) {
+  if (!allow_round_corner || rectangle.height() < kSmallCornerRadius) {
     path->addRect(gfx::RectToSkRect(rectangle));
+  } else if (rectangle.height() < kCornerRadius) {
+    path->addRoundRect(gfx::RectToSkRect(rectangle), kSmallCornerRadius,
+                       kSmallCornerRadius);
   } else {
     path->addRoundRect(gfx::RectToSkRect(rectangle), kCornerRadius,
                        kCornerRadius);
diff --git a/ui/views/view.cc b/ui/views/view.cc
index 3d94cbee..548272b 100644
--- a/ui/views/view.cc
+++ b/ui/views/view.cc
@@ -1300,7 +1300,14 @@
 }
 
 gfx::NativeCursor View::GetCursor(const ui::MouseEvent& event) {
+#if defined(OS_WIN)
+  static ui::Cursor arrow;
+  if (!arrow.platform())
+    arrow.SetPlatformCursor(LoadCursor(nullptr, IDC_ARROW));
+  return arrow;
+#else
   return gfx::kNullCursor;
+#endif
 }
 
 bool View::HitTestPoint(const gfx::Point& point) const {
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
index 31db3cd..f9e9123 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
@@ -23,7 +23,6 @@
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/base/class_property.h"
 #include "ui/base/cursor/cursor_loader_win.h"
-#include "ui/base/cursor/win/win_cursor.h"
 #include "ui/base/ime/input_method.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/base/win/event_creation_utils.h"
@@ -635,10 +634,7 @@
   ui::CursorLoaderWin cursor_loader;
   cursor_loader.SetPlatformCursor(&cursor);
 
-  ui::WinCursor* platform_cursor =
-      static_cast<ui::WinCursor*>(cursor.platform());
-  DCHECK(platform_cursor);
-  message_handler_->SetCursor(platform_cursor->hcursor());
+  message_handler_->SetCursor(cursor.platform());
 }
 
 void DesktopWindowTreeHostWin::OnCursorVisibilityChangedNative(bool show) {
diff --git a/ui/webui/resources/cr_components/BUILD.gn b/ui/webui/resources/cr_components/BUILD.gn
index d24f957..f164b9d 100644
--- a/ui/webui/resources/cr_components/BUILD.gn
+++ b/ui/webui/resources/cr_components/BUILD.gn
@@ -159,6 +159,19 @@
       "chromeos/cellular_setup/setup_loading_page.m.js",
       "chromeos/cellular_setup/subflow_behavior.m.js",
       "chromeos/cellular_setup/webview_post_util.m.js",
+      "chromeos/multidevice_setup/mojo_api.m.js",
+      "chromeos/multidevice_setup/fake_mojo_service.m.js",
+      "chromeos/multidevice_setup/button_bar.m.js",
+      "chromeos/multidevice_setup/multidevice_setup.m.js",
+      "chromeos/multidevice_setup/multidevice_setup_browser_proxy.m.js",
+      "chromeos/multidevice_setup/multidevice_setup_delegate.m.js",
+      "chromeos/multidevice_setup/password_page.m.js",
+      "chromeos/multidevice_setup/setup_succeeded_page.m.js",
+      "chromeos/multidevice_setup/start_setup_page.m.js",
+      "chromeos/multidevice_setup/ui_page_container_behavior.m.js",
+      "chromeos/multidevice_setup/multidevice_setup_shared_css.m.js",
+      "chromeos/multidevice_setup/ui_page.m.js",
+      "chromeos/multidevice_setup/icons.m.js",
       "chromeos/network_health/network_health_container.m.js",
       "chromeos/network_health/network_health_summary.m.js",
       "chromeos/network_health/network_health_mojo.m.js",
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/BUILD.gn b/ui/webui/resources/cr_components/chromeos/multidevice_setup/BUILD.gn
index 9a36596..6ffdeae2 100644
--- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/BUILD.gn
+++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/BUILD.gn
@@ -108,8 +108,10 @@
     ":multidevice_setup.m",
     ":multidevice_setup_browser_proxy.m",
     ":multidevice_setup_delegate.m",
+    ":multidevice_setup_shared_css.m",
     ":setup_succeeded_page.m",
     ":start_setup_page.m",
+    ":ui_page.m",
     ":ui_page_container_behavior.m",
     "//ui/webui/resources/js:web_ui_listener_behavior.m",
   ]
@@ -148,6 +150,7 @@
     ":fake_mojo_service.m",
     ":mojo_api.m",
     ":multidevice_setup_delegate.m",
+    ":multidevice_setup_shared_css.m",
     ":password_page.m",
     ":setup_succeeded_page.m",
     ":start_setup_page.m",
@@ -171,7 +174,10 @@
 
 js_library("password_page.m") {
   sources = [ "$root_gen_dir/ui/webui/resources/cr_components/chromeos/multidevice_setup/password_page.m.js" ]
-  deps = [ ":multidevice_setup_browser_proxy.m" ]
+  deps = [
+    ":multidevice_setup_browser_proxy.m",
+    ":multidevice_setup_shared_css.m",
+  ]
   extra_deps = [ ":password_page_module" ]
   externs_list = chrome_extension_public_externs +
                  [ "$externs_path/quick_unlock_private.js" ]
@@ -182,6 +188,7 @@
   sources = [ "$root_gen_dir/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_succeeded_page.m.js" ]
   deps = [
     ":multidevice_setup_browser_proxy.m",
+    ":multidevice_setup_shared_css.m",
     ":ui_page_container_behavior.m",
   ]
   extra_deps = [ ":setup_succeeded_page_module" ]
@@ -191,6 +198,7 @@
   sources = [ "$root_gen_dir/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.m.js" ]
   deps = [
     ":multidevice_setup_delegate.m",
+    ":multidevice_setup_shared_css.m",
     ":ui_page_container_behavior.m",
     "//ui/webui/resources/js:web_ui_listener_behavior.m",
   ]
@@ -206,14 +214,29 @@
   extra_deps = [ ":modulize" ]
 }
 
+js_library("multidevice_setup_shared_css.m") {
+  sources = [ "$root_gen_dir/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup_shared_css.m.js" ]
+  deps = []
+  extra_deps = [ ":multidevice_setup_shared_css_module" ]
+}
+
+js_library("ui_page.m") {
+  sources = [ "$root_gen_dir/ui/webui/resources/cr_components/chromeos/multidevice_setup/ui_page.m.js" ]
+  deps = [ ":multidevice_setup_shared_css.m" ]
+  extra_deps = [ ":ui_page_module" ]
+}
+
 group("polymer3_elements") {
   public_deps = [
     ":button_bar_module",
+    ":icons_module",
     ":modulize",
     ":multidevice_setup_module",
+    ":multidevice_setup_shared_css_module",
     ":password_page_module",
     ":setup_succeeded_page_module",
     ":start_setup_page_module",
+    ":ui_page_module",
   ]
 }
 
@@ -257,6 +280,26 @@
   auto_imports = cr_components_chromeos_auto_imports
 }
 
+polymer_modulizer("multidevice_setup_shared_css") {
+  js_file = "multidevice_setup_shared_css.m.js"
+  html_file = "multidevice_setup_shared_css.html"
+  html_type = "style-module"
+}
+
+polymer_modulizer("ui_page") {
+  js_file = "ui_page.js"
+  html_file = "ui_page.html"
+  html_type = "dom-module"
+  namespace_rewrites = cr_components_chromeos_namespace_rewrites
+  auto_imports = cr_components_chromeos_auto_imports
+}
+
+polymer_modulizer("icons") {
+  js_file = "icons.m.js"
+  html_file = "icons.html"
+  html_type = "iron-iconset"
+}
+
 js_modulizer("modulize") {
   input_files = [
     "fake_mojo_service.js",
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/mojo_api.js b/ui/webui/resources/cr_components/chromeos/multidevice_setup/mojo_api.js
index fe1aaf6..6d5ba3a 100644
--- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/mojo_api.js
+++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/mojo_api.js
@@ -4,6 +4,11 @@
 
 // clang-format off
 // #import {addSingletonGetter} from 'chrome://resources/js/cr.m.js';
+// #import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
+// #import 'chrome://resources/mojo/mojo/public/mojom/base/time.mojom-lite.js';
+// #import 'chrome://resources/mojo/chromeos/components/multidevice/mojom/multidevice_types.mojom-lite.js';
+// #import 'chrome://resources/mojo/chromeos/services/device_sync/public/mojom/device_sync.mojom-lite.js';
+// #import 'chrome://resources/mojo/chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom-lite.js';
 // clang-format on
 
 cr.define('multidevice_setup', function() {
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_succeeded_page.html b/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_succeeded_page.html
index 37f85df..1a58eb6 100644
--- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_succeeded_page.html
+++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_succeeded_page.html
@@ -16,8 +16,8 @@
 
       #page-icon {
         background-image: -webkit-image-set(
-                              url(setup_succeeded_icon_1x.png) 1x,
-                              url(setup_succeeded_icon_2x.png) 2x);
+                              url(chrome://resources/cr_components/chromeos/multidevice_setup/setup_succeeded_icon_1x.png) 1x,
+                              url(chrome://resources/cr_components/chromeos/multidevice_setup/setup_succeeded_icon_2x.png) 2x);
         height: 156px;
         margin-top: 64px;
         width: 416px;
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.html b/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.html
index f3b5939a..97e0268 100644
--- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.html
+++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.html
@@ -38,8 +38,8 @@
       }
 
       #page-icon {
-        background-image: -webkit-image-set(url(start_setup_icon_1x.png) 1x,
-                                            url(start_setup_icon_2x.png) 2x);
+        background-image: -webkit-image-set(url(chrome://resources/cr_components/chromeos/multidevice_setup/start_setup_icon_1x.png) 1x,
+                                            url(chrome://resources/cr_components/chromeos/multidevice_setup/start_setup_icon_2x.png) 2x);
         height: 116px;
         margin-top: 10px;
         width: 320px;
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/ui_page.html b/ui/webui/resources/cr_components/chromeos/multidevice_setup/ui_page.html
index 5d4d3be6..b308b54 100644
--- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/ui_page.html
+++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/ui_page.html
@@ -1,5 +1,5 @@
 <link rel="import" href="../../../html/polymer.html">
-
+<link rel="import" href="chrome://resources/html/load_time_data.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="icons.html">
 <link rel="import" href="multidevice_setup_shared_css.html">
diff --git a/weblayer/browser/java/BUILD.gn b/weblayer/browser/java/BUILD.gn
index cb57758..644da7b 100644
--- a/weblayer/browser/java/BUILD.gn
+++ b/weblayer/browser/java/BUILD.gn
@@ -169,6 +169,7 @@
     "org/chromium/weblayer_private/WebLayerTabModalPresenter.java",
     "org/chromium/weblayer_private/WebMessageReplyProxyImpl.java",
     "org/chromium/weblayer_private/WebShareServiceFactory.java",
+    "org/chromium/weblayer_private/WebappsHelper.java",
     "org/chromium/weblayer_private/media/MediaRouteDialogFragmentImpl.java",
     "org/chromium/weblayer_private/media/MediaRouterClientImpl.java",
     "org/chromium/weblayer_private/media/MediaSessionManager.java",
@@ -440,6 +441,7 @@
     "org/chromium/weblayer_private/WebLayerImpl.java",
     "org/chromium/weblayer_private/WebMessageReplyProxyImpl.java",
     "org/chromium/weblayer_private/WebViewCompatibilityHelperImpl.java",
+    "org/chromium/weblayer_private/WebappsHelper.java",
     "org/chromium/weblayer_private/media/MediaRouterClientImpl.java",
     "org/chromium/weblayer_private/media/MediaStreamManager.java",
     "org/chromium/weblayer_private/metrics/MetricsServiceClient.java",
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/WebappsHelper.java b/weblayer/browser/java/org/chromium/weblayer_private/WebappsHelper.java
new file mode 100644
index 0000000..7e6f370
--- /dev/null
+++ b/weblayer/browser/java/org/chromium/weblayer_private/WebappsHelper.java
@@ -0,0 +1,23 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private;
+
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.net.Uri;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.components.webapps.WebappsUtils;
+
+class WebappsHelper {
+    private WebappsHelper() {}
+
+    @CalledByNative
+    public static void addShortcut(String id, String url, String userTitle, Bitmap icon,
+            boolean isIconAdaptive, int source, String iconUrl) {
+        Intent shortcutIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+        WebappsUtils.addShortcutToHomescreen(id, userTitle, icon, isIconAdaptive, shortcutIntent);
+    }
+}
diff --git a/weblayer/browser/webapps/weblayer_webapps_client.cc b/weblayer/browser/webapps/weblayer_webapps_client.cc
index 97506fa..1898d87 100644
--- a/weblayer/browser/webapps/weblayer_webapps_client.cc
+++ b/weblayer/browser/webapps/weblayer_webapps_client.cc
@@ -8,9 +8,27 @@
 #include "components/security_state/content/content_utils.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
 #include "weblayer/browser/infobar_service.h"
+#include "weblayer/browser/java/jni/WebappsHelper_jni.h"
+
+#if defined(OS_ANDROID)
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/guid.h"
+#include "base/strings/string16.h"
+#include "components/webapps/browser/android/add_to_homescreen_params.h"
+#include "components/webapps/browser/android/shortcut_info.h"
+#include "ui/android/color_helpers.h"
+#include "ui/gfx/android/java_bitmap.h"
+#include "url/gurl.h"
+#endif
 
 namespace weblayer {
 
+using base::android::ConvertUTF16ToJavaString;
+using base::android::ConvertUTF8ToJavaString;
+using base::android::JavaParamRef;
+using base::android::ScopedJavaLocalRef;
+
 // static
 void WebLayerWebappsClient::Create() {
   static base::NoDestructor<WebLayerWebappsClient> instance;
@@ -71,7 +89,22 @@
 void WebLayerWebappsClient::InstallShortcut(
     content::WebContents* web_contents,
     const webapps::AddToHomescreenParams& params) {
-  NOTIMPLEMENTED();
+  const webapps::ShortcutInfo& info = *params.shortcut_info;
+  JNIEnv* env = base::android::AttachCurrentThread();
+  ScopedJavaLocalRef<jstring> java_id =
+      ConvertUTF8ToJavaString(env, base::GenerateGUID());
+  ScopedJavaLocalRef<jstring> java_url =
+      ConvertUTF8ToJavaString(env, info.url.spec());
+  ScopedJavaLocalRef<jstring> java_user_title =
+      ConvertUTF16ToJavaString(env, info.user_title);
+  ScopedJavaLocalRef<jstring> java_best_primary_icon_url =
+      ConvertUTF8ToJavaString(env, info.best_primary_icon_url.spec());
+  ScopedJavaLocalRef<jobject> java_bitmap;
+  if (!params.primary_icon.drawsNothing())
+    java_bitmap = gfx::ConvertToJavaBitmap(params.primary_icon);
+  Java_WebappsHelper_addShortcut(env, java_id, java_url, java_user_title,
+                                 java_bitmap, params.has_maskable_primary_icon,
+                                 info.source, java_best_primary_icon_url);
 }
 #endif