diff --git a/DEPS b/DEPS
index b381a79..4d14d27 100644
--- a/DEPS
+++ b/DEPS
@@ -234,11 +234,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': '688cb15faa641680346f7510bbf6e5d77053bc0e',
+  'skia_revision': '0774db13d24ce59ae9560701d7c19772669f9ac4',
   # 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': '99489318d40626fb10047f33c4f7fd893fd89bc9',
+  'v8_revision': '0e0109edceda7c23a76aa7657f3c70f9b79dc8df',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
@@ -246,7 +246,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '7737c2bcff2e7f0c3ad7dc769c615c0d0c94df79',
+  'swiftshader_revision': '4c687cc2f8ea9d096a66ad43c08805d36a559ede',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -301,7 +301,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'f61cb71c3cd559d3ccce35544ea626f32b9de3c6',
+  'catapult_revision': 'e6e1eb6895e71193452e361388240498955c51eb',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -309,7 +309,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': '93c0859f32546392eb58e3463d349499dbcec8b8',
+  'devtools_frontend_revision': '80e47cf285206fb6c6fa101005949b2c6bfd1c14',
   # 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.
@@ -349,7 +349,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': '4b7ba255c3a62edb99182f17a3de7c77720313fd',
+  'dawn_revision': '7260fe69b7251ff7c673308dc8bdebda344147f8',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -791,7 +791,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'KaJVIPBjh2qbCob6hsVzLk1brSl4iuZVYNpUH0sgg20C',
+          'version': 'B7B3qXoWKXASrnI5M2ySp9skL0Ozjnw8ViPqwmmTlfEC',
       },
     ],
     'condition': 'checkout_android',
@@ -890,7 +890,7 @@
           },
           {
               'package': 'chromium/third_party/android_sdk/public/cmdline-tools',
-              'version': 'AuYa11pULKT8AI14_owabJrkZoRGuovL-nvwmiONlYEC',
+              'version': 'Ez2NWws2SJYCF6qw2O-mSCqK6424l3ZdSTpppLyVR_cC',
           },
       ],
       'condition': 'checkout_android_native_support',
@@ -1030,7 +1030,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '176a9e8764598a6da911e6be2fd12e46def8d6f5',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '1c7dec337d646d7b842d52355576729992382f0f',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1413,7 +1413,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'f32b6ac45880108dee6eaa9e585aa8c6217607cb',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '19fb767c32d05d75314d376933b98be246e0b723',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1457,8 +1457,8 @@
   'src/third_party/qemu-linux-x64': {
       'packages': [
           {
-              'package': 'fuchsia/qemu/linux-amd64',
-              'version': '9cc486c5b18a0be515c39a280ca9a309c54cf994'
+              'package': 'fuchsia/third_party/qemu/linux-amd64',
+              'version': 'FFZaD9tecL-z0lq2XP_7UqiAaMgRGwXTyvcmkv7XCQcC'
           },
       ],
       'condition': 'host_os == "linux" and checkout_fuchsia',
@@ -1468,8 +1468,8 @@
   'src/third_party/qemu-mac-x64': {
       'packages': [
           {
-              'package': 'fuchsia/qemu/mac-amd64',
-              'version': '2d3358ae9a569b2d4a474f498b32b202a152134f'
+              'package': 'fuchsia/third_party/qemu/mac-amd64',
+              'version': '79L6B9YhuL7uIg_CxwlQcZqLOixVtS2Cctn7dmVg0q4C'
           },
       ],
       'condition': 'host_os == "mac" and checkout_fuchsia',
@@ -1692,7 +1692,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@8e7e2748ffff032f17357340acccfa37a8f10364',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@b375548ba1b2c4e463e6e536cd41d2775ee72ef6',
     'condition': 'checkout_src_internal',
   },
 
@@ -1733,7 +1733,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/projector_app/app',
-        'version': '83She84pH_XiIJLWleSamUFIRmsfU4x6Vb5Fq2dFDbAC',
+        'version': 'dA9xqxm4XUSyXF3ehqpywnp7uJvD4o10mEj3qLkSMEsC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/android_webview/browser/metrics/visibility_metrics_logger_unittest.cc b/android_webview/browser/metrics/visibility_metrics_logger_unittest.cc
index 58298f1..66050aca 100644
--- a/android_webview/browser/metrics/visibility_metrics_logger_unittest.cc
+++ b/android_webview/browser/metrics/visibility_metrics_logger_unittest.cc
@@ -4,7 +4,9 @@
 
 #include "android_webview/browser/metrics/visibility_metrics_logger.h"
 
+#include "android_webview/common/aw_features.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/time/clock.h"
 #include "content/public/test/browser_task_environment.h"
@@ -351,4 +353,86 @@
       170);
 }
 
+TEST_F(VisibilityMetricsLoggerTest, TestScreenPortion) {
+  // t=0: client created
+  // t=10: client visible, navigates to web content, screen percentage 0%
+  // t=30: 7% screen percentage
+  // t=35: 42% screen percentage
+  // t=45: 100% screen percentage
+  // t=60: client invisible
+  // t=70: client visible
+  // t=95: client navigates away from web content
+  // t=100: client deleted
+
+  // Time with no visible client: 10 + 10 = 20
+  // Time with client visible: 20 + 5 + 10 + 15 + 25 + 5 = 80
+  // Time with client displaying web content: 20 + 5 + 10 + 15 + 25 = 75
+  // Time displaying web content with portion kExactlyZeroPercent: 20
+  // Time displaying web content with portion kZeroPercent: 5
+  // Time displaying web content with portion kFortyPercent: 10
+  // Time displaying web content with portion kOneHundredPercent: 15 + 25 = 40
+
+  base::HistogramTester histogram_tester;
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      android_webview::features::kWebViewMeasureScreenCoverage);
+  std::unique_ptr<TestClient> client = std::make_unique<TestClient>(logger());
+
+  task_environment().FastForwardBy(base::Seconds(10));
+  client->SetViewVisible(true);
+  client->SetViewAttached(true);
+  client->SetWindowVisible(true);
+  client->SetSchemeHttpOrHttps(true);
+  // If pixels is 0 then time spent is logged under kExactlyZeroPercent,
+  // otherwise the screen portion is calculated as percentage / 10.
+  logger()->UpdateOpenWebScreenArea(/*pixels=*/0, /*percentage=*/0);
+
+  task_environment().FastForwardBy(base::Seconds(20));
+  logger()->UpdateOpenWebScreenArea(/*pixels=*/14, /*percentage=*/7);
+
+  task_environment().FastForwardBy(base::Seconds(5));
+  logger()->UpdateOpenWebScreenArea(/*pixels=*/84, /*percentage=*/42);
+
+  task_environment().FastForwardBy(base::Seconds(10));
+  logger()->UpdateOpenWebScreenArea(/*pixels=*/200, /*percentage=*/100);
+
+  task_environment().FastForwardBy(base::Seconds(15));
+  client->SetViewVisible(false);
+
+  task_environment().FastForwardBy(base::Seconds(10));
+  client->SetViewVisible(true);
+
+  task_environment().FastForwardBy(base::Seconds(25));
+  client->SetSchemeHttpOrHttps(false);
+
+  task_environment().FastForwardBy(base::Seconds(5));
+  logger()->RecordMetrics();
+  client.reset();
+
+  histogram_tester.ExpectBucketCount(
+      "Android.WebView.Visibility.Global",
+      VisibilityMetricsLogger::Visibility::kNotVisible, 20);
+  histogram_tester.ExpectBucketCount(
+      "Android.WebView.Visibility.Global",
+      VisibilityMetricsLogger::Visibility::kVisible, 80);
+  histogram_tester.ExpectBucketCount(
+      "Android.WebView.WebViewOpenWebVisible.Global",
+      VisibilityMetricsLogger::WebViewOpenWebVisibility::kDisplayOpenWebContent,
+      75);
+  histogram_tester.ExpectBucketCount(
+      "Android.WebView.WebViewOpenWebVisible.ScreenPortion2",
+      VisibilityMetricsLogger::WebViewOpenWebScreenPortion::kExactlyZeroPercent,
+      20);
+  histogram_tester.ExpectBucketCount(
+      "Android.WebView.WebViewOpenWebVisible.ScreenPortion2",
+      VisibilityMetricsLogger::WebViewOpenWebScreenPortion::kZeroPercent, 5);
+  histogram_tester.ExpectBucketCount(
+      "Android.WebView.WebViewOpenWebVisible.ScreenPortion2",
+      VisibilityMetricsLogger::WebViewOpenWebScreenPortion::kFortyPercent, 10);
+  histogram_tester.ExpectBucketCount(
+      "Android.WebView.WebViewOpenWebVisible.ScreenPortion2",
+      VisibilityMetricsLogger::WebViewOpenWebScreenPortion::kOneHundredPercent,
+      40);
+}
+
 }  // namespace android_webview
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 7b2e5528..7ebda1cd 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -571,6 +571,11 @@
 const base::Feature kFilesBannerFramework{"FilesBannerFramework",
                                           base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Enable the simple archive extraction.
+// https://crbug.com/953256
+const base::Feature kFilesExtractArchive{"FilesExtractArchive",
+                                         base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables the System Web App (SWA) version of file manager.
 const base::Feature kFilesSWA{"FilesSWA", base::FEATURE_DISABLED_BY_DEFAULT};
 
@@ -582,9 +587,6 @@
 const base::Feature kFilesTrash{"FilesTrash",
                                 base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kFilesZipUnpack{"FilesZipUnpack",
-                                    base::FEATURE_DISABLED_BY_DEFAULT};
-
 // Enables filters in Files app Recents view.
 const base::Feature kFiltersInRecents{"FiltersInRecents",
                                       base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index 1c370def..7e7f399 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -221,11 +221,11 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kFilesArchivemount2;
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kFilesBannerFramework;
+COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kFilesExtractArchive;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kFilesSWA;
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kFilesSinglePartitionFormat;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kFilesTrash;
-COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kFilesZipUnpack;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kFiltersInRecents;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kFirmwareUpdaterApp;
 COMPONENT_EXPORT(ASH_CONSTANTS)
diff --git a/ash/public/cpp/holding_space/holding_space_prefs.cc b/ash/public/cpp/holding_space/holding_space_prefs.cc
index 4657a178..1c9b475 100644
--- a/ash/public/cpp/holding_space/holding_space_prefs.cc
+++ b/ash/public/cpp/holding_space/holding_space_prefs.cc
@@ -51,11 +51,25 @@
   registry->RegisterTimePref(kTimeOfFirstPin, unix_epoch);
 }
 
+void ResetProfilePrefsForTesting(PrefService* prefs) {
+  prefs->ClearPref(kPreviewsEnabled);
+  prefs->ClearPref(kTimeOfFirstAdd);
+  prefs->ClearPref(kTimeOfFirstAvailability);
+  prefs->ClearPref(kTimeOfFirstEntry);
+  prefs->ClearPref(kTimeOfFirstFilesAppChipPress);
+  prefs->ClearPref(kTimeOfFirstPin);
+}
+
 void AddPreviewsEnabledChangedCallback(PrefChangeRegistrar* registrar,
                                        base::RepeatingClosure callback) {
   registrar->Add(kPreviewsEnabled, std::move(callback));
 }
 
+void AddTimeOfFirstAddChangedCallback(PrefChangeRegistrar* registrar,
+                                      base::RepeatingClosure callback) {
+  registrar->Add(kTimeOfFirstAdd, std::move(callback));
+}
+
 bool IsPreviewsEnabled(PrefService* prefs) {
   return prefs->GetBoolean(kPreviewsEnabled);
 }
diff --git a/ash/public/cpp/holding_space/holding_space_prefs.h b/ash/public/cpp/holding_space/holding_space_prefs.h
index 7c7494e..cae6d5b 100644
--- a/ash/public/cpp/holding_space/holding_space_prefs.h
+++ b/ash/public/cpp/holding_space/holding_space_prefs.h
@@ -23,11 +23,19 @@
 // Registers holding space profile preferences to `registry`.
 ASH_PUBLIC_EXPORT void RegisterProfilePrefs(PrefRegistrySimple* registry);
 
+// Resets all preferences to their default values.
+ASH_PUBLIC_EXPORT void ResetProfilePrefsForTesting(PrefService* prefs);
+
 // Adds `callback` to `registrar` to be invoked on changes to previews enabled.
 ASH_PUBLIC_EXPORT void AddPreviewsEnabledChangedCallback(
     PrefChangeRegistrar* registrar,
     base::RepeatingClosure callback);
 
+// Adds `callback` to `registrar` to be invoked on changes to time of first add.
+ASH_PUBLIC_EXPORT void AddTimeOfFirstAddChangedCallback(
+    PrefChangeRegistrar* registrar,
+    base::RepeatingClosure callback);
+
 // Returns whether previews are enabled.
 ASH_PUBLIC_EXPORT bool IsPreviewsEnabled(PrefService* prefs);
 
diff --git a/ash/system/holding_space/holding_space_tray.cc b/ash/system/holding_space/holding_space_tray.cc
index 7f0f394..29ea29f 100644
--- a/ash/system/holding_space/holding_space_tray.cc
+++ b/ash/system/holding_space/holding_space_tray.cc
@@ -641,12 +641,20 @@
   pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>();
   pref_change_registrar_->Init(prefs);
 
-  // NOTE: The callback being bound is scoped to `pref_change_registrar_` which
-  // is owned by `this` so it is safe to bind with an unretained raw pointer.
+  // NOTE: The binding of these callbacks is scoped to `pref_change_registrar_`
+  // which is owned by `this` so it is safe to bind with an unretained raw
+  // pointer.
   holding_space_prefs::AddPreviewsEnabledChangedCallback(
       pref_change_registrar_.get(),
       base::BindRepeating(&HoldingSpaceTray::UpdatePreviewsState,
                           base::Unretained(this)));
+  holding_space_prefs::AddTimeOfFirstAddChangedCallback(
+      pref_change_registrar_.get(), base::BindRepeating(
+                                        [](HoldingSpaceTray* tray) {
+                                          tray->SetShouldAnimate(true);
+                                          tray->UpdateVisibility();
+                                        },
+                                        base::Unretained(this)));
 }
 
 void HoldingSpaceTray::UpdatePreviewsState() {
diff --git a/ash/system/model/system_tray_model.h b/ash/system/model/system_tray_model.h
index a354cf6..9adaba0 100644
--- a/ash/system/model/system_tray_model.h
+++ b/ash/system/model/system_tray_model.h
@@ -88,9 +88,6 @@
   std::unique_ptr<TrayNetworkStateModel> network_state_model_;
   std::unique_ptr<ActiveNetworkIcon> active_network_icon_;
 
-  // TODO(tetsui): Add following as a sub-model of SystemTrayModel:
-  // * BluetoothModel
-
   // Client interface in chrome browser. May be null in tests.
   SystemTrayClient* client_ = nullptr;
 };
diff --git a/ash/webui/camera_app_ui/resources/utils/dev/images/camera_app_icons_128.png b/ash/webui/camera_app_ui/resources/utils/dev/images/camera_app_icons_128.png
deleted file mode 100644
index 97557c6..0000000
--- a/ash/webui/camera_app_ui/resources/utils/dev/images/camera_app_icons_128.png
+++ /dev/null
Binary files differ
diff --git a/ash/webui/camera_app_ui/resources/utils/dev/images/camera_app_icons_48.png b/ash/webui/camera_app_ui/resources/utils/dev/images/camera_app_icons_48.png
deleted file mode 100644
index 04d1dcfb..0000000
--- a/ash/webui/camera_app_ui/resources/utils/dev/images/camera_app_icons_48.png
+++ /dev/null
Binary files differ
diff --git a/ash/webui/firmware_update_ui/BUILD.gn b/ash/webui/firmware_update_ui/BUILD.gn
index 8452739..4599bf8 100644
--- a/ash/webui/firmware_update_ui/BUILD.gn
+++ b/ash/webui/firmware_update_ui/BUILD.gn
@@ -16,6 +16,7 @@
 
   deps = [
     "//ash/webui/resources:firmware_update_app_resources",
+    "//chromeos/strings/",
     "//content/public/browser",
     "//ui/resources:webui_generated_resources_grd_grit",
     "//ui/resources:webui_resources_grd_grit",
diff --git a/ash/webui/firmware_update_ui/firmware_update_app_ui.cc b/ash/webui/firmware_update_ui/firmware_update_app_ui.cc
index 4e7f470..301a326c 100644
--- a/ash/webui/firmware_update_ui/firmware_update_app_ui.cc
+++ b/ash/webui/firmware_update_ui/firmware_update_app_ui.cc
@@ -7,6 +7,7 @@
 #include "ash/grit/ash_firmware_update_app_resources.h"
 #include "ash/grit/ash_firmware_update_app_resources_map.h"
 #include "ash/webui/firmware_update_ui/url_constants.h"
+#include "chromeos/strings/grit/chromeos_strings.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
@@ -30,6 +31,26 @@
                           IDR_WEBUI_JS_TEST_LOADER_UTIL_JS);
 }
 
+void AddFirmwareUpdateAppStrings(content::WebUIDataSource* source) {
+  static constexpr webui::LocalizedString kLocalizedStrings[] = {
+      {"appTitle", IDS_FIRMWARE_TITLE_TEXT},
+      {"criticalUpdate", IDS_FIRMWARE_CRITICAL_UPDATE_TEXT},
+      {"prepareDevice", IDS_FIRMWARE_PREPARE_DEVICE_TEXT},
+      {"nextButton", IDS_FIRMWARE_NEXT_BUTTON_TEXT},
+      {"cancelButton", IDS_FIRMWARE_CANCEL_BUTTON_TEXT},
+      {"doneButton", IDS_FIRMWARE_DONE_BUTTON_TEXT},
+      {"updateButton", IDS_FIRMWARE_UPDATE_BUTTON_TEXT},
+      {"updating", IDS_FIRMWARE_UPDATING_TEXT},
+      {"deviceUpToDate", IDS_FIRMWARE_DEVICE_UP_TO_DATE_TEXT},
+      {"hasBeenUpdated", IDS_FIRMWARE_HAS_BEEN_UPDATED_TEXT},
+      {"updatingInfo", IDS_FIRMWARE_UPDATING_INFO_TEXT},
+      {"installing", IDS_FIRMWARE_INSTALLING_TEXT},
+      {"upToDate", IDS_FIRMWARE_UP_TO_DATE_TEXT}};
+
+  source->AddLocalizedStrings(kLocalizedStrings);
+  source->UseStringsJs();
+}
+
 }  // namespace
 
 FirmwareUpdateAppUI::FirmwareUpdateAppUI(content::WebUI* web_ui)
@@ -46,6 +67,8 @@
   SetUpWebUIDataSource(source.get(), resources,
                        IDR_ASH_FIRMWARE_UPDATE_APP_INDEX_HTML);
 
+  AddFirmwareUpdateAppStrings(source.get());
+
   auto* browser_context = web_ui->GetWebContents()->GetBrowserContext();
   content::WebUIDataSource::Add(browser_context, source.release());
 }
diff --git a/ash/webui/firmware_update_ui/resources/BUILD.gn b/ash/webui/firmware_update_ui/resources/BUILD.gn
index c462de7..5e7223f 100644
--- a/ash/webui/firmware_update_ui/resources/BUILD.gn
+++ b/ash/webui/firmware_update_ui/resources/BUILD.gn
@@ -75,6 +75,7 @@
     ":firmware_update_dialog",
     ":peripheral_updates_list",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/js:i18n_behavior.m",
   ]
 }
 
@@ -82,6 +83,7 @@
   deps = [
     ":firmware_update_types",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/js:i18n_behavior.m",
   ]
 }
 
@@ -111,6 +113,7 @@
   deps = [
     ":firmware_update_types",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/js:i18n_behavior.m",
   ]
 }
 
diff --git a/ash/webui/firmware_update_ui/resources/firmware_update_app.html b/ash/webui/firmware_update_ui/resources/firmware_update_app.html
index dcfd4be..cf8b6493c 100644
--- a/ash/webui/firmware_update_ui/resources/firmware_update_app.html
+++ b/ash/webui/firmware_update_ui/resources/firmware_update_app.html
@@ -7,8 +7,7 @@
 </style>
 <div id="container" class="firmware-default-font">
   <h1 id="header" class="firmware-header-font">
-    <!-- TODO(michaelcheco): i18n string -->
-    Firmware updates
+    [[i18n('appTitle')]]
   </h1>
   <peripheral-updates-list></peripheral-updates-list>
   <firmware-update-dialog></firmware-update-dialog>
diff --git a/ash/webui/firmware_update_ui/resources/firmware_update_app.js b/ash/webui/firmware_update_ui/resources/firmware_update_app.js
index 7063b1f..5250c4f 100644
--- a/ash/webui/firmware_update_ui/resources/firmware_update_app.js
+++ b/ash/webui/firmware_update_ui/resources/firmware_update_app.js
@@ -7,14 +7,27 @@
 import './firmware_update_dialog.js';
 import './peripheral_updates_list.js';
 
-import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import './strings.m.js';
+
+import {I18nBehavior, I18nBehaviorInterface} from 'chrome://resources/js/i18n_behavior.m.js';
+import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 /**
  * @fileoverview
  * 'firmware-update-app' is the main landing page for the firmware
  * update app.
  */
-export class FirmwareUpdateAppElement extends PolymerElement {
+
+/**
+ * @constructor
+ * @extends {PolymerElement}
+ * @implements {I18nBehaviorInterface}
+ */
+const FirmwareUpdateAppElementBase =
+    mixinBehaviors([I18nBehavior], PolymerElement);
+
+/** @polymer */
+export class FirmwareUpdateAppElement extends FirmwareUpdateAppElementBase {
   static get is() {
     return 'firmware-update-app';
   }
diff --git a/ash/webui/firmware_update_ui/resources/firmware_update_dialog.html b/ash/webui/firmware_update_ui/resources/firmware_update_dialog.html
index 217043ac1..988ba12 100644
--- a/ash/webui/firmware_update_ui/resources/firmware_update_dialog.html
+++ b/ash/webui/firmware_update_ui/resources/firmware_update_dialog.html
@@ -50,8 +50,7 @@
   <cr-dialog id="devicePrepDialog" show-on-attach
     on-close="closeDialog_">
   <div slot="title">
-    <!-- TODO(michaelcheco): i18n string -->
-    Prepare your device
+    [[i18n('prepareDevice')]]
   </div>
   <div slot="body">
     <div id="updateInstructions" hidden$="[[!update.updateModeInstructions]]">
@@ -60,14 +59,12 @@
   </div>
   <div slot="button-container">
     <cr-button id="cancelButton" on-click="closeDialog_">
-      <!-- TODO(michaelcheco): i18n string -->
-      Cancel
+      [[i18n('cancelButton')]]
     </cr-button>
     <cr-button class="action-button"
         id="nextButton"
         on-click="startUpdate_">
-      <!-- TODO(michaelcheco): i18n string -->
-      Next
+      [[i18n('nextButton')]]
     </cr-button>
   </div>
   </cr-dialog>
@@ -80,7 +77,6 @@
     </div>
     <div slot="body">
       <div>
-        <!-- TODO(michaelcheco): i18n string -->
         [[computeUpdateDialogBodyText_(dialogState)]]
       </div>
     </div>
@@ -98,8 +94,7 @@
       <cr-button class="action-button"
           on-click="closeDialog_"
           id="updateDoneButton">
-        <!-- TODO(michaelcheco): i18n string -->
-        Done
+        [[i18n('doneButton')]]
       </cr-button>
     </div>
   </cr-dialog>
diff --git a/ash/webui/firmware_update_ui/resources/firmware_update_dialog.js b/ash/webui/firmware_update_ui/resources/firmware_update_dialog.js
index 6cd9c0fb..fc69c97f 100644
--- a/ash/webui/firmware_update_ui/resources/firmware_update_dialog.js
+++ b/ash/webui/firmware_update_ui/resources/firmware_update_dialog.js
@@ -4,10 +4,12 @@
 
 import './firmware_shared_css.js';
 import './firmware_shared_fonts.js';
+import './strings.m.js';
 
 import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.m.js';
 import 'chrome://resources/polymer/v3_0/paper-progress/paper-progress.js';
-import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {I18nBehavior, I18nBehaviorInterface} from 'chrome://resources/js/i18n_behavior.m.js';
+import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {FirmwareUpdate, InstallationProgress, UpdateControllerInterface} from './firmware_update_types.js';
 import {getUpdateController} from './mojo_interface_provider.js';
 
@@ -23,7 +25,18 @@
  * @fileoverview
  * 'firmware-update-dialog' displays information related to a firmware update.
  */
-export class FirmwareUpdateDialogElement extends PolymerElement {
+
+/**
+ * @constructor
+ * @extends {PolymerElement}
+ * @implements {I18nBehaviorInterface}
+ */
+const FirmwareUpdateDialogElementBase =
+    mixinBehaviors([I18nBehavior], PolymerElement);
+
+/** @polymer */
+export class FirmwareUpdateDialogElement extends
+    FirmwareUpdateDialogElementBase {
   static get is() {
     return 'firmware-update-dialog';
   }
@@ -156,8 +169,8 @@
    */
   computeUpdateDialogTitle_() {
     return this.isUpdateInProgress_() ?
-        `Updating ${this.update.deviceName}` :
-        `Your ${this.update.deviceName} is up to date`;
+        this.i18n('updating', this.update.deviceName) :
+        this.i18n('deviceUpToDate', this.update.deviceName);
   }
 
   /**
@@ -166,9 +179,8 @@
    */
   computeProgressText_() {
     if (this.installationProgress && this.installationProgress.percentage) {
-      return `Installing (${this.computePercentageValue_()})%`;
+      return this.i18n('installing', this.computePercentageValue_());
     }
-    // TODO(michaelcheco): i18n string.
     return '';
   }
 
@@ -178,14 +190,9 @@
    */
   computeUpdateDialogBodyText_() {
     const {deviceName, version} = this.update;
-    // TODO(michaelcheco): i18n string.
     return this.dialogState === DialogState.UPDATE_DONE ?
-        `Firmware ${deviceName} has been updated to version ${version}` :
-        `
-    While updating, you can minimize window but do not unplug your
-    device. This may take a few minutes and your device might not work
-    during this update.
-    `;
+        this.i18n('hasBeenUpdated', deviceName, version) :
+        this.i18n('updatingInfo');
   }
 }
 
diff --git a/ash/webui/firmware_update_ui/resources/update_card.html b/ash/webui/firmware_update_ui/resources/update_card.html
index 77e5898a..f4e78d6 100644
--- a/ash/webui/firmware_update_ui/resources/update_card.html
+++ b/ash/webui/firmware_update_ui/resources/update_card.html
@@ -26,14 +26,12 @@
       <span>[[update.version]]</span>
       <span id ="priorityText"
           hidden$="[[!isCriticalUpdate_(update.priority)]]">
-        <!-- TODO(michaelcheco): i18n string -->
-        Critical update
+        [[i18n('criticalUpdate')]]
       </span>
     </div>
     <div id="description">[[update.description]]</div>
   </div>
   <cr-button id="updateButton" on-click="onUpdateButtonClicked_">
-    <!-- TODO(michaelcheco): i18n string -->
-    Update
+    [[i18n('updateButton')]]
   </cr-button>
 </div>
diff --git a/ash/webui/firmware_update_ui/resources/update_card.js b/ash/webui/firmware_update_ui/resources/update_card.js
index b16e485b..2340efc 100644
--- a/ash/webui/firmware_update_ui/resources/update_card.js
+++ b/ash/webui/firmware_update_ui/resources/update_card.js
@@ -5,15 +5,26 @@
 import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
 import './firmware_shared_css.js';
 import './firmware_shared_fonts.js';
+import './strings.m.js';
 
-import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {I18nBehavior, I18nBehaviorInterface} from 'chrome://resources/js/i18n_behavior.m.js';
+import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {FirmwareUpdate, UpdatePriority} from './firmware_update_types.js';
 
 /**
  * @fileoverview
  * 'update-card' displays information about a peripheral update.
  */
-export class UpdateCardElement extends PolymerElement {
+
+/**
+ * @constructor
+ * @extends {PolymerElement}
+ * @implements {I18nBehaviorInterface}
+ */
+const UpdateCardElementBase = mixinBehaviors([I18nBehavior], PolymerElement);
+
+/** @polymer */
+export class UpdateCardElement extends UpdateCardElementBase {
   static get is() {
     return 'update-card';
   }
diff --git a/ash/webui/telemetry_extension_ui/test/telemetry_extension_ui_browsertest.cc b/ash/webui/telemetry_extension_ui/test/telemetry_extension_ui_browsertest.cc
index d282b57b..30bc737 100644
--- a/ash/webui/telemetry_extension_ui/test/telemetry_extension_ui_browsertest.cc
+++ b/ash/webui/telemetry_extension_ui/test/telemetry_extension_ui_browsertest.cc
@@ -19,6 +19,8 @@
 #include "chromeos/dbus/cros_healthd/cros_healthd_client.h"
 #include "chromeos/dbus/cros_healthd/fake_cros_healthd_client.h"
 
+namespace ash {
+
 namespace {
 
 // Folder containing the resources for JS browser tests.
@@ -40,8 +42,8 @@
 }  // namespace
 
 TelemetryExtensionUiBrowserTest::TelemetryExtensionUiBrowserTest()
-    : SandboxedWebUiAppTestBase(ash::kChromeUITelemetryExtensionURL,
-                                ash::kChromeUIUntrustedTelemetryExtensionURL,
+    : SandboxedWebUiAppTestBase(kChromeUITelemetryExtensionURL,
+                                kChromeUIUntrustedTelemetryExtensionURL,
                                 {base::FilePath(kUntrustedTestHandlers),
                                  base::FilePath(kUntrustedTestUtils),
                                  base::FilePath(kUntrustedTestCases)}) {}
@@ -528,3 +530,5 @@
                      system_events_weak_ptr_factory_.GetWeakPtr(), callback),
       base::Seconds(1));
 }
+
+}  // namespace ash
diff --git a/ash/webui/telemetry_extension_ui/test/telemetry_extension_ui_browsertest.h b/ash/webui/telemetry_extension_ui/test/telemetry_extension_ui_browsertest.h
index e8de57a..0a1f048f 100644
--- a/ash/webui/telemetry_extension_ui/test/telemetry_extension_ui_browsertest.h
+++ b/ash/webui/telemetry_extension_ui/test/telemetry_extension_ui_browsertest.h
@@ -10,6 +10,8 @@
 #include "base/command_line.h"
 #include "base/memory/weak_ptr.h"
 
+namespace ash {
+
 class TelemetryExtensionUiBrowserTest : public SandboxedWebUiAppTestBase {
  public:
   TelemetryExtensionUiBrowserTest();
@@ -52,4 +54,6 @@
       system_events_weak_ptr_factory_{this};
 };
 
+}  // namespace ash
+
 #endif  // ASH_WEBUI_TELEMETRY_EXTENSION_UI_TEST_TELEMETRY_EXTENSION_UI_BROWSERTEST_H_
diff --git a/ash/webui/telemetry_extension_ui/test/telemetry_extension_ui_browsertest.js b/ash/webui/telemetry_extension_ui/test/telemetry_extension_ui_browsertest.js
index 4df75691..95aecedd 100644
--- a/ash/webui/telemetry_extension_ui/test/telemetry_extension_ui_browsertest.js
+++ b/ash/webui/telemetry_extension_ui/test/telemetry_extension_ui_browsertest.js
@@ -29,12 +29,12 @@
 
   /** @override */
   get featureList() {
-    return {enabled: ['chromeos::features::kTelemetryExtension']};
+    return {enabled: ['ash::features::kTelemetryExtension']};
   }
 
   /** @override */
   get typedefCppFixture() {
-    return 'TelemetryExtensionUiBrowserTest';
+    return 'ash::TelemetryExtensionUiBrowserTest';
   }
 
   /** @override */
diff --git a/base/allocator/partition_allocator/starscan/pcscan_internal.cc b/base/allocator/partition_allocator/starscan/pcscan_internal.cc
index da00b6f..045e26f 100644
--- a/base/allocator/partition_allocator/starscan/pcscan_internal.cc
+++ b/base/allocator/partition_allocator/starscan/pcscan_internal.cc
@@ -51,6 +51,16 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 
+// TODO(bikineev): Temporarily disable inlining in *Scan to get clearer
+// stacktraces.
+#define PA_STARSCAN_NOINLINE_SCAN_FUNCTIONS
+
+#if defined(PA_STARSCAN_NOINLINE_SCAN_FUNCTIONS)
+#define PA_SCAN_INLINE NOINLINE
+#else
+#define PA_SCAN_INLINE ALWAYS_INLINE
+#endif
+
 namespace base {
 namespace internal {
 
@@ -181,7 +191,7 @@
 //
 // |maybe_inner_ptr| must be within a normal-bucket super page and can also
 // point to guard pages or slot-span metadata.
-ALWAYS_INLINE GetSlotStartResult
+PA_SCAN_INLINE GetSlotStartResult
 GetSlotStartInSuperPage(uintptr_t maybe_inner_ptr) {
   char* maybe_inner_ptr_as_char_ptr = reinterpret_cast<char*>(maybe_inner_ptr);
   PA_SCAN_DCHECK(IsManagedByNormalBuckets(maybe_inner_ptr_as_char_ptr));
@@ -536,12 +546,12 @@
   friend class base::RefCountedThreadSafe<PCScanTask>;
   ~PCScanTask() = default;
 
-  ALWAYS_INLINE AllocationStateMap* TryFindScannerBitmapForPointer(
+  PA_SCAN_INLINE AllocationStateMap* TryFindScannerBitmapForPointer(
       uintptr_t maybe_ptr) const;
 
   // Lookup and marking functions. Return size of the object if marked or zero
   // otherwise.
-  ALWAYS_INLINE size_t TryMarkObjectInNormalBuckets(uintptr_t maybe_ptr) const;
+  PA_SCAN_INLINE size_t TryMarkObjectInNormalBuckets(uintptr_t maybe_ptr) const;
 
   // Scans stack, only called from safepoints.
   void ScanStack();
@@ -588,7 +598,7 @@
   PCScan& pcscan_;
 };
 
-ALWAYS_INLINE AllocationStateMap* PCScanTask::TryFindScannerBitmapForPointer(
+PA_SCAN_INLINE AllocationStateMap* PCScanTask::TryFindScannerBitmapForPointer(
     uintptr_t maybe_ptr) const {
   PA_SCAN_DCHECK(
       IsManagedByPartitionAllocRegularPool(reinterpret_cast<void*>(maybe_ptr)));
@@ -632,7 +642,7 @@
 // TryMarkObjectInNormalBuckets() marks it again in the bitmap and clears
 // from the scanner bitmap. This way, when scanning is done, all uncleared
 // entries in the scanner bitmap correspond to unreachable objects.
-ALWAYS_INLINE size_t
+PA_SCAN_INLINE size_t
 PCScanTask::TryMarkObjectInNormalBuckets(uintptr_t maybe_ptr) const {
   // Check if |maybe_ptr| points somewhere to the heap.
   auto* state_map = TryFindScannerBitmapForPointer(maybe_ptr);
@@ -760,7 +770,7 @@
 #endif
   }
 
-  ALWAYS_INLINE void CheckPointer(uintptr_t maybe_ptr) {
+  PA_SCAN_INLINE void CheckPointer(uintptr_t maybe_ptr) {
     quarantine_size_ +=
         task_.TryMarkObjectInNormalBuckets(memory::UnmaskPtr(maybe_ptr));
   }
diff --git a/base/android/reached_addresses_bitset.cc b/base/android/reached_addresses_bitset.cc
index 295635b..789ccda 100644
--- a/base/android/reached_addresses_bitset.cc
+++ b/base/android/reached_addresses_bitset.cc
@@ -14,18 +14,35 @@
 namespace {
 constexpr size_t kBitsPerElement = sizeof(uint32_t) * 8;
 
-#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
+// Below an array of uint32_t in BSS is introduced and then casted to an array
+// of std::atomic<uint32_t>. In C++20 constructing an std::atomic is not
+// 'trivial'. See https://github.com/microsoft/STL/issues/661 for reasons of
+// this change in the standard.
+//
+// Assert that both types have the same size. The sizes do not have to match
+// according to a note in [atomics.types.generic] in C++17. With this assertion
+// in place it is unlikely that the constructor produces the value other than
+// (uint32_t)0.
+static_assert(sizeof(uint32_t) == sizeof(std::atomic<uint32_t>), "");
+
+// Keep the array in BSS only for non-official builds to avoid potential harm to
+// data locality and unspecified behavior from the reinterpret_cast below. In
+// order to start new experiments with base::Feature(ReachedCodeProfiler) on
+// Canary/Dev this array will need to be reintroduced to official builds.
+#if BUILDFLAG(SUPPORTS_CODE_ORDERING) && !defined(OFFICIAL_BUILD)
 // Enough for 1 << 29 bytes of code, 512MB.
 constexpr size_t kTextBitfieldSize = 1 << 20;
-std::atomic<uint32_t> g_text_bitfield[kTextBitfieldSize];
+uint32_t g_text_bitfield[kTextBitfieldSize];
 #endif
 }  // namespace
 
 // static
 ReachedAddressesBitset* ReachedAddressesBitset::GetTextBitset() {
-#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
-  static ReachedAddressesBitset text_bitset(kStartOfText, kEndOfText,
-                                            g_text_bitfield, kTextBitfieldSize);
+#if BUILDFLAG(SUPPORTS_CODE_ORDERING) && !defined(OFFICIAL_BUILD)
+  static ReachedAddressesBitset text_bitset(
+      kStartOfText, kEndOfText,
+      reinterpret_cast<std::atomic<uint32_t>*>(g_text_bitfield),
+      kTextBitfieldSize);
   return &text_bitset;
 #else
   return nullptr;
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 846c419..76b9c19 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-7.20211117.2.1
+7.20211117.3.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 846c419..76b9c19 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-7.20211117.2.1
+7.20211117.3.1
diff --git a/chrome/VERSION b/chrome/VERSION
index ffcff76d..1840b97a 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=98
 MINOR=0
-BUILD=4712
+BUILD=4713
 PATCH=0
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/StartupTabPreloader.java b/chrome/android/java/src/org/chromium/chrome/browser/init/StartupTabPreloader.java
index 9fcc220..3a43e56f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/StartupTabPreloader.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/StartupTabPreloader.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.init;
 
 import android.content.Intent;
+import android.os.SystemClock;
 import android.text.TextUtils;
 
 import androidx.annotation.VisibleForTesting;
@@ -39,7 +40,8 @@
  * This class attempts to preload the tab if the url is known from the intent when the profile
  * is created. This is done to improve startup latency.
  */
-public class StartupTabPreloader implements ProfileManager.Observer, DestroyObserver {
+public class StartupTabPreloader implements ProfileManager.Observer, DestroyObserver,
+                                            ActivityTabStartupMetricsTracker.Observer {
     public static final String EXTRA_DISABLE_STARTUP_TAB_PRELOADER =
             "org.chromium.chrome.browser.init.DISABLE_STARTUP_TAB_PRELOADER";
     private static boolean sFailNextTabMatchForTesting;
@@ -54,6 +56,12 @@
     private StartupTabObserver mObserver;
     private ActivityTabStartupMetricsTracker mStartupMetricsTracker;
 
+    // The time at which the tab preload decision was made. Recorded only for non-incognito
+    // startups.
+    private long mLoadDecisionMs;
+    // Records whether a preload was triggered.
+    boolean mTriggerPreload;
+
     public static void failNextTabMatchForTesting() {
         sFailNextTabMatchForTesting = true;
     }
@@ -71,6 +79,7 @@
 
         mActivityLifecycleDispatcher.register(this);
         ProfileManager.addObserver(this);
+        ActivityTabStartupMetricsTracker.addObserver(this);
     }
 
     @Override
@@ -79,9 +88,23 @@
         mTab = null;
 
         ProfileManager.removeObserver(this);
+        ActivityTabStartupMetricsTracker.removeObserver(this);
         mActivityLifecycleDispatcher.unregister(this);
     }
 
+    @Override
+    public void onFirstNavigationStart() {
+        if (mLoadDecisionMs == 0) return;
+
+        long currentTimeMs = SystemClock.uptimeMillis();
+        long triggerpointToFirstNavigationStartMs = currentTimeMs - mLoadDecisionMs;
+
+        String suffix = mTriggerPreload ? ".Load" : ".NoLoad";
+        RecordHistogram.recordMediumTimesHistogram(
+                "Android.StartupTabPreloader.LoadDecisionToFirstNavigationStart" + suffix,
+                triggerpointToFirstNavigationStartMs);
+    }
+
     /**
      * Returns the Tab if loadUrlParams and type match, otherwise the Tab is discarded.
      *
@@ -145,10 +168,11 @@
             if (profile.isOffTheRecord()) return;
 
             ProfileManager.removeObserver(this);
-            boolean shouldLoad = shouldLoadTab();
-            if (shouldLoad) loadTab();
+            mTriggerPreload = shouldLoadTab();
+            mLoadDecisionMs = SystemClock.uptimeMillis();
+            if (mTriggerPreload) loadTab();
             RecordHistogram.recordBooleanHistogram(
-                    "Startup.Android.StartupTabPreloader.TabLoaded", shouldLoad);
+                    "Startup.Android.StartupTabPreloader.TabLoaded", mTriggerPreload);
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/metrics/ActivityTabStartupMetricsTracker.java b/chrome/android/java/src/org/chromium/chrome/browser/metrics/ActivityTabStartupMetricsTracker.java
index 18cc0713..4631a400 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/metrics/ActivityTabStartupMetricsTracker.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/metrics/ActivityTabStartupMetricsTracker.java
@@ -6,6 +6,8 @@
 
 import android.os.SystemClock;
 
+import org.chromium.base.ObserverList;
+import org.chromium.base.ThreadUtils;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.chrome.browser.paint_preview.StartupPaintPreviewHelper;
@@ -25,11 +27,37 @@
 public class ActivityTabStartupMetricsTracker {
     private static final String UMA_HISTOGRAM_TABBED_SUFFIX = ".Tabbed";
 
+    /** Observer for startup metrics. */
+    public interface Observer {
+        /**
+         * Called when the initial navigation upon startup is started. This will be fired at most
+         * once.
+         */
+        void onFirstNavigationStart();
+    }
+
+    private static ObserverList<Observer> sObservers;
+
+    /** Adds an observer. */
+    public static boolean addObserver(Observer observer) {
+        ThreadUtils.assertOnUiThread();
+        if (sObservers == null) sObservers = new ObserverList<>();
+        return sObservers.addObserver(observer);
+    }
+
+    /** Removes an observer. */
+    public static boolean removeObserver(Observer observer) {
+        ThreadUtils.assertOnUiThread();
+        if (sObservers == null) return false;
+        return sObservers.removeObserver(observer);
+    }
+
     private class PageLoadMetricsObserverImpl implements PageLoadMetrics.Observer {
         private static final long NO_NAVIGATION_ID = -1;
 
         private long mNavigationId = NO_NAVIGATION_ID;
         private boolean mShouldRecordHistograms;
+        private boolean mInvokedOnFirstNavigationStart;
 
         @Override
         public void onNewNavigation(WebContents webContents, long navigationId,
@@ -38,6 +66,13 @@
 
             mNavigationId = navigationId;
             mShouldRecordHistograms = mShouldTrackStartupMetrics;
+
+            if (!mInvokedOnFirstNavigationStart) {
+                for (Observer observer : sObservers) {
+                    observer.onFirstNavigationStart();
+                }
+                mInvokedOnFirstNavigationStart = true;
+            }
         }
 
         @Override
@@ -48,7 +83,9 @@
             recordFirstContentfulPaint(navigationStartTick / 1000 + firstContentfulPaintMs);
         }
 
-        void reset() {
+        void resetMetricsRecordingStateForInitialNavigation() {
+            // NOTE: |mInvokedOnFirstNavigationStart| is intentionally not reset to avoid duplicate
+            // observer notifications.
             mNavigationId = NO_NAVIGATION_ID;
             mShouldRecordHistograms = false;
         }
@@ -128,7 +165,7 @@
         // Note that observers are not created in all contexts (e.g., CCT).
         if (mPageLoadMetricsObserver == null) return;
 
-        mPageLoadMetricsObserver.reset();
+        mPageLoadMetricsObserver.resetMetricsRecordingStateForInitialNavigation();
     }
 
     /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunActivitySigninAndSyncTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunActivitySigninAndSyncTest.java
index 6e92cc0..8c4a6bb 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunActivitySigninAndSyncTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunActivitySigninAndSyncTest.java
@@ -31,6 +31,7 @@
 import org.junit.ClassRule;
 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.junit.MockitoJUnit;
@@ -70,6 +71,9 @@
             new DisableAnimationsTestRule();
 
     @Rule
+    public final TestRule mCommandLindFlagRule = CommandLineFlags.getTestRule();
+
+    @Rule
     public final MockitoRule mMockitoRule = MockitoJUnit.rule();
 
     @Rule
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationTest.java
index e6bf5ee4..f35c7e4 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationTest.java
@@ -112,6 +112,9 @@
     @Rule
     public JniMocker mJniMocker = new JniMocker();
 
+    @Rule
+    public TestRule mCommandLineFlagsRule = CommandLineFlags.getTestRule();
+
     @Mock
     public FirstRunAppRestrictionInfo mMockAppRestrictionInfo;
     @Mock
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/TosAndUmaFirstRunFragmentWithEnterpriseSupportTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/TosAndUmaFirstRunFragmentWithEnterpriseSupportTest.java
index 32470417..833e5ac 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/TosAndUmaFirstRunFragmentWithEnterpriseSupportTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/TosAndUmaFirstRunFragmentWithEnterpriseSupportTest.java
@@ -31,6 +31,7 @@
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Captor;
@@ -109,6 +110,8 @@
                     .setRevision(RENDER_TEST_REVISION)
                     .setDescription(RENDER_TEST_REVISION_DESCRIPTION)
                     .build();
+    @Rule
+    public TestRule mCommandLineFlagsRule = CommandLineFlags.getTestRule();
 
     @Mock
     public FirstRunAppRestrictionInfo mMockAppRestrictionInfo;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/init/StartupTabPreloaderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/init/StartupTabPreloaderTest.java
index 13fd5d7c..99bac902 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/init/StartupTabPreloaderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/init/StartupTabPreloaderTest.java
@@ -52,6 +52,10 @@
             "Startup.Android.Cold.TimeToFirstVisibleContent";
     private static final String VISIBLE_CONTENT_HISTOGRAM =
             "Startup.Android.Cold.TimeToVisibleContent";
+    private static final String PRELOAD_TRIGGER_TO_FIRST_NAVIGATION_START_PRELOAD =
+            "Android.StartupTabPreloader.LoadDecisionToFirstNavigationStart.Load";
+    private static final String PRELOAD_TRIGGER_TO_FIRST_NAVIGATION_START_NO_PRELOAD =
+            "Android.StartupTabPreloader.LoadDecisionToFirstNavigationStart.NoLoad";
 
     @Rule
     public ChromeTabbedActivityTestRule mActivityRule = new ChromeTabbedActivityTestRule();
@@ -105,6 +109,14 @@
                 RecordHistogram.getHistogramTotalCountForTesting(FIRST_VISIBLE_CONTENT_HISTOGRAM));
         Assert.assertEquals(
                 1, RecordHistogram.getHistogramTotalCountForTesting(VISIBLE_CONTENT_HISTOGRAM));
+
+        // Startup tab preload-specific startup metrics should also have been recorded.
+        Assert.assertEquals(1,
+                RecordHistogram.getHistogramTotalCountForTesting(
+                        PRELOAD_TRIGGER_TO_FIRST_NAVIGATION_START_PRELOAD));
+        Assert.assertEquals(0,
+                RecordHistogram.getHistogramTotalCountForTesting(
+                        PRELOAD_TRIGGER_TO_FIRST_NAVIGATION_START_NO_PRELOAD));
     }
 
     @Test
@@ -140,6 +152,54 @@
                 RecordHistogram.getHistogramTotalCountForTesting(FIRST_VISIBLE_CONTENT_HISTOGRAM));
         Assert.assertEquals(
                 1, RecordHistogram.getHistogramTotalCountForTesting(VISIBLE_CONTENT_HISTOGRAM));
+
+        // Startup tab preload-specific startup metrics should also have been recorded.
+        Assert.assertEquals(1,
+                RecordHistogram.getHistogramTotalCountForTesting(
+                        PRELOAD_TRIGGER_TO_FIRST_NAVIGATION_START_PRELOAD));
+        Assert.assertEquals(0,
+                RecordHistogram.getHistogramTotalCountForTesting(
+                        PRELOAD_TRIGGER_TO_FIRST_NAVIGATION_START_NO_PRELOAD));
+    }
+
+    @Test
+    @LargeTest
+    @DisableFeatures(ChromeFeatureList.ELIDE_TAB_PRELOAD_AT_STARTUP)
+    public void testStartupTabPreloaderStartupLoadingMetricsRecordedWhenTabNotPreloaded()
+            throws Exception {
+        Intent intent = new Intent(Intent.ACTION_VIEW);
+        intent.addCategory(Intent.CATEGORY_LAUNCHER);
+        intent.putExtra(StartupTabPreloader.EXTRA_DISABLE_STARTUP_TAB_PRELOADER, true);
+        mActivityRule.startMainActivityFromIntent(
+                intent, mServerRule.getServer().getURL(TEST_PAGE));
+
+        // The StartupTabPreloader should not have loaded a url.
+        Assert.assertEquals(
+                0, RecordHistogram.getHistogramValueCountForTesting(TAB_LOADED_HISTOGRAM, 1));
+        Assert.assertEquals(
+                0, RecordHistogram.getHistogramValueCountForTesting(TAB_TAKEN_HISTOGRAM, 1));
+
+        // First contentful paint should be recorded.
+        CriteriaHelper.pollUiThread(()
+                                            -> RecordHistogram.getHistogramTotalCountForTesting(
+                                                       FIRST_CONTENTFUL_PAINT_HISTOGRAM)
+                        == 1);
+        // First contentful paint is the last startup metric to be recorded, so the other startup
+        // metrics should also have been recorded at this point.
+        Assert.assertEquals(
+                1, RecordHistogram.getHistogramTotalCountForTesting(FIRST_COMMIT_HISTOGRAM));
+        Assert.assertEquals(1,
+                RecordHistogram.getHistogramTotalCountForTesting(FIRST_VISIBLE_CONTENT_HISTOGRAM));
+        Assert.assertEquals(
+                1, RecordHistogram.getHistogramTotalCountForTesting(VISIBLE_CONTENT_HISTOGRAM));
+
+        // Startup tab preload-specific startup metrics should also have been recorded.
+        Assert.assertEquals(0,
+                RecordHistogram.getHistogramTotalCountForTesting(
+                        PRELOAD_TRIGGER_TO_FIRST_NAVIGATION_START_PRELOAD));
+        Assert.assertEquals(1,
+                RecordHistogram.getHistogramTotalCountForTesting(
+                        PRELOAD_TRIGGER_TO_FIRST_NAVIGATION_START_NO_PRELOAD));
     }
 
     @Test
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/init/StartupTabPreloaderUnitTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/init/StartupTabPreloaderUnitTest.java
index 6ac908a..876be01 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/init/StartupTabPreloaderUnitTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/init/StartupTabPreloaderUnitTest.java
@@ -16,6 +16,7 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.Callback;
+import org.chromium.base.ThreadUtils;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.browser.IntentHandler;
@@ -176,6 +177,10 @@
 
     private StartupTabPreloader createStartupTabPreloader(
             Intent intent, TabCreatorManager tabCreatorManager) {
+        // StartupTabPreloader calls into code that asserts that it is on the UI thread, which
+        // doesn't exist in this unittesting context.
+        ThreadUtils.setThreadAssertsDisabledForTesting(true);
+
         return new StartupTabPreloader(
                 new Supplier<Intent>() {
                     @Override
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SyncConsentFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SyncConsentFragmentTest.java
index 898ae503..99a85711 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SyncConsentFragmentTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SyncConsentFragmentTest.java
@@ -39,6 +39,7 @@
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
@@ -111,6 +112,9 @@
     }
 
     @Rule
+    public final TestRule mCommandLindFlagRule = CommandLineFlags.getTestRule();
+
+    @Rule
     public final DisableAnimationsTestRule mNoAnimationsRule = new DisableAnimationsTestRule();
 
     @Rule
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 069b3fb..c4f1980f 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -4055,12 +4055,6 @@
   <message name="IDS_LACROS_DATA_MIGRATION_SCREEN_SUBTITLE" desc="Shows the % of progress.">
     <ph name="PERCENT">$1<ex>100</ex></ph>% completed
   </message>
-  <message name="IDS_LACROS_DATA_MIGRATION_SCREEN_SKIP_BUTTON" desc="Skip button for data migration.">
-    Skip
-  </message>
-  <message name="IDS_LACROS_DATA_MIGRATION_SCREEN_SKIP_SUGGESTION" desc="Message shown when skip button is visible.">
-    This is taking longer than expected, you can skip or wait until it's done.
-  </message>
 
   <!-- Print Job Notification -->
   <message name="IDS_PRINT_JOB_NOTIFICATION_DISPLAY_SOURCE" desc="The context title of printing notification.">
diff --git a/chrome/app/chromeos_strings_grdp/IDS_LACROS_DATA_MIGRATION_SCREEN_SKIP_BUTTON.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_LACROS_DATA_MIGRATION_SCREEN_SKIP_BUTTON.png.sha1
deleted file mode 100644
index ab72be9..0000000
--- a/chrome/app/chromeos_strings_grdp/IDS_LACROS_DATA_MIGRATION_SCREEN_SKIP_BUTTON.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-362e221fcf529324e0e6fa25e2790fc5e6b70f26
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_LACROS_DATA_MIGRATION_SCREEN_SKIP_SUGGESTION.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_LACROS_DATA_MIGRATION_SCREEN_SKIP_SUGGESTION.png.sha1
deleted file mode 100644
index ab72be9..0000000
--- a/chrome/app/chromeos_strings_grdp/IDS_LACROS_DATA_MIGRATION_SCREEN_SKIP_SUGGESTION.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-362e221fcf529324e0e6fa25e2790fc5e6b70f26
\ No newline at end of file
diff --git a/chrome/app/os_settings_search_tag_strings.grdp b/chrome/app/os_settings_search_tag_strings.grdp
index 603123c..66a04d8 100644
--- a/chrome/app/os_settings_search_tag_strings.grdp
+++ b/chrome/app/os_settings_search_tag_strings.grdp
@@ -1054,6 +1054,18 @@
   <message name="IDS_OS_SETTINGS_TAG_PRIVACY_PERIPHERAL_DATA_ACCESS_PROTECTION_ALT5" desc="Text for search result item which, when clicked, navigates the user to privacy settings, with a toggle to enable/disable peripheral data access protection. Alternate phrase for: 'Data access protection for peripherals', 'PCIe', 'Thunderbolt', 'USB', 'USB4'">
     USB-C
   </message>
+  <message name="IDS_OS_SETTINGS_TAG_SMART_PRIVACY" desc="Text for search result item which, when clicked, navigates the user to the smart privacy subpage." translateable="false">
+    Smart privacy protections
+  </message>
+  <message name="IDS_OS_SETTINGS_TAG_SMART_PRIVACY_SNOOPING" desc="Text for search result item which, when clicked, navigates the user to the smart privacy subpage with a toggle to enable/disable snooping protection. Alternate phrase for: 'Shoulder surfing', 'Peeking'" translateable="false">
+    Snooping protection
+  </message>
+  <message name="IDS_OS_SETTINGS_TAG_SMART_PRIVACY_SNOOPING_ALT1" desc="Text for search result item which, when clicked, navigates the user to the smart privacy subpage with a toggle to enable/disable snooping protection. Alternate phrase for: 'Snooping protection', 'Peeking'" translateable="false">
+    Shoulder surfing
+  </message>
+  <message name="IDS_OS_SETTINGS_TAG_SMART_PRIVACY_SNOOPING_ALT2" desc="Text for search result item which, when clicked, navigates the user to the smart privacy subpage with a toggle to enable/disable snooping protection. Alternate phrase for: 'Snooping protection', 'Shoulder surfing'" translateable="false">
+    Peeking
+  </message>
 
   <!-- Languages and Input section. -->
   <message name="IDS_OS_SETTINGS_TAG_LANGUAGES" desc="Text for search result item which, when clicked, navigates the user to languages settings.">
@@ -1342,6 +1354,9 @@
   <message name="IDS_OS_SETTINGS_TAG_ABOUT_DIAGNOSTICS_ALT4" desc="Text for search result item which, when clicked, navigates the user to 'Diagnostics' settings, with a link which opens the Diagnostics App. Alternate phrase for 'Diagnostics', 'Troubleshooting', 'Battery Health', 'CPU Usage'">
     Memory Usage
   </message>
+  <message name="IDS_OS_SETTINGS_TAG_ABOUT_FIRMWARE_UPDATES" desc="Text for search result item which, when clicked, navigates the user to 'Firmware updates' settings, with a link which opens the 'Firmware updates' app.">
+    Firmware updates
+  </message>
 
   <!-- On Startup section. -->
   <message name="IDS_OS_SETTINGS_TAG_ON_STARTUP" desc="Text for search result item which, when clicked, navigates the user to On Startup settings, with a radio group to configure the restore apps and pages options">
diff --git a/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_ABOUT_FIRMWARE_UPDATES.png.sha1 b/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_ABOUT_FIRMWARE_UPDATES.png.sha1
new file mode 100644
index 0000000..4b52655
--- /dev/null
+++ b/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_ABOUT_FIRMWARE_UPDATES.png.sha1
@@ -0,0 +1 @@
+6ff1935e71886889be4b509739bc14c0762c0913
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index 16a66656..ce609aa0 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -222,6 +222,9 @@
   <message name="IDS_SETTINGS_ABOUT_PAGE_DIAGNOSTICS" desc="Text of the button which allows the user to diagnose their device.">
     Diagnostics
   </message>
+  <message name="IDS_SETTINGS_ABOUT_PAGE_FIRMWARE_UPDATES" desc="Text of the button for the surface which allows users to update all their peripheral firmwares in one place.">
+    Firmware updates
+  </message>
 
   <!-- People (OS settings) -->
   <message name="IDS_OS_SETTINGS_PROFILE_NAME" desc="Label with device account first name, showing which user is currently signed in.">
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ABOUT_PAGE_FIRMWARE_UPDATES.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ABOUT_PAGE_FIRMWARE_UPDATES.png.sha1
new file mode 100644
index 0000000..4f7cf3e
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ABOUT_PAGE_FIRMWARE_UPDATES.png.sha1
@@ -0,0 +1 @@
+ed4b095a8d3ce79314a8e45bc31b025bfb7595cd
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 62e39820..5b95192 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -3621,6 +3621,7 @@
       "apps/app_service/extension_uninstaller.h",
       "apps/app_service/intent_util.cc",
       "apps/app_service/intent_util.h",
+      "apps/app_service/launch_result_type.h",
       "apps/app_service/launch_utils.cc",
       "apps/app_service/launch_utils.h",
       "apps/app_service/metrics/app_service_metrics.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index ea6e7d7..87f512a3 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -4425,6 +4425,9 @@
     {"files-banner-framework", flag_descriptions::kFilesBannerFrameworkName,
      flag_descriptions::kFilesBannerFrameworkDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(chromeos::features::kFilesBannerFramework)},
+    {"files-extract-archive", flag_descriptions::kFilesExtractArchiveName,
+     flag_descriptions::kFilesExtractArchiveDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(chromeos::features::kFilesExtractArchive)},
     {"files-filters-in-recents", flag_descriptions::kFiltersInRecentsName,
      flag_descriptions::kFiltersInRecentsDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(chromeos::features::kFiltersInRecents)},
@@ -4438,9 +4441,6 @@
     {"files-trash", flag_descriptions::kFilesTrashName,
      flag_descriptions::kFilesTrashDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(chromeos::features::kFilesTrash)},
-    {"files-zip-unpack", flag_descriptions::kFilesZipUnpackName,
-     flag_descriptions::kFilesZipUnpackDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(chromeos::features::kFilesZipUnpack)},
     {"force-spectre-v2-mitigation",
      flag_descriptions::kForceSpectreVariant2MitigationName,
      flag_descriptions::kForceSpectreVariant2MitigationDescription, kOsCrOS,
@@ -5290,10 +5290,6 @@
      FEATURE_VALUE_TYPE(commerce::kShoppingList)},
 #endif  // OS_ANDROID
 
-    {"enable-layout-ng", flag_descriptions::kEnableLayoutNGName,
-     flag_descriptions::kEnableLayoutNGDescription, kOsAll,
-     FEATURE_VALUE_TYPE(blink::features::kLayoutNG)},
-
     {"enable-lazy-image-loading",
      flag_descriptions::kEnableLazyImageLoadingName,
      flag_descriptions::kEnableLazyImageLoadingDescription, kOsAll,
diff --git a/chrome/browser/apps/app_service/app_service_proxy_base.cc b/chrome/browser/apps/app_service/app_service_proxy_base.cc
index 2a30bf2..c148e2f 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_base.cc
+++ b/chrome/browser/apps/app_service/app_service_proxy_base.cc
@@ -15,6 +15,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/apps/app_service/app_icon/app_icon_source.h"
+#include "chrome/browser/apps/app_service/app_launch_params.h"
 #include "chrome/browser/apps/app_service/launch_utils.h"
 #include "chrome/browser/apps/app_service/metrics/app_service_metrics.h"
 #include "chrome/browser/ash/file_manager/app_id.h"
@@ -364,6 +365,18 @@
                       launch_source, std::move(window_info));
 }
 
+void AppServiceProxyBase::LaunchAppWithParams(AppLaunchParams&& params,
+                                              LaunchCallback callback) {
+  auto app_type = ConvertMojomAppTypToAppType(
+      app_registry_cache_.GetAppType(params.app_id));
+  auto* publisher = GetPublisher(app_type);
+  if (!publisher) {
+    std::move(callback).Run(LaunchResult());
+    return;
+  }
+  publisher->LaunchAppWithParams(std::move(params), std::move(callback));
+}
+
 void AppServiceProxyBase::SetPermission(const std::string& app_id,
                                         apps::mojom::PermissionPtr permission) {
   if (app_service_.is_connected()) {
diff --git a/chrome/browser/apps/app_service/app_service_proxy_base.h b/chrome/browser/apps/app_service/app_service_proxy_base.h
index 473ab1d..eae5ae6 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_base.h
+++ b/chrome/browser/apps/app_service/app_service_proxy_base.h
@@ -16,6 +16,7 @@
 #include "base/memory/weak_ptr.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/apps/app_service/browser_app_launcher.h"
+#include "chrome/browser/apps/app_service/launch_result_type.h"
 #include "chrome/browser/apps/app_service/publishers/app_publisher.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/services/app_service/public/cpp/app_capability_access_cache.h"
@@ -38,6 +39,8 @@
 
 class AppServiceMojomImpl;
 
+struct AppLaunchParams;
+
 struct IntentLaunchInfo {
   IntentLaunchInfo();
   ~IntentLaunchInfo();
@@ -156,6 +159,11 @@
                         apps::mojom::LaunchSource launch_source,
                         apps::mojom::WindowInfoPtr window_info = nullptr);
 
+  // Launches an app for the given |params.app_id|. The |params| can also
+  // contain other param such as launch container, window diposition, etc.
+  void LaunchAppWithParams(AppLaunchParams&& params,
+                           LaunchCallback callback = base::DoNothing());
+
   // Sets |permission| for the app identified by |app_id|.
   void SetPermission(const std::string& app_id,
                      apps::mojom::PermissionPtr permission);
diff --git a/chrome/browser/apps/app_service/app_service_proxy_lacros.cc b/chrome/browser/apps/app_service/app_service_proxy_lacros.cc
index d4499e0..a36e272e1 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_lacros.cc
+++ b/chrome/browser/apps/app_service/app_service_proxy_lacros.cc
@@ -14,6 +14,7 @@
 #include "base/notreached.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/apps/app_service/app_icon/app_icon_source.h"
+#include "chrome/browser/apps/app_service/app_launch_params.h"
 #include "chrome/browser/apps/app_service/browser_app_instance_forwarder.h"
 #include "chrome/browser/apps/app_service/browser_app_instance_tracker.h"
 #include "chrome/browser/apps/app_service/intent_util.h"
@@ -344,6 +345,12 @@
                       launch_source, std::move(window_info));
 }
 
+void AppServiceProxyLacros::LaunchAppWithParams(AppLaunchParams&& params,
+                                                LaunchCallback callback) {
+  // TODO(crbug.com/1244506): Add params on crosapi and implement this.
+  std::move(callback).Run(LaunchResult());
+}
+
 void AppServiceProxyLacros::SetPermission(
     const std::string& app_id,
     apps::mojom::PermissionPtr permission) {
diff --git a/chrome/browser/apps/app_service/app_service_proxy_lacros.h b/chrome/browser/apps/app_service/app_service_proxy_lacros.h
index 3dd4f95..3c25a040 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_lacros.h
+++ b/chrome/browser/apps/app_service/app_service_proxy_lacros.h
@@ -14,6 +14,7 @@
 #include "base/memory/weak_ptr.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/apps/app_service/browser_app_launcher.h"
+#include "chrome/browser/apps/app_service/launch_result_type.h"
 #include "chromeos/crosapi/mojom/app_service.mojom.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/services/app_service/public/cpp/app_capability_access_cache.h"
@@ -44,6 +45,8 @@
 class BrowserAppInstanceForwarder;
 class BrowserAppInstanceTracker;
 
+struct AppLaunchParams;
+
 struct IntentLaunchInfo {
   std::string app_id;
   std::string activity_name;
@@ -148,6 +151,11 @@
                         apps::mojom::LaunchSource launch_source,
                         apps::mojom::WindowInfoPtr window_info = nullptr);
 
+  // Launches an app for the given |params.app_id|. The |params| can also
+  // contain other param such as launch container, window diposition, etc.
+  void LaunchAppWithParams(AppLaunchParams&& params,
+                           LaunchCallback callback = base::DoNothing());
+
   // Sets |permission| for the app identified by |app_id|.
   void SetPermission(const std::string& app_id,
                      apps::mojom::PermissionPtr permission);
diff --git a/chrome/browser/apps/app_service/launch_result_type.h b/chrome/browser/apps/app_service/launch_result_type.h
new file mode 100644
index 0000000..0f4ce463
--- /dev/null
+++ b/chrome/browser/apps/app_service/launch_result_type.h
@@ -0,0 +1,23 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_APPS_APP_SERVICE_LAUNCH_RESULT_TYPE_H_
+#define CHROME_BROWSER_APPS_APP_SERVICE_LAUNCH_RESULT_TYPE_H_
+
+#include "base/callback_forward.h"
+#include "base/unguessable_token.h"
+
+namespace apps {
+// LaunchResult, and LaunchCallback can be used in Chrome Ash, lacros, and other
+// desktop platforms. So this struct can't be moved to AppPublisher.
+
+struct LaunchResult {
+  base::UnguessableToken instance_id;
+};
+
+using LaunchCallback = base::OnceCallback<void(LaunchResult&&)>;
+
+}  // namespace apps
+
+#endif  // CHROME_BROWSER_APPS_APP_SERVICE_LAUNCH_RESULT_TYPE_H_
diff --git a/chrome/browser/apps/app_service/publishers/app_publisher.h b/chrome/browser/apps/app_service/publishers/app_publisher.h
index 3737567..b3024d8 100644
--- a/chrome/browser/apps/app_service/publishers/app_publisher.h
+++ b/chrome/browser/apps/app_service/publishers/app_publisher.h
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "chrome/browser/apps/app_service/app_service_proxy_forward.h"
+#include "chrome/browser/apps/app_service/launch_result_type.h"
 #include "components/services/app_service/public/cpp/app_types.h"
 #include "components/services/app_service/public/cpp/icon_types.h"
 
@@ -17,12 +18,6 @@
 
 struct AppLaunchParams;
 
-struct LaunchResult {
-  base::UnguessableToken instance_id;
-};
-
-using LaunchCallback = base::OnceCallback<void(LaunchResult&&)>;
-
 // AppPublisher parent class (in the App Service sense) for all app publishers.
 // See components/services/app_service/README.md.
 //
diff --git a/chrome/browser/apps/app_service/publishers/arc_apps.h b/chrome/browser/apps/app_service/publishers/arc_apps.h
index 82c84e4..161e6b77 100644
--- a/chrome/browser/apps/app_service/publishers/arc_apps.h
+++ b/chrome/browser/apps/app_service/publishers/arc_apps.h
@@ -24,6 +24,7 @@
 #include "chrome/browser/apps/app_service/app_notifications.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_forward.h"
 #include "chrome/browser/apps/app_service/app_shortcut_item.h"
+#include "chrome/browser/apps/app_service/launch_result_type.h"
 #include "chrome/browser/apps/app_service/paused_apps.h"
 #include "chrome/browser/apps/app_service/publishers/app_publisher.h"
 #include "chrome/browser/ash/arc/app_shortcuts/arc_app_shortcuts_request.h"
diff --git a/chrome/browser/apps/app_service/publishers/borealis_apps.h b/chrome/browser/apps/app_service/publishers/borealis_apps.h
index afec6c2..55e47a99 100644
--- a/chrome/browser/apps/app_service/publishers/borealis_apps.h
+++ b/chrome/browser/apps/app_service/publishers/borealis_apps.h
@@ -11,6 +11,7 @@
 #include "base/scoped_observation.h"
 #include "chrome/browser/apps/app_service/app_icon/icon_key_util.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_forward.h"
+#include "chrome/browser/apps/app_service/launch_result_type.h"
 #include "chrome/browser/apps/app_service/publishers/app_publisher.h"
 #include "chrome/browser/ash/borealis/borealis_window_manager.h"
 #include "chrome/browser/ash/guest_os/guest_os_registry_service.h"
diff --git a/chrome/browser/apps/app_service/publishers/built_in_chromeos_apps.h b/chrome/browser/apps/app_service/publishers/built_in_chromeos_apps.h
index 96990d0..bdcbb1e 100644
--- a/chrome/browser/apps/app_service/publishers/built_in_chromeos_apps.h
+++ b/chrome/browser/apps/app_service/publishers/built_in_chromeos_apps.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "chrome/browser/apps/app_service/app_service_proxy_forward.h"
+#include "chrome/browser/apps/app_service/launch_result_type.h"
 #include "chrome/browser/apps/app_service/publishers/app_publisher.h"
 #include "components/services/app_service/public/cpp/publisher_base.h"
 #include "components/services/app_service/public/mojom/app_service.mojom.h"
diff --git a/chrome/browser/apps/app_service/publishers/crostini_apps.h b/chrome/browser/apps/app_service/publishers/crostini_apps.h
index ed8c3a51c..a1ea72b 100644
--- a/chrome/browser/apps/app_service/publishers/crostini_apps.h
+++ b/chrome/browser/apps/app_service/publishers/crostini_apps.h
@@ -13,6 +13,7 @@
 #include "chrome/browser/apps/app_service/app_icon/app_icon_factory.h"
 #include "chrome/browser/apps/app_service/app_icon/icon_key_util.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_forward.h"
+#include "chrome/browser/apps/app_service/launch_result_type.h"
 #include "chrome/browser/apps/app_service/publishers/app_publisher.h"
 #include "chrome/browser/ash/guest_os/guest_os_registry_service.h"
 #include "components/keyed_service/core/keyed_service.h"
diff --git a/chrome/browser/apps/app_service/publishers/extension_apps_base.h b/chrome/browser/apps/app_service/publishers/extension_apps_base.h
index b5d59e3..5fd62a2 100644
--- a/chrome/browser/apps/app_service/publishers/extension_apps_base.h
+++ b/chrome/browser/apps/app_service/publishers/extension_apps_base.h
@@ -13,6 +13,7 @@
 #include "base/scoped_observation.h"
 #include "chrome/browser/apps/app_service/app_icon/app_icon_factory.h"
 #include "chrome/browser/apps/app_service/app_icon/icon_key_util.h"
+#include "chrome/browser/apps/app_service/launch_result_type.h"
 #include "chrome/browser/apps/app_service/publishers/app_publisher.h"
 #include "components/services/app_service/public/cpp/publisher_base.h"
 #include "components/services/app_service/public/mojom/app_service.mojom.h"
diff --git a/chrome/browser/apps/app_service/publishers/plugin_vm_apps.h b/chrome/browser/apps/app_service/publishers/plugin_vm_apps.h
index d9409eb..83a0d87b 100644
--- a/chrome/browser/apps/app_service/publishers/plugin_vm_apps.h
+++ b/chrome/browser/apps/app_service/publishers/plugin_vm_apps.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "chrome/browser/apps/app_service/app_icon/icon_key_util.h"
+#include "chrome/browser/apps/app_service/launch_result_type.h"
 #include "chrome/browser/apps/app_service/publishers/app_publisher.h"
 #include "chrome/browser/ash/guest_os/guest_os_registry_service.h"
 #include "chrome/browser/ash/plugin_vm/plugin_vm_manager.h"
diff --git a/chrome/browser/apps/app_service/publishers/remote_apps.h b/chrome/browser/apps/app_service/publishers/remote_apps.h
index a07fc6a..34b3826 100644
--- a/chrome/browser/apps/app_service/publishers/remote_apps.h
+++ b/chrome/browser/apps/app_service/publishers/remote_apps.h
@@ -11,6 +11,7 @@
 
 #include "chrome/browser/apps/app_service/app_icon/icon_key_util.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_forward.h"
+#include "chrome/browser/apps/app_service/launch_result_type.h"
 #include "chrome/browser/apps/app_service/publishers/app_publisher.h"
 #include "chrome/browser/ash/remote_apps/remote_apps_model.h"
 #include "components/services/app_service/public/cpp/icon_types.h"
diff --git a/chrome/browser/apps/app_service/publishers/standalone_browser_apps.h b/chrome/browser/apps/app_service/publishers/standalone_browser_apps.h
index d787643..65baa4b 100644
--- a/chrome/browser/apps/app_service/publishers/standalone_browser_apps.h
+++ b/chrome/browser/apps/app_service/publishers/standalone_browser_apps.h
@@ -8,6 +8,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observation.h"
 #include "chrome/browser/apps/app_service/app_icon/icon_key_util.h"
+#include "chrome/browser/apps/app_service/launch_result_type.h"
 #include "chrome/browser/apps/app_service/publishers/app_publisher.h"
 #include "chrome/browser/ash/crosapi/browser_manager.h"
 #include "chrome/browser/ash/crosapi/browser_manager_observer.h"
diff --git a/chrome/browser/apps/app_service/publishers/standalone_browser_extension_apps.h b/chrome/browser/apps/app_service/publishers/standalone_browser_extension_apps.h
index 4ebe459..9165c0e2 100644
--- a/chrome/browser/apps/app_service/publishers/standalone_browser_extension_apps.h
+++ b/chrome/browser/apps/app_service/publishers/standalone_browser_extension_apps.h
@@ -9,6 +9,7 @@
 
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_forward.h"
+#include "chrome/browser/apps/app_service/launch_result_type.h"
 #include "chrome/browser/apps/app_service/publishers/app_publisher.h"
 #include "chrome/browser/ash/crosapi/browser_manager.h"
 #include "chromeos/crosapi/mojom/app_service.mojom.h"
diff --git a/chrome/browser/apps/app_service/publishers/web_apps_crosapi.h b/chrome/browser/apps/app_service/publishers/web_apps_crosapi.h
index 618cca59..8e490db 100644
--- a/chrome/browser/apps/app_service/publishers/web_apps_crosapi.h
+++ b/chrome/browser/apps/app_service/publishers/web_apps_crosapi.h
@@ -11,6 +11,7 @@
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/apps/app_service/app_icon/icon_key_util.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_forward.h"
+#include "chrome/browser/apps/app_service/launch_result_type.h"
 #include "chrome/browser/apps/app_service/publishers/app_publisher.h"
 #include "chromeos/crosapi/mojom/app_service.mojom.h"
 #include "components/keyed_service/core/keyed_service.h"
diff --git a/chrome/browser/ash/file_manager/file_manager_string_util.cc b/chrome/browser/ash/file_manager/file_manager_string_util.cc
index 7f530af..eac875b 100644
--- a/chrome/browser/ash/file_manager/file_manager_string_util.cc
+++ b/chrome/browser/ash/file_manager/file_manager_string_util.cc
@@ -976,8 +976,6 @@
                    base::FeatureList::IsEnabled(chromeos::features::kFilesSWA));
   dict->SetBoolKey("FILES_TRASH_ENABLED", base::FeatureList::IsEnabled(
                                               chromeos::features::kFilesTrash));
-  dict->SetBoolKey("ZIP_UNPACK", base::FeatureList::IsEnabled(
-                                     chromeos::features::kFilesZipUnpack));
   dict->SetBoolKey(
       "DRIVE_DSS_PIN_ENABLED",
       base::FeatureList::IsEnabled(
@@ -992,5 +990,9 @@
       "FILES_BANNER_FRAMEWORK",
       base::FeatureList::IsEnabled(chromeos::features::kFilesBannerFramework));
 
+  dict->SetBoolKey(
+      "EXTRACT_ARCHIVE",
+      base::FeatureList::IsEnabled(chromeos::features::kFilesExtractArchive));
+
   dict->SetStringKey("UI_LOCALE", locale);
 }
diff --git a/chrome/browser/ash/input_method/assistive_suggester.cc b/chrome/browser/ash/input_method/assistive_suggester.cc
index 25d1a4f..edb62ad 100644
--- a/chrome/browser/ash/input_method/assistive_suggester.cc
+++ b/chrome/browser/ash/input_method/assistive_suggester.cc
@@ -137,6 +137,11 @@
 void RecordMultiWordTextInputState(PrefService* pref_service,
                                    AssistiveSuggesterSwitch* suggester_switch,
                                    const std::string& engine_id) {
+  if (IsLacrosEnabled()) {
+    RecordTextInputStateMetric(AssistiveTextInputState::kUnsupportedClient);
+    return;
+  }
+
   if (!suggester_switch->IsMultiWordSuggestionAllowed()) {
     RecordTextInputStateMetric(
         AssistiveTextInputState::kFeatureBlockedByDenylist);
@@ -149,11 +154,6 @@
     return;
   }
 
-  if (IsLacrosEnabled()) {
-    RecordTextInputStateMetric(AssistiveTextInputState::kUnsupportedClient);
-    return;
-  }
-
   if (!IsUsEnglishEngineId(engine_id)) {
     RecordTextInputStateMetric(AssistiveTextInputState::kUnsupportedLanguage);
     return;
diff --git a/chrome/browser/ash/login/screens/lacros_data_migration_screen.cc b/chrome/browser/ash/login/screens/lacros_data_migration_screen.cc
index fbea6ef..c767b43 100644
--- a/chrome/browser/ash/login/screens/lacros_data_migration_screen.cc
+++ b/chrome/browser/ash/login/screens/lacros_data_migration_screen.cc
@@ -8,8 +8,6 @@
 #include "base/command_line.h"
 #include "base/task/bind_post_task.h"
 #include "base/threading/sequenced_task_runner_handle.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/time.h"
 #include "chrome/browser/ash/crosapi/browser_data_migrator.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "content/public/browser/browser_thread.h"
@@ -19,27 +17,13 @@
 namespace ash {
 namespace {
 constexpr char kUserActionCancel[] = "cancel";
-constexpr base::TimeDelta kShowSkipButtonDuration = base::Seconds(20);
-
-class MigratorDelegateImpl
-    : public LacrosDataMigrationScreen::MigratorDelegate {
-  base::OnceClosure Migrate(
-      const std::string& user_id_hash,
-      const base::RepeatingCallback<void(int)>& progress_callback) override {
-    return BrowserDataMigrator::Migrate(
-        user_id_hash, progress_callback,
-        base::BindOnce(&chrome::AttemptRestart));
-  }
-};
-
-}  // namespace
+}
 
 LacrosDataMigrationScreen::LacrosDataMigrationScreen(
     LacrosDataMigrationScreenView* view)
     : BaseScreen(LacrosDataMigrationScreenView::kScreenId,
                  OobeScreenPriority::SCREEN_DEVICE_DEVELOPER_MODIFICATION),
-      view_(view),
-      migrator_delegate_(std::make_unique<MigratorDelegateImpl>()) {
+      view_(view) {
   DCHECK(view_);
   if (view_)
     view_->Bind(this);
@@ -60,45 +44,29 @@
   if (!view_)
     return;
 
-  // user_id_hash_ is not empty if it is already set by
-  // `SetUserIdHashForTesting()`.
-  if (user_id_hash_.empty()) {
-    user_id_hash_ = base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-        switches::kBrowserDataMigrationForUser);
-  }
-  DCHECK(!user_id_hash_.empty()) << "user_id_hash_ should not be empty.";
-
+  const std::string user_id_hash =
+      base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+          switches::kBrowserDataMigrationForUser);
   base::RepeatingCallback<void(int)> progress_callback = base::BindPostTask(
       base::SequencedTaskRunnerHandle::Get(),
       base::BindRepeating(&LacrosDataMigrationScreen::OnProgressUpdate,
                           weak_factory_.GetWeakPtr()),
       FROM_HERE);
-
-  cancel_callback_ =
-      migrator_delegate_->Migrate(user_id_hash_, progress_callback);
+  // TODO(crbug.com/1178702): Hide skip button and only show it after 10s.
+  // Start browser data migration.
+  cancel_callback_ = BrowserDataMigrator::Migrate(
+      user_id_hash, progress_callback, base::BindOnce(&chrome::AttemptRestart));
 
   // Show the screen.
   view_->Show();
 
   GetWakeLock()->RequestWakeLock();
-
-  // Post a delayed task to show the skip button after
-  // `kShowSkipButtonDuration`.
-  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE,
-      base::BindOnce(&LacrosDataMigrationScreen::ShowSkipButton,
-                     weak_factory_.GetWeakPtr()),
-      kShowSkipButtonDuration);
 }
 
 void LacrosDataMigrationScreen::OnProgressUpdate(int progress) {
   view_->SetProgressValue(progress);
 }
 
-void LacrosDataMigrationScreen::ShowSkipButton() {
-  view_->ShowSkipButton();
-}
-
 void LacrosDataMigrationScreen::OnUserAction(const std::string& action_id) {
   if (action_id == kUserActionCancel) {
     if (cancel_callback_) {
@@ -134,14 +102,4 @@
   return wake_lock_.get();
 }
 
-void LacrosDataMigrationScreen::SetMigratorDelegateForTesting(
-    std::unique_ptr<MigratorDelegate> migrator_delegate) {
-  migrator_delegate_ = std::move(migrator_delegate);
-}
-
-void LacrosDataMigrationScreen::SetUserIdHashForTesting(
-    const std::string& user_id_hash) {
-  user_id_hash_ = user_id_hash;
-}
-
 }  // namespace ash
diff --git a/chrome/browser/ash/login/screens/lacros_data_migration_screen.h b/chrome/browser/ash/login/screens/lacros_data_migration_screen.h
index 1ec5835..5680ba2 100644
--- a/chrome/browser/ash/login/screens/lacros_data_migration_screen.h
+++ b/chrome/browser/ash/login/screens/lacros_data_migration_screen.h
@@ -18,17 +18,6 @@
 // directory. The screen is shown during login.
 class LacrosDataMigrationScreen : public BaseScreen {
  public:
-  // MigratorDelegate initiates the migration. A fake migrator delegate can be
-  // set for testing.
-  class MigratorDelegate {
-   public:
-    // Calls the actual migrator method `ash::BrowserDataMigrator::Migrate()`.
-    virtual base::OnceClosure Migrate(
-        const std::string& user_id_hash,
-        const base::RepeatingCallback<void(int)>& progress_callback) = 0;
-    virtual ~MigratorDelegate() = default;
-  };
-
   explicit LacrosDataMigrationScreen(LacrosDataMigrationScreenView* view);
   ~LacrosDataMigrationScreen() override;
   LacrosDataMigrationScreen(const LacrosDataMigrationScreen&) = delete;
@@ -47,17 +36,6 @@
   // value. `progress` is then passed to `LacrosDataMigrationView`.
   void OnProgressUpdate(int progress);
 
-  // Posted as a delayed task from `ShowImpl()`. It calls the method of the same
-  // name on `LacrosDataMigrationScreenView`.
-  void ShowSkipButton();
-
-  // Set `migrator_delegate_` for testing.
-  void SetMigratorDelegateForTesting(
-      std::unique_ptr<MigratorDelegate> migrator_delegate);
-
-  // Set `user_id_hash_` for testing.
-  void SetUserIdHashForTesting(const std::string& user_id_hash);
-
  private:
   // BaseScreen:
   void ShowImpl() override;
@@ -70,10 +48,8 @@
 
   LacrosDataMigrationScreenView* view_;
   // Callback to cancel migration. Stores the return value from
-  // `migrator_delegate->Migrate()`.
+  // `BrowserDataMigrator::Migrate()`.
   base::OnceClosure cancel_callback_;
-  std::unique_ptr<MigratorDelegate> migrator_delegate_;
-  std::string user_id_hash_;
   base::WeakPtrFactory<LacrosDataMigrationScreen> weak_factory_{this};
 };
 
diff --git a/chrome/browser/ash/login/screens/lacros_data_migration_screen_browsertest.cc b/chrome/browser/ash/login/screens/lacros_data_migration_screen_browsertest.cc
deleted file mode 100644
index 8e12009..0000000
--- a/chrome/browser/ash/login/screens/lacros_data_migration_screen_browsertest.cc
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ash/login/screens/lacros_data_migration_screen.h"
-
-#include "ash/constants/ash_switches.h"
-#include "base/memory/ptr_util.h"
-#include "base/test/task_environment.h"
-#include "chrome/browser/ash/crosapi/browser_data_migrator.h"
-#include "chrome/browser/ash/login/test/device_state_mixin.h"
-#include "chrome/browser/ash/login/test/js_checker.h"
-#include "chrome/browser/ash/login/test/login_manager_mixin.h"
-#include "chrome/browser/ash/login/test/oobe_base_test.h"
-#include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host_mojo.h"
-#include "chrome/browser/ash/login/wizard_controller.h"
-#include "chrome/browser/ui/webui/chromeos/login/lacros_data_migration_screen_handler.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "chrome/test/base/mixin_based_in_process_browser_test.h"
-#include "content/public/test/browser_test.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-namespace ash {
-namespace {
-constexpr char kLacrosDataMigrationId[] = "lacros-data-migration";
-const test::UIPath kSkipButton = {kLacrosDataMigrationId, "cancelButton"};
-
-class FakeMigrator : public LacrosDataMigrationScreen::MigratorDelegate {
- public:
-  base::OnceClosure Migrate(
-      const std::string& user_id_hash,
-      const base::RepeatingCallback<void(int)>& progress_callback) override {
-    return base::BindOnce([](bool* cancel_called) { *cancel_called = true; },
-                          &cancel_called_);
-  }
-
-  // Checks if the returned function from `Migrate()` is called.
-  bool IsCancelCalled() { return cancel_called_; }
-
- private:
-  bool cancel_called_ = false;
-};
-
-class LacrosDataMigrationScreenTest : public OobeBaseTest {
- public:
-  LacrosDataMigrationScreenTest() {
-    // Adding a user and marking OOBE completed with DeviceStateMixin ensures
-    // that chrome://oobe/login is loaded instead of chrome://oobe/oobe and that
-    // LoginDisplayHostMojo is created instead of LoginDisplayHostWebUI.
-    login_mixin_.AppendRegularUsers(1);
-  }
-  LacrosDataMigrationScreenTest(const LacrosDataMigrationScreenTest&) = delete;
-  LacrosDataMigrationScreenTest& operator=(
-      const LacrosDataMigrationScreenTest&) = delete;
-  ~LacrosDataMigrationScreenTest() override = default;
-
-  void SetUpOnMainThread() override {
-    LoginDisplayHostMojo* login_display_host =
-        static_cast<LoginDisplayHostMojo*>(LoginDisplayHost::default_host());
-    // Call `StartWizard()` with any screen to ensure that
-    // `LoginDisplayHostMojo::EnsureOobeDialogLoaded()` is called but do not
-    // show `LacrosDataMigrationScreen` yet because that will start the
-    // migration before stubbing certain methonds.
-    login_display_host->StartWizard(GaiaView::kScreenId);
-    LacrosDataMigrationScreen* lacros_data_migration_screen =
-        static_cast<LacrosDataMigrationScreen*>(
-            WizardController::default_controller()->GetScreen(
-                LacrosDataMigrationScreenView::kScreenId));
-    fake_migrator_ = new FakeMigrator();
-    lacros_data_migration_screen->SetMigratorDelegateForTesting(
-        base::WrapUnique(fake_migrator_));
-    lacros_data_migration_screen->SetUserIdHashForTesting("user");
-    OobeBaseTest::SetUpOnMainThread();
-  }
-
- protected:
-  FakeMigrator* fake_migrator() { return fake_migrator_; }
-
- private:
-  // This is owned by `LacrosDataMigrationScreen`.
-  FakeMigrator* fake_migrator_;
-  DeviceStateMixin device_state_{
-      &mixin_host_, DeviceStateMixin::State::OOBE_COMPLETED_CONSUMER_OWNED};
-  LoginManagerMixin login_mixin_{&mixin_host_};
-};
-
-IN_PROC_BROWSER_TEST_F(LacrosDataMigrationScreenTest, SkipButton) {
-  OobeScreenWaiter waiter(LacrosDataMigrationScreenView::kScreenId);
-  WizardController::default_controller()->AdvanceToScreen(
-      LacrosDataMigrationScreenView::kScreenId);
-  waiter.Wait();
-
-  test::OobeJS().ExpectHiddenPath(kSkipButton);
-
-  LacrosDataMigrationScreen* lacros_data_migration_screen =
-      static_cast<LacrosDataMigrationScreen*>(
-          WizardController::default_controller()->GetScreen(
-              LacrosDataMigrationScreenView::kScreenId));
-  lacros_data_migration_screen->ShowSkipButton();
-
-  test::OobeJS().ExpectVisiblePath(kSkipButton);
-
-  EXPECT_FALSE(fake_migrator()->IsCancelCalled());
-  test::OobeJS().TapOnPath(kSkipButton);
-  EXPECT_TRUE(fake_migrator()->IsCancelCalled());
-}
-}  // namespace
-}  // namespace ash
diff --git a/chrome/browser/ash/system_extensions/system_extensions_sandboxed_unpacker.h b/chrome/browser/ash/system_extensions/system_extensions_sandboxed_unpacker.h
index 4591814..a4f696c 100644
--- a/chrome/browser/ash/system_extensions/system_extensions_sandboxed_unpacker.h
+++ b/chrome/browser/ash/system_extensions/system_extensions_sandboxed_unpacker.h
@@ -22,7 +22,6 @@
   ~SystemExtensionsSandboxedUnpacker();
 
   enum class Status {
-    kOk,
     // This is used for the default constructor of `StatusOrSystemExtension`.
     kUnknown,
     kFailedDirectoryMissing,
diff --git a/chrome/browser/ash/system_extensions/system_extensions_status_or.h b/chrome/browser/ash/system_extensions/system_extensions_status_or.h
index e3ab833..07602fe 100644
--- a/chrome/browser/ash/system_extensions/system_extensions_status_or.h
+++ b/chrome/browser/ash/system_extensions/system_extensions_status_or.h
@@ -6,15 +6,16 @@
 #define CHROME_BROWSER_ASH_SYSTEM_EXTENSIONS_SYSTEM_EXTENSIONS_STATUS_OR_H_
 
 #include "chrome/browser/ash/system_extensions/system_extension.h"
+#include "third_party/abseil-cpp/absl/types/variant.h"
 
 // SystemExtensionsStatusOr is a union of an status enum class and an object.
 // This class either holds an object in a usable state, or a status code
 // explaining why `T` is not present. This class is typically the return value
 // of a function which may fail.
 //
-// An SystemExtensionsStatusOr can never hold an "OK" status (an `S::kOk`
-// value); instead, the presence of `T` indicates success. Instead of checking
-// for a `kOk` value, use the `ok()` member function.
+// An SystemExtensionsStatusOr can never hold an "OK" status, instead, the
+// presence of `T` indicates success. Instead of checking for a `kOk` value, use
+// the `ok()` member function.
 //
 // There is nothing SystemExtensions specific about this class so if needed
 // this can be moved to //base.
@@ -24,43 +25,42 @@
   // Constructs a new `SystemExtensionsStatusOr` with an `S::kUnknown` status.
   // This constructor is marked 'explicit' to prevent usages in return values
   // such as 'return {};'.
-  explicit SystemExtensionsStatusOr() : status_(S::kUnknown) {}  // NOLINT
+  explicit SystemExtensionsStatusOr()  // NOLINT
+      : status_or_value_(S::kUnknown) {}
 
-  // All of these are implicit, so that one may just return Status or
-  // SystemExtension.
-  SystemExtensionsStatusOr(S status) : status_(status) {}  // NOLINT
-  SystemExtensionsStatusOr(T value)                        // NOLINT
-      : status_(S::kOk), value_(std::move(value)) {}
+  // All of these are implicit, so that one may just return `S` or `T`.
+  SystemExtensionsStatusOr(S status) : status_or_value_(status) {}  // NOLINT
+  SystemExtensionsStatusOr(T value)                                 // NOLINT
+      : status_or_value_(std::move(value)) {}
 
   SystemExtensionsStatusOr(SystemExtensionsStatusOr&&) = default;
   SystemExtensionsStatusOr& operator=(SystemExtensionsStatusOr&&) = default;
 
   ~SystemExtensionsStatusOr() = default;
 
-  bool ok() const { return status_ == S::kOk; }
+  bool ok() const { return absl::holds_alternative<T>(status_or_value_); }
 
   // Returns the status code when the status is not kOk. Crashes if the
   // status is kOk.
   S status() {
     CHECK(!ok());
-    return status_;
+    return absl::get<S>(status_or_value_);
   }
 
-  // Returns the object if ok() is true. CHECKs otherwise.
+  // Returns `T` if ok() is true. CHECKs otherwise.
   const T& value() const& {
     CHECK(ok());
-    return value_;
+    return absl::get<T>(status_or_value_);
   }
 
-  // Returns the SystemExtension if ok() is true. CHECKs otherwise.
+  // Returns the `T` if ok() is true. CHECKs otherwise.
   T&& value() && {
     CHECK(ok());
-    return std::move(value_);
+    return std::move(absl::get<T>(status_or_value_));
   }
 
  private:
-  S status_;
-  T value_;
+  absl::variant<S, T> status_or_value_;
 };
 
 template <typename S>
diff --git a/chrome/browser/browser_switcher/alternative_browser_driver.h b/chrome/browser/browser_switcher/alternative_browser_driver.h
index 4cdd2f8..91cd084 100644
--- a/chrome/browser/browser_switcher/alternative_browser_driver.h
+++ b/chrome/browser/browser_switcher/alternative_browser_driver.h
@@ -48,9 +48,9 @@
   // method is most appropriate.
   virtual void TryLaunch(const GURL& url, LaunchCallback cb) = 0;
 
-  // Returns the localized string for the name of the alternative browser, if it
-  // was auto-detected. If the name couldn't be auto-detected, returns an empty
-  // string.
+  // Returns the string for the name of the alternative browser, if it was
+  // auto-detected. If the name couldn't be auto-detected, returns
+  // "alternative browser"
   virtual std::string GetBrowserName() const = 0;
 
   // Returns the type of browser as an enum, if it was auto-detected. Otherwise,
diff --git a/chrome/browser/browser_switcher/alternative_browser_driver_posix.cc b/chrome/browser/browser_switcher/alternative_browser_driver_posix.cc
index 83082518..4003b6b3 100644
--- a/chrome/browser/browser_switcher/alternative_browser_driver_posix.cc
+++ b/chrome/browser/browser_switcher/alternative_browser_driver_posix.cc
@@ -263,7 +263,7 @@
 std::string AlternativeBrowserDriverImpl::GetBrowserName() const {
   std::string path = prefs_->GetAlternativeBrowserPath();
   const auto* mapping = FindBrowserMapping(path);
-  return mapping ? mapping->browser_name : std::string();
+  return mapping ? mapping->browser_name : "alternative browser";
 }
 
 BrowserType AlternativeBrowserDriverImpl::GetBrowserType() const {
diff --git a/chrome/browser/browser_switcher/alternative_browser_driver_unittest.cc b/chrome/browser/browser_switcher/alternative_browser_driver_unittest.cc
index 0d050ae..e2cf991 100644
--- a/chrome/browser/browser_switcher/alternative_browser_driver_unittest.cc
+++ b/chrome/browser/browser_switcher/alternative_browser_driver_unittest.cc
@@ -107,14 +107,14 @@
 #elif defined(OS_MAC)
   std::string expected = "Safari";
 #else
-  std::string expected;
+  std::string expected = "alternative browser";
 #endif
   std::string actual = driver()->GetBrowserName();
   EXPECT_EQ(expected, actual);
 
   SetBrowserPath("bogus.exe");
   actual = driver()->GetBrowserName();
-  EXPECT_EQ("", actual);
+  EXPECT_EQ("alternative browser", actual);
 
 #if defined(OS_WIN)
   SetBrowserPath("${ie}");
diff --git a/chrome/browser/browser_switcher/alternative_browser_driver_win.cc b/chrome/browser/browser_switcher/alternative_browser_driver_win.cc
index 9ff7d7a8..5063bd7 100644
--- a/chrome/browser/browser_switcher/alternative_browser_driver_win.cc
+++ b/chrome/browser/browser_switcher/alternative_browser_driver_win.cc
@@ -334,7 +334,7 @@
 std::string AlternativeBrowserDriverImpl::GetBrowserName() const {
   std::wstring path = base::UTF8ToWide(prefs_->GetAlternativeBrowserPath());
   const auto* mapping = FindBrowserMapping(path, false);
-  return mapping ? mapping->browser_name : std::string();
+  return mapping ? mapping->browser_name : "alternative browser";
 }
 
 BrowserType AlternativeBrowserDriverImpl::GetBrowserType() const {
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
index e221d1d3..f4a9d12 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
@@ -25,6 +25,8 @@
 #include "ash/public/cpp/autotest_ambient_api.h"
 #include "ash/public/cpp/autotest_desks_api.h"
 #include "ash/public/cpp/autotest_private_api_utils.h"
+#include "ash/public/cpp/holding_space/holding_space_model.h"
+#include "ash/public/cpp/holding_space/holding_space_prefs.h"
 #include "ash/public/cpp/login_screen.h"
 #include "ash/public/cpp/metrics_util.h"
 #include "ash/public/cpp/overview_test_api.h"
@@ -102,6 +104,8 @@
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
 #include "chrome/browser/ui/ash/default_pinned_apps.h"
+#include "chrome/browser/ui/ash/holding_space/holding_space_keyed_service.h"
+#include "chrome/browser/ui/ash/holding_space/holding_space_keyed_service_factory.h"
 #include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.h"
 #include "chrome/browser/ui/ash/shelf/shelf_spinner_controller.h"
@@ -139,6 +143,7 @@
 #include "components/app_restore/window_properties.h"
 #include "components/policy/core/browser/policy_conversions.h"
 #include "components/policy/core/common/policy_service.h"
+#include "components/prefs/pref_service.h"
 #include "components/services/app_service/public/cpp/types_util.h"
 #include "components/services/app_service/public/mojom/types.mojom.h"
 #include "components/user_manager/user.h"
@@ -5267,6 +5272,51 @@
           smoothness)));
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateResetHoldingSpaceFunction
+////////////////////////////////////////////////////////////////////////////////
+
+AutotestPrivateResetHoldingSpaceFunction::
+    AutotestPrivateResetHoldingSpaceFunction() = default;
+
+AutotestPrivateResetHoldingSpaceFunction::
+    ~AutotestPrivateResetHoldingSpaceFunction() = default;
+
+ExtensionFunction::ResponseAction
+AutotestPrivateResetHoldingSpaceFunction::Run() {
+  auto params(api::autotest_private::ResetHoldingSpace::Params::Create(args()));
+  EXTENSION_FUNCTION_VALIDATE(params);
+
+  Profile* profile = Profile::FromBrowserContext(browser_context());
+
+  ash::HoldingSpaceKeyedService* service =
+      ash::HoldingSpaceKeyedServiceFactory::GetInstance()->GetService(profile);
+
+  if (service == nullptr)
+    return RespondNow(Error("Failed to get `HoldingSpaceKeyedService`."));
+
+  service->RemoveAll();
+
+  PrefService* prefs = profile->GetPrefs();
+  ash::holding_space_prefs::ResetProfilePrefsForTesting(prefs);
+
+  if (!ash::holding_space_prefs::MarkTimeOfFirstAvailability(prefs)) {
+    return RespondNow(
+        Error("Failed to call `MarkTimeOfFirstAvailability()` after clearing "
+              "prefs."));
+  }
+
+  if (!params->options || !params->options->mark_time_of_first_add)
+    return RespondNow(NoArguments());
+
+  if (!ash::holding_space_prefs::MarkTimeOfFirstAdd(prefs)) {
+    return RespondNow(
+        Error("Failed to call `MarkTimeOfFirstAdd()` after clearing prefs."));
+  }
+
+  return RespondNow(NoArguments());
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // AutotestPrivateAPI
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
index 7982e81..1b6b5c8 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
@@ -1431,6 +1431,17 @@
   ResponseAction Run() override;
 };
 
+class AutotestPrivateResetHoldingSpaceFunction : public ExtensionFunction {
+ public:
+  AutotestPrivateResetHoldingSpaceFunction();
+  DECLARE_EXTENSION_FUNCTION("autotestPrivate.resetHoldingSpace",
+                             AUTOTESTPRIVATE_RESETHOLDINGSPACE)
+
+ private:
+  ~AutotestPrivateResetHoldingSpaceFunction() override;
+  ResponseAction Run() override;
+};
+
 template <>
 KeyedService*
 BrowserContextKeyedAPIFactory<AutotestPrivateAPI>::BuildServiceInstanceFor(
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_apitest.cc b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_apitest.cc
index 3a33cbb..d58350d 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_apitest.cc
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_apitest.cc
@@ -10,10 +10,13 @@
 #include "ash/components/arc/test/arc_util_test_support.h"
 #include "ash/components/arc/test/connection_holder_util.h"
 #include "ash/components/arc/test/fake_app_instance.h"
+#include "ash/public/cpp/holding_space/holding_space_prefs.h"
 #include "ash/public/cpp/overview_test_api.h"
 #include "ash/public/cpp/test/shell_test_api.h"
+#include "base/json/json_writer.h"
 #include "base/test/bind.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/values.h"
 #include "build/build_config.h"
 #include "chrome/browser/ash/arc/arc_util.h"
 #include "chrome/browser/ash/arc/session/arc_session_manager.h"
@@ -82,13 +85,33 @@
         ->set_test_mode(true);
   }
 
+  bool RunAutotestPrivateExtensionTest(const std::string& test_suite) {
+    return RunAutotestPrivateExtensionTest(
+        test_suite,
+        /*suite_args=*/std::vector<base::Value>());
+  }
+
+  bool RunAutotestPrivateExtensionTest(const std::string& test_suite,
+                                       std::vector<base::Value> suite_args) {
+    base::DictionaryValue custom_args;
+    custom_args.SetKey("testSuite", base::Value(test_suite));
+    custom_args.SetKey("args", base::Value(suite_args));
+
+    std::string json;
+    if (!base::JSONWriter::Write(custom_args, &json)) {
+      LOG(ERROR) << "Failed to parse custom args into json.";
+      return false;
+    }
+
+    return RunExtensionTest("autotest_private", {.custom_arg = json.c_str()},
+                            {.load_as_component = true});
+  }
+
   ash::ScopedTestingCrosSettings scoped_testing_cros_settings_;
 };
 
 IN_PROC_BROWSER_TEST_F(AutotestPrivateApiTest, AutotestPrivate) {
-  ASSERT_TRUE(RunExtensionTest("autotest_private", {.custom_arg = "default"},
-                               {.load_as_component = true}))
-      << message_;
+  ASSERT_TRUE(RunAutotestPrivateExtensionTest("default")) << message_;
 }
 
 // Set of tests where ARC is enabled and test apps and packages are registered.
@@ -126,24 +149,66 @@
       true /* sync */));
   app_instance->SendRefreshPackageList(std::move(packages));
 
-  ASSERT_TRUE(RunExtensionTest("autotest_private", {.custom_arg = "arcEnabled"},
-                               {.load_as_component = true}))
-      << message_;
+  ASSERT_TRUE(RunAutotestPrivateExtensionTest("arcEnabled")) << message_;
 
   arc::SetArcPlayStoreEnabledForProfile(profile(), false);
 }
 
 IN_PROC_BROWSER_TEST_F(AutotestPrivateApiTest, ScrollableShelfAPITest) {
-  ASSERT_TRUE(RunExtensionTest("autotest_private",
-                               {.custom_arg = "scrollableShelf"},
-                               {.load_as_component = true}))
-      << message_;
+  ASSERT_TRUE(RunAutotestPrivateExtensionTest("scrollableShelf")) << message_;
 }
 
 IN_PROC_BROWSER_TEST_F(AutotestPrivateApiTest, ShelfAPITest) {
-  ASSERT_TRUE(RunExtensionTest("autotest_private", {.custom_arg = "shelf"},
-                               {.load_as_component = true}))
+  ASSERT_TRUE(RunAutotestPrivateExtensionTest("shelf")) << message_;
+}
+
+class AutotestPrivateHoldingSpaceApiTest
+    : public AutotestPrivateApiTest,
+      public ::testing::WithParamInterface<bool /* mark_time_of_first_add */> {
+};
+
+INSTANTIATE_TEST_CASE_P(All,
+                        AutotestPrivateHoldingSpaceApiTest,
+                        ::testing::Bool() /* mark_time_of_first_add */);
+
+IN_PROC_BROWSER_TEST_P(AutotestPrivateHoldingSpaceApiTest,
+                       HoldingSpaceAPITest) {
+  auto* prefs = browser()->profile()->GetPrefs();
+
+  ash::holding_space_prefs::SetPreviewsEnabled(prefs, false);
+  ash::holding_space_prefs::MarkTimeOfFirstAdd(prefs);
+  ash::holding_space_prefs::MarkTimeOfFirstAvailability(prefs);
+  ash::holding_space_prefs::MarkTimeOfFirstEntry(prefs);
+  ash::holding_space_prefs::MarkTimeOfFirstFilesAppChipPress(prefs);
+  ash::holding_space_prefs::MarkTimeOfFirstPin(prefs);
+
+  const bool mark_time_of_first_add = GetParam();
+
+  base::DictionaryValue options;
+  options.SetBoolean("markTimeOfFirstAdd", mark_time_of_first_add);
+  std::vector<base::Value> suite_args;
+  suite_args.emplace_back(std::move(options));
+
+  ASSERT_TRUE(
+      RunAutotestPrivateExtensionTest("holdingSpace", std::move(suite_args)))
       << message_;
+
+  absl::optional<base::Time> timeOfFirstAdd =
+      ash::holding_space_prefs::GetTimeOfFirstAdd(prefs);
+  absl::optional<base::Time> timeOfFirstAvailability =
+      ash::holding_space_prefs::GetTimeOfFirstAvailability(prefs);
+
+  ASSERT_TRUE(ash::holding_space_prefs::IsPreviewsEnabled(prefs));
+  ASSERT_EQ(timeOfFirstAdd.has_value(), mark_time_of_first_add);
+  ASSERT_NE(timeOfFirstAvailability, absl::nullopt);
+  ASSERT_EQ(ash::holding_space_prefs::GetTimeOfFirstEntry(prefs),
+            absl::nullopt);
+  ASSERT_EQ(ash::holding_space_prefs::GetTimeOfFirstFilesAppChipPress(prefs),
+            absl::nullopt);
+  ASSERT_EQ(ash::holding_space_prefs::GetTimeOfFirstPin(prefs), absl::nullopt);
+
+  if (timeOfFirstAdd)
+    ASSERT_GT(timeOfFirstAdd, timeOfFirstAvailability);
 }
 
 class AutotestPrivateApiOverviewTest : public AutotestPrivateApiTest {
@@ -178,10 +243,7 @@
 };
 
 IN_PROC_BROWSER_TEST_F(AutotestPrivateApiOverviewTest, Default) {
-  ASSERT_TRUE(RunExtensionTest("autotest_private",
-                               {.custom_arg = "overviewDefault"},
-                               {.load_as_component = true}))
-      << message_;
+  ASSERT_TRUE(RunAutotestPrivateExtensionTest("overviewDefault")) << message_;
 }
 
 IN_PROC_BROWSER_TEST_F(AutotestPrivateApiOverviewTest, Drag) {
@@ -206,10 +268,7 @@
   const gfx::Point end_point(start_point.x() + 50, start_point.y());
   generator.MoveTouch(end_point);
 
-  ASSERT_TRUE(RunExtensionTest("autotest_private",
-                               {.custom_arg = "overviewDrag"},
-                               {.load_as_component = true}))
-      << message_;
+  ASSERT_TRUE(RunAutotestPrivateExtensionTest("overviewDrag")) << message_;
 }
 
 IN_PROC_BROWSER_TEST_F(AutotestPrivateApiOverviewTest, LeftSnapped) {
@@ -234,9 +293,7 @@
   generator.MoveTouch(end_point);
   generator.ReleaseTouch();
 
-  ASSERT_TRUE(RunExtensionTest("autotest_private",
-                               {.custom_arg = "splitviewLeftSnapped"},
-                               {.load_as_component = true}))
+  ASSERT_TRUE(RunAutotestPrivateExtensionTest("splitviewLeftSnapped"))
       << message_;
 }
 
@@ -274,9 +331,7 @@
 
 // GetAllEnterprisePolicies Sanity check.
 IN_PROC_BROWSER_TEST_F(AutotestPrivateWithPolicyApiTest, PolicyAPITest) {
-  ASSERT_TRUE(RunExtensionTest("autotest_private",
-                               {.custom_arg = "enterprisePolicies"},
-                               {.load_as_component = true}))
+  ASSERT_TRUE(RunAutotestPrivateExtensionTest("enterprisePolicies"))
       << message_;
 }
 
@@ -329,9 +384,7 @@
       wm::ActivationChangeObserver::ActivationReason::ACTIVATION_CLIENT,
       arc_widget->GetNativeWindow(), arc_widget->GetNativeWindow());
 
-  ASSERT_TRUE(RunExtensionTest("autotest_private",
-                               {.custom_arg = "arcPerformanceTracing"},
-                               {.load_as_component = true}))
+  ASSERT_TRUE(RunAutotestPrivateExtensionTest("arcPerformanceTracing"))
       << message_;
 }
 
@@ -349,10 +402,7 @@
 
 // TODO(crbug.com/1201545): Fix flakiness.
 IN_PROC_BROWSER_TEST_F(AutotestPrivateSystemWebAppsTest, SystemWebApps) {
-  ASSERT_TRUE(RunExtensionTest("autotest_private",
-                               {.custom_arg = "systemWebApps"},
-                               {.load_as_component = true}))
-      << message_;
+  ASSERT_TRUE(RunAutotestPrivateExtensionTest("systemWebApps")) << message_;
 }
 
 }  // namespace extensions
diff --git a/chrome/browser/devtools/protocol/devtools_protocol_browsertest.cc b/chrome/browser/devtools/protocol/devtools_protocol_browsertest.cc
index 624b3ef4..5d91d3b 100644
--- a/chrome/browser/devtools/protocol/devtools_protocol_browsertest.cc
+++ b/chrome/browser/devtools/protocol/devtools_protocol_browsertest.cc
@@ -8,6 +8,7 @@
 #include "base/bind.h"
 #include "base/files/file_util.h"
 #include "base/path_service.h"
+#include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/test/values_test_util.h"
 #include "base/threading/thread_restrictions.h"
@@ -44,7 +45,10 @@
 #include "third_party/boringssl/src/include/openssl/ssl.h"
 
 using DevToolsProtocolTest = DevToolsProtocolTestBase;
+using testing::AllOf;
 using testing::Eq;
+using testing::Contains;
+using testing::Not;
 
 namespace {
 
@@ -112,6 +116,81 @@
   // Should not crash by this point.
 }
 
+IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest,
+                       InputDispatchEventsToCorrectTarget) {
+  Attach();
+
+  std::string setup_logging = R"(
+      window.logs = [];
+      ['dragenter', 'keydown', 'mousedown', 'mouseenter', 'mouseleave',
+       'mousemove', 'mouseout', 'mouseover', 'mouseup', 'click', 'touchcancel',
+       'touchend', 'touchmove', 'touchstart',
+      ].forEach((event) =>
+        window.addEventListener(event, (e) => logs.push(e.type)));)";
+  content::WebContents* target_web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), GURL("about:blank"), WindowOpenDisposition::NEW_FOREGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
+  content::WebContents* other_web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  EXPECT_TRUE(
+      content::EvalJs(target_web_contents, setup_logging).error.empty());
+  EXPECT_TRUE(content::EvalJs(other_web_contents, setup_logging).error.empty());
+
+  base::DictionaryValue params;
+  params.SetStringKey("button", "left");
+  params.SetIntKey("clickCount", 1);
+  params.SetIntKey("x", 100);
+  params.SetIntKey("y", 250);
+  params.SetIntKey("clickCount", 1);
+
+  params.SetStringKey("type", "mousePressed");
+  SendCommandSync("Input.dispatchMouseEvent", params.Clone());
+
+  params.SetStringKey("type", "mouseMoved");
+  params.SetIntKey("y", 270);
+  SendCommandSync("Input.dispatchMouseEvent", params.Clone());
+
+  params.SetStringKey("type", "mouseReleased");
+  SendCommandSync("Input.dispatchMouseEvent", std::move(params));
+
+  params = base::DictionaryValue();
+  params.SetIntKey("x", 100);
+  params.SetIntKey("y", 250);
+  params.SetStringPath("type", "dragEnter");
+  params.SetIntPath("data.dragOperationsMask", 1);
+  params.SetPath("data.items", base::ListValue());
+  SendCommandSync("Input.dispatchDragEvent", std::move(params));
+
+  params = base::DictionaryValue();
+  params.SetIntKey("x", 100);
+  params.SetIntKey("y", 250);
+  SendCommandSync("Input.synthesizeTapGesture", std::move(params));
+
+  params = base::DictionaryValue();
+  params.SetStringKey("type", "keyDown");
+  params.SetStringKey("key", "a");
+  SendCommandSync("Input.dispatchKeyEvent", std::move(params));
+
+  content::EvalJsResult main_target_events =
+      content::EvalJs(target_web_contents, "logs.join(' ')");
+  content::EvalJsResult other_target_events =
+      content::EvalJs(other_web_contents, "logs.join(' ')");
+  // mouse events might happen in the other_target if the real mouse pointer
+  // happens to be over the browser window
+  EXPECT_THAT(base::SplitString(main_target_events.ExtractString(), " ",
+                                base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL),
+              AllOf(Contains("mouseover"), Contains("mousedown"), Contains("mousemove"),
+                    Contains("mouseup"), Contains("click"), Contains("dragenter"),
+                    Contains("keydown")));
+  EXPECT_THAT(base::SplitString(other_target_events.ExtractString(), " ",
+                                base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL),
+              AllOf(Not(Contains("click")), Not(Contains("dragenter")),
+                    Not(Contains("keydown"))));
+}
+
 class DevToolsProtocolTest_AppId : public DevToolsProtocolTest {
  public:
   DevToolsProtocolTest_AppId() {
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 7fc5488..8c893d0 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -2169,11 +2169,6 @@
     "expiry_milestone": 95
   },
   {
-    "name": "enable-layout-ng",
-    "owners": [ "layout-dev@chromium.org" ],
-    "expiry_milestone": 80
-  },
-  {
     "name": "enable-lazy-frame-loading",
     "owners": [ "//components/data_reduction_proxy/OWNERS" ],
     "expiry_milestone": 90
@@ -3133,6 +3128,11 @@
     "expiry_milestone": 100
   },
   {
+    "name": "files-extract-archive",
+    "owners": [ "adanilo", "simmonsjosh@google.com" ],
+    "expiry_milestone": 112
+  },
+  {
     "name": "files-filters-in-recents",
     "owners": [ "simmonsjosh@google.com", "benhartney@google.com" ],
     "expiry_milestone": 98
@@ -3153,11 +3153,6 @@
     "expiry_milestone": 105
   },
   {
-    "name": "files-zip-unpack",
-    "owners": [ "fdegros", "jboulic"],
-    "expiry_milestone": 100
-  },
-  {
     "name": "fill-on-account-select",
     "owners": [ "kazinova@google.com", "vasilii" ],
     "expiry_milestone": 100
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 7471e3a..bb631661 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1003,10 +1003,6 @@
 const char kEnableCanvasContextLostInBackgroundDescription[] =
     "Enable canvas context to be cleared when it is running in background";
 
-const char kEnableLayoutNGName[] = "Enable LayoutNG";
-const char kEnableLayoutNGDescription[] =
-    "Enable Blink's next generation layout engine.";
-
 const char kEnableLazyFrameLoadingName[] = "Enable lazy frame loading";
 const char kEnableLazyFrameLoadingDescription[] =
     "Defers the loading of iframes marked with the attribute 'loading=lazy' "
@@ -4698,6 +4694,10 @@
 const char kFilesBannerFrameworkDescription[] =
     "Enable the updated branner framework in Files app";
 
+const char kFilesExtractArchiveName[] = "Extract archive in Files app";
+const char kFilesExtractArchiveDescription[] =
+    "Enable the simplified archive extraction feature in Files app";
+
 const char kFilesSinglePartitionFormatName[] =
     "Enable Partitioning of Removable Disks.";
 const char kFilesSinglePartitionFormatDescription[] =
@@ -4711,10 +4711,6 @@
 const char kFilesTrashDescription[] =
     "Enable trash for My files volume in Files App.";
 
-const char kFilesZipUnpackName[] = "New ZIP unpacking in Files App";
-const char kFilesZipUnpackDescription[] =
-    "Enable new ZIP archive extraction system in File Manager.";
-
 const char kForceSpectreVariant2MitigationName[] =
     "Force Spectre variant 2 mitigagtion";
 const char kForceSpectreVariant2MitigationDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index e9de20d..15215af 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -548,9 +548,6 @@
 extern const char kEnableGpuServiceLoggingName[];
 extern const char kEnableGpuServiceLoggingDescription[];
 
-extern const char kEnableLayoutNGName[];
-extern const char kEnableLayoutNGDescription[];
-
 extern const char kEnableLazyFrameLoadingName[];
 extern const char kEnableLazyFrameLoadingDescription[];
 
@@ -2700,6 +2697,9 @@
 extern const char kFilesBannerFrameworkName[];
 extern const char kFilesBannerFrameworkDescription[];
 
+extern const char kFilesExtractArchiveName[];
+extern const char kFilesExtractArchiveDescription[];
+
 extern const char kFilesSinglePartitionFormatName[];
 extern const char kFilesSinglePartitionFormatDescription[];
 
@@ -2709,9 +2709,6 @@
 extern const char kFilesTrashName[];
 extern const char kFilesTrashDescription[];
 
-extern const char kFilesZipUnpackName[];
-extern const char kFilesZipUnpackDescription[];
-
 extern const char kFiltersInRecentsName[];
 extern const char kFiltersInRecentsDescription[];
 
diff --git a/chrome/browser/net/network_context_configuration_browsertest.cc b/chrome/browser/net/network_context_configuration_browsertest.cc
index 29b641a..bd3dfa59 100644
--- a/chrome/browser/net/network_context_configuration_browsertest.cc
+++ b/chrome/browser/net/network_context_configuration_browsertest.cc
@@ -612,7 +612,7 @@
         break;
       case NetworkContextType::kSafeBrowsing:
         g_browser_process->safe_browsing_service()
-            ->FlushNetworkInterfaceForTesting();
+            ->FlushNetworkInterfaceForTesting(GetProfile());
         break;
       case NetworkContextType::kProfile:
       case NetworkContextType::kIncognitoProfile:
diff --git a/chrome/browser/policy/policy_network_browsertest.cc b/chrome/browser/policy/policy_network_browsertest.cc
index 28ebc96e..96a6d07 100644
--- a/chrome/browser/policy/policy_network_browsertest.cc
+++ b/chrome/browser/policy/policy_network_browsertest.cc
@@ -215,7 +215,7 @@
     CrashNetworkService();
     // Make sure the NetworkContext has noticed the pipe was closed.
     g_browser_process->safe_browsing_service()
-        ->FlushNetworkInterfaceForTesting();
+        ->FlushNetworkInterfaceForTesting(browser()->profile());
     EXPECT_FALSE(IsQuicEnabledForSafeBrowsing(browser()->profile()));
   }
 }
@@ -284,7 +284,7 @@
     CrashNetworkService();
     // Make sure the NetworkContext has noticed the pipe was closed.
     g_browser_process->safe_browsing_service()
-        ->FlushNetworkInterfaceForTesting();
+        ->FlushNetworkInterfaceForTesting(browser()->profile());
     EXPECT_TRUE(IsQuicEnabledForSafeBrowsing(browser()->profile()));
   }
 }
diff --git a/chrome/browser/resources/chromeos/login/debug/debug.js b/chrome/browser/resources/chromeos/login/debug/debug.js
index f8280213..51e0945 100644
--- a/chrome/browser/resources/chromeos/login/debug/debug.js
+++ b/chrome/browser/resources/chromeos/login/debug/debug.js
@@ -846,14 +846,6 @@
     {
       id: 'lacros-data-migration',
       kind: ScreenKind.OTHER,
-      defaultState: 'default',
-      handledSteps: 'skip-revealed',
-      states: [{
-        id: 'skip-revealed',
-        trigger: (screen) => {
-          screen.showSkipButton();
-        }
-      }],
     },
     {
       id: 'terms-of-service',
diff --git a/chrome/browser/resources/chromeos/login/screens/login/lacros_data_migration.html b/chrome/browser/resources/chromeos/login/screens/login/lacros_data_migration.html
index 327d90601..36b62aa 100644
--- a/chrome/browser/resources/chromeos/login/screens/login/lacros_data_migration.html
+++ b/chrome/browser/resources/chromeos/login/screens/login/lacros_data_migration.html
@@ -23,23 +23,17 @@
           value="[[progressValue_]]">
       </paper-progress>
       <div slot="subtitle">
-        <p>
-          [[i18nDynamic(locale, 'lacrosDataMigrationSubtitle', progressValue_)]]
-        </p>
-        <p hidden="[[!canCancel_]]">
-          [[i18nDynamic(locale, 'lacrosDataMigrationSkipSuggestion')]]
-        </p>
+        [[i18nDynamic(locale, 'lacrosDataMigrationSubtitle', progressValue_)]]
       </div>
       <div slot="content" class="flex layout vertical center center-justified">
         <img src="/images/update_boot.svg"
             class="oobe-illustration" aria-hidden="true">
       </div>
       <!-- Cancel button -->
-      <div slot="bottom-buttons"
+      <div slot="bottom-buttons" hidden="[[!canCancel]]"
           class="flex layout horizontal">
-        <oobe-text-button id="cancelButton"  hidden="[[!canCancel_]]"
-            on-click="onCancelButtonClicked_"
-            text-key="lacrosDataMigrationSkipButton">
+        <oobe-text-button id="cancelButton" on-click="onCancelButtonClicked_"
+            text-key="cancelButton">
         </oobe-text-button>
       </div>
     </oobe-adaptive-dialog>
diff --git a/chrome/browser/resources/chromeos/login/screens/login/lacros_data_migration.js b/chrome/browser/resources/chromeos/login/screens/login/lacros_data_migration.js
index db53f8fce..7b58000 100644
--- a/chrome/browser/resources/chromeos/login/screens/login/lacros_data_migration.js
+++ b/chrome/browser/resources/chromeos/login/screens/login/lacros_data_migration.js
@@ -27,19 +27,16 @@
   constructor() {
     super();
     this.progressValue_ = 0;
-    this.canCancel_ = false;
   }
 
   static get properties() {
     return {
       progressValue_: {type: Number},
-
-      canCancel_: {type: Boolean}
     };
   }
 
   get EXTERNAL_API() {
-    return ['setProgressValue', 'showSkipButton'];
+    return ['setProgressValue'];
   }
 
   /**
@@ -50,13 +47,6 @@
     this.progressValue_ = progress;
   }
 
-  /**
-   * Called to make the skip button visible.
-   */
-  showSkipButton() {
-    this.canCancel_ = true;
-  }
-
   ready() {
     super.ready();
     this.initializeLoginScreen('LacrosDataMigrationScreen', {
@@ -65,7 +55,6 @@
   }
 
   onCancelButtonClicked_() {
-    assert(this.canCancel_);
     this.userActed('cancel');
   }
 }
diff --git a/chrome/browser/resources/print_preview/print_preview.ts b/chrome/browser/resources/print_preview/print_preview.ts
index 557f7ee6..4c984ab8 100644
--- a/chrome/browser/resources/print_preview/print_preview.ts
+++ b/chrome/browser/resources/print_preview/print_preview.ts
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+export {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
 export {PluralStringProxyImpl as PrintPreviewPluralStringProxyImpl} from 'chrome://resources/js/plural_string_proxy.js';
 export {CloudPrintInterface, CloudPrintInterfaceEventType} from './cloud_print_interface.js';
 export {CloudPrintInterfaceImpl} from './cloud_print_interface_impl.js';
@@ -14,7 +15,7 @@
 export {DestinationErrorType, DestinationStore, DestinationStoreEventType} from './data/destination_store.js';
 export {PageLayoutInfo} from './data/document_info.js';
 export {LocalDestinationInfo, ProvisionalDestinationInfo} from './data/local_parsers.js';
-export {CustomMarginsOrientation, Margins, MarginsType} from './data/margins.js';
+export {CustomMarginsOrientation, Margins, MarginsSetting, MarginsType} from './data/margins.js';
 export {MeasurementSystem, MeasurementSystemUnitType} from './data/measurement_system.js';
 export {DuplexMode, DuplexType, getInstance, PrintPreviewModelElement, whenReady} from './data/model.js';
 // <if expr="chromeos or lacros">
diff --git a/chrome/browser/resources/print_preview/ui/button_strip.ts b/chrome/browser/resources/print_preview/ui/button_strip.ts
index 0ce4e91e..71287b5 100644
--- a/chrome/browser/resources/print_preview/ui/button_strip.ts
+++ b/chrome/browser/resources/print_preview/ui/button_strip.ts
@@ -181,5 +181,11 @@
   // </if>
 }
 
+declare global {
+  interface HTMLElementTagNameMap {
+    'print-preview-button-strip': PrintPreviewButtonStripElement;
+  }
+}
+
 customElements.define(
     PrintPreviewButtonStripElement.is, PrintPreviewButtonStripElement);
diff --git a/chrome/browser/resources/print_preview/ui/color_settings.ts b/chrome/browser/resources/print_preview/ui/color_settings.ts
index cd35dd0..5d27b330 100644
--- a/chrome/browser/resources/print_preview/ui/color_settings.ts
+++ b/chrome/browser/resources/print_preview/ui/color_settings.ts
@@ -61,5 +61,11 @@
   }
 }
 
+declare global {
+  interface HTMLElementTagNameMap {
+    'print-preview-color-settings': PrintPreviewColorSettingsElement;
+  }
+}
+
 customElements.define(
     PrintPreviewColorSettingsElement.is, PrintPreviewColorSettingsElement);
diff --git a/chrome/browser/resources/print_preview/ui/copies_settings.ts b/chrome/browser/resources/print_preview/ui/copies_settings.ts
index 4fa78272..23f2e0f 100644
--- a/chrome/browser/resources/print_preview/ui/copies_settings.ts
+++ b/chrome/browser/resources/print_preview/ui/copies_settings.ts
@@ -121,5 +121,11 @@
   }
 }
 
+declare global {
+  interface HTMLElementTagNameMap {
+    'print-preview-copies-settings': PrintPreviewCopiesSettingsElement;
+  }
+}
+
 customElements.define(
     PrintPreviewCopiesSettingsElement.is, PrintPreviewCopiesSettingsElement);
diff --git a/chrome/browser/resources/print_preview/ui/margin_control_container.ts b/chrome/browser/resources/print_preview/ui/margin_control_container.ts
index f0c0428..9f8aafc 100644
--- a/chrome/browser/resources/print_preview/ui/margin_control_container.ts
+++ b/chrome/browser/resources/print_preview/ui/margin_control_container.ts
@@ -516,6 +516,13 @@
   }
 }
 
+declare global {
+  interface HTMLElementTagNameMap {
+    'print-preview-margin-control-container':
+        PrintPreviewMarginControlContainerElement;
+  }
+}
+
 customElements.define(
     PrintPreviewMarginControlContainerElement.is,
     PrintPreviewMarginControlContainerElement);
diff --git a/chrome/browser/resources/settings/chromeos/os_about_page/about_page_browser_proxy.js b/chrome/browser/resources/settings/chromeos/os_about_page/about_page_browser_proxy.js
index 1dfee94ca..3bb3a65 100644
--- a/chrome/browser/resources/settings/chromeos/os_about_page/about_page_browser_proxy.js
+++ b/chrome/browser/resources/settings/chromeos/os_about_page/about_page_browser_proxy.js
@@ -175,6 +175,9 @@
   /** Opens the OS help page. */
   openOsHelpPage() {}
 
+  /** Opens the firmware updates page. */
+  openFirmwareUpdatesPage() {}
+
   /**
    * Checks for available update and applies if it exists.
    */
@@ -272,6 +275,11 @@
   }
 
   /** @override */
+  openFirmwareUpdatesPage() {
+    chrome.send('openFirmwareUpdatesPage');
+  }
+
+  /** @override */
   requestUpdate() {
     chrome.send('requestUpdate');
   }
diff --git a/chrome/browser/resources/settings/chromeos/os_about_page/os_about_page.html b/chrome/browser/resources/settings/chromeos/os_about_page/os_about_page.html
index d36db58..106ec9c 100644
--- a/chrome/browser/resources/settings/chromeos/os_about_page/os_about_page.html
+++ b/chrome/browser/resources/settings/chromeos/os_about_page/os_about_page.html
@@ -161,6 +161,12 @@
           label="$i18n{aboutDiagnostics}" external
           deep-link-focus-id$="[[Setting.kDiagnostics]]">
       </cr-link-row>
+      <cr-link-row class="hr" id="firmwareUpdates"
+          on-click="onFirmwareUpdatesClick_"
+          hidden$="[[!showFirmwareUpdatesApp_]]"
+          label="$i18n{aboutFirmwareUpdates}" external
+          deep-link-focus-id$="[[Setting.kFirmwareUpdates]]">
+      </cr-link-row>
       <cr-link-row class="hr" id="detailed-build-info-trigger"
           on-click="onDetailedBuildInfoClick_"
           label="$i18n{aboutDetailedBuildInfo}"
diff --git a/chrome/browser/resources/settings/chromeos/os_about_page/os_about_page.js b/chrome/browser/resources/settings/chromeos/os_about_page/os_about_page.js
index 02e786f7..e6d1ee7 100644
--- a/chrome/browser/resources/settings/chromeos/os_about_page/os_about_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_about_page/os_about_page.js
@@ -158,6 +158,12 @@
       }
     },
 
+    /** @protected */
+    showFirmwareUpdatesApp_: {
+      type: Boolean,
+      value: () => loadTimeData.getBoolean('isFirmwareUpdaterAppEnabled'),
+    },
+
     /** @private {!Map<string, string>} */
     focusConfig_: {
       type: Object,
@@ -212,6 +218,7 @@
         chromeos.settings.mojom.Setting.kReportAnIssue,
         chromeos.settings.mojom.Setting.kTermsOfService,
         chromeos.settings.mojom.Setting.kDiagnostics,
+        chromeos.settings.mojom.Setting.kFirmwareUpdates,
       ]),
     },
   },
@@ -345,6 +352,13 @@
   },
 
   /** @private */
+  onFirmwareUpdatesClick_() {
+    assert(this.showFirmwareUpdatesApp_);
+    this.aboutBrowserProxy_.openFirmwareUpdatesPage();
+    recordSettingChange(chromeos.settings.mojom.Setting.kFirmwareUpdates);
+  },
+
+  /** @private */
   onRelaunchClick_() {
     recordSettingChange();
     LifetimeBrowserProxyImpl.getInstance().relaunch();
diff --git a/chrome/browser/resources/settings/lazy_load.ts b/chrome/browser/resources/settings/lazy_load.ts
index 04535a17..c774327 100644
--- a/chrome/browser/resources/settings/lazy_load.ts
+++ b/chrome/browser/resources/settings/lazy_load.ts
@@ -135,6 +135,7 @@
 export {SettingsCategoryDefaultRadioGroupElement} from './site_settings/settings_category_default_radio_group.js';
 export {SiteDetailsElement} from './site_settings/site_details.js';
 export {SiteDetailsPermissionElement} from './site_settings/site_details_permission.js';
+export {SiteEntryElement} from './site_settings/site_entry.js';
 export {SiteListElement} from './site_settings/site_list.js';
 export {SiteListEntryElement} from './site_settings/site_list_entry.js';
 export {ContentSettingProvider, CookiePrimarySetting, DefaultContentSetting, OriginInfo, RawChooserException, RawSiteException, RecentSitePermissions, SiteException, SiteGroup, SiteSettingsPrefsBrowserProxy, SiteSettingsPrefsBrowserProxyImpl, ZoomLevelEntry} from './site_settings/site_settings_prefs_browser_proxy.js';
diff --git a/chrome/browser/resources/settings/privacy_page/personalization_options.ts b/chrome/browser/resources/settings/privacy_page/personalization_options.ts
index 51dc54a..7fe8bf9 100644
--- a/chrome/browser/resources/settings/privacy_page/personalization_options.ts
+++ b/chrome/browser/resources/settings/privacy_page/personalization_options.ts
@@ -208,6 +208,13 @@
   }
 
   private shouldShowDriveSuggest_(): boolean {
+    // <if expr="chromeos">
+    if (loadTimeData.getBoolean('syncSettingsCategorizationEnabled') &&
+        loadTimeData.getBoolean('isOSSettings')) {
+      // Should be hidden in OS settings.
+      return false;
+    }
+    // </if>
     return loadTimeData.getBoolean('driveSuggestAvailable') &&
         !!this.syncStatus && !!this.syncStatus.signedIn &&
         this.syncStatus.statusAction !== StatusAction.REAUTHENTICATE;
diff --git a/chrome/browser/resources/settings/site_favicon.ts b/chrome/browser/resources/settings/site_favicon.ts
index d768e898..af244d4 100644
--- a/chrome/browser/resources/settings/site_favicon.ts
+++ b/chrome/browser/resources/settings/site_favicon.ts
@@ -76,4 +76,10 @@
   }
 }
 
+declare global {
+  interface HTMLElementTagNameMap {
+    'site-favicon': SiteFaviconElement;
+  }
+}
+
 customElements.define(SiteFaviconElement.is, SiteFaviconElement);
diff --git a/chrome/browser/resources/settings/site_settings/site_entry.ts b/chrome/browser/resources/settings/site_settings/site_entry.ts
index 2980f0eb..2c2e0a38 100644
--- a/chrome/browser/resources/settings/site_settings/site_entry.ts
+++ b/chrome/browser/resources/settings/site_settings/site_entry.ts
@@ -40,9 +40,11 @@
   },
 }
 
-interface SiteEntryElement {
+export interface SiteEntryElement {
   $: {
     expandIcon: CrIconButtonElement,
+    collapseParent: HTMLElement,
+    cookies: HTMLElement,
     originList: CrLazyRenderElement<IronCollapseElement>,
     toggleButton: HTMLElement,
   };
@@ -59,7 +61,7 @@
       BaseMixinInterface
     };
 
-class SiteEntryElement extends SiteEntryElementBase {
+export class SiteEntryElement extends SiteEntryElementBase {
   static get is() {
     return 'site-entry';
   }
@@ -482,4 +484,10 @@
   }
 }
 
+declare global {
+  interface HTMLElementTagNameMap {
+    'site-entry': SiteEntryElement;
+  }
+}
+
 customElements.define(SiteEntryElement.is, SiteEntryElement);
diff --git a/chrome/browser/safe_browsing/network_context_service.cc b/chrome/browser/safe_browsing/network_context_service.cc
index 35a11cf..cd948f9 100644
--- a/chrome/browser/safe_browsing/network_context_service.cc
+++ b/chrome/browser/safe_browsing/network_context_service.cc
@@ -39,6 +39,10 @@
   return network_context_->GetURLLoaderFactory();
 }
 
+void NetworkContextService::FlushNetworkInterfaceForTesting() {
+  network_context_->FlushForTesting();
+}
+
 network::mojom::NetworkContextParamsPtr
 NetworkContextService::CreateNetworkContextParams() {
   auto params = SystemNetworkContextManager::GetInstance()
diff --git a/chrome/browser/safe_browsing/network_context_service.h b/chrome/browser/safe_browsing/network_context_service.h
index b960f9b..e3f894d 100644
--- a/chrome/browser/safe_browsing/network_context_service.h
+++ b/chrome/browser/safe_browsing/network_context_service.h
@@ -34,6 +34,8 @@
   // Get the URLLoaderFactory associated to this profile
   scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory();
 
+  void FlushNetworkInterfaceForTesting();
+
  private:
   network::mojom::NetworkContextParamsPtr CreateNetworkContextParams();
 
diff --git a/chrome/browser/safe_browsing/safe_browsing_service.cc b/chrome/browser/safe_browsing/safe_browsing_service.cc
index 70eb4999..411050af 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_service.cc
@@ -46,7 +46,6 @@
 #include "components/safe_browsing/buildflags.h"
 #include "components/safe_browsing/content/browser/client_side_phishing_model.h"
 #include "components/safe_browsing/content/browser/safe_browsing_navigation_observer_manager.h"
-#include "components/safe_browsing/content/browser/safe_browsing_network_context.h"
 #include "components/safe_browsing/content/browser/triggers/trigger_manager.h"
 #include "components/safe_browsing/content/browser/ui_manager.h"
 #include "components/safe_browsing/content/browser/web_ui/safe_browsing_ui.h"
@@ -140,12 +139,6 @@
   bool result = base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
   DCHECK(result);
 
-  network_context_ =
-      std::make_unique<safe_browsing::SafeBrowsingNetworkContext>(
-          user_data_dir, features::ShouldTriggerNetworkDataMigration(),
-          base::BindRepeating(&SafeBrowsingService::CreateNetworkContextParams,
-                              base::Unretained(this)));
-
   WebUIInfoSingleton::GetInstance()->set_safe_browsing_service(this);
 
   ui_manager_ = CreateUIManager();
@@ -187,7 +180,6 @@
 
   WebUIInfoSingleton::GetInstance()->set_safe_browsing_service(nullptr);
 
-  network_context_->ServiceShuttingDown();
   proxy_config_monitor_.reset();
 }
 
@@ -214,9 +206,14 @@
   return service->GetURLLoaderFactory();
 }
 
-void SafeBrowsingService::FlushNetworkInterfaceForTesting() {
-  if (network_context_)
-    network_context_->FlushForTesting();
+void SafeBrowsingService::FlushNetworkInterfaceForTesting(
+    content::BrowserContext* browser_context) {
+  NetworkContextService* service =
+      NetworkContextServiceFactory::GetForBrowserContext(browser_context);
+  if (!service)
+    return;
+
+  service->FlushNetworkInterfaceForTesting();
 }
 
 const scoped_refptr<SafeBrowsingUIManager>& SafeBrowsingService::ui_manager()
diff --git a/chrome/browser/safe_browsing/safe_browsing_service.h b/chrome/browser/safe_browsing/safe_browsing_service.h
index 203358d..d55a9fd 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service.h
+++ b/chrome/browser/safe_browsing/safe_browsing_service.h
@@ -69,7 +69,6 @@
 #endif
 class PasswordProtectionService;
 class SafeBrowsingDatabaseManager;
-class SafeBrowsingNetworkContext;
 class SafeBrowsingServiceFactory;
 class SafeBrowsingUIManager;
 class TriggerManager;
@@ -133,7 +132,8 @@
       content::BrowserContext* browser_context);
 
   // Flushes above two interfaces to avoid races in tests.
-  void FlushNetworkInterfaceForTesting();
+  void FlushNetworkInterfaceForTesting(
+      content::BrowserContext* browser_context);
 
   const scoped_refptr<SafeBrowsingUIManager>& ui_manager() const;
 
@@ -272,10 +272,6 @@
 
   std::unique_ptr<ProxyConfigMonitor> proxy_config_monitor_;
 
-  // This owns the URLRequestContext inside the network service. This is used by
-  // SimpleURLLoader for safe browsing requests.
-  std::unique_ptr<safe_browsing::SafeBrowsingNetworkContext> network_context_;
-
   // Provides phishing and malware statistics. Accessed on UI thread.
   std::unique_ptr<PingManager> ping_manager_;
 
diff --git a/chrome/browser/signin/signin_util.cc b/chrome/browser/signin/signin_util.cc
index 399fe13..96763400 100644
--- a/chrome/browser/signin/signin_util.cc
+++ b/chrome/browser/signin/signin_util.cc
@@ -63,12 +63,9 @@
     virtual void OnProfileDeleted(DeleteProfileDialogManager* manager) = 0;
   };
 
-  DeleteProfileDialogManager(Profile* profile,
-                             std::string primary_account_email,
+  DeleteProfileDialogManager(std::string primary_account_email,
                              Delegate* delegate)
-      : profile_(profile),
-        primary_account_email_(primary_account_email),
-        delegate_(delegate) {}
+      : primary_account_email_(primary_account_email), delegate_(delegate) {}
 
   DeleteProfileDialogManager(const DeleteProfileDialogManager&) = delete;
   DeleteProfileDialogManager& operator=(const DeleteProfileDialogManager&) =
@@ -76,16 +73,21 @@
 
   ~DeleteProfileDialogManager() override { BrowserList::RemoveObserver(this); }
 
-  void PresentDialogOnAllBrowserWindows() {
+  void PresentDialogOnAllBrowserWindows(Profile* profile) {
+    DCHECK(profile);
+    DCHECK(profile_path_.empty());
+    profile_path_ = profile->GetPath();
+
     BrowserList::AddObserver(this);
-    Browser* active_browser = chrome::FindLastActiveWithProfile(profile_);
+    Browser* active_browser = chrome::FindLastActiveWithProfile(profile);
     if (active_browser)
       OnBrowserSetLastActive(active_browser);
   }
 
   void OnBrowserSetLastActive(Browser* browser) override {
-    DCHECK(profile_);
-    if (browser->profile() != profile_)
+    DCHECK(!profile_path_.empty());
+
+    if (profile_path_ != browser->profile()->GetPath())
       return;
 
     DCHECK(browser->window()->GetNativeWindow());
@@ -99,15 +101,15 @@
                 gaia::ExtractDomainName(primary_account_email_))));
 
     webui::DeleteProfileAtPath(
-        profile_->GetPath(),
+        profile_path_,
         ProfileMetrics::DELETE_PROFILE_PRIMARY_ACCOUNT_NOT_ALLOWED);
     delegate_->OnProfileDeleted(this);
   }
 
  private:
-  Profile* profile_;
   std::string primary_account_email_;
   Delegate* delegate_;
+  base::FilePath profile_path_;
 };
 #endif  // defined(CAN_DELETE_PROFILE)
 
@@ -145,8 +147,8 @@
     if (delete_profile_dialog_manager_)
       return;
     delete_profile_dialog_manager_ =
-        std::make_unique<DeleteProfileDialogManager>(profile, email, this);
-    delete_profile_dialog_manager_->PresentDialogOnAllBrowserWindows();
+        std::make_unique<DeleteProfileDialogManager>(email, this);
+    delete_profile_dialog_manager_->PresentDialogOnAllBrowserWindows(profile);
   }
 
   void OnProfileDeleted(DeleteProfileDialogManager* dialog_manager) override {
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 5e0423f..327e47c 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -5040,6 +5040,8 @@
       "web_applications/web_app_dialog_utils.h",
       "web_applications/web_app_launch_manager.cc",
       "web_applications/web_app_launch_manager.h",
+      "web_applications/web_app_launch_process.cc",
+      "web_applications/web_app_launch_process.h",
       "web_applications/web_app_launch_utils.cc",
       "web_applications/web_app_launch_utils.h",
       "web_applications/web_app_menu_model.cc",
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.cc b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.cc
index 54c4765d..7865a8e9 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.cc
@@ -332,6 +332,10 @@
   return holding_space_model_.UpdateItem(id);
 }
 
+void HoldingSpaceKeyedService::RemoveAll() {
+  holding_space_model_.RemoveAll();
+}
+
 void HoldingSpaceKeyedService::CancelItem(const HoldingSpaceItem* item) {
   // Currently it is only possible to cancel download type items.
   if (!HoldingSpaceItem::IsDownload(item->type()) || !downloads_delegate_)
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.h b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.h
index 8a77a5f..74db9dd 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.h
+++ b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.h
@@ -147,6 +147,9 @@
   std::unique_ptr<HoldingSpaceModel::ScopedItemUpdate> UpdateItem(
       const std::string& id);
 
+  // Removes all holding space items directly from the model.
+  void RemoveAll();
+
   // Attempts to cancel/pause/resume the specified holding space `item`.
   void CancelItem(const HoldingSpaceItem* item);
   void PauseItem(const HoldingSpaceItem* item);
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc
index 88916f2b..7656d8c 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc
@@ -2013,6 +2013,42 @@
   EXPECT_TRUE(BitmapsAreEqual(actual_image, expected_image));
 }
 
+TEST_F(HoldingSpaceKeyedServiceTest, RemoveAll) {
+  // Wait for the holding space model to attach.
+  TestingProfile* profile = GetProfile();
+  HoldingSpaceModelAttachedWaiter(profile).Wait();
+
+  // Verify the holding space `model` is empty.
+  HoldingSpaceModel* const model = HoldingSpaceController::Get()->model();
+  ASSERT_EQ(0u, model->items().size());
+
+  // Create a test mount point.
+  std::unique_ptr<ScopedTestMountPoint> mount_point =
+      ScopedTestMountPoint::CreateAndMountDownloads(profile);
+  ASSERT_TRUE(mount_point->IsValid());
+
+  auto* service =
+      HoldingSpaceKeyedServiceFactory::GetInstance()->GetService(profile);
+
+  // Create files on the file system.
+  const base::FilePath download_path = mount_point->CreateFile(
+      /*relative_path=*/base::FilePath("bar"), /*content=*/"bar");
+  const base::FilePath pinned_file_path = mount_point->CreateFile(
+      /*relative_path=*/base::FilePath("foo"), /*content=*/"foo");
+
+  // Add them both to holding space, one in pinned files the other in downloads.
+  service->AddDownload(HoldingSpaceItem::Type::kDownload, download_path);
+  service->AddPinnedFiles(
+      {file_manager::util::GetFileManagerFileSystemContext(profile)
+           ->CrackURLInFirstPartyContext(
+               holding_space_util::ResolveFileSystemUrl(profile,
+                                                        pinned_file_path))});
+
+  ASSERT_EQ(2u, model->items().size());
+  service->RemoveAll();
+  EXPECT_EQ(0u, model->items().size());
+}
+
 // Base class for tests of in-progress downloads integration. Parameterized by
 // whether the holding space in-progress downloads feature is enabled.
 class HoldingSpaceKeyedServiceInProgressDownloadsTest
diff --git a/chrome/browser/ui/chrome_pages.cc b/chrome/browser/ui/chrome_pages.cc
index 57f5b5622..2ef9f7a 100644
--- a/chrome/browser/ui/chrome_pages.cc
+++ b/chrome/browser/ui/chrome_pages.cc
@@ -467,6 +467,12 @@
   LaunchSystemWebAppAsync(profile, web_app::SystemAppType::DIAGNOSTICS);
 }
 
+void ShowFirmwareUpdatesApp(Profile* profile) {
+  DCHECK(base::FeatureList::IsEnabled(chromeos::features::kFirmwareUpdaterApp));
+
+  LaunchSystemWebAppAsync(profile, web_app::SystemAppType::FIRMWARE_UPDATE);
+}
+
 GURL GetOSSettingsUrl(const std::string& sub_page) {
   DCHECK(sub_page.empty() || chromeos::settings::IsOSSettingsSubPage(sub_page))
       << sub_page;
diff --git a/chrome/browser/ui/chrome_pages.h b/chrome/browser/ui/chrome_pages.h
index 4b0c7c03..e0c47e5 100644
--- a/chrome/browser/ui/chrome_pages.h
+++ b/chrome/browser/ui/chrome_pages.h
@@ -173,6 +173,9 @@
 void ShowScanningApp(Profile* profile);
 
 void ShowDiagnosticsApp(Profile* profile);
+
+void ShowFirmwareUpdatesApp(Profile* profile);
+
 #endif
 
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
diff --git a/chrome/browser/ui/views/autofill/payments/local_card_migration_browsertest.cc b/chrome/browser/ui/views/autofill/payments/local_card_migration_browsertest.cc
index d315803..6fb7cc85 100644
--- a/chrome/browser/ui/views/autofill/payments/local_card_migration_browsertest.cc
+++ b/chrome/browser/ui/views/autofill/payments/local_card_migration_browsertest.cc
@@ -27,6 +27,7 @@
 #include "chrome/browser/ui/autofill/payments/local_card_migration_dialog_controller_impl.h"
 #include "chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/location_bar/location_bar.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -172,25 +173,8 @@
         "components/test/data/autofill");
     embedded_test_server()->StartAcceptingConnections();
 
-    SyncServiceFactory::GetAsSyncServiceImplForProfile(browser()->profile())
-        ->OverrideNetworkForTest(
-            fake_server::CreateFakeServerHttpPostProviderFactory(
-                GetFakeServer()->AsWeakPtr()));
-    std::string username;
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-    // In ChromeOS browser tests, the profile may already by authenticated with
-    // stub account |user_manager::kStubUserEmail|.
-    CoreAccountInfo info =
-        IdentityManagerFactory::GetForProfile(browser()->profile())
-            ->GetPrimaryAccountInfo(signin::ConsentLevel::kSync);
-    username = info.email;
-#endif
-    if (username.empty())
-      username = "user@gmail.com";
-
-    harness_ = SyncServiceImplHarness::Create(
-        browser()->profile(), username, "password",
-        SyncServiceImplHarness::SigninType::FAKE_SIGNIN);
+    ASSERT_TRUE(SetupClients());
+    chrome::NewTab(GetBrowser(0));
 
     // Set up the URL loader factory for the payments client so we can intercept
     // those network requests too.
@@ -214,19 +198,18 @@
             ->local_card_migration_manager_.get();
 
     local_card_migration_manager_->SetEventObserverForTesting(this);
-    personal_data_ =
-        PersonalDataManagerFactory::GetForProfile(browser()->profile());
+    personal_data_ = PersonalDataManagerFactory::GetForProfile(GetProfile(0));
 
     // Wait for Personal Data Manager to be fully loaded to prevent that
     // spurious notifications deceive the tests.
-    WaitForPersonalDataManagerToBeLoaded(browser()->profile());
+    WaitForPersonalDataManagerToBeLoaded(GetProfile(0));
 
     // Set up the fake geolocation data.
     geolocation_overrider_ =
         std::make_unique<device::ScopedGeolocationOverrider>(
             kFakeGeolocationLatitude, kFakeGeolocationLongitude);
 
-    ASSERT_TRUE(harness_->SetupSync());
+    ASSERT_TRUE(SetupSync());
 
     // Set the billing_customer_number to designate existence of a Payments
     // account.
@@ -249,7 +232,7 @@
   void SetPaymentsCustomerData(const PaymentsCustomerData& customer_data) {
     scoped_refptr<AutofillWebDataService> wds =
         WebDataServiceFactory::GetAutofillWebDataForProfile(
-            browser()->profile(), ServiceAccessType::EXPLICIT_ACCESS);
+            GetProfile(0), ServiceAccessType::EXPLICIT_ACCESS);
     base::RunLoop loop;
     wds->GetDBTaskRunner()->PostTaskAndReply(
         FROM_HERE,
@@ -275,9 +258,9 @@
 
   void NavigateTo(const std::string& file_path) {
     ASSERT_TRUE(ui_test_utils::NavigateToURL(
-        browser(), file_path.find("data:") == 0U
-                       ? GURL(file_path)
-                       : embedded_test_server()->GetURL(file_path)));
+        GetBrowser(0), file_path.find("data:") == 0U
+                           ? GURL(file_path)
+                           : embedded_test_server()->GetURL(file_path)));
   }
 
   void OnDecideToRequestLocalCardMigration() override {
@@ -314,7 +297,7 @@
     if (set_nickname)
       local_card.SetNickname(u"card nickname");
 
-    AddTestCreditCard(browser()->profile(), local_card);
+    AddTestCreditCard(GetProfile(0), local_card);
     return local_card;
   }
 
@@ -326,7 +309,7 @@
                          card_number.substr(0, 12));
     server_card.set_record_type(CreditCard::FULL_SERVER_CARD);
     server_card.set_server_id("full_id_" + card_number);
-    AddTestServerCreditCard(browser()->profile(), server_card);
+    AddTestServerCreditCard(GetProfile(0), server_card);
     return server_card;
   }
 
@@ -468,7 +451,7 @@
 
   PageActionIconView* GetLocalCardMigrationIconView() {
     BrowserView* browser_view =
-        BrowserView::GetBrowserViewForBrowser(browser());
+        BrowserView::GetBrowserViewForBrowser(GetBrowser(0));
     PageActionIconView* icon =
         browser_view->toolbar_button_provider()->GetPageActionIconView(
             PageActionIconType::kLocalCardMigration);
@@ -499,7 +482,7 @@
   }
 
   content::WebContents* GetActiveWebContents() {
-    return browser()->tab_strip_model()->GetActiveWebContents();
+    return GetBrowser(0)->tab_strip_model()->GetActiveWebContents();
   }
 
   void ResetEventWaiterForSequence(std::list<DialogEvent> event_sequence) {
@@ -513,15 +496,13 @@
     return &test_url_loader_factory_;
   }
 
-  void WaitForCardDeletion() {
-    WaitForPersonalDataChange(browser()->profile());
-  }
+  void WaitForCardDeletion() { WaitForPersonalDataChange(GetProfile(0)); }
 
   void WaitForAnimationToComplete() {
     if (base::FeatureList::IsEnabled(
             features::kAutofillEnableToolbarStatusChip)) {
       views::test::WaitForAnimatingLayoutManager(
-          BrowserView::GetBrowserViewForBrowser(browser())
+          BrowserView::GetBrowserViewForBrowser(GetBrowser(0))
               ->toolbar()
               ->toolbar_account_icon_container());
     }
@@ -532,8 +513,6 @@
   PersonalDataManager* personal_data_;
   PersonalDataLoadedObserverMock personal_data_observer_;
 
-  std::unique_ptr<SyncServiceImplHarness> harness_;
-
  private:
   std::unique_ptr<autofill::EventWaiter<DialogEvent>> event_waiter_;
   std::unique_ptr<net::FakeURLFetcherFactory> url_fetcher_factory_;
@@ -1062,7 +1041,8 @@
 #endif
 IN_PROC_BROWSER_TEST_F(LocalCardMigrationBrowserTestForStatusChip,
                        MAYBE_ActivateFirstInactiveBubbleForAccessibility) {
-  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
+  BrowserView* browser_view =
+      BrowserView::GetBrowserViewForBrowser(GetBrowser(0));
   ToolbarView* toolbar_view = browser_view->toolbar();
   EXPECT_FALSE(toolbar_view->toolbar_account_icon_container()
                    ->page_action_icon_controller()
@@ -1102,8 +1082,10 @@
   EXPECT_TRUE(GetLocalCardMigrationIconView()->GetVisible());
   EXPECT_TRUE(GetLocalCardMigrationOfferBubbleViews()->GetVisible());
 
-  AddTabAtIndex(1, GURL("http://example.com/"), ui::PAGE_TRANSITION_TYPED);
-  TabStripModel* tab_model = browser()->tab_strip_model();
+  AddTabAtIndexToBrowser(GetBrowser(0), 1, GURL("http://example.com/"),
+                         ui::PAGE_TRANSITION_TYPED,
+                         /*check_navigation_success=*/true);
+  TabStripModel* tab_model = GetBrowser(0)->tab_strip_model();
   tab_model->ActivateTabAt(1, {TabStripModel::GestureType::kOther});
   WaitForAnimationToComplete();
 
@@ -1196,7 +1178,7 @@
   UseCardAndWaitForMigrationOffer(kFirstCardNumber);
   views::test::WidgetDestroyedWaiter destroyed_waiter(
       GetLocalCardMigrationOfferBubbleViews()->GetWidget());
-  browser()->tab_strip_model()->CloseAllTabs();
+  GetBrowser(0)->tab_strip_model()->CloseAllTabs();
   destroyed_waiter.Wait();
 
   histogram_tester.ExpectUniqueSample(
diff --git a/chrome/browser/ui/web_applications/web_app_launch_manager.cc b/chrome/browser/ui/web_applications/web_app_launch_manager.cc
index df2c401..77991690 100644
--- a/chrome/browser/ui/web_applications/web_app_launch_manager.cc
+++ b/chrome/browser/ui/web_applications/web_app_launch_manager.cc
@@ -11,79 +11,31 @@
 #include "base/files/file_path.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/no_destructor.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/time/time.h"
-#include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
-#include "chrome/browser/app_mode/app_mode_utils.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
 #include "chrome/browser/apps/app_service/launch_utils.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/ui/browser_list.h"
-#include "chrome/browser/ui/browser_navigator.h"
-#include "chrome/browser/ui/browser_navigator_params.h"
-#include "chrome/browser/ui/browser_window.h"
-#include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/ui/web_applications/app_browser_controller.h"
-#include "chrome/browser/ui/web_applications/share_target_utils.h"
-#include "chrome/browser/ui/web_applications/system_web_app_ui_utils.h"
+#include "chrome/browser/ui/web_applications/web_app_launch_process.h"
 #include "chrome/browser/ui/web_applications/web_app_launch_utils.h"
-#include "chrome/browser/web_applications/os_integration_manager.h"
-#include "chrome/browser/web_applications/system_web_apps/system_web_app_manager.h"
 #include "chrome/browser/web_applications/web_app.h"
 #include "chrome/browser/web_applications/web_app_constants.h"
 #include "chrome/browser/web_applications/web_app_helpers.h"
 #include "chrome/browser/web_applications/web_app_install_utils.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/browser/web_applications/web_app_registrar.h"
-#include "chrome/browser/web_applications/web_app_sync_bridge.h"
-#include "chrome/browser/web_applications/web_app_tab_helper.h"
-#include "chrome/browser/web_launch/web_launch_files_helper.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_switches.h"
-#include "components/services/app_service/public/cpp/intent_util.h"
-#include "components/site_engagement/content/site_engagement_service.h"
-#include "components/webapps/browser/banners/app_banner_settings_helper.h"
-#include "content/public/browser/page_navigator.h"
 #include "content/public/browser/render_view_host.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/common/referrer.h"
 #include "extensions/common/constants.h"
 #include "third_party/blink/public/common/custom_handlers/protocol_handler_utils.h"
-#include "ui/base/page_transition_types.h"
 #include "ui/base/window_open_disposition.h"
-#include "ui/display/display.h"
-#include "ui/display/scoped_display_for_new_windows.h"
-#include "ui/display/screen.h"
 #include "url/gurl.h"
 
 namespace web_app {
 
 namespace {
 
-// TODO(crbug.com/1019239): Passing a WebAppProvider seems to be a bit of an
-// anti-pattern. We should refactor this and other existing functions in this
-// file to receive an OsIntegrationManager instead.
-absl::optional<GURL> GetProtocolHandlingTranslatedUrl(
-    WebAppProvider& provider,
-    const apps::AppLaunchParams& params) {
-  if (!params.protocol_handler_launch_url.has_value())
-    return absl::nullopt;
-
-  GURL protocol_url(params.protocol_handler_launch_url.value());
-  if (!protocol_url.is_valid())
-    return absl::nullopt;
-
-  absl::optional<GURL> translated_url =
-      provider.os_integration_manager().TranslateProtocolUrl(params.app_id,
-                                                             protocol_url);
-
-  return translated_url;
-}
-
 WebAppLaunchManager::OpenApplicationCallback&
 GetOpenApplicationCallbackForTesting() {
   static base::NoDestructor<WebAppLaunchManager::OpenApplicationCallback>
@@ -91,323 +43,6 @@
   return *callback;
 }
 
-class LaunchProcess {
- public:
-  LaunchProcess(Profile& profile, const apps::AppLaunchParams& params);
-
-  content::WebContents* Run();
-
- private:
-  const apps::ShareTarget* MaybeGetShareTarget() const;
-  std::tuple<GURL, bool /*is_file_handling*/> GetLaunchUrl(
-      const apps::ShareTarget* share_target) const;
-  WindowOpenDisposition GetNavigationDisposition(bool is_new_browser) const;
-  content::WebContents* MaybeLaunchSystemWebApp(const GURL& launch_url);
-  std::tuple<Browser*, bool /*is_new_browser*/> EnsureBrowser();
-  LaunchHandler::RouteTo GetLaunchRouteTo() const;
-
-  Browser* MaybeFindBrowserForLaunch() const;
-  Browser* CreateBrowserForLaunch();
-  content::WebContents* NavigateBrowser(Browser* browser,
-                                        bool is_new_browser,
-                                        const GURL& launch_url,
-                                        const apps::ShareTarget* share_target);
-  void MaybeEnqueueWebLaunchParams(const GURL& launch_url,
-                                   bool is_file_handling,
-                                   content::WebContents* web_contents);
-  void RecordMetrics(const GURL& launch_url,
-                     content::WebContents* web_contents);
-
-  Profile& profile_;
-  WebAppProvider& provider_;
-  const apps::AppLaunchParams& params_;
-  const WebApp* web_app_ = nullptr;
-};
-
-LaunchProcess::LaunchProcess(Profile& profile,
-                             const apps::AppLaunchParams& params)
-    : profile_(profile),
-      provider_(*WebAppProvider::GetForLocalAppsUnchecked(&profile)),
-      params_(params),
-      web_app_(provider_.registrar().GetAppById(params.app_id)) {}
-
-content::WebContents* LaunchProcess::Run() {
-  if (Browser::GetCreationStatusForProfile(&profile_) !=
-          Browser::CreationStatus::kOk ||
-      !provider_.registrar().IsInstalled(params_.app_id)) {
-    return nullptr;
-  }
-
-  // Place new windows on the specified display.
-  display::ScopedDisplayForNewWindows scoped_display(params_.display_id);
-
-  const apps::ShareTarget* share_target = MaybeGetShareTarget();
-  GURL launch_url;
-  bool is_file_handling = false;
-  std::tie(launch_url, is_file_handling) = GetLaunchUrl(share_target);
-
-#if defined(OS_CHROMEOS)
-  // TODO(crbug.com/1265381): URL Handlers allows web apps to be opened with
-  // associated origin URLs. There's no utility function to test whether a URL
-  // is in a web app's extended scope at the moment.
-  // Because URL Handlers is not implemented for Chrome OS we can perform this
-  // DCHECK on the basic scope.
-  DCHECK(provider_.registrar().IsUrlInAppScope(launch_url, params_.app_id));
-#endif
-
-  // System Web Apps have their own launch code path.
-  // TODO(crbug.com/1231886): Don't use a separate code path so that SWAs can
-  // maintain feature parity with regular web apps (e.g. launch_handler
-  // behaviours).
-  content::WebContents* web_contents = MaybeLaunchSystemWebApp(launch_url);
-  if (web_contents)
-    return web_contents;
-
-  Browser* browser = nullptr;
-  bool is_new_browser;
-  std::tie(browser, is_new_browser) = EnsureBrowser();
-
-  web_contents =
-      NavigateBrowser(browser, is_new_browser, launch_url, share_target);
-  if (!web_contents)
-    return nullptr;
-
-  MaybeEnqueueWebLaunchParams(launch_url, is_file_handling, web_contents);
-
-  RecordMetrics(launch_url, web_contents);
-
-  return web_contents;
-}
-
-const apps::ShareTarget* LaunchProcess::MaybeGetShareTarget() const {
-  DCHECK(web_app_);
-  bool is_share_intent =
-      params_.intent &&
-      (params_.intent->action == apps_util::kIntentActionSend ||
-       params_.intent->action == apps_util::kIntentActionSendMultiple);
-  return is_share_intent && web_app_->share_target().has_value()
-             ? &web_app_->share_target().value()
-             : nullptr;
-}
-
-std::tuple<GURL, bool /*is_file_handling*/> LaunchProcess::GetLaunchUrl(
-    const apps::ShareTarget* share_target) const {
-  DCHECK(web_app_);
-  GURL launch_url;
-  bool is_file_handling = false;
-  bool is_note_taking_intent =
-      params_.intent &&
-      params_.intent->action == apps_util::kIntentActionCreateNote;
-
-  if (!params_.override_url.is_empty()) {
-    launch_url = params_.override_url;
-  } else if (params_.url_handler_launch_url.has_value() &&
-             params_.url_handler_launch_url->is_valid()) {
-    // Handle url_handlers launch.
-    launch_url = params_.url_handler_launch_url.value();
-  } else if (absl::optional<GURL> file_handler_url =
-                 provider_.os_integration_manager().GetMatchingFileHandlerURL(
-                     params_.app_id, params_.launch_files)) {
-    // Handle file_handlers launch.
-    launch_url = file_handler_url.value();
-    is_file_handling = true;
-  } else if (absl::optional<GURL> protocol_handler_translated_url =
-                 GetProtocolHandlingTranslatedUrl(provider_, params_)) {
-    // Handle protocol_handlers launch.
-    launch_url = protocol_handler_translated_url.value();
-  } else if (share_target) {
-    // Handle share_target launch.
-    launch_url = share_target->action;
-  } else if (is_note_taking_intent &&
-             web_app_->note_taking_new_note_url().is_valid()) {
-    // Handle Create Note launch.
-    launch_url = web_app_->note_taking_new_note_url();
-  } else {
-    // This is a default launch.
-    launch_url = provider_.registrar().GetAppLaunchUrl(params_.app_id);
-  }
-  DCHECK(launch_url.is_valid());
-
-  return {launch_url, is_file_handling};
-}
-
-WindowOpenDisposition LaunchProcess::GetNavigationDisposition(
-    bool is_new_browser) const {
-  if (is_new_browser) {
-    // By opening a new window we've already performed part of a "disposition",
-    // the only remaining thing for Navigate() to do is navigate the new window.
-    return WindowOpenDisposition::CURRENT_TAB;
-    // TODO(crbug.com/1200944): Use NEW_FOREGROUND_TAB instead of CURRENT_TAB.
-    // The window has no tabs so it doesn't make sense to open the "current"
-    // tab. We use it anyway because it happens to work.
-    // If NEW_FOREGROUND_TAB is used the the WindowCanOpenTabs() check fails
-    // when `launch_url` is out of scope for web app windows causing it to
-    // open another separate browser window. It should be updated to check the
-    // extended scope.
-  }
-
-  // If launch handler is routing to an existing client, we want to use the
-  // existing WebContents rather than opening a new tab.
-  if (GetLaunchRouteTo() == LaunchHandler::RouteTo::kExistingClient) {
-    return WindowOpenDisposition::CURRENT_TAB;
-  }
-
-  // Only CURRENT_TAB and NEW_FOREGROUND_TAB dispositions are supported for web
-  // app launches.
-  return params_.disposition == WindowOpenDisposition::CURRENT_TAB
-             ? WindowOpenDisposition::CURRENT_TAB
-             : WindowOpenDisposition::NEW_FOREGROUND_TAB;
-}
-
-LaunchHandler::RouteTo LaunchProcess::GetLaunchRouteTo() const {
-  DCHECK(web_app_);
-  LaunchHandler launch_handler =
-      web_app_->launch_handler().value_or(LaunchHandler());
-  if (launch_handler.route_to == LaunchHandler::RouteTo::kAuto)
-    return LaunchHandler::RouteTo::kNewClient;
-  return launch_handler.route_to;
-}
-
-content::WebContents* LaunchProcess::MaybeLaunchSystemWebApp(
-    const GURL& launch_url) {
-  absl::optional<SystemAppType> system_app_type =
-      GetSystemWebAppTypeForAppId(&profile_, params_.app_id);
-  if (!system_app_type)
-    return nullptr;
-
-  Browser* browser =
-      LaunchSystemWebAppImpl(&profile_, *system_app_type, launch_url, params_);
-  return browser->tab_strip_model()->GetActiveWebContents();
-}
-
-std::tuple<Browser*, bool /*is_new_browser*/> LaunchProcess::EnsureBrowser() {
-  Browser* browser = MaybeFindBrowserForLaunch();
-  bool is_new_browser = false;
-  if (browser) {
-    browser->window()->Activate();
-  } else {
-    browser = CreateBrowserForLaunch();
-    is_new_browser = true;
-  }
-  browser->window()->Show();
-  return {browser, is_new_browser};
-}
-
-Browser* LaunchProcess::MaybeFindBrowserForLaunch() const {
-  if (params_.container == apps::mojom::LaunchContainer::kLaunchContainerTab) {
-    return chrome::FindTabbedBrowser(
-        &profile_, /*match_original_profiles=*/false,
-        display::Screen::GetScreen()->GetDisplayForNewWindows().id());
-  }
-
-  if (!provider_.registrar().IsTabbedWindowModeEnabled(params_.app_id) &&
-      GetLaunchRouteTo() == LaunchHandler::RouteTo::kNewClient) {
-    return nullptr;
-  }
-
-  for (Browser* browser : *BrowserList::GetInstance()) {
-    if (browser->profile() == &profile_ &&
-        AppBrowserController::IsForWebApp(browser, params_.app_id)) {
-      return browser;
-    }
-  }
-
-  return nullptr;
-}
-
-Browser* LaunchProcess::CreateBrowserForLaunch() {
-  if (params_.container == apps::mojom::LaunchContainer::kLaunchContainerTab) {
-    return Browser::Create(Browser::CreateParams(Browser::TYPE_NORMAL,
-                                                 &profile_,
-                                                 /*user_gesture=*/true));
-  }
-
-  return CreateWebApplicationWindow(&profile_, params_.app_id,
-                                    params_.disposition, params_.restore_id);
-}
-
-content::WebContents* LaunchProcess::NavigateBrowser(
-    Browser* browser,
-    bool is_new_browser,
-    const GURL& launch_url,
-    const apps::ShareTarget* share_target) {
-  WindowOpenDisposition navigation_disposition =
-      GetNavigationDisposition(is_new_browser);
-
-  if (share_target) {
-    NavigateParams nav_params =
-        NavigateParamsForShareTarget(browser, *share_target, *params_.intent);
-    nav_params.disposition = navigation_disposition;
-    return NavigateWebAppUsingParams(params_.app_id, nav_params);
-  }
-
-  TabStripModel* const tab_strip = browser->tab_strip_model();
-  if (tab_strip->empty() ||
-      navigation_disposition != WindowOpenDisposition::CURRENT_TAB) {
-    return NavigateWebApplicationWindow(browser, params_.app_id, launch_url,
-                                        navigation_disposition);
-  }
-
-  content::WebContents* existing_tab = tab_strip->GetActiveWebContents();
-  DCHECK(existing_tab);
-  const int tab_index = tab_strip->GetIndexOfWebContents(existing_tab);
-
-  existing_tab->OpenURL(content::OpenURLParams(
-      launch_url,
-      content::Referrer::SanitizeForRequest(
-          launch_url,
-          content::Referrer(existing_tab->GetURL(),
-                            network::mojom::ReferrerPolicy::kDefault)),
-      navigation_disposition, ui::PAGE_TRANSITION_AUTO_BOOKMARK,
-      /*is_renderer_initiated=*/false));
-
-  content::WebContents* web_contents = tab_strip->GetActiveWebContents();
-  tab_strip->ActivateTabAt(tab_index, {TabStripModel::GestureType::kOther});
-  WebAppTabHelper::FromWebContents(web_contents)->SetAppId(params_.app_id);
-  return web_contents;
-}
-
-void LaunchProcess::MaybeEnqueueWebLaunchParams(
-    const GURL& launch_url,
-    bool is_file_handling,
-    content::WebContents* web_contents) {
-  if (is_file_handling || web_app_->launch_handler().has_value()) {
-    web_launch::WebLaunchFilesHelper::EnqueueLaunchParams(
-        web_contents, provider_.registrar().GetAppScope(web_app_->app_id()),
-        /*await_navigation=*/true, launch_url,
-        /*launch_dir=*/{},
-        is_file_handling ? params_.launch_files
-                         : std::vector<base::FilePath>());
-  }
-}
-
-void LaunchProcess::RecordMetrics(const GURL& launch_url,
-                                  content::WebContents* web_contents) {
-  // TODO(crbug.com/1014328): Populate WebApp metrics instead of Extensions.
-  if (params_.container == apps::mojom::LaunchContainer::kLaunchContainerTab) {
-    UMA_HISTOGRAM_ENUMERATION("Extensions.AppTabLaunchType",
-                              extensions::LAUNCH_TYPE_REGULAR, 100);
-  } else if (params_.container ==
-             apps::mojom::LaunchContainer::kLaunchContainerWindow) {
-    RecordAppWindowLaunch(&profile_, params_.app_id);
-  }
-  UMA_HISTOGRAM_ENUMERATION("Extensions.BookmarkAppLaunchSource",
-                            apps::GetAppLaunchSource(params_.launch_source));
-  UMA_HISTOGRAM_ENUMERATION("Extensions.BookmarkAppLaunchContainer",
-                            params_.container);
-
-  // Record the launch time in the site engagement service. A recent web
-  // app launch will provide an engagement boost to the origin.
-  site_engagement::SiteEngagementService::Get(&profile_)
-      ->SetLastShortcutLaunchTime(web_contents, launch_url);
-  provider_.sync_bridge().SetAppLastLaunchTime(params_.app_id,
-                                               base::Time::Now());
-  // Refresh the app banner added to homescreen event. The user may have
-  // cleared their browsing data since installing the app, which removes the
-  // event and will potentially permit a banner to be shown for the site.
-  RecordAppBanner(web_contents, launch_url);
-}
-
 }  // namespace
 
 WebAppLaunchManager::WebAppLaunchManager(Profile* profile)
@@ -421,7 +56,7 @@
   if (GetOpenApplicationCallbackForTesting())
     return GetOpenApplicationCallbackForTesting().Run(std::move(params));
 
-  return LaunchProcess(*profile_, params).Run();
+  return WebAppLaunchProcess(*profile_, params).Run();
 }
 
 void WebAppLaunchManager::LaunchApplication(
diff --git a/chrome/browser/ui/web_applications/web_app_launch_process.cc b/chrome/browser/ui/web_applications/web_app_launch_process.cc
new file mode 100644
index 0000000..05aaa20a
--- /dev/null
+++ b/chrome/browser/ui/web_applications/web_app_launch_process.cc
@@ -0,0 +1,345 @@
+// 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/web_applications/web_app_launch_process.h"
+
+#include "base/files/file_path.h"
+#include "base/metrics/histogram_macros.h"
+#include "build/build_config.h"
+#include "chrome/browser/apps/app_service/app_launch_params.h"
+#include "chrome/browser/apps/app_service/launch_utils.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/browser_navigator.h"
+#include "chrome/browser/ui/browser_navigator_params.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/web_applications/app_browser_controller.h"
+#include "chrome/browser/ui/web_applications/share_target_utils.h"
+#include "chrome/browser/ui/web_applications/system_web_app_ui_utils.h"
+#include "chrome/browser/ui/web_applications/web_app_launch_utils.h"
+#include "chrome/browser/web_applications/os_integration_manager.h"
+#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
+#include "chrome/browser/web_applications/web_app_registrar.h"
+#include "chrome/browser/web_applications/web_app_sync_bridge.h"
+#include "chrome/browser/web_applications/web_app_tab_helper.h"
+#include "chrome/browser/web_launch/web_launch_files_helper.h"
+#include "components/services/app_service/public/cpp/intent_util.h"
+#include "components/site_engagement/content/site_engagement_service.h"
+#include "extensions/common/constants.h"
+#include "ui/display/scoped_display_for_new_windows.h"
+#include "url/gurl.h"
+
+namespace web_app {
+
+namespace {
+
+absl::optional<GURL> GetProtocolHandlingTranslatedUrl(
+    OsIntegrationManager& os_integration_manager,
+    const apps::AppLaunchParams& params) {
+  if (!params.protocol_handler_launch_url.has_value())
+    return absl::nullopt;
+
+  GURL protocol_url(params.protocol_handler_launch_url.value());
+  if (!protocol_url.is_valid())
+    return absl::nullopt;
+
+  absl::optional<GURL> translated_url =
+      os_integration_manager.TranslateProtocolUrl(params.app_id, protocol_url);
+
+  return translated_url;
+}
+
+}  // namespace
+
+WebAppLaunchProcess::WebAppLaunchProcess(Profile& profile,
+                                         const apps::AppLaunchParams& params)
+    : profile_(profile),
+      provider_(*WebAppProvider::GetForLocalAppsUnchecked(&profile)),
+      params_(params),
+      web_app_(provider_.registrar().GetAppById(params.app_id)) {}
+
+content::WebContents* WebAppLaunchProcess::Run() {
+  if (Browser::GetCreationStatusForProfile(&profile_) !=
+          Browser::CreationStatus::kOk ||
+      !provider_.registrar().IsInstalled(params_.app_id)) {
+    return nullptr;
+  }
+
+  // Place new windows on the specified display.
+  display::ScopedDisplayForNewWindows scoped_display(params_.display_id);
+
+  const apps::ShareTarget* share_target = MaybeGetShareTarget();
+  GURL launch_url;
+  bool is_file_handling = false;
+  std::tie(launch_url, is_file_handling) = GetLaunchUrl(share_target);
+
+#if defined(OS_CHROMEOS)
+  // TODO(crbug.com/1265381): URL Handlers allows web apps to be opened with
+  // associated origin URLs. There's no utility function to test whether a URL
+  // is in a web app's extended scope at the moment.
+  // Because URL Handlers is not implemented for Chrome OS we can perform this
+  // DCHECK on the basic scope.
+  DCHECK(provider_.registrar().IsUrlInAppScope(launch_url, params_.app_id));
+#endif
+
+  // System Web Apps have their own launch code path.
+  // TODO(crbug.com/1231886): Don't use a separate code path so that SWAs can
+  // maintain feature parity with regular web apps (e.g. launch_handler
+  // behaviours).
+  content::WebContents* web_contents = MaybeLaunchSystemWebApp(launch_url);
+  if (web_contents)
+    return web_contents;
+
+  Browser* browser = nullptr;
+  bool is_new_browser;
+  std::tie(browser, is_new_browser) = EnsureBrowser();
+
+  web_contents =
+      NavigateBrowser(browser, is_new_browser, launch_url, share_target);
+  if (!web_contents)
+    return nullptr;
+
+  MaybeEnqueueWebLaunchParams(launch_url, is_file_handling, web_contents);
+
+  RecordMetrics(launch_url, web_contents);
+
+  return web_contents;
+}
+
+const apps::ShareTarget* WebAppLaunchProcess::MaybeGetShareTarget() const {
+  DCHECK(web_app_);
+  bool is_share_intent =
+      params_.intent &&
+      (params_.intent->action == apps_util::kIntentActionSend ||
+       params_.intent->action == apps_util::kIntentActionSendMultiple);
+  return is_share_intent && web_app_->share_target().has_value()
+             ? &web_app_->share_target().value()
+             : nullptr;
+}
+
+std::tuple<GURL, bool /*is_file_handling*/> WebAppLaunchProcess::GetLaunchUrl(
+    const apps::ShareTarget* share_target) const {
+  DCHECK(web_app_);
+  GURL launch_url;
+  bool is_file_handling = false;
+  bool is_note_taking_intent =
+      params_.intent &&
+      params_.intent->action == apps_util::kIntentActionCreateNote;
+
+  if (!params_.override_url.is_empty()) {
+    launch_url = params_.override_url;
+  } else if (params_.url_handler_launch_url.has_value() &&
+             params_.url_handler_launch_url->is_valid()) {
+    // Handle url_handlers launch.
+    launch_url = params_.url_handler_launch_url.value();
+  } else if (absl::optional<GURL> file_handler_url =
+                 provider_.os_integration_manager().GetMatchingFileHandlerURL(
+                     params_.app_id, params_.launch_files)) {
+    // Handle file_handlers launch.
+    launch_url = file_handler_url.value();
+    is_file_handling = true;
+  } else if (absl::optional<GURL> protocol_handler_translated_url =
+                 GetProtocolHandlingTranslatedUrl(
+                     provider_.os_integration_manager(), params_)) {
+    // Handle protocol_handlers launch.
+    launch_url = protocol_handler_translated_url.value();
+  } else if (share_target) {
+    // Handle share_target launch.
+    launch_url = share_target->action;
+  } else if (is_note_taking_intent &&
+             web_app_->note_taking_new_note_url().is_valid()) {
+    // Handle Create Note launch.
+    launch_url = web_app_->note_taking_new_note_url();
+  } else {
+    // This is a default launch.
+    launch_url = provider_.registrar().GetAppLaunchUrl(params_.app_id);
+  }
+  DCHECK(launch_url.is_valid());
+
+  return {launch_url, is_file_handling};
+}
+
+WindowOpenDisposition WebAppLaunchProcess::GetNavigationDisposition(
+    bool is_new_browser) const {
+  if (is_new_browser) {
+    // By opening a new window we've already performed part of a "disposition",
+    // the only remaining thing for Navigate() to do is navigate the new window.
+    return WindowOpenDisposition::CURRENT_TAB;
+    // TODO(crbug.com/1200944): Use NEW_FOREGROUND_TAB instead of CURRENT_TAB.
+    // The window has no tabs so it doesn't make sense to open the "current"
+    // tab. We use it anyway because it happens to work.
+    // If NEW_FOREGROUND_TAB is used the the WindowCanOpenTabs() check fails
+    // when `launch_url` is out of scope for web app windows causing it to
+    // open another separate browser window. It should be updated to check the
+    // extended scope.
+  }
+
+  // If launch handler is routing to an existing client, we want to use the
+  // existing WebContents rather than opening a new tab.
+  if (GetLaunchRouteTo() == LaunchHandler::RouteTo::kExistingClient) {
+    return WindowOpenDisposition::CURRENT_TAB;
+  }
+
+  // Only CURRENT_TAB and NEW_FOREGROUND_TAB dispositions are supported for web
+  // app launches.
+  return params_.disposition == WindowOpenDisposition::CURRENT_TAB
+             ? WindowOpenDisposition::CURRENT_TAB
+             : WindowOpenDisposition::NEW_FOREGROUND_TAB;
+}
+
+LaunchHandler::RouteTo WebAppLaunchProcess::GetLaunchRouteTo() const {
+  DCHECK(web_app_);
+  LaunchHandler launch_handler =
+      web_app_->launch_handler().value_or(LaunchHandler());
+  if (launch_handler.route_to == LaunchHandler::RouteTo::kAuto)
+    return LaunchHandler::RouteTo::kNewClient;
+  return launch_handler.route_to;
+}
+
+content::WebContents* WebAppLaunchProcess::MaybeLaunchSystemWebApp(
+    const GURL& launch_url) {
+  absl::optional<SystemAppType> system_app_type =
+      GetSystemWebAppTypeForAppId(&profile_, params_.app_id);
+  if (!system_app_type)
+    return nullptr;
+
+  Browser* browser =
+      LaunchSystemWebAppImpl(&profile_, *system_app_type, launch_url, params_);
+  return browser->tab_strip_model()->GetActiveWebContents();
+}
+
+std::tuple<Browser*, bool /*is_new_browser*/>
+WebAppLaunchProcess::EnsureBrowser() {
+  Browser* browser = MaybeFindBrowserForLaunch();
+  bool is_new_browser = false;
+  if (browser) {
+    browser->window()->Activate();
+  } else {
+    browser = CreateBrowserForLaunch();
+    is_new_browser = true;
+  }
+  browser->window()->Show();
+  return {browser, is_new_browser};
+}
+
+Browser* WebAppLaunchProcess::MaybeFindBrowserForLaunch() const {
+  if (params_.container == apps::mojom::LaunchContainer::kLaunchContainerTab) {
+    return chrome::FindTabbedBrowser(
+        &profile_, /*match_original_profiles=*/false,
+        display::Screen::GetScreen()->GetDisplayForNewWindows().id());
+  }
+
+  if (!provider_.registrar().IsTabbedWindowModeEnabled(params_.app_id) &&
+      GetLaunchRouteTo() == LaunchHandler::RouteTo::kNewClient) {
+    return nullptr;
+  }
+
+  for (Browser* browser : *BrowserList::GetInstance()) {
+    if (browser->profile() == &profile_ &&
+        AppBrowserController::IsForWebApp(browser, params_.app_id)) {
+      return browser;
+    }
+  }
+
+  return nullptr;
+}
+
+Browser* WebAppLaunchProcess::CreateBrowserForLaunch() {
+  if (params_.container == apps::mojom::LaunchContainer::kLaunchContainerTab) {
+    return Browser::Create(Browser::CreateParams(Browser::TYPE_NORMAL,
+                                                 &profile_,
+                                                 /*user_gesture=*/true));
+  }
+
+  return CreateWebApplicationWindow(&profile_, params_.app_id,
+                                    params_.disposition, params_.restore_id);
+}
+
+content::WebContents* WebAppLaunchProcess::NavigateBrowser(
+    Browser* browser,
+    bool is_new_browser,
+    const GURL& launch_url,
+    const apps::ShareTarget* share_target) {
+  WindowOpenDisposition navigation_disposition =
+      GetNavigationDisposition(is_new_browser);
+
+  if (share_target) {
+    NavigateParams nav_params =
+        NavigateParamsForShareTarget(browser, *share_target, *params_.intent);
+    nav_params.disposition = navigation_disposition;
+    return NavigateWebAppUsingParams(params_.app_id, nav_params);
+  }
+
+  TabStripModel* const tab_strip = browser->tab_strip_model();
+  if (tab_strip->empty() ||
+      navigation_disposition != WindowOpenDisposition::CURRENT_TAB) {
+    return NavigateWebApplicationWindow(browser, params_.app_id, launch_url,
+                                        navigation_disposition);
+  }
+
+  content::WebContents* existing_tab = tab_strip->GetActiveWebContents();
+  DCHECK(existing_tab);
+  const int tab_index = tab_strip->GetIndexOfWebContents(existing_tab);
+
+  existing_tab->OpenURL(content::OpenURLParams(
+      launch_url,
+      content::Referrer::SanitizeForRequest(
+          launch_url,
+          content::Referrer(existing_tab->GetURL(),
+                            network::mojom::ReferrerPolicy::kDefault)),
+      navigation_disposition, ui::PAGE_TRANSITION_AUTO_BOOKMARK,
+      /*is_renderer_initiated=*/false));
+
+  content::WebContents* web_contents = tab_strip->GetActiveWebContents();
+  tab_strip->ActivateTabAt(tab_index, {TabStripModel::GestureType::kOther});
+  WebAppTabHelper::FromWebContents(web_contents)->SetAppId(params_.app_id);
+  return web_contents;
+}
+
+void WebAppLaunchProcess::MaybeEnqueueWebLaunchParams(
+    const GURL& launch_url,
+    bool is_file_handling,
+    content::WebContents* web_contents) {
+  if (is_file_handling || web_app_->launch_handler().has_value()) {
+    web_launch::WebLaunchFilesHelper::EnqueueLaunchParams(
+        web_contents, provider_.registrar().GetAppScope(web_app_->app_id()),
+        /*await_navigation=*/true, launch_url,
+        /*launch_dir=*/{},
+        is_file_handling ? params_.launch_files
+                         : std::vector<base::FilePath>());
+  }
+}
+
+void WebAppLaunchProcess::RecordMetrics(const GURL& launch_url,
+                                        content::WebContents* web_contents) {
+  // TODO(crbug.com/1014328): Populate WebApp metrics instead of Extensions.
+  if (params_.container == apps::mojom::LaunchContainer::kLaunchContainerTab) {
+    UMA_HISTOGRAM_ENUMERATION("Extensions.AppTabLaunchType",
+                              extensions::LAUNCH_TYPE_REGULAR, 100);
+  } else if (params_.container ==
+             apps::mojom::LaunchContainer::kLaunchContainerWindow) {
+    RecordAppWindowLaunch(&profile_, params_.app_id);
+  }
+  UMA_HISTOGRAM_ENUMERATION("Extensions.BookmarkAppLaunchSource",
+                            apps::GetAppLaunchSource(params_.launch_source));
+  UMA_HISTOGRAM_ENUMERATION("Extensions.BookmarkAppLaunchContainer",
+                            params_.container);
+
+  // Record the launch time in the site engagement service. A recent web
+  // app launch will provide an engagement boost to the origin.
+  site_engagement::SiteEngagementService::Get(&profile_)
+      ->SetLastShortcutLaunchTime(web_contents, launch_url);
+  provider_.sync_bridge().SetAppLastLaunchTime(params_.app_id,
+                                               base::Time::Now());
+  // Refresh the app banner added to homescreen event. The user may have
+  // cleared their browsing data since installing the app, which removes the
+  // event and will potentially permit a banner to be shown for the site.
+  RecordAppBanner(web_contents, launch_url);
+}
+
+}  // namespace web_app
diff --git a/chrome/browser/ui/web_applications/web_app_launch_process.h b/chrome/browser/ui/web_applications/web_app_launch_process.h
new file mode 100644
index 0000000..d30d220
--- /dev/null
+++ b/chrome/browser/ui/web_applications/web_app_launch_process.h
@@ -0,0 +1,73 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEB_APPLICATIONS_WEB_APP_LAUNCH_PROCESS_H_
+#define CHROME_BROWSER_UI_WEB_APPLICATIONS_WEB_APP_LAUNCH_PROCESS_H_
+
+#include "base/memory/raw_ptr.h"
+#include "third_party/blink/public/common/manifest/manifest.h"
+
+class Browser;
+enum class WindowOpenDisposition;
+class GURL;
+class Profile;
+
+namespace apps {
+struct AppLaunchParams;
+struct ShareTarget;
+}  // namespace apps
+
+namespace content {
+class WebContents;
+}  // namespace content
+
+namespace web_app {
+
+class WebApp;
+class WebAppProvider;
+
+// Used by WebAppLaunchManager, this executes an individual launch of a web app
+// from any entry point (OS launcher, file handler, protocol handler,
+// link capturing, etc.).
+//
+// Implements the behaviour of the `launch_handler` manifest field:
+// https://github.com/WICG/sw-launch/blob/main/launch_handler.md
+class WebAppLaunchProcess {
+ public:
+  WebAppLaunchProcess(Profile& profile, const apps::AppLaunchParams& params);
+  WebAppLaunchProcess(const WebAppLaunchProcess&) = delete;
+
+  content::WebContents* Run();
+
+ private:
+  using RouteTo = blink::Manifest::LaunchHandler::RouteTo;
+  const apps::ShareTarget* MaybeGetShareTarget() const;
+  std::tuple<GURL, bool /*is_file_handling*/> GetLaunchUrl(
+      const apps::ShareTarget* share_target) const;
+  WindowOpenDisposition GetNavigationDisposition(bool is_new_browser) const;
+  content::WebContents* MaybeLaunchSystemWebApp(const GURL& launch_url);
+  std::tuple<Browser*, bool /*is_new_browser*/> EnsureBrowser();
+  RouteTo GetLaunchRouteTo() const;
+
+  Browser* MaybeFindBrowserForLaunch() const;
+  Browser* CreateBrowserForLaunch();
+  content::WebContents* NavigateBrowser(Browser* browser,
+                                        bool is_new_browser,
+                                        const GURL& launch_url,
+                                        const apps::ShareTarget* share_target);
+  void MaybeEnqueueWebLaunchParams(const GURL& launch_url,
+                                   bool is_file_handling,
+                                   content::WebContents* web_contents);
+  void RecordMetrics(const GURL& launch_url,
+                     content::WebContents* web_contents);
+
+  Profile& profile_;
+  WebAppProvider& provider_;
+  const apps::AppLaunchParams& params_;
+  const raw_ptr<const WebApp> web_app_;
+};
+
+}  // namespace web_app
+
+#endif  // CHROME_BROWSER_UI_WEB_APPLICATIONS_WEB_APP_LAUNCH_PROCESS_H_
diff --git a/chrome/browser/ui/webui/chromeos/login/lacros_data_migration_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/lacros_data_migration_screen_handler.cc
index 6487b3a9..1b8d5bf9 100644
--- a/chrome/browser/ui/webui/chromeos/login/lacros_data_migration_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/lacros_data_migration_screen_handler.cc
@@ -30,10 +30,6 @@
                IDS_LACROS_DATA_MIGRATION_SCREEN_TITLE);
   builder->Add("lacrosDataMigrationSubtitle",
                IDS_LACROS_DATA_MIGRATION_SCREEN_SUBTITLE);
-  builder->Add("lacrosDataMigrationSkipButton",
-               IDS_LACROS_DATA_MIGRATION_SCREEN_SKIP_BUTTON);
-  builder->Add("lacrosDataMigrationSkipSuggestion",
-               IDS_LACROS_DATA_MIGRATION_SCREEN_SKIP_SUGGESTION);
 }
 
 void LacrosDataMigrationScreenHandler::Bind(LacrosDataMigrationScreen* screen) {
@@ -58,10 +54,6 @@
   CallJS("login.LacrosDataMigrationScreen.setProgressValue", progress);
 }
 
-void LacrosDataMigrationScreenHandler::ShowSkipButton() {
-  CallJS("login.LacrosDataMigrationScreen.showSkipButton");
-}
-
 void LacrosDataMigrationScreenHandler::Initialize() {
   if (show_on_init_) {
     Show();
diff --git a/chrome/browser/ui/webui/chromeos/login/lacros_data_migration_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/lacros_data_migration_screen_handler.h
index e2f03e3e..3f356154 100644
--- a/chrome/browser/ui/webui/chromeos/login/lacros_data_migration_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/lacros_data_migration_screen_handler.h
@@ -32,9 +32,6 @@
 
   // Updates the progress bar.
   virtual void SetProgressValue(int progress) = 0;
-
-  // Displays the skip button.
-  virtual void ShowSkipButton() = 0;
 };
 
 class LacrosDataMigrationScreenHandler : public BaseScreenHandler,
@@ -59,7 +56,6 @@
   void Unbind() override;
   void Show() override;
   void SetProgressValue(int progress) override;
-  void ShowSkipButton() override;
 
  private:
   // BaseScreenHandler:
diff --git a/chrome/browser/ui/webui/settings/about_handler.cc b/chrome/browser/ui/webui/settings/about_handler.cc
index fa39673..127f194 100644
--- a/chrome/browser/ui/webui/settings/about_handler.cc
+++ b/chrome/browser/ui/webui/settings/about_handler.cc
@@ -287,6 +287,12 @@
       "openDiagnostics",
       base::BindRepeating(&AboutHandler::HandleOpenDiagnostics,
                           base::Unretained(this)));
+
+  web_ui()->RegisterDeprecatedMessageCallback(
+      "openFirmwareUpdatesPage",
+      base::BindRepeating(&AboutHandler::HandleOpenFirmwareUpdates,
+                          base::Unretained(this)));
+
   web_ui()->RegisterDeprecatedMessageCallback(
       "openOsHelpPage", base::BindRepeating(&AboutHandler::HandleOpenOsHelpPage,
                                             base::Unretained(this)));
@@ -439,6 +445,11 @@
   chrome::ShowDiagnosticsApp(profile_);
 }
 
+void AboutHandler::HandleOpenFirmwareUpdates(const base::ListValue* args) {
+  DCHECK(args->GetList().empty());
+  chrome::ShowFirmwareUpdatesApp(profile_);
+}
+
 void AboutHandler::HandleCheckInternetConnection(const base::ListValue* args) {
   CHECK_EQ(1U, args->GetList().size());
   const std::string& callback_id = args->GetList()[0].GetString();
diff --git a/chrome/browser/ui/webui/settings/about_handler.h b/chrome/browser/ui/webui/settings/about_handler.h
index eba44e0..7ad6f8d 100644
--- a/chrome/browser/ui/webui/settings/about_handler.h
+++ b/chrome/browser/ui/webui/settings/about_handler.h
@@ -158,6 +158,8 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   void HandleOpenDiagnostics(const base::ListValue* args);
 
+  void HandleOpenFirmwareUpdates(const base::ListValue* args);
+
   void HandleGetRegulatoryInfo(const base::ListValue* args);
 
   // Callback for when the directory with the regulatory label image and alt
diff --git a/chrome/browser/ui/webui/settings/chromeos/about_section.cc b/chrome/browser/ui/webui/settings/chromeos/about_section.cc
index 04a3864..722dcce 100644
--- a/chrome/browser/ui/webui/settings/chromeos/about_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/about_section.cc
@@ -113,6 +113,18 @@
   return *tags;
 }
 
+const std::vector<SearchConcept>& GetFirmwareUpdatesAppSearchConcepts() {
+  static const base::NoDestructor<std::vector<SearchConcept>> tags({
+      {IDS_OS_SETTINGS_TAG_ABOUT_FIRMWARE_UPDATES,
+       mojom::kAboutChromeOsDetailsSubpagePath,
+       mojom::SearchResultIcon::kChrome,
+       mojom::SearchResultDefaultRank::kMedium,
+       mojom::SearchResultType::kSetting,
+       {.setting = mojom::Setting::kFirmwareUpdates}},
+  });
+  return *tags;
+}
+
 const std::vector<SearchConcept>& GetDeviceNameSearchConcepts() {
   static const base::NoDestructor<std::vector<SearchConcept>> tags({
       {IDS_OS_SETTINGS_TAG_ABOUT_DEVICE_NAME,
@@ -200,6 +212,10 @@
     updater.AddSearchTags(GetDiagnosticsAppSearchConcepts());
   }
 
+  if (base::FeatureList::IsEnabled(chromeos::features::kFirmwareUpdaterApp)) {
+    updater.AddSearchTags(GetFirmwareUpdatesAppSearchConcepts());
+  }
+
   if (base::FeatureList::IsEnabled(
           chromeos::features::kEnableHostnameSetting)) {
     updater.AddSearchTags(GetDeviceNameSearchConcepts());
@@ -216,6 +232,7 @@
     {"aboutReportAnIssue", IDS_SETTINGS_ABOUT_PAGE_REPORT_AN_ISSUE},
 #endif
     {"aboutDiagnostics", IDS_SETTINGS_ABOUT_PAGE_DIAGNOSTICS},
+    {"aboutFirmwareUpdates", IDS_SETTINGS_ABOUT_PAGE_FIRMWARE_UPDATES},
     {"aboutRelaunch", IDS_SETTINGS_ABOUT_PAGE_RELAUNCH},
     {"aboutUpgradeCheckStarted", IDS_SETTINGS_ABOUT_UPGRADE_CHECK_STARTED},
     {"aboutUpgradeRelaunch", IDS_SETTINGS_UPGRADE_SUCCESSFUL_RELAUNCH},
@@ -390,6 +407,10 @@
       "diagnosticsAppEnabled",
       base::FeatureList::IsEnabled(chromeos::features::kDiagnosticsApp));
 
+  html_source->AddBoolean(
+      "isFirmwareUpdaterAppEnabled",
+      base::FeatureList::IsEnabled(chromeos::features::kFirmwareUpdaterApp));
+
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
   html_source->AddString("aboutTermsURL", chrome::kChromeUITermsURL);
   html_source->AddLocalizedString("aboutProductTos",
@@ -439,7 +460,8 @@
   static constexpr mojom::Setting kAboutChromeOsDetailsSettings[] = {
       mojom::Setting::kCheckForOsUpdate,    mojom::Setting::kSeeWhatsNew,
       mojom::Setting::kGetHelpWithChromeOs, mojom::Setting::kReportAnIssue,
-      mojom::Setting::kTermsOfService,      mojom::Setting::kDiagnostics};
+      mojom::Setting::kTermsOfService,      mojom::Setting::kDiagnostics,
+      mojom::Setting::kFirmwareUpdates};
   RegisterNestedSettingBulk(mojom::Subpage::kAboutChromeOsDetails,
                             kAboutChromeOsDetailsSettings, generator);
 
diff --git a/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom b/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom
index 73e7e29..a0be0d5c9 100644
--- a/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom
+++ b/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom
@@ -200,6 +200,7 @@
   kAddFingerprintV2 = 1111,
   kRemoveFingerprintV2 = 1112,
   kPeripheralDataAccessProtection = 1113,
+  kSnoopingProtection = 1114,
 
   // Languages and Input section.
   kAddLanguage = 1200,
@@ -267,6 +268,7 @@
   kTermsOfService = 1706,
   kDiagnostics = 1707,
   kChangeDeviceName = 1708,
+  kFirmwareUpdates = 1709,
 
   // Kerberos section.
   kAddKerberosTicketV2 = 1800,
diff --git a/chrome/browser/ui/webui/settings/chromeos/privacy_section.cc b/chrome/browser/ui/webui/settings/chromeos/privacy_section.cc
index 6abfc21b..b591ae9 100644
--- a/chrome/browser/ui/webui/settings/chromeos/privacy_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/privacy_section.cc
@@ -109,8 +109,6 @@
             {.subpage = mojom::Subpage::kSecurityAndSignInV2}}});
     }
 
-    // TODO(chromium:1262869): add smart privacy search concepts.
-
     return all_tags;
   }());
 
@@ -164,6 +162,26 @@
   return *tags;
 }
 
+const std::vector<SearchConcept>& GetSmartPrivacySearchConcepts() {
+  static const base::NoDestructor<std::vector<SearchConcept>> tags(
+      {{IDS_OS_SETTINGS_TAG_SMART_PRIVACY_SNOOPING,
+        mojom::kSmartPrivacySubpagePath,
+        mojom::SearchResultIcon::kShield,
+        mojom::SearchResultDefaultRank::kMedium,
+        mojom::SearchResultType::kSetting,
+        {.setting = mojom::Setting::kSnoopingProtection},
+        {IDS_OS_SETTINGS_TAG_SMART_PRIVACY_SNOOPING_ALT1,
+         IDS_OS_SETTINGS_TAG_SMART_PRIVACY_SNOOPING_ALT2}},
+       {IDS_OS_SETTINGS_TAG_SMART_PRIVACY,
+        mojom::kSmartPrivacySubpagePath,
+        mojom::SearchResultIcon::kShield,
+        mojom::SearchResultDefaultRank::kMedium,
+        mojom::SearchResultType::kSubpage,
+        {.subpage = mojom::Subpage::kSmartPrivacy}}});
+
+  return *tags;
+}
+
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
 const std::vector<SearchConcept>& GetPrivacyGoogleChromeSearchConcepts() {
   static const base::NoDestructor<std::vector<SearchConcept>> tags({
@@ -218,6 +236,10 @@
   if (chromeos::features::IsPciguardUiEnabled()) {
     updater.AddSearchTags(GetPciguardSearchConcepts());
   }
+
+  if (ash::features::IsSnoopingProtectionEnabled()) {
+    updater.AddSearchTags(GetSmartPrivacySearchConcepts());
+  }
 }
 
 PrivacySection::~PrivacySection() = default;
@@ -375,6 +397,8 @@
       IDS_OS_SETTINGS_SMART_PRIVACY_TITLE, mojom::Subpage::kSmartPrivacy,
       mojom::SearchResultIcon::kShield, mojom::SearchResultDefaultRank::kMedium,
       mojom::kSmartPrivacySubpagePath);
+  RegisterNestedSettingBulk(mojom::Subpage::kSmartPrivacy,
+                            {{mojom::Setting::kSnoopingProtection}}, generator);
 }
 
 bool PrivacySection::AreFingerprintSettingsAllowed() {
diff --git a/chrome/browser/web_applications/app_service/web_apps.h b/chrome/browser/web_applications/app_service/web_apps.h
index b5545f2..10792d5 100644
--- a/chrome/browser/web_applications/app_service/web_apps.h
+++ b/chrome/browser/web_applications/app_service/web_apps.h
@@ -12,6 +12,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observation.h"
 #include "build/chromeos_buildflags.h"
+#include "chrome/browser/apps/app_service/launch_result_type.h"
 #include "chrome/browser/apps/app_service/publishers/app_publisher.h"
 #include "chrome/browser/web_applications/app_service/web_app_publisher_helper.h"
 #include "chrome/browser/web_applications/web_app_id.h"
diff --git a/chrome/browser/web_applications/web_app_proto_utils.cc b/chrome/browser/web_applications/web_app_proto_utils.cc
index f86d7ee..f9cdd478 100644
--- a/chrome/browser/web_applications/web_app_proto_utils.cc
+++ b/chrome/browser/web_applications/web_app_proto_utils.cc
@@ -81,6 +81,9 @@
 }
 
 sync_pb::WebAppSpecifics WebAppToSyncProto(const WebApp& app) {
+  DCHECK(!app.start_url().is_empty());
+  DCHECK(app.start_url().is_valid());
+
   sync_pb::WebAppSpecifics sync_proto;
   if (app.manifest_id().has_value())
     sync_proto.set_manifest_id(app.manifest_id().value());
diff --git a/chrome/browser/web_applications/web_app_sync_bridge.cc b/chrome/browser/web_applications/web_app_sync_bridge.cc
index 2a96e647..bcaa1129 100644
--- a/chrome/browser/web_applications/web_app_sync_bridge.cc
+++ b/chrome/browser/web_applications/web_app_sync_bridge.cc
@@ -771,8 +771,10 @@
 
   const sync_pb::WebAppSpecifics& specifics = entity_data.specifics.web_app();
   const GURL start_url(specifics.start_url());
-  DCHECK(!start_url.is_empty());
-  DCHECK(start_url.is_valid());
+  if (start_url.is_empty() || !start_url.is_valid()) {
+    DLOG(ERROR) << "GetClientTag: start_url parse error.";
+    return std::string();
+  }
 
   absl::optional<std::string> manifest_id = absl::nullopt;
   if (specifics.has_manifest_id())
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index e759ccb..cc59f4b 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1637193204-e5ba9fe8d9b1fbcf3ae90b98f1e62d3e080b3ec9.profdata
+chrome-linux-main-1637235889-a85b2f13939122953dd1cce0e97f01f74feaac2b.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 19cf1d7..66e3476 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1637193204-c44e60b3a110e5e789228a478afd2d9380a13d10.profdata
+chrome-mac-main-1637235889-f25cd44c04387a04a9b21fff487f282cb21ec2ac.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 8792be2..720bbd3 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1637193204-05f8a04b3a0697b9b788db1fd49e1c4abaa13560.profdata
+chrome-win32-main-1637225975-91b3bd1fe58f1ba9d31308244df09804bcdf4f19.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index fb3ff24..33fc82c 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1637193204-57a455ee87b529d85233e774f3d3faeb8bc7446b.profdata
+chrome-win64-main-1637225975-57c417340ab5182751e354c1b5154d63f3e2ea4e.profdata
diff --git a/chrome/common/extensions/api/autotest_private.idl b/chrome/common/extensions/api/autotest_private.idl
index 8626d70..f604db6 100644
--- a/chrome/common/extensions/api/autotest_private.idl
+++ b/chrome/common/extensions/api/autotest_private.idl
@@ -587,6 +587,14 @@
   // getDisplaySmoothness is called.
   callback GetDisplaySmoothnessCallback = void (long smoothness);
 
+  // Options for resetting the holding space.
+  dictionary ResetHoldingSpaceOptions {
+    // Whether to call `ash::holding_space_prefs::MarkTimeOfFirstAdd()` after
+    // reset. Used to show the holding space tray in tests, since it's otherwise
+    // hidden after OOBE.
+    boolean markTimeOfFirstAdd;
+  };
+
   interface Functions {
     // Must be called to allow autotestPrivateAPI events to be fired.
     static void initializeEvents();
@@ -1202,6 +1210,11 @@
     [supportsPromises] static void getDisplaySmoothness(
         optional DOMString displayId,
         GetDisplaySmoothnessCallback callback);
+
+    // Resets the holding space by removing all items and clearing the prefs.
+    [supportsPromises] static void resetHoldingSpace(
+        optional ResetHoldingSpaceOptions options,
+        VoidCallback callback);
   };
 
   interface Events {
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc
index 27fd89f7..1289572 100644
--- a/chrome/common/webui_url_constants.cc
+++ b/chrome/common/webui_url_constants.cc
@@ -271,6 +271,7 @@
 const char kChromeUICryptohomeHost[] = "cryptohome";
 const char kChromeUIDeviceEmulatorHost[] = "device-emulator";
 const char kChromeUIDiagnosticsAppURL[] = "chrome://diagnostics";
+const char kChromeUIFirmwareUpdatesAppURL[] = "chrome://accessory-update";
 const char kChromeUIIntenetConfigDialogURL[] =
     "chrome://internet-config-dialog/";
 const char kChromeUIIntenetDetailDialogURL[] =
diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h
index def804a..10fb1f2 100644
--- a/chrome/common/webui_url_constants.h
+++ b/chrome/common/webui_url_constants.h
@@ -261,6 +261,7 @@
 extern const char kChromeUIDiagnosticsAppURL[];
 extern const char kChromeUIEmojiPickerURL[];
 extern const char kChromeUIEmojiPickerHost[];
+extern const char kChromeUIFirmwareUpdatesAppURL[];
 extern const char kChromeUIIntenetConfigDialogURL[];
 extern const char kChromeUIIntenetDetailDialogURL[];
 extern const char kChromeUIInternetConfigDialogHost[];
diff --git a/chrome/services/sharing/nearby/platform/wifi_lan_socket.cc b/chrome/services/sharing/nearby/platform/wifi_lan_socket.cc
index ee7194c..1f27696 100644
--- a/chrome/services/sharing/nearby/platform/wifi_lan_socket.cc
+++ b/chrome/services/sharing/nearby/platform/wifi_lan_socket.cc
@@ -73,15 +73,10 @@
   return *bidirectional_stream_.GetOutputStream();
 }
 
-// Note: This is thread safe because we can call reset() on
-// |tcp_connected_socket_| as many times as we want, and
-// BidirectionalStream::Close() is thread safe.
+// Note: Both CloseTcpSocketIfNecessary() and BidirectionalStream::Close() are
+// thread safe.
 Exception WifiLanSocket::Close() {
-  if (tcp_connected_socket_) {
-    VLOG(1) << "WifiLanSocket::" << __func__
-            << ": Closing TCP connected socket.";
-    tcp_connected_socket_.reset();
-  }
+  CloseTcpSocketIfNecessary();
 
   return bidirectional_stream_.Close();
 }
@@ -97,6 +92,16 @@
   Close();
 }
 
+void WifiLanSocket::CloseTcpSocketIfNecessary() {
+  base::AutoLock lock(lock_);
+
+  if (!tcp_connected_socket_)
+    return;
+
+  VLOG(1) << "WifiLanSocket::" << __func__ << ": Closing TCP connected socket.";
+  tcp_connected_socket_.reset();
+}
+
 }  // namespace chrome
 }  // namespace nearby
 }  // namespace location
diff --git a/chrome/services/sharing/nearby/platform/wifi_lan_socket.h b/chrome/services/sharing/nearby/platform/wifi_lan_socket.h
index bb50dfb6..b06440675 100644
--- a/chrome/services/sharing/nearby/platform/wifi_lan_socket.h
+++ b/chrome/services/sharing/nearby/platform/wifi_lan_socket.h
@@ -6,6 +6,7 @@
 #define CHROME_SERVICES_SHARING_NEARBY_PLATFORM_WIFI_LAN_SOCKET_H_
 
 #include "base/memory/scoped_refptr.h"
+#include "base/synchronization/lock.h"
 #include "chrome/services/sharing/nearby/platform/bidirectional_stream.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/shared_remote.h"
@@ -69,6 +70,11 @@
   // Close if the TCP socket disconnects.
   void OnTcpConnectedSocketDisconnected();
 
+  void CloseTcpSocketIfNecessary();
+
+  // Protects |tcp_connected_socket_| while closing.
+  base::Lock lock_;
+
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
   mojo::SharedRemote<network::mojom::TCPConnectedSocket> tcp_connected_socket_;
   BidirectionalStream bidirectional_stream_;
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 7d4a6d6..cd5a67a 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3163,7 +3163,6 @@
         "../browser/ash/login/screens/fingerprint_setup_browsertest.cc",
         "../browser/ash/login/screens/gesture_navigation_screen_browsertest.cc",
         "../browser/ash/login/screens/hid_detection_screen_browsertest.cc",
-        "../browser/ash/login/screens/lacros_data_migration_screen_browsertest.cc",
         "../browser/ash/login/screens/management_transition_screen_browsertest.cc",
         "../browser/ash/login/screens/marketing_opt_in_screen_browsertest.cc",
         "../browser/ash/login/screens/mock_arc_terms_of_service_screen.cc",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/FirstRunControllerTest.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/FirstRunControllerTest.java
index e8975ff0..2b24104b 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/FirstRunControllerTest.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/FirstRunControllerTest.java
@@ -36,6 +36,9 @@
     @Rule
     public final TestRule mChain = RuleChain.outerRule(mChromeUiRule).around(mUiAutomatorRule);
 
+    @Rule
+    public TestRule mCommandLineFlagsRule = CommandLineFlags.getTestRule();
+
     @Before
     public void setUp() {
         mChromeUiRule.launchApplication();
diff --git a/chrome/test/data/extensions/api_test/autotest_private/test.js b/chrome/test/data/extensions/api_test/autotest_private/test.js
index 8129ff5..d05639c2 100644
--- a/chrome/test/data/extensions/api_test/autotest_private/test.js
+++ b/chrome/test/data/extensions/api_test/autotest_private/test.js
@@ -1390,7 +1390,7 @@
     // Because the File app has been unpinned, there is no update in pin state.
     chrome.test.assertEq([], unpinResults);
 
-    chrome.test.succeed()
+    chrome.test.succeed();
   }
 ];
 
@@ -1405,6 +1405,14 @@
       }));
 }];
 
+var holdingSpaceTests = [
+  function resetHoldingSpace(options) {
+    // State after this call is checked in C++ test code.
+    chrome.autotestPrivate.resetHoldingSpace(options,
+      chrome.test.callbackPass());
+  },
+];
+
 // Tests that requires a concrete system web app installation.
 var systemWebAppsTests = [
   function getRegisteredSystemWebApps() {
@@ -1456,13 +1464,22 @@
   'splitviewLeftSnapped': splitviewLeftSnappedTests,
   'scrollableShelf': scrollableShelfTests,
   'shelf': shelfTests,
+  'holdingSpace': holdingSpaceTests,
   'systemWebApps': systemWebAppsTests,
 };
 
 chrome.test.getConfig(function(config) {
-  var suite = test_suites[config.customArg];
-  if (config.customArg in test_suites) {
-    chrome.test.runTests(test_suites[config.customArg]);
+  var customArg = JSON.parse(config.customArg);
+  // In the customArg object, we expect the name of the test suite at the
+  // 'testSuite' key, and the arguments to be passed to the test functions as an
+  // array at the 'args' key.
+  var [suite_name, args] = [customArg['testSuite'], customArg['args']];
+
+  chrome.test.assertTrue(Array.isArray(args));
+
+  if (suite_name in test_suites) {
+    var suite = test_suites[suite_name].map(f => f.bind({}, ...args));
+    chrome.test.runTests(suite);
   } else {
     chrome.test.fail('Invalid test suite');
   }
diff --git a/chrome/test/data/webui/chromeos/firmware_update/firmware_update_test.js b/chrome/test/data/webui/chromeos/firmware_update/firmware_update_test.js
index cb3746e..0aa4300 100644
--- a/chrome/test/data/webui/chromeos/firmware_update/firmware_update_test.js
+++ b/chrome/test/data/webui/chromeos/firmware_update/firmware_update_test.js
@@ -94,7 +94,7 @@
     // TODO(michaelcheco): Remove this stub test once the page has more
     // capabilities to test.
     assertEquals(
-        'Firmware updates',
+        'Update peripherals',
         page.shadowRoot.querySelector('#header').textContent.trim());
   });
 
diff --git a/chrome/test/data/webui/print_preview/BUILD.gn b/chrome/test/data/webui/print_preview/BUILD.gn
index 9d644764..db9746fce 100644
--- a/chrome/test/data/webui/print_preview/BUILD.gn
+++ b/chrome/test/data/webui/print_preview/BUILD.gn
@@ -10,6 +10,8 @@
 # Print Preview test files that contain // <if expr> and therefore require
 # preprocessing.
 preprocessed_tests = [
+  "button_strip_test.ts",
+  "color_settings_test.ts",
   "destination_settings_test.js",
   "destination_store_test.js",
   "invalid_settings_browsertest.js",
@@ -30,12 +32,10 @@
 non_preprocessed_tests = [
   "advanced_dialog_test.ts",
   "advanced_item_test.ts",
-  "button_strip_interactive_test.js",
-  "button_strip_test.js",
+  "button_strip_interactive_test.ts",
   "cloud_print_interface_stub.ts",
-  "color_settings_test.js",
-  "copies_settings_test.js",
-  "custom_margins_test.js",
+  "copies_settings_test.ts",
+  "custom_margins_test.ts",
   "destination_dialog_cros_interactive_test.js",
   "destination_dialog_cros_test.js",
   "destination_dialog_interactive_test.js",
diff --git a/chrome/test/data/webui/print_preview/button_strip_interactive_test.js b/chrome/test/data/webui/print_preview/button_strip_interactive_test.ts
similarity index 65%
rename from chrome/test/data/webui/print_preview/button_strip_interactive_test.js
rename to chrome/test/data/webui/print_preview/button_strip_interactive_test.ts
index 8075e6ff..cd72580 100644
--- a/chrome/test/data/webui/print_preview/button_strip_interactive_test.js
+++ b/chrome/test/data/webui/print_preview/button_strip_interactive_test.ts
@@ -4,26 +4,24 @@
 
 import {Destination, DestinationConnectionStatus, DestinationOrigin, DestinationType, PrintPreviewButtonStripElement, State} from 'chrome://print/print_preview.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
-import {assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
-window.button_strip_interactive_test = {};
-const button_strip_interactive_test = window.button_strip_interactive_test;
-button_strip_interactive_test.suiteName = 'ButtonStripInteractiveTest';
-/** @enum {string} */
-button_strip_interactive_test.TestNames = {
-  FocusPrintOnReady: 'focus print on ready',
+const button_strip_interactive_test = {
+  suiteName: 'ButtonStripInteractiveTest',
+  TestNames: {
+    FocusPrintOnReady: 'focus print on ready',
+  },
 };
 
-suite(button_strip_interactive_test.suiteName, function() {
-  /** @type {!PrintPreviewButtonStripElement} */
-  let buttonStrip;
+Object.assign(
+    window, {button_strip_interactive_test: button_strip_interactive_test});
 
-  /** @override */
+suite(button_strip_interactive_test.suiteName, function() {
+  let buttonStrip: PrintPreviewButtonStripElement;
+
   setup(function() {
     document.body.innerHTML = '';
-    buttonStrip = /** @type {!PrintPreviewButtonStripElement} */ (
-        document.createElement('print-preview-button-strip'));
+    buttonStrip = document.createElement('print-preview-button-strip');
     buttonStrip.destination = new Destination(
         'FooDevice', DestinationType.GOOGLE, DestinationOrigin.COOKIES,
         'FooName', DestinationConnectionStatus.ONLINE);
@@ -38,8 +36,8 @@
       assert(button_strip_interactive_test.TestNames.FocusPrintOnReady),
       function() {
         const printButton =
-            assert(buttonStrip.shadowRoot.querySelector('.action-button'));
-        const whenFocusDone = eventToPromise('focus', printButton);
+            assert(buttonStrip.shadowRoot!.querySelector('.action-button'));
+        const whenFocusDone = eventToPromise('focus', printButton!);
 
         // Simulate initialization finishing.
         buttonStrip.state = State.READY;
diff --git a/chrome/test/data/webui/print_preview/button_strip_test.js b/chrome/test/data/webui/print_preview/button_strip_test.js
deleted file mode 100644
index c6bd7e22..0000000
--- a/chrome/test/data/webui/print_preview/button_strip_test.js
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import {Destination, DestinationConnectionStatus, DestinationOrigin, DestinationType, PrintPreviewButtonStripElement, State} from 'chrome://print/print_preview.js';
-import {assert} from 'chrome://resources/js/assert.m.js';
-import {isWindows} from 'chrome://resources/js/cr.m.js';
-
-import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {eventToPromise} from 'chrome://webui-test/test_util.js';
-
-window.button_strip_test = {};
-const button_strip_test = window.button_strip_test;
-button_strip_test.suiteName = 'ButtonStripTest';
-/** @enum {string} */
-button_strip_test.TestNames = {
-  ButtonStripChangesForState: 'button strip changes for state',
-  ButtonOrder: 'button order',
-  ButtonStripFiresEvents: 'button strip fires events',
-};
-
-suite(button_strip_test.suiteName, function() {
-  /** @type {!PrintPreviewButtonStripElement} */
-  let buttonStrip;
-
-  /** @override */
-  setup(function() {
-    document.body.innerHTML = '';
-    buttonStrip = /** @type {!PrintPreviewButtonStripElement} */ (
-        document.createElement('print-preview-button-strip'));
-
-    buttonStrip.destination = new Destination(
-        'FooDevice', DestinationType.GOOGLE, DestinationOrigin.COOKIES,
-        'FooName', DestinationConnectionStatus.ONLINE);
-    buttonStrip.state = State.READY;
-    // No max sheets limit is specified.
-    buttonStrip.maxSheets = 0;
-    document.body.appendChild(buttonStrip);
-  });
-
-  // Tests that the correct message is shown for non-READY states, and that
-  // the print button is disabled appropriately.
-  test(
-      assert(button_strip_test.TestNames.ButtonStripChangesForState),
-      function() {
-        const printButton =
-            buttonStrip.shadowRoot.querySelector('.action-button');
-        assertFalse(printButton.disabled);
-
-        buttonStrip.state = State.NOT_READY;
-        assertTrue(printButton.disabled);
-
-        buttonStrip.state = State.PRINTING;
-        assertTrue(printButton.disabled);
-
-        buttonStrip.state = State.ERROR;
-        assertTrue(printButton.disabled);
-
-        buttonStrip.state = State.FATAL_ERROR;
-        assertTrue(printButton.disabled);
-      });
-
-  // Tests that the buttons are in the correct order for different platforms.
-  // See https://crbug.com/880562.
-  test(assert(button_strip_test.TestNames.ButtonOrder), function() {
-    // Verify that there are only 2 buttons.
-    assertEquals(
-        2, buttonStrip.shadowRoot.querySelectorAll('cr-button').length);
-
-    const firstButton =
-        buttonStrip.shadowRoot.querySelector('cr-button:not(:last-child)');
-    const lastButton =
-        buttonStrip.shadowRoot.querySelector('cr-button:last-child');
-    const printButton =
-        buttonStrip.shadowRoot.querySelector('cr-button.action-button');
-    const cancelButton =
-        buttonStrip.shadowRoot.querySelector('cr-button.cancel-button');
-
-    if (isWindows) {
-      // On Windows, the print button is on the left.
-      assertEquals(firstButton, printButton);
-      assertEquals(lastButton, cancelButton);
-    } else {
-      assertEquals(firstButton, cancelButton);
-      assertEquals(lastButton, printButton);
-    }
-  });
-
-  // Tests that the button strip fires print-requested and cancel-requested
-  // events.
-  test(assert(button_strip_test.TestNames.ButtonStripFiresEvents), function() {
-    const printButton =
-        buttonStrip.shadowRoot.querySelector('cr-button.action-button');
-    const cancelButton =
-        buttonStrip.shadowRoot.querySelector('cr-button.cancel-button');
-
-    const whenPrintRequested = eventToPromise('print-requested', buttonStrip);
-    printButton.click();
-    return whenPrintRequested.then(() => {
-      const whenCancelRequested =
-          eventToPromise('cancel-requested', buttonStrip);
-      cancelButton.click();
-      return whenCancelRequested;
-    });
-  });
-});
diff --git a/chrome/test/data/webui/print_preview/button_strip_test.ts b/chrome/test/data/webui/print_preview/button_strip_test.ts
new file mode 100644
index 0000000..b7090133
--- /dev/null
+++ b/chrome/test/data/webui/print_preview/button_strip_test.ts
@@ -0,0 +1,104 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {CrButtonElement, Destination, DestinationConnectionStatus, DestinationOrigin, DestinationType, PrintPreviewButtonStripElement, State} from 'chrome://print/print_preview.js';
+import {assert} from 'chrome://resources/js/assert.m.js';
+import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
+
+const button_strip_test = {
+  suiteName: 'ButtonStripTest',
+  TestNames: {
+    ButtonStripChangesForState: 'button strip changes for state',
+    ButtonOrder: 'button order',
+    ButtonStripFiresEvents: 'button strip fires events',
+  },
+};
+
+Object.assign(window, {button_strip_test: button_strip_test});
+
+suite(button_strip_test.suiteName, function() {
+  let buttonStrip: PrintPreviewButtonStripElement;
+
+  setup(function() {
+    document.body.innerHTML = '';
+    buttonStrip = document.createElement('print-preview-button-strip');
+
+    buttonStrip.destination = new Destination(
+        'FooDevice', DestinationType.GOOGLE, DestinationOrigin.COOKIES,
+        'FooName', DestinationConnectionStatus.ONLINE);
+    buttonStrip.state = State.READY;
+    // No max sheets limit is specified.
+    buttonStrip.maxSheets = 0;
+    document.body.appendChild(buttonStrip);
+  });
+
+  // Tests that the correct message is shown for non-READY states, and that
+  // the print button is disabled appropriately.
+  test(
+      assert(button_strip_test.TestNames.ButtonStripChangesForState),
+      function() {
+        const printButton =
+            buttonStrip.shadowRoot!.querySelector<CrButtonElement>(
+                '.action-button')!;
+        assertFalse(printButton.disabled);
+
+        buttonStrip.state = State.NOT_READY;
+        assertTrue(printButton.disabled);
+
+        buttonStrip.state = State.PRINTING;
+        assertTrue(printButton.disabled);
+
+        buttonStrip.state = State.ERROR;
+        assertTrue(printButton.disabled);
+
+        buttonStrip.state = State.FATAL_ERROR;
+        assertTrue(printButton.disabled);
+      });
+
+  // Tests that the buttons are in the correct order for different platforms.
+  // See https://crbug.com/880562.
+  test(assert(button_strip_test.TestNames.ButtonOrder), function() {
+    // Verify that there are only 2 buttons.
+    assertEquals(
+        2, buttonStrip.shadowRoot!.querySelectorAll('cr-button').length);
+
+    const firstButton =
+        buttonStrip.shadowRoot!.querySelector('cr-button:not(:last-child)');
+    const lastButton =
+        buttonStrip.shadowRoot!.querySelector('cr-button:last-child');
+    const printButton =
+        buttonStrip.shadowRoot!.querySelector('cr-button.action-button');
+    const cancelButton =
+        buttonStrip.shadowRoot!.querySelector('cr-button.cancel-button');
+
+    // <if expr="is_win">
+    // On Windows, the print button is on the left.
+    assertEquals(firstButton, printButton);
+    assertEquals(lastButton, cancelButton);
+    // </if>
+    // <if expr="not is_win">
+    assertEquals(firstButton, cancelButton);
+    assertEquals(lastButton, printButton);
+    // </if>
+  });
+
+  // Tests that the button strip fires print-requested and cancel-requested
+  // events.
+  test(assert(button_strip_test.TestNames.ButtonStripFiresEvents), function() {
+    const printButton = buttonStrip.shadowRoot!.querySelector<HTMLElement>(
+        'cr-button.action-button')!;
+    const cancelButton = buttonStrip.shadowRoot!.querySelector<HTMLElement>(
+        'cr-button.cancel-button')!;
+
+    const whenPrintRequested = eventToPromise('print-requested', buttonStrip);
+    printButton.click();
+    return whenPrintRequested.then(() => {
+      const whenCancelRequested =
+          eventToPromise('cancel-requested', buttonStrip);
+      cancelButton.click();
+      return whenCancelRequested;
+    });
+  });
+});
diff --git a/chrome/test/data/webui/print_preview/color_settings_test.js b/chrome/test/data/webui/print_preview/color_settings_test.js
deleted file mode 100644
index 7a8be017..0000000
--- a/chrome/test/data/webui/print_preview/color_settings_test.js
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'chrome://print/print_preview.js';
-
-import {assert} from 'chrome://resources/js/assert.m.js';
-import {isChromeOS, isLacros} from 'chrome://resources/js/cr.m.js';
-
-import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {eventToPromise, fakeDataBind} from 'chrome://webui-test/test_util.js';
-
-import {selectOption} from './print_preview_test_utils.js';
-
-suite('ColorSettingsTest', function() {
-  /** @type {!PrintPreviewColorSettingsElement} */
-  let colorSection;
-
-  /** @type {!PrintPreviewModelElement} */
-  let model;
-
-  /** @override */
-  setup(function() {
-    document.body.innerHTML = '';
-    model = /** @type {!PrintPreviewModelElement} */ (
-        document.createElement('print-preview-model'));
-    document.body.appendChild(model);
-
-    colorSection = /** @type {!PrintPreviewColorSettingsElement} */ (
-        document.createElement('print-preview-color-settings'));
-    colorSection.settings = model.settings;
-    colorSection.disabled = false;
-    fakeDataBind(model, colorSection, 'settings');
-    model.set('settings.color.available', true);
-    document.body.appendChild(colorSection);
-  });
-
-  // Tests that setting the setting updates the UI.
-  test('set setting', async () => {
-    const select = colorSection.shadowRoot.querySelector('select');
-    assertEquals('color', select.value);
-
-    colorSection.setSetting('color', false);
-    await eventToPromise('process-select-change', colorSection);
-    assertEquals('bw', select.value);
-  });
-
-  // Tests that selecting a new option in the dropdown updates the setting.
-  test('select option', async () => {
-    // Verify that the selected option and names are as expected.
-    const select = colorSection.shadowRoot.querySelector('select');
-    assertEquals('color', select.value);
-    assertTrue(/** @type {boolean} */ (colorSection.getSettingValue('color')));
-    assertFalse(colorSection.getSetting('color').setFromUi);
-    assertEquals(2, select.options.length);
-
-    // Verify that selecting an new option in the dropdown sets the setting.
-    await selectOption(colorSection, 'bw');
-    assertFalse(/** @type {boolean} */ (colorSection.getSettingValue('color')));
-    assertTrue(colorSection.getSetting('color').setFromUi);
-  });
-
-  if (isChromeOS || isLacros) {
-    // Tests that if the setting is enforced by enterprise policy it is
-    // disabled.
-    test('disabled by policy', function() {
-      // Verify that the selected option and names are as expected.
-      const select = colorSection.shadowRoot.querySelector('select');
-      assertFalse(select.disabled);
-
-      model.set('settings.color.setByPolicy', true);
-      assertTrue(select.disabled);
-    });
-  }
-});
diff --git a/chrome/test/data/webui/print_preview/color_settings_test.ts b/chrome/test/data/webui/print_preview/color_settings_test.ts
new file mode 100644
index 0000000..35236a1
--- /dev/null
+++ b/chrome/test/data/webui/print_preview/color_settings_test.ts
@@ -0,0 +1,69 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://print/print_preview.js';
+
+import {PrintPreviewColorSettingsElement, PrintPreviewModelElement} from 'chrome://print/print_preview.js';
+
+import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {eventToPromise, fakeDataBind} from 'chrome://webui-test/test_util.js';
+
+import {selectOption} from './print_preview_test_utils.js';
+
+suite('ColorSettingsTest', function() {
+  let colorSection: PrintPreviewColorSettingsElement;
+
+  let model: PrintPreviewModelElement;
+
+  setup(function() {
+    document.body.innerHTML = '';
+    model = document.createElement('print-preview-model');
+    document.body.appendChild(model);
+
+    colorSection = document.createElement('print-preview-color-settings');
+    colorSection.settings = model.settings;
+    colorSection.disabled = false;
+    fakeDataBind(model, colorSection, 'settings');
+    model.set('settings.color.available', true);
+    document.body.appendChild(colorSection);
+  });
+
+  // Tests that setting the setting updates the UI.
+  test('set setting', async () => {
+    const select = colorSection.shadowRoot!.querySelector('select')!;
+    assertEquals('color', select.value);
+
+    colorSection.setSetting('color', false);
+    await eventToPromise('process-select-change', colorSection);
+    assertEquals('bw', select.value);
+  });
+
+  // Tests that selecting a new option in the dropdown updates the setting.
+  test('select option', async () => {
+    // Verify that the selected option and names are as expected.
+    const select = colorSection.shadowRoot!.querySelector('select')!;
+    assertEquals('color', select.value);
+    assertTrue(colorSection.getSettingValue('color') as boolean);
+    assertFalse(colorSection.getSetting('color').setFromUi);
+    assertEquals(2, select.options.length);
+
+    // Verify that selecting an new option in the dropdown sets the setting.
+    await selectOption(colorSection, 'bw');
+    assertFalse(colorSection.getSettingValue('color') as boolean);
+    assertTrue(colorSection.getSetting('color').setFromUi);
+  });
+
+  // <if expr="chromeos or lacros">
+  // Tests that if the setting is enforced by enterprise policy it is
+  // disabled.
+  test('disabled by policy', function() {
+    // Verify that the selected option and names are as expected.
+    const select = colorSection.shadowRoot!.querySelector('select')!;
+    assertFalse(select.disabled);
+
+    model.set('settings.color.setByPolicy', true);
+    assertTrue(select.disabled);
+  });
+  // </if>
+});
diff --git a/chrome/test/data/webui/print_preview/copies_settings_test.js b/chrome/test/data/webui/print_preview/copies_settings_test.ts
similarity index 81%
rename from chrome/test/data/webui/print_preview/copies_settings_test.js
rename to chrome/test/data/webui/print_preview/copies_settings_test.ts
index aa086c1..622cf6f1 100644
--- a/chrome/test/data/webui/print_preview/copies_settings_test.js
+++ b/chrome/test/data/webui/print_preview/copies_settings_test.ts
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 import {DEFAULT_MAX_COPIES, PrintPreviewCopiesSettingsElement, PrintPreviewModelElement} from 'chrome://print/print_preview.js';
-import {assert} from 'chrome://resources/js/assert.m.js';
 
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {fakeDataBind} from 'chrome://webui-test/test_util.js';
@@ -11,22 +10,17 @@
 import {triggerInputEvent} from './print_preview_test_utils.js';
 
 suite('CopiesSettingsTest', function() {
-  /** @type {!PrintPreviewCopiesSettingsElement} */
-  let copiesSection;
+  let copiesSection: PrintPreviewCopiesSettingsElement;
 
-  /** @type {!PrintPreviewModelElement} */
-  let model;
+  let model: PrintPreviewModelElement;
 
-  /** @override */
   setup(function() {
     document.body.innerHTML = '';
-    model = /** @type {!PrintPreviewModelElement} */ (
-        document.createElement('print-preview-model'));
+    model = document.createElement('print-preview-model');
     document.body.appendChild(model);
     model.set('settings.collate.available', true);
 
-    copiesSection = /** @type {!PrintPreviewCopiesSettingsElement} */ (
-        document.createElement('print-preview-copies-settings'));
+    copiesSection = document.createElement('print-preview-copies-settings');
     copiesSection.settings = model.settings;
     copiesSection.disabled = false;
     fakeDataBind(model, copiesSection, 'settings');
@@ -35,12 +29,12 @@
 
   /**
    * Confirms that |max| is currently set as copiesSection's maxCopies.
-   * @param {number} max Expected maximum copies value to check.
+   * @param max Expected maximum copies value to check.
    */
-  async function checkCopiesMax(max) {
-    const input = copiesSection.shadowRoot
-                      .querySelector('print-preview-number-settings-section')
-                      .getInput();
+  async function checkCopiesMax(max: number) {
+    const input =
+        copiesSection.shadowRoot!
+            .querySelector('print-preview-number-settings-section')!.getInput();
 
     // Check that |max| copies is valid.
     await triggerInputEvent(input, max.toString(), copiesSection);
@@ -55,9 +49,8 @@
   // supported.
   test('set copies max', async () => {
     const copiesInput =
-        copiesSection.shadowRoot
-            .querySelector('print-preview-number-settings-section')
-            .getInput();
+        copiesSection.shadowRoot!
+            .querySelector('print-preview-number-settings-section')!.getInput();
     assertEquals('1', copiesInput.value);
     assertFalse(copiesSection.getSetting('copies').setFromUi);
 
@@ -70,7 +63,8 @@
   });
 
   test('collate visibility', async () => {
-    const collateSection = copiesSection.shadowRoot.querySelector('.checkbox');
+    const collateSection =
+        copiesSection.shadowRoot!.querySelector<HTMLElement>('.checkbox')!;
     assertTrue(collateSection.hidden);
 
     copiesSection.setSetting('copies', 2);
@@ -83,9 +77,8 @@
 
     // Set copies empty.
     const copiesInput =
-        copiesSection.shadowRoot
-            .querySelector('print-preview-number-settings-section')
-            .getInput();
+        copiesSection.shadowRoot!
+            .querySelector('print-preview-number-settings-section')!.getInput();
     await triggerInputEvent(copiesInput, '', copiesSection);
     assertTrue(collateSection.hidden);
 
@@ -102,9 +95,8 @@
   // correctly.
   test('set copies', async () => {
     const copiesInput =
-        copiesSection.shadowRoot
-            .querySelector('print-preview-number-settings-section')
-            .getInput();
+        copiesSection.shadowRoot!
+            .querySelector('print-preview-number-settings-section')!.getInput();
     assertEquals('1', copiesInput.value);
     assertFalse(copiesSection.getSetting('copies').setFromUi);
 
@@ -143,9 +135,8 @@
   // Verifies that the inputs update when the value is updated.
   test('update from settings', function() {
     const copiesInput =
-        copiesSection.shadowRoot
-            .querySelector('print-preview-number-settings-section')
-            .getInput();
+        copiesSection.shadowRoot!
+            .querySelector('print-preview-number-settings-section')!.getInput();
     const collateCheckbox = copiesSection.$.collate;
 
     assertEquals('1', copiesInput.value);
diff --git a/chrome/test/data/webui/print_preview/custom_margins_test.js b/chrome/test/data/webui/print_preview/custom_margins_test.ts
similarity index 79%
rename from chrome/test/data/webui/print_preview/custom_margins_test.js
rename to chrome/test/data/webui/print_preview/custom_margins_test.ts
index e008698..84c218a2f 100644
--- a/chrome/test/data/webui/print_preview/custom_margins_test.js
+++ b/chrome/test/data/webui/print_preview/custom_margins_test.ts
@@ -2,64 +2,57 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {CustomMarginsOrientation, Margins, MarginsType, MeasurementSystem, MeasurementSystemUnitType, PrintPreviewMarginControlContainerElement, PrintPreviewMarginControlElement, PrintPreviewModelElement, Size, State} from 'chrome://print/print_preview.js';
+import {CustomMarginsOrientation, Margins, MarginsSetting, MarginsType, MeasurementSystem, MeasurementSystemUnitType, PrintPreviewMarginControlContainerElement, PrintPreviewMarginControlElement, PrintPreviewModelElement, Size, State} from 'chrome://print/print_preview.js';
 import {assert, assertNotReached} from 'chrome://resources/js/assert.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise, fakeDataBind} from 'chrome://webui-test/test_util.js';
 
-window.custom_margins_test = {};
-const custom_margins_test = window.custom_margins_test;
-custom_margins_test.suiteName = 'CustomMarginsTest';
-/** @enum {string} */
-custom_margins_test.TestNames = {
-  ControlsCheck: 'controls check',
-  SetFromStickySettings: 'set from sticky settings',
-  DragControls: 'drag controls',
-  SetControlsWithTextbox: 'set controls with textbox',
-  SetControlsWithTextboxMetric: 'set controls with textbox metric',
-  RestoreStickyMarginsAfterDefault: 'restore sticky margins after default',
-  MediaSizeClearsCustomMargins: 'media size clears custom margins',
-  LayoutClearsCustomMargins: 'layout clears custom margins',
-  IgnoreDocumentMarginsFromPDF: 'ignore document margins from pdf',
-  MediaSizeClearsCustomMarginsPDF: 'media size clears custom margins pdf',
-  RequestScrollToOutOfBoundsTextbox: 'request scroll to out of bounds textbox',
-  ControlsDisabledOnError: 'controls disabled on error',
+const custom_margins_test = {
+  suiteName: 'CustomMarginsTest',
+  TestNames: {
+    ControlsCheck: 'controls check',
+    SetFromStickySettings: 'set from sticky settings',
+    DragControls: 'drag controls',
+    SetControlsWithTextbox: 'set controls with textbox',
+    SetControlsWithTextboxMetric: 'set controls with textbox metric',
+    RestoreStickyMarginsAfterDefault: 'restore sticky margins after default',
+    MediaSizeClearsCustomMargins: 'media size clears custom margins',
+    LayoutClearsCustomMargins: 'layout clears custom margins',
+    IgnoreDocumentMarginsFromPDF: 'ignore document margins from pdf',
+    MediaSizeClearsCustomMarginsPDF: 'media size clears custom margins pdf',
+    RequestScrollToOutOfBoundsTextbox:
+        'request scroll to out of bounds textbox',
+    ControlsDisabledOnError: 'controls disabled on error',
+  },
 };
 
+Object.assign(window, {custom_margins_test: custom_margins_test});
 suite(custom_margins_test.suiteName, function() {
-  /** @type {!PrintPreviewMarginControlContainerElement} */
-  let container;
+  let container: PrintPreviewMarginControlContainerElement;
 
-  /** @type {PrintPreviewModelElement} */
-  let model;
+  let model: PrintPreviewModelElement;
 
-  /** @type {!Array<!CustomMarginsOrientation>} */
-  let sides = [];
+  let sides: CustomMarginsOrientation[] = [];
 
-  /** @type {!MeasurementSystem} */
-  let measurementSystem;
+  let measurementSystem: MeasurementSystem;
 
-  /** @type {number} */
-  const pixelsPerInch = 100;
+  const pixelsPerInch: number = 100;
 
-  /** @type {number} */
-  const pointsPerInch = 72.0;
+  const pointsPerInch: number = 72.0;
 
-  /** @type {number} */
-  const defaultMarginPts = 36;  // 0.5 inch
+  const defaultMarginPts: number = 36;  // 0.5 inch
 
   // Keys for the custom margins setting, in order.
-  const keys = ['marginTop', 'marginRight', 'marginBottom', 'marginLeft'];
+  const keys: string[] =
+      ['marginTop', 'marginRight', 'marginBottom', 'marginLeft'];
 
-  /** @override */
   setup(function() {
     document.body.innerHTML = '';
     measurementSystem =
         new MeasurementSystem(',', '.', MeasurementSystemUnitType.IMPERIAL);
-    model = /** @type {!PrintPreviewModelElement} */ (
-        document.createElement('print-preview-model'));
+    model = document.createElement('print-preview-model');
     document.body.appendChild(model);
     model.set('settings.mediaSize.available', true);
 
@@ -68,8 +61,8 @@
       CustomMarginsOrientation.BOTTOM, CustomMarginsOrientation.LEFT
     ];
 
-    container = /** @type {!PrintPreviewMarginControlContainerElement} */ (
-        document.createElement('print-preview-margin-control-container'));
+    container =
+        document.createElement('print-preview-margin-control-container');
     container.previewLoaded = false;
     // 8.5 x 11, in points
     container.pageSize = new Size(612, 794);
@@ -78,10 +71,9 @@
     container.state = State.NOT_READY;
   });
 
-  /** @return {!NodeList<!PrintPreviewMarginControlElement>} */
-  function getControls() {
-    return /** @type {!NodeList<!PrintPreviewMarginControlElement>} */ (
-        container.shadowRoot.querySelectorAll('print-preview-margin-control'));
+  function getControls(): PrintPreviewMarginControlElement[] {
+    return Array.from(
+        container.shadowRoot!.querySelectorAll('print-preview-margin-control'));
   }
 
   /*
@@ -111,23 +103,25 @@
   }
 
   /**
-   * @param {!NodeList<!PrintPreviewMarginControlElement>} controls
-   * @return {!Promise} Promise that resolves when transitionend has fired
+   * @return Promise that resolves when transitionend has fired
    *     for all of the controls.
    */
-  function getAllTransitions(controls) {
-    return Promise.all(Array.from(controls).map(
-        control => eventToPromise('transitionend', control)));
+  function getAllTransitions(controls: PrintPreviewMarginControlElement[]):
+      Promise<any[]> {
+    return Promise.all(
+        controls.map(control => eventToPromise('transitionend', control)));
   }
 
   /**
    * Simulates dragging the margin control.
-   * @param {!PrintPreviewMarginControlElement} control The control to move.
-   * @param {number} start The starting position for the control in pixels.
-   * @param {number} end The ending position for the control in pixels.
+   * @param control The control to move.
+   * @param start The starting position for the control in pixels.
+   * @param end The ending position for the control in pixels.
    */
-  function dragControl(control, start, end) {
-    if (window.getComputedStyle(control)['pointer-events'] === 'none') {
+  function dragControl(
+      control: PrintPreviewMarginControlElement, start: number, end: number) {
+    if (window.getComputedStyle(control).getPropertyValue('pointer-events') ===
+        'none') {
       return;
     }
 
@@ -141,12 +135,12 @@
         yEnd = end;
         break;
       case CustomMarginsOrientation.RIGHT:
-        xStart = control.clipSize.width - start;
-        xEnd = control.clipSize.width - end;
+        xStart = control.clipSize!.width - start;
+        xEnd = control.clipSize!.width - end;
         break;
       case CustomMarginsOrientation.BOTTOM:
-        yStart = control.clipSize.height - start;
-        yEnd = control.clipSize.height - end;
+        yStart = control.clipSize!.height - start;
+        yEnd = control.clipSize!.height - end;
         break;
       case CustomMarginsOrientation.LEFT:
         xStart = start;
@@ -168,18 +162,20 @@
 
   /**
    * Tests setting the margin control with its textbox.
-   * @param {!PrintPreviewMarginControlElement} control The control.
-   * @param {string} key The control's key in the custom margin setting.
-   * @param {number} currentValuePts The current margin value in points.
-   * @param {string} input The new textbox input for the margin.
-   * @param {boolean} invalid Whether the new value is invalid.
-   * @param {number=} newValuePts the new margin value in pts. If not
+   * @param control The control.
+   * @param key The control's key in the custom margin setting.
+   * @param currentValuePts The current margin value in points.
+   * @param input The new textbox input for the margin.
+   * @param invalid Whether the new value is invalid.
+   * @param newValuePts the new margin value in pts. If not
    *     specified, computes the value assuming it is in bounds and assuming
    *     the default measurement system.
-   * @return {!Promise} Promise that resolves when the test is complete.
+   * @return Promise that resolves when the test is complete.
    */
   function testControlTextbox(
-      control, key, currentValuePts, input, invalid, newValuePts) {
+      control: PrintPreviewMarginControlElement, key: string,
+      currentValuePts: number, input: string, invalid: boolean,
+      newValuePts?: number): Promise<void> {
     if (newValuePts === undefined) {
       newValuePts = invalid ? currentValuePts :
                               Math.round(parseFloat(input) * pointsPerInch);
@@ -207,10 +203,8 @@
   /*
    * Initializes the settings custom margins to some test values, and returns
    * a map with the values.
-   * @return {!Map<!CustomMarginsOrientation,
-   *               number>}
    */
-  function setupCustomMargins() {
+  function setupCustomMargins(): Map<CustomMarginsOrientation, number> {
     const orientationEnum = CustomMarginsOrientation;
     const marginValues = new Map([
       [orientationEnum.TOP, 72], [orientationEnum.RIGHT, 36],
@@ -228,11 +222,12 @@
   /*
    * Tests that the custom margins and margin value are cleared when the
    * setting |settingName| is set to have value |newValue|.
-   * @param {string} settingName The name of the setting to check.
-   * @param {*} newValue The value to set the setting to.
-   * @return {!Promise} Promise that resolves when the check is complete.
+   * @param settingName The name of the setting to check.
+   * @param newValue The value to set the setting to.
+   * @return Promise that resolves when the check is complete.
    */
-  function validateMarginsClearedForSetting(settingName, newValue) {
+  function validateMarginsClearedForSetting(
+      settingName: string, newValue: any) {
     const marginValues = setupCustomMargins();
     return finishSetup().then(() => {
       // Simulate setting custom margins.
@@ -241,7 +236,7 @@
       // Validate control positions are set based on the custom values.
       const controls = getControls();
       controls.forEach((control, index) => {
-        const side = sides[index];
+        const side = sides[index]!;
         assertEquals(side, control.side);
         assertEquals(marginValues.get(side), control.getPositionInPts());
       });
@@ -268,10 +263,8 @@
   // Test that controls correctly appear when custom margins are selected and
   // disappear when the preview is loading.
   test(assert(custom_margins_test.TestNames.ControlsCheck), function() {
-    /** @return {!MarginsSetting} */
-    const getCustomMarginsValue = function() {
-      return /** @type {!MarginsSetting} */ (
-          container.getSettingValue('customMargins'));
+    const getCustomMarginsValue = function(): MarginsSetting {
+      return container.getSettingValue('customMargins') as MarginsSetting;
     };
     return finishSetup()
         .then(() => {
@@ -318,7 +311,7 @@
         })
         .then(function() {
           const controls = getControls();
-          controls.forEach((control, index) => {
+          controls.forEach(control => {
             assertEquals('0', window.getComputedStyle(control).opacity);
             assertTrue(control.invisible);
             assertTrue(control.disabled);
@@ -340,7 +333,7 @@
 
       // Validate control positions have been updated.
       controls.forEach((control, index) => {
-        const side = sides[index];
+        const side = sides[index]!;
         assertEquals(side, control.side);
         assertEquals(marginValues.get(side), control.getPositionInPts());
       });
@@ -357,9 +350,12 @@
      *     of controls.
      * @param {number} newPositionInPts The new position in points.
      */
-    const testControl = function(control, index, newPositionInPts) {
-      const oldValue = container.getSettingValue('customMargins');
-      assertEquals(defaultMarginPts, oldValue[keys[index]]);
+    const testControl = function(
+        control: PrintPreviewMarginControlElement, index: number,
+        newPositionInPts: number): Promise<void> {
+      const oldValue =
+          container.getSettingValue('customMargins') as {[k: string]: number};
+      assertEquals(defaultMarginPts, oldValue[keys[index]!]);
 
       // Compute positions in pixels.
       const oldPositionInPixels =
@@ -371,7 +367,7 @@
       dragControl(control, oldPositionInPixels, newPositionInPixels);
       return whenDragChanged.then(function() {
         const newValue = container.getSettingValue('customMargins');
-        assertEquals(newPositionInPts, newValue[keys[index]]);
+        assertEquals(newPositionInPts, newValue[keys[index]!]);
       });
     };
 
@@ -387,40 +383,41 @@
       // with the correct initial margin offset.
       // Set all controls to 108 = 1.5" in points.
       window.requestAnimationFrame(function() {
-        return testControl(controls[0], 0, 108)
-            .then(testControl(controls[1], 1, 108))
-            .then(testControl(controls[2], 2, 108))
-            .then(testControl(controls[3], 3, 108));
+        return testControl(controls[0]!, 0, 108)
+            .then(() => testControl(controls[1]!, 1, 108))
+            .then(() => testControl(controls[2]!, 2, 108))
+            .then(() => testControl(controls[3]!, 3, 108));
       });
     });
   });
 
   /**
-   * @param {!NodeList<!PrintPreviewMarginControlElement>} controls
-   * @param {number} currentValue Current margin value in pts
-   * @param {string} input String to set in margin textboxes
-   * @param {boolean} invalid Whether the string is invalid
-   * @param {number=} newValuePts the new margin value in pts. If not
+   * @param currentValue Current margin value in pts
+   * @param input String to set in margin textboxes
+   * @param invalid Whether the string is invalid
+   * @param newValuePts the new margin value in pts. If not
    *     specified, computes the value assuming it is in bounds and assuming
    *     the default measurement system.
-   * @return {!Promise} Promise that resolves when all controls have been
+   * @return Promise that resolves when all controls have been
    *     tested.
    */
   function testAllTextboxes(
-      controls, currentValue, input, invalid, newValuePts) {
+      controls: PrintPreviewMarginControlElement[], currentValue: number,
+      input: string, invalid: boolean, newValuePts?: number): Promise<void> {
     return testControlTextbox(
-               controls[0], keys[0], currentValue, input, invalid, newValuePts)
+               controls[0]!, keys[0]!, currentValue, input, invalid,
+               newValuePts)
         .then(
             () => testControlTextbox(
-                controls[1], keys[1], currentValue, input, invalid,
+                controls[1]!, keys[1]!, currentValue, input, invalid,
                 newValuePts))
         .then(
             () => testControlTextbox(
-                controls[2], keys[2], currentValue, input, invalid,
+                controls[2]!, keys[2]!, currentValue, input, invalid,
                 newValuePts))
         .then(
             () => testControlTextbox(
-                controls[3], keys[3], currentValue, input, invalid,
+                controls[3]!, keys[3]!, currentValue, input, invalid,
                 newValuePts));
   }
 
@@ -433,7 +430,7 @@
           // Set a shorter delay for testing so the test doesn't take too
           // long.
           controls.forEach(c => {
-            c.getInput().setAttribute('data-timeout-delay', 1);
+            c.getInput().setAttribute('data-timeout-delay', '1');
           });
           model.set('settings.margins.value', MarginsType.CUSTOM);
           flush();
@@ -460,11 +457,11 @@
               .then(() => testAllTextboxes(controls, newMargin2, value3, false))
               .then(
                   () => testControlTextbox(
-                      controls[0], keys[0], newMargin3, '100', false,
+                      controls[0]!, keys[0]!, newMargin3, '100', false,
                       maxTopMargin))
               .then(
                   () => testControlTextbox(
-                      controls[0], keys[0], maxTopMargin, '1,000', false,
+                      controls[0]!, keys[0]!, maxTopMargin, '1,000', false,
                       maxTopMargin));
         });
       });
@@ -483,7 +480,7 @@
           // Set a shorter delay for testing so the test doesn't take too
           // long.
           controls.forEach(c => {
-            c.getInput().setAttribute('data-timeout-delay', 1);
+            c.getInput().setAttribute('data-timeout-delay', '1');
           });
           model.set('settings.margins.value', MarginsType.CUSTOM);
           flush();
@@ -525,11 +522,11 @@
                       newMargin3Pts))
               .then(
                   () => testControlTextbox(
-                      controls[0], keys[0], newMargin3Pts, '1.000.000', false,
+                      controls[0]!, keys[0]!, newMargin3Pts, '1.000.000', false,
                       maxTopMargin))
               .then(
                   () => testControlTextbox(
-                      controls[0], keys[0], maxTopMargin, '1.000', false,
+                      controls[0]!, keys[0]!, maxTopMargin, '1.000', false,
                       maxTopMargin));
         });
       });
@@ -547,7 +544,7 @@
 
           // Validate control positions are set based on the custom values.
           controls.forEach((control, index) => {
-            const side = sides[index];
+            const side = sides[index]!;
             assertEquals(side, control.side);
             assertEquals(marginValues.get(side), control.getPositionInPts());
           });
@@ -557,7 +554,7 @@
 
           // Validate control positions still reflect the custom values.
           controls.forEach((control, index) => {
-            const side = sides[index];
+            const side = sides[index]!;
             assertEquals(side, control.side);
             assertEquals(marginValues.get(side), control.getPositionInPts());
           });
@@ -664,7 +661,7 @@
 
               // Focus the bottom control, which is currently not visible since
               // the viewer is showing only the top left quarter of the page.
-              const bottomControl = controls[2];
+              const bottomControl = controls[2]!;
               const whenEventFired =
                   eventToPromise('text-focus-position', container);
               bottomControl.$.input.focus();
diff --git a/chrome/test/data/webui/print_preview/print_preview_test_utils.ts b/chrome/test/data/webui/print_preview/print_preview_test_utils.ts
index 63e6275..945f309e 100644
--- a/chrome/test/data/webui/print_preview/print_preview_test_utils.ts
+++ b/chrome/test/data/webui/print_preview/print_preview_test_utils.ts
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 import {CapabilitiesResponse, Cdd, DEFAULT_MAX_COPIES, Destination, DestinationCertificateStatus, DestinationConnectionStatus, DestinationOrigin, DestinationStore, DestinationType, GooglePromotedDestinationId, LocalDestinationInfo, MeasurementSystemUnitType, MediaSizeCapability, MediaSizeOption, NativeInitialSettings, VendorCapabilityValueType} from 'chrome://print/print_preview.js';
+import {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.m.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {WebUIListenerMixin} from 'chrome://resources/js/web_ui_listener_mixin.js';
@@ -301,7 +302,7 @@
  * @return Promise that resolves when the input-change event has fired.
  */
 export function triggerInputEvent(
-    inputElement: HTMLInputElement, input: string,
+    inputElement: HTMLInputElement|CrInputElement, input: string,
     parentElement: HTMLElement): Promise<void> {
   inputElement.value = input;
   inputElement.dispatchEvent(
diff --git a/chrome/test/data/webui/settings/BUILD.gn b/chrome/test/data/webui/settings/BUILD.gn
index 88371827..32238a2 100644
--- a/chrome/test/data/webui/settings/BUILD.gn
+++ b/chrome/test/data/webui/settings/BUILD.gn
@@ -104,7 +104,7 @@
   "site_data_test.js",
   "site_details_permission_tests.ts",
   "site_details_tests.ts",
-  "site_entry_tests.js",
+  "site_entry_tests.ts",
   "site_favicon_test.js",
   "site_list_tests.ts",
   "site_settings_page_test.ts",
diff --git a/chrome/test/data/webui/settings/chromeos/os_about_page_tests.js b/chrome/test/data/webui/settings/chromeos/os_about_page_tests.js
index 3092b8c..f199b87 100644
--- a/chrome/test/data/webui/settings/chromeos/os_about_page_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/os_about_page_tests.js
@@ -79,6 +79,26 @@
     }
 
     /**
+     * @param {string} id
+     */
+    function navigateToSettingsPageWithId(id) {
+      const params = new URLSearchParams;
+      params.append('settingId', id);
+      settings.Router.getInstance().navigateTo(
+          settings.routes.ABOUT_ABOUT, params);
+
+      Polymer.dom.flush();
+    }
+
+    /**
+     * @param {string} id
+     * @return {!HTMLButtonElement}
+     */
+    function getDeepLinkButtonElementById(id) {
+      return page.$$(`#${id}`).shadowRoot.querySelector('cr-icon-button');
+    }
+
+    /**
      * Test that the status icon and status message update according to
      * incoming 'update-status-changed' events.
      */
@@ -543,19 +563,48 @@
       await initNewPage();
       Polymer.dom.flush();
 
-      const params = new URLSearchParams;
-      params.append('settingId', '1707');  // Setting::kDiagnostics
-      settings.Router.getInstance().navigateTo(
-          settings.routes.ABOUT_ABOUT, params);
+      const diagnosticsId = '1707';
+      navigateToSettingsPageWithId(diagnosticsId);
 
-      Polymer.dom.flush();
-
-      const deepLinkElement =
-          page.$$('#diagnostics').shadowRoot.querySelector('cr-icon-button');
+      const deepLinkElement = getDeepLinkButtonElementById('diagnostics');
       await test_util.waitAfterNextRender(deepLinkElement);
       assertEquals(
           deepLinkElement, getDeepActiveElement(),
-          'Diagnostics should be focused for settingId=1707.');
+          `Diagnostics should be focused for settingId=${diagnosticsId}.`);
+    });
+
+    test('LaunchFirmwareUpdates', async function() {
+      loadTimeData.overrideValues({
+        isDeepLinkingEnabled: true,
+        isFirmwareUpdaterAppEnabled: true,
+      });
+
+      await initNewPage();
+      Polymer.dom.flush();
+
+      assertTrue(!!page.$.firmwareUpdates);
+      page.$.firmwareUpdates.click();
+      await aboutBrowserProxy.whenCalled('openFirmwareUpdatesPage');
+    });
+
+    test('Deep link to firmware updates', async () => {
+      loadTimeData.overrideValues({
+        isDeepLinkingEnabled: true,
+        isFirmwareUpdaterAppEnabled: true,
+      });
+
+      await initNewPage();
+      Polymer.dom.flush();
+
+      const firmwareUpdatesId = '1709';
+      navigateToSettingsPageWithId(firmwareUpdatesId);
+
+      const deepLinkElement = getDeepLinkButtonElementById('firmwareUpdates');
+      await test_util.waitAfterNextRender(deepLinkElement);
+      assertEquals(
+          deepLinkElement, getDeepActiveElement(),
+          `Firmware updates should be focused for settingId=${
+              firmwareUpdatesId}.`);
     });
 
     // Regression test for crbug.com/1220294
diff --git a/chrome/test/data/webui/settings/chromeos/test_about_page_browser_proxy_chromeos.js b/chrome/test/data/webui/settings/chromeos/test_about_page_browser_proxy_chromeos.js
index 768c52e..af968cb 100644
--- a/chrome/test/data/webui/settings/chromeos/test_about_page_browser_proxy_chromeos.js
+++ b/chrome/test/data/webui/settings/chromeos/test_about_page_browser_proxy_chromeos.js
@@ -27,6 +27,7 @@
       'refreshTPMFirmwareUpdateStatus',
       'requestUpdate',
       'setChannel',
+      'openFirmwareUpdatesPage',
     ]);
 
     /** @private {!UpdateStatus} */
@@ -217,4 +218,9 @@
   launchReleaseNotes() {
     this.methodCalled('launchReleaseNotes');
   }
+
+  /** @override */
+  openFirmwareUpdatesPage() {
+    this.methodCalled('openFirmwareUpdatesPage');
+  }
 }
diff --git a/chrome/test/data/webui/settings/site_entry_tests.js b/chrome/test/data/webui/settings/site_entry_tests.ts
similarity index 78%
rename from chrome/test/data/webui/settings/site_entry_tests.js
rename to chrome/test/data/webui/settings/site_entry_tests.ts
index 0a4ebe4c..7de992d 100644
--- a/chrome/test/data/webui/settings/site_entry_tests.js
+++ b/chrome/test/data/webui/settings/site_entry_tests.ts
@@ -7,8 +7,9 @@
 
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {ContentSettingsTypes, LocalDataBrowserProxyImpl, SiteSettingsPrefsBrowserProxyImpl, SortMethod} from 'chrome://settings/lazy_load.js';
+import {LocalDataBrowserProxyImpl, SiteEntryElement, SiteSettingsPrefsBrowserProxyImpl, SortMethod} from 'chrome://settings/lazy_load.js';
 import {Router, routes} from 'chrome://settings/settings.js';
+import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {TestLocalDataBrowserProxy} from './test_local_data_browser_proxy.js';
@@ -20,7 +21,6 @@
 suite('SiteEntry_DisabledConsolidatedControls', function() {
   /**
    * An example eTLD+1 Object with multiple origins grouped under it.
-   * @type {!SiteGroup}
    */
   const TEST_MULTIPLE_SITE_GROUP = createSiteGroup('example.com', [
     'http://example.com',
@@ -30,44 +30,25 @@
 
   /**
    * An example eTLD+1 Object with a single origin in it.
-   * @type {!SiteGroup}
    */
   const TEST_SINGLE_SITE_GROUP = createSiteGroup('foo.com', [
     'https://login.foo.com',
   ]);
 
-  const TEST_COOKIE_LIST = {
-    id: 'foo',
-    children: [
-      {domain: 'example.com'},
-      {domain: 'example.com'},
-      {domain: 'example.com'},
-    ]
-  };
-
   /**
    * The mock proxy object to use during test.
-   * @type {TestSiteSettingsPrefsBrowserProxy}
    */
-  let browserProxy;
+  let browserProxy: TestSiteSettingsPrefsBrowserProxy;
 
   /**
    * The mock local data proxy object to use during test.
-   * @type {TestLocalDataBrowserProxy}
    */
-  let localDataBrowserProxy;
+  let localDataBrowserProxy: TestLocalDataBrowserProxy;
 
   /**
    * A site list element created before each test.
-   * @type {SiteList}
    */
-  let testElement;
-
-  /**
-   * The clickable element that expands to show the list of origins.
-   * @type {Element}
-   */
-  let toggleButton;
+  let testElement: SiteEntryElement;
 
   suiteSetup(function() {
     loadTimeData.overrideValues({
@@ -82,12 +63,9 @@
     SiteSettingsPrefsBrowserProxyImpl.setInstance(browserProxy);
     LocalDataBrowserProxyImpl.setInstance(localDataBrowserProxy);
 
-    PolymerTest.clearBody();
+    document.body.innerHTML = '';
     testElement = document.createElement('site-entry');
-    assertTrue(!!testElement);
     document.body.appendChild(testElement);
-
-    toggleButton = testElement.$.toggleButton;
   });
 
   teardown(function() {
@@ -106,28 +84,32 @@
 
   test('expands and closes to show more origins', function() {
     testElement.siteGroup = TEST_MULTIPLE_SITE_GROUP;
-    assertTrue(testElement.grouped_(testElement.siteGroup));
-    assertEquals('false', toggleButton.getAttribute('aria-expanded'));
+    assertFalse(testElement.$.expandIcon.hidden);
+    assertEquals(
+        'false', testElement.$.toggleButton.getAttribute('aria-expanded'));
     const originList = testElement.$.originList.get();
     assertTrue(originList.classList.contains('iron-collapse-closed'));
     assertEquals('true', originList.getAttribute('aria-hidden'));
 
-    toggleButton.click();
-    assertEquals('true', toggleButton.getAttribute('aria-expanded'));
+    testElement.$.toggleButton.click();
+    assertEquals(
+        'true', testElement.$.toggleButton.getAttribute('aria-expanded'));
     assertTrue(originList.classList.contains('iron-collapse-opened'));
     assertEquals('false', originList.getAttribute('aria-hidden'));
   });
 
   test('with single origin navigates to Site Details', function() {
     testElement.siteGroup = TEST_SINGLE_SITE_GROUP;
-    assertFalse(testElement.grouped_(testElement.siteGroup));
-    assertEquals('false', toggleButton.getAttribute('aria-expanded'));
+    assertTrue(testElement.$.expandIcon.hidden);
+    assertEquals(
+        'false', testElement.$.toggleButton.getAttribute('aria-expanded'));
     const originList = testElement.$.originList.get();
     assertTrue(originList.classList.contains('iron-collapse-closed'));
     assertEquals('true', originList.getAttribute('aria-hidden'));
 
-    toggleButton.click();
-    assertEquals('false', toggleButton.getAttribute('aria-expanded'));
+    testElement.$.toggleButton.click();
+    assertEquals(
+        'false', testElement.$.toggleButton.getAttribute('aria-expanded'));
     assertTrue(originList.classList.contains('iron-collapse-closed'));
     assertEquals('true', originList.getAttribute('aria-hidden'));
     assertEquals(
@@ -143,25 +125,28 @@
     flush();
     const collapseChild = testElement.$.originList.get();
     flush();
-    const originList = collapseChild.querySelectorAll('.origin-link');
+    const originList =
+        collapseChild.querySelectorAll<HTMLElement>('.origin-link');
     assertEquals(3, originList.length);
 
     // Test clicking on one of these origins takes the user to Site Details,
     // with the correct origin.
-    originList[1].click();
+    originList[1]!.click();
     assertEquals(
         routes.SITE_SETTINGS_SITE_DETAILS.path,
         Router.getInstance().getCurrentRoute().path);
     assertEquals(
-        TEST_MULTIPLE_SITE_GROUP.origins[1].origin,
+        TEST_MULTIPLE_SITE_GROUP.origins[1]!.origin,
         Router.getInstance().getQueryParameters().get('site'));
   });
 
   test('with single origin, shows overflow menu', function() {
     testElement.siteGroup = TEST_SINGLE_SITE_GROUP;
     flush();
-    const overflowMenuButton = testElement.$$('#overflowMenuButton');
-    assertFalse(overflowMenuButton.closest('.row-aligned').hidden);
+    const overflowMenuButton =
+        testElement.$$<HTMLElement>('#overflowMenuButton')!;
+    assertFalse(
+        overflowMenuButton.closest<HTMLElement>('.row-aligned')!.hidden);
   });
 
   test('clear data for single origin fires the right method', async function() {
@@ -176,9 +161,9 @@
 
     for (let i = 0; i < originList.length; i++) {
       const menuOpened = eventToPromise('open-menu', testElement);
-      const originEntry = originList[i];
+      const originEntry = originList[i]!;
       const overflowMenuButton =
-          originEntry.querySelector('#originOverflowMenuButton');
+          originEntry.querySelector<HTMLElement>('#originOverflowMenuButton')!;
       overflowMenuButton.click();
       const openMenuEvent = await menuOpened;
 
@@ -186,7 +171,7 @@
       const {actionScope, index, origin} = args;
       assertEquals('origin', actionScope);
       assertEquals(testElement.listIndex, index);
-      assertEquals(testElement.siteGroup.origins[i].origin, origin);
+      assertEquals(testElement.siteGroup.origins[i]!.origin, origin);
     }
   });
 
@@ -197,19 +182,20 @@
         testElement.siteGroup =
             JSON.parse(JSON.stringify(TEST_MULTIPLE_SITE_GROUP));
         flush();
-        toggleButton.click();
+        testElement.$.toggleButton.click();
         assertTrue(testElement.$.originList.get().opened);
 
         // Remove all origins except one, then make sure it's not still
         // expanded.
-        testElement.siteGroup.origins.splice(1);
+        const siteGroupUpdated =
+            JSON.parse(JSON.stringify(TEST_MULTIPLE_SITE_GROUP));
+        siteGroupUpdated.origins.splice(1);
+        testElement.siteGroup = siteGroupUpdated;
         assertEquals(1, testElement.siteGroup.origins.length);
-        testElement.onSiteGroupChanged_(testElement.siteGroup);
         assertFalse(testElement.$.originList.get().opened);
       });
 
   test('cookies show correctly for grouped entries', function() {
-    localDataBrowserProxy.setCookieDetails(TEST_COOKIE_LIST);
     testElement.siteGroup = TEST_MULTIPLE_SITE_GROUP;
     flush();
     const cookiesLabel = testElement.$.cookies;
@@ -226,7 +212,7 @@
         .then((args) => {
           assertEquals(3, args);
           assertFalse(cookiesLabel.hidden);
-          assertEquals('· 3 cookies', cookiesLabel.textContent.trim());
+          assertEquals('· 3 cookies', cookiesLabel.textContent!.trim());
         });
   });
 
@@ -249,7 +235,7 @@
         .then((args) => {
           assertEquals(3, args);
           assertFalse(cookiesLabel.hidden);
-          assertEquals('· 3 cookies', cookiesLabel.textContent.trim());
+          assertEquals('· 3 cookies', cookiesLabel.textContent!.trim());
         });
   });
 
@@ -264,12 +250,14 @@
     testSiteGroup.origins[2].usage = numBytes3;
     testElement.siteGroup = testSiteGroup;
     flush();
-    return browserProxy.whenCalled('getFormattedBytes').then((args) => {
+    return browserProxy.whenCalled('getFormattedBytes').then(args => {
       const sumBytes = numBytes1 + numBytes2 + numBytes3;
+      assertEquals(sumBytes, args);
       assertEquals(
           `${sumBytes} B`,
-          testElement.root.querySelector('#displayName .data-unit')
-              .textContent.trim());
+          testElement.shadowRoot!
+              .querySelector<HTMLElement>(
+                  '#displayName .data-unit')!.textContent!.trim());
     });
   });
 
@@ -280,11 +268,13 @@
     testSiteGroup.origins[0].usage = numBytes;
     testElement.siteGroup = testSiteGroup;
     flush();
-    return browserProxy.whenCalled('getFormattedBytes').then((args) => {
+    return browserProxy.whenCalled('getFormattedBytes').then(args => {
+      assertEquals(numBytes, args);
       assertEquals(
           `${numBytes} B`,
-          testElement.root.querySelector('#displayName .data-unit')
-              .textContent.trim());
+          testElement.shadowRoot!
+              .querySelector<HTMLElement>(
+                  '#displayName .data-unit')!.textContent!.trim());
     });
   });
 
@@ -304,10 +294,12 @@
         flush();
         return browserProxy.whenCalled('getFormattedBytes').then((args) => {
           const sumBytes = numBytes1 + numBytes2 + numBytes3;
+          assertEquals(sumBytes, args);
           assertEquals(
               `${sumBytes} B`,
-              testElement.root.querySelector('#displayName .data-unit')
-                  .textContent.trim());
+              testElement.shadowRoot!
+                  .querySelector<HTMLElement>(
+                      '#displayName .data-unit')!.textContent!.trim());
         });
       });
 
@@ -320,7 +312,7 @@
     testElement.siteGroup = testSiteGroup;
     flush();
     assertEquals(
-        testElement.$.collapseParent.querySelector('site-favicon').url,
+        testElement.$.collapseParent.querySelector('site-favicon')!.url,
         'https://www.example.com');
   });
 
@@ -334,7 +326,7 @@
     testElement.siteGroup = testSiteGroup;
     flush();
     assertEquals(
-        testElement.$.collapseParent.querySelector('site-favicon').url,
+        testElement.$.collapseParent.querySelector('site-favicon')!.url,
         'https://login.example.com');
   });
 
@@ -351,7 +343,7 @@
     testElement.siteGroup = testSiteGroup;
     flush();
     assertEquals(
-        testElement.$.collapseParent.querySelector('site-favicon').url,
+        testElement.$.collapseParent.querySelector('site-favicon')!.url,
         'https://abc.example.com');
   });
 
@@ -376,13 +368,16 @@
     assertEquals(3, origins.length);
     assertEquals(
         'www.example.com',
-        origins[0].querySelector('#originSiteRepresentation').innerText.trim());
+        origins[0]!.querySelector<HTMLElement>(
+                       '#originSiteRepresentation')!.innerText.trim());
     assertEquals(
         'example.com',
-        origins[1].querySelector('#originSiteRepresentation').innerText.trim());
+        origins[1]!.querySelector<HTMLElement>(
+                       '#originSiteRepresentation')!.innerText.trim());
     assertEquals(
         'login.example.com',
-        origins[2].querySelector('#originSiteRepresentation').innerText.trim());
+        origins[2]!.querySelector<HTMLElement>(
+                       '#originSiteRepresentation')!.innerText.trim());
   });
 
   test('can be sorted by storage', function() {
@@ -406,13 +401,16 @@
     assertEquals(3, origins.length);
     assertEquals(
         'www.example.com',
-        origins[0].querySelector('#originSiteRepresentation').innerText.trim());
+        origins[0]!.querySelector<HTMLElement>(
+                       '#originSiteRepresentation')!.innerText.trim());
     assertEquals(
         'login.example.com',
-        origins[1].querySelector('#originSiteRepresentation').innerText.trim());
+        origins[1]!.querySelector<HTMLElement>(
+                       '#originSiteRepresentation')!.innerText.trim());
     assertEquals(
         'example.com',
-        origins[2].querySelector('#originSiteRepresentation').innerText.trim());
+        origins[2]!.querySelector<HTMLElement>(
+                       '#originSiteRepresentation')!.innerText.trim());
   });
 
   test('can be sorted by name', function() {
@@ -436,20 +434,22 @@
     assertEquals(3, origins.length);
     assertEquals(
         'example.com',
-        origins[0].querySelector('#originSiteRepresentation').innerText.trim());
+        origins[0]!.querySelector<HTMLElement>(
+                       '#originSiteRepresentation')!.innerText.trim());
     assertEquals(
         'login.example.com',
-        origins[1].querySelector('#originSiteRepresentation').innerText.trim());
+        origins[1]!.querySelector<HTMLElement>(
+                       '#originSiteRepresentation')!.innerText.trim());
     assertEquals(
         'www.example.com',
-        origins[2].querySelector('#originSiteRepresentation').innerText.trim());
+        origins[2]!.querySelector<HTMLElement>(
+                       '#originSiteRepresentation')!.innerText.trim());
   });
 });
 
 suite('SiteEntry_EnabledConsolidatedControls', function() {
   /**
    * An example eTLD+1 Object with multiple origins grouped under it.
-   * @type {!SiteGroup}
    */
   const TEST_MULTIPLE_SITE_GROUP = createSiteGroup('example.com', [
     'http://example.com',
@@ -458,39 +458,19 @@
   ]);
 
   /**
-   * An example eTLD+1 Object with a single origin in it.
-   * @type {!SiteGroup}
-   */
-  const TEST_SINGLE_SITE_GROUP = createSiteGroup('foo.com', [
-    'https://login.foo.com',
-  ]);
-
-  const TEST_COOKIE_LIST = {
-    id: 'foo',
-    children: [
-      {domain: 'example.com'},
-      {domain: 'example.com'},
-      {domain: 'example.com'},
-    ]
-  };
-
-  /**
    * The mock proxy object to use during test.
-   * @type {TestSiteSettingsPrefsBrowserProxy}
    */
-  let browserProxy;
+  let browserProxy: TestSiteSettingsPrefsBrowserProxy;
 
   /**
    * The mock local data proxy object to use during test.
-   * @type {TestLocalDataBrowserProxy}
    */
-  let localDataBrowserProxy;
+  let localDataBrowserProxy: TestLocalDataBrowserProxy;
 
   /**
    * A site list element created before each test.
-   * @type {SiteList}
    */
-  let testElement;
+  let testElement: SiteEntryElement;
 
   suiteSetup(function() {
     loadTimeData.overrideValues({
@@ -502,12 +482,11 @@
   setup(function() {
     browserProxy = new TestSiteSettingsPrefsBrowserProxy();
     localDataBrowserProxy = new TestLocalDataBrowserProxy();
-    SiteSettingsPrefsBrowserProxyImpl.instance_ = browserProxy;
-    LocalDataBrowserProxyImpl.instance_ = localDataBrowserProxy;
+    SiteSettingsPrefsBrowserProxyImpl.setInstance(browserProxy);
+    LocalDataBrowserProxyImpl.setInstance(localDataBrowserProxy);
 
-    PolymerTest.clearBody();
+    document.body.innerHTML = '';
     testElement = document.createElement('site-entry');
-    assertTrue(!!testElement);
     document.body.appendChild(testElement);
   });
 
@@ -523,13 +502,13 @@
 
     for (let i = 0; i < originList.length; i++) {
       const siteRemoved = eventToPromise('remove-site', testElement);
-      originList[i].querySelector('#removeOriginButton').click();
+      originList[i]!.querySelector<HTMLElement>('#removeOriginButton')!.click();
       const siteRemovedEvent = await siteRemoved;
 
       const {actionScope, index, origin} = siteRemovedEvent.detail;
       assertEquals('origin', actionScope);
       assertEquals(testElement.listIndex, index);
-      assertEquals(testElement.siteGroup.origins[i].origin, origin);
+      assertEquals(testElement.siteGroup.origins[i]!.origin, origin);
     }
   });
 
@@ -539,7 +518,7 @@
     flush();
 
     const siteRemoved = eventToPromise('remove-site', testElement);
-    testElement.$$('#removeSiteButton').click();
+    testElement.$$<HTMLElement>('#removeSiteButton')!.click();
     const siteRemovedEvent = await siteRemoved;
 
     const {actionScope, index, origin} = siteRemovedEvent.detail;
diff --git a/chrome/test/origin_policy/origin_policy_browsertest.cc b/chrome/test/origin_policy/origin_policy_browsertest.cc
index f1bebfa..1b83b66 100644
--- a/chrome/test/origin_policy/origin_policy_browsertest.cc
+++ b/chrome/test/origin_policy/origin_policy_browsertest.cc
@@ -5,10 +5,19 @@
 #include "base/bind.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
+#include "chrome/browser/chrome_content_browser_client.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/security_interstitials/content/security_interstitial_page.h"
+#include "components/security_interstitials/content/security_interstitial_tab_helper.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/storage_partition_config.h"
+#include "content/public/common/content_client.h"
 #include "content/public/common/content_features.h"
 #include "content/public/test/browser_test.h"
+#include "content/public/test/content_mock_cert_verifier.h"
+#include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/test/embedded_test_server/http_response.h"
@@ -21,6 +30,35 @@
 // The title of the Origin Policy error interstitial. This is used to determine
 // whether the page load was blocked by the origin policy throttle.
 const char16_t kErrorInterstitialTitle[] = u"Origin Policy Error";
+
+class TestContentBrowserClient : public ChromeContentBrowserClient {
+ public:
+  TestContentBrowserClient(const GURL& site,
+                           const std::string& partition_domain)
+      : site_(site), partition_domain_(partition_domain) {}
+  ~TestContentBrowserClient() override = default;
+  TestContentBrowserClient(const TestContentBrowserClient&) = delete;
+  TestContentBrowserClient& operator=(const TestContentBrowserClient&) = delete;
+
+ protected:
+  // ChromeContentBrowserClient:
+  content::StoragePartitionConfig GetStoragePartitionConfigForSite(
+      content::BrowserContext* browser_context,
+      const GURL& site) override {
+    if (site == site_) {
+      return content::StoragePartitionConfig::Create(
+          browser_context, partition_domain_, /*partition_name=*/"",
+          browser_context->IsOffTheRecord());
+    }
+    return ChromeContentBrowserClient::GetStoragePartitionConfigForSite(
+        browser_context, site);
+  }
+
+ private:
+  GURL site_;
+  std::string partition_domain_;
+};
+
 }  // namespace
 
 namespace content {
@@ -39,7 +77,13 @@
 
   ~OriginPolicyBrowserTest() override = default;
 
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    InProcessBrowserTest::SetUpCommandLine(command_line);
+    mock_cert_verifier_.SetUpCommandLine(command_line);
+  }
+
   void SetUpInProcessBrowserTestFixture() override {
+    mock_cert_verifier_.SetUpInProcessBrowserTestFixture();
     server_ = std::make_unique<net::test_server::EmbeddedTestServer>(
         net::test_server::EmbeddedTestServer::TYPE_HTTPS);
     server_->AddDefaultHandlers(base::FilePath(kDataRoot));
@@ -50,17 +94,27 @@
     feature_list_.InitAndEnableFeature(features::kOriginPolicy);
   }
 
-  void TearDownInProcessBrowserTestFixture() override { server_.reset(); }
+  void SetUpOnMainThread() override {
+    host_resolver()->AddRule("*", "127.0.0.1");
+    mock_cert_verifier_.mock_cert_verifier()->set_default_result(net::OK);
+  }
+
+  void TearDownInProcessBrowserTestFixture() override {
+    server_.reset();
+    mock_cert_verifier_.TearDownInProcessBrowserTestFixture();
+  }
 
   net::test_server::EmbeddedTestServer* server() { return server_.get(); }
 
   // Most tests here are set up to use the page title to distinguish between
   // successful load or the error page. For those tests, this method implements
   // the bulk of the test logic.
-  std::u16string NavigateToAndReturnTitle(const char* url) {
+  std::u16string NavigateToAndReturnTitle(const char* path,
+                                          const char* host = nullptr) {
     EXPECT_TRUE(server());
-    EXPECT_TRUE(
-        ui_test_utils::NavigateToURL(browser(), GURL(server()->GetURL(url))));
+    GURL url =
+        GURL(host ? server()->GetURL(host, path) : server()->GetURL(path));
+    EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
     std::u16string title;
     ui_test_utils::GetCurrentTabTitle(browser(), &title);
     return title;
@@ -94,6 +148,7 @@
   }
 
   std::unique_ptr<net::test_server::EmbeddedTestServer> server_;
+  content::ContentMockCertVerifier mock_cert_verifier_;
   base::test::ScopedFeatureList feature_list_;
 
   net::HttpStatusCode status_;
@@ -144,4 +199,43 @@
             NavigateToAndReturnTitle("/page-with-policy.html"));
 }
 
+IN_PROC_BROWSER_TEST_F(OriginPolicyBrowserTest, NonDefaultStoragePartition) {
+  const char* partitioned_host = "partitioned.com";
+  const char* partitioned_site = "https://partitioned.com/";
+  const char* normal_host = "example.com";
+
+  TestContentBrowserClient test_browser_client(GURL(partitioned_site),
+                                               "test_partition");
+  content::ContentBrowserClient* old_browser_client =
+      content::SetBrowserClientForTesting(&test_browser_client);
+
+  SetStatus(net::HTTP_TEMPORARY_REDIRECT);
+  SetLocationHeader("/.well-known/origin-policy/example-policy");
+
+  // Verify that both the normal and partitioned pages hit the interstitial.
+  EXPECT_EQ(kErrorInterstitialTitle,
+            NavigateToAndReturnTitle("/page-with-policy.html", normal_host));
+  EXPECT_EQ(
+      kErrorInterstitialTitle,
+      NavigateToAndReturnTitle("/page-with-policy.html", partitioned_host));
+
+  // Simulate clicking Allow in the interstitial.
+  auto* helper =
+      security_interstitials::SecurityInterstitialTabHelper::FromWebContents(
+          browser()->tab_strip_model()->GetActiveWebContents());
+  auto* interstitial =
+      helper->GetBlockingPageForCurrentlyCommittedNavigationForTesting();
+  EXPECT_NE(nullptr, interstitial);
+  interstitial->CommandReceived("1");  // "1" == Proceed
+
+  // The normal site should still be blocked, but not the partitioned one.
+  EXPECT_EQ(kErrorInterstitialTitle,
+            NavigateToAndReturnTitle("/page-with-policy.html", normal_host));
+  EXPECT_EQ(
+      u"Page With Policy",
+      NavigateToAndReturnTitle("/page-with-policy.html", partitioned_host));
+
+  content::SetBrowserClientForTesting(old_browser_client);
+}
+
 }  // namespace content
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd
index 40982ea..0847e9a 100644
--- a/chromeos/chromeos_strings.grd
+++ b/chromeos/chromeos_strings.grd
@@ -2419,6 +2419,47 @@
       <message name="IDS_SHIMLESS_RMA_CONFIRM_DEVICE_INFO_SKU_WARNING" translateable="false" desc="The text warning explaining when the device's SKU should be changed.">
         The SKU should only be changed if the new component(s) are different from the ones they replaced. For example, a touchscreen replacing a non-touchscreen, or memory being upgraded from 8GB to 16GB.
       </message>
+
+      <!-- Firmware Update -->
+      <message name="IDS_FIRMWARE_TITLE_TEXT" desc="The title of the Firmware update app.">
+        Update peripherals
+      </message>
+      <message name="IDS_FIRMWARE_CRITICAL_UPDATE_TEXT" desc="The text shown when an update is deemed critcal.">
+        Critical update
+      </message>
+      <message name="IDS_FIRMWARE_PREPARE_DEVICE_TEXT" desc="Title of the dialog that explains how to prepare the device to be updated.">
+        Prepare your device
+      </message>
+      <message name="IDS_FIRMWARE_NEXT_BUTTON_TEXT" desc="Label for button that transitions the user to the firmware update dialog.">
+        Next
+      </message>
+      <message name="IDS_FIRMWARE_CANCEL_BUTTON_TEXT" desc="Label for the button that cancels the dialog.">
+        Cancel
+      </message>
+      <message name="IDS_FIRMWARE_DONE_BUTTON_TEXT" desc="Label for button shown when the firmware update is completed.">
+        Done
+      </message>
+      <message name="IDS_FIRMWARE_UPDATE_BUTTON_TEXT" desc="Label for button to start the firmware update.">
+        Update
+      </message>
+      <message name="IDS_FIRMWARE_UPDATING_TEXT" desc="Label for showing that a device is being updated.">
+        Updating <ph name="DEVICE_NAME">$1<ex>Logitech keyboard</ex></ph>
+      </message>
+      <message name="IDS_FIRMWARE_DEVICE_UP_TO_DATE_TEXT" desc="Label for when a device is up to date.">
+        Your <ph name="DEVICE_NAME">$1<ex>Logitech keyboard</ex></ph> is up to date
+      </message>
+      <message name="IDS_FIRMWARE_HAS_BEEN_UPDATED_TEXT" desc="Label for displaying which version the device has been updated to.">
+        Firmware <ph name="DEVICE_NAME">$1<ex>Logitech keyboard</ex></ph> has been updated to version <ph name="VERSION">$2<ex>2.0.12</ex></ph>
+      </message>
+      <message name="IDS_FIRMWARE_UPDATING_INFO_TEXT" desc="Label for communicating information about what actions a user can take during an update.">
+        While updating, you can minimize window but do not unplug your device. This may take a few minutes and your device might not work during this update
+      </message>
+      <message name="IDS_FIRMWARE_INSTALLING_TEXT" desc="Label that displays what percentage of the update has been installed.">
+        Installing - <ph name="PERCENTAGE_VALUE">$1<ex>94</ex></ph>%
+      </message>
+      <message name="IDS_FIRMWARE_UP_TO_DATE_TEXT" desc="Label shown when no firmware updates are available.">
+        All peripherals are up to date
+      </message>
     </messages>
   </release>
 </grit>
diff --git a/chromeos/chromeos_strings_grd/IDS_FIRMWARE_CANCEL_BUTTON_TEXT.png.sha1 b/chromeos/chromeos_strings_grd/IDS_FIRMWARE_CANCEL_BUTTON_TEXT.png.sha1
new file mode 100644
index 0000000..979db84
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_FIRMWARE_CANCEL_BUTTON_TEXT.png.sha1
@@ -0,0 +1 @@
+1461b5afa4350cb9eef34ba915a00055948b40a3
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_FIRMWARE_CRITICAL_UPDATE_TEXT.png.sha1 b/chromeos/chromeos_strings_grd/IDS_FIRMWARE_CRITICAL_UPDATE_TEXT.png.sha1
new file mode 100644
index 0000000..7306ba4
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_FIRMWARE_CRITICAL_UPDATE_TEXT.png.sha1
@@ -0,0 +1 @@
+1d73a1c1768c5d710a3fd1f8cb0206a81266ef4c
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_FIRMWARE_DEVICE_UP_TO_DATE_TEXT.png.sha1 b/chromeos/chromeos_strings_grd/IDS_FIRMWARE_DEVICE_UP_TO_DATE_TEXT.png.sha1
new file mode 100644
index 0000000..416a2c461
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_FIRMWARE_DEVICE_UP_TO_DATE_TEXT.png.sha1
@@ -0,0 +1 @@
+eeb9a317e7c1a463aa7e50401202e7a1c57c626e
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_FIRMWARE_DONE_BUTTON_TEXT.png.sha1 b/chromeos/chromeos_strings_grd/IDS_FIRMWARE_DONE_BUTTON_TEXT.png.sha1
new file mode 100644
index 0000000..416a2c461
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_FIRMWARE_DONE_BUTTON_TEXT.png.sha1
@@ -0,0 +1 @@
+eeb9a317e7c1a463aa7e50401202e7a1c57c626e
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_FIRMWARE_HAS_BEEN_UPDATED_TEXT.png.sha1 b/chromeos/chromeos_strings_grd/IDS_FIRMWARE_HAS_BEEN_UPDATED_TEXT.png.sha1
new file mode 100644
index 0000000..416a2c461
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_FIRMWARE_HAS_BEEN_UPDATED_TEXT.png.sha1
@@ -0,0 +1 @@
+eeb9a317e7c1a463aa7e50401202e7a1c57c626e
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_FIRMWARE_INSTALLING_TEXT.png.sha1 b/chromeos/chromeos_strings_grd/IDS_FIRMWARE_INSTALLING_TEXT.png.sha1
new file mode 100644
index 0000000..947663d
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_FIRMWARE_INSTALLING_TEXT.png.sha1
@@ -0,0 +1 @@
+547ccff2209eb9e2e7c7a0886563ab0b3d6d0712
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_FIRMWARE_NEXT_BUTTON_TEXT.png.sha1 b/chromeos/chromeos_strings_grd/IDS_FIRMWARE_NEXT_BUTTON_TEXT.png.sha1
new file mode 100644
index 0000000..979db84
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_FIRMWARE_NEXT_BUTTON_TEXT.png.sha1
@@ -0,0 +1 @@
+1461b5afa4350cb9eef34ba915a00055948b40a3
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_FIRMWARE_PREPARE_DEVICE_TEXT.png.sha1 b/chromeos/chromeos_strings_grd/IDS_FIRMWARE_PREPARE_DEVICE_TEXT.png.sha1
new file mode 100644
index 0000000..979db84
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_FIRMWARE_PREPARE_DEVICE_TEXT.png.sha1
@@ -0,0 +1 @@
+1461b5afa4350cb9eef34ba915a00055948b40a3
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_FIRMWARE_TITLE_TEXT.png.sha1 b/chromeos/chromeos_strings_grd/IDS_FIRMWARE_TITLE_TEXT.png.sha1
new file mode 100644
index 0000000..7306ba4
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_FIRMWARE_TITLE_TEXT.png.sha1
@@ -0,0 +1 @@
+1d73a1c1768c5d710a3fd1f8cb0206a81266ef4c
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_FIRMWARE_UPDATE_BUTTON_TEXT.png.sha1 b/chromeos/chromeos_strings_grd/IDS_FIRMWARE_UPDATE_BUTTON_TEXT.png.sha1
new file mode 100644
index 0000000..7306ba4
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_FIRMWARE_UPDATE_BUTTON_TEXT.png.sha1
@@ -0,0 +1 @@
+1d73a1c1768c5d710a3fd1f8cb0206a81266ef4c
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_FIRMWARE_UPDATING_INFO_TEXT.png.sha1 b/chromeos/chromeos_strings_grd/IDS_FIRMWARE_UPDATING_INFO_TEXT.png.sha1
new file mode 100644
index 0000000..947663d
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_FIRMWARE_UPDATING_INFO_TEXT.png.sha1
@@ -0,0 +1 @@
+547ccff2209eb9e2e7c7a0886563ab0b3d6d0712
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_FIRMWARE_UPDATING_TEXT.png.sha1 b/chromeos/chromeos_strings_grd/IDS_FIRMWARE_UPDATING_TEXT.png.sha1
new file mode 100644
index 0000000..947663d
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_FIRMWARE_UPDATING_TEXT.png.sha1
@@ -0,0 +1 @@
+547ccff2209eb9e2e7c7a0886563ab0b3d6d0712
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_FIRMWARE_UP_TO_DATE_TEXT.png.sha1 b/chromeos/chromeos_strings_grd/IDS_FIRMWARE_UP_TO_DATE_TEXT.png.sha1
new file mode 100644
index 0000000..1abc3aa
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_FIRMWARE_UP_TO_DATE_TEXT.png.sha1
@@ -0,0 +1 @@
+9c92a6bebf3322c3f093d75188aada094ff64c8f
\ No newline at end of file
diff --git a/components/autofill/core/common/autofill_features.cc b/components/autofill/core/common/autofill_features.cc
index 312d283..641c144 100644
--- a/components/autofill/core/common/autofill_features.cc
+++ b/components/autofill/core/common/autofill_features.cc
@@ -117,9 +117,8 @@
 // account-based storage when sync the transport is enabled.
 const base::Feature kAutofillEnableAccountWalletStorage {
   "AutofillEnableAccountWalletStorage",
-#if BUILDFLAG(IS_CHROMEOS_ASH) || defined(OS_IOS)
-      // Wallet transport is only currently available on Win/Mac/Linux/Android.
-      // (Somehow, swapping this check makes iOS unhappy?)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+      // Wallet transport is currently unavailable on ChromeOS.
       base::FEATURE_DISABLED_BY_DEFAULT
 #else
       base::FEATURE_ENABLED_BY_DEFAULT
diff --git a/components/autofill/core/common/autofill_payments_features.cc b/components/autofill/core/common/autofill_payments_features.cc
index d1cb9020..e17f6790 100644
--- a/components/autofill/core/common/autofill_payments_features.cc
+++ b/components/autofill/core/common/autofill_payments_features.cc
@@ -90,13 +90,8 @@
 // When enabled and user is signed in, a footer indicating user's e-mail address
 // and profile picture will appear at the bottom of SaveCardInfoBar.
 const base::Feature kAutofillEnableSaveCardInfoBarAccountIndicationFooter{
-  "AutofillEnableSaveCardInfoBarAccountIndicationFooter",
-#if defined(OS_IOS)
-      base::FEATURE_DISABLED_BY_DEFAULT
-#else
-      base::FEATURE_ENABLED_BY_DEFAULT
-#endif
-};
+    "AutofillEnableSaveCardInfoBarAccountIndicationFooter",
+    base::FEATURE_ENABLED_BY_DEFAULT};
 
 // When enabled, if the user interacts with the manual fallback bottom sheet
 // on Android, it'll remain sticky until the user dismisses it.
diff --git a/components/policy/test_support/BUILD.gn b/components/policy/test_support/BUILD.gn
index b8d7860..173d599 100644
--- a/components/policy/test_support/BUILD.gn
+++ b/components/policy/test_support/BUILD.gn
@@ -5,7 +5,10 @@
 static_library("test_support") {
   testonly = true
 
-  data = [ "policy_testserver.py" ]
+  data = [
+    "asn1der.py",
+    "policy_testserver.py",
+  ]
 
   sources = [
     "client_storage.cc",
diff --git a/components/policy/test_support/OWNERS b/components/policy/test_support/OWNERS
index 76cec38b..a60e0da6 100644
--- a/components/policy/test_support/OWNERS
+++ b/components/policy/test_support/OWNERS
@@ -1,3 +1,4 @@
 # policy_testserver.py is used in tast and autotest integration tests.
 
 per-file policy_testserver.py=ftirelo@chromium.org, pmarko@chromium.org, vsavu@google.com
+per-file asn1der.py=ftirelo@chromium.org, pmarko@chromium.org, vsavu@google.com
diff --git a/components/policy/test_support/asn1der.py b/components/policy/test_support/asn1der.py
new file mode 100644
index 0000000..00aca5c
--- /dev/null
+++ b/components/policy/test_support/asn1der.py
@@ -0,0 +1,69 @@
+# Copyright (c) 2011 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Helper module for ASN.1/DER encoding."""
+
+import binascii
+import struct
+
+# Tags as defined by ASN.1.
+INTEGER = 2
+BIT_STRING = 3
+NULL = 5
+OBJECT_IDENTIFIER = 6
+SEQUENCE = 0x30
+
+def Data(tag, data):
+  """Generic type-length-value encoder.
+
+  Args:
+    tag: the tag.
+    data: the data for the given tag.
+  Returns:
+    encoded TLV value.
+  """
+  if len(data) < 128:
+    return struct.pack(">BB", tag, len(data)) + data;
+  assert len(data) <= 0xffff;
+  return struct.pack(">BBH", tag, 0x82, len(data)) + data;
+
+def Integer(value):
+  """Encodes an integer.
+
+  Args:
+    value: the long value.
+  Returns:
+    encoded TLV value.
+  """
+  data = '%x' % value
+  if (len(data) % 2 == 1):
+    # Odd number of non-zero bytes - pad out our data to a full number of bytes.
+    data = '0' + data
+
+  # If the high bit is set, need to prepend a null byte to denote a positive
+  # number.
+  if (int(data[0], 16) >= 8):
+    data = '00' + data
+
+  return Data(INTEGER, binascii.unhexlify(data))
+
+def Bitstring(value):
+  """Encodes a bit string.
+
+  Args:
+    value: a string holding the binary data.
+  Returns:
+    encoded TLV value.
+  """
+  return Data(BIT_STRING, b'\x00' + value)
+
+def Sequence(values):
+  """Encodes a sequence of other values.
+
+  Args:
+    values: the list of values, must be strings holding already encoded data.
+  Returns:
+    encoded TLV value.
+  """
+  return Data(SEQUENCE, b''.join(values))
diff --git a/components/policy/test_support/policy_testserver.py b/components/policy/test_support/policy_testserver.py
index 06980ee..d123cbe0 100644
--- a/components/policy/test_support/policy_testserver.py
+++ b/components/policy/test_support/policy_testserver.py
@@ -65,8 +65,6 @@
 
 import base64
 from six.moves import BaseHTTPServer
-from cryptography.hazmat.primitives.asymmetric import padding, rsa
-from cryptography.hazmat.primitives import hashes, serialization
 import glob
 import google.protobuf.text_format
 import hashlib
@@ -78,10 +76,15 @@
 import six
 import sys
 import time
+import tlslite
+import tlslite.api
+import tlslite.utils
+import tlslite.utils.cryptomath
 from six.moves import urllib
 from six.moves.urllib import request as urllib_request
 from six.moves.urllib import parse as urlparse
 
+import asn1der
 import testserver_base
 
 import device_management_backend_pb2 as dm
@@ -110,6 +113,9 @@
 except ImportError:
   crypto = None
 
+# ASN.1 object identifier for PKCS#1/RSA.
+PKCS1_RSA_OID = b'\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01'
+
 # List of machines that trigger the server to send kiosk enrollment response
 # for the register request.
 KIOSK_MACHINE_IDS = [ 'KIOSK' ]
@@ -1336,8 +1342,8 @@
 
     # Sign the serialized policy data
     if msg.signature_type == dm.PolicyFetchRequest.SHA1_RSA:
-      response.policy_data_signature = signing_key['private_key'].sign(
-          response.policy_data, padding.PKCS1v15(), hashes.SHA1())
+      response.policy_data_signature = bytes(
+          signing_key['private_key'].hashAndSign(response.policy_data))
       if msg.public_key_version != signing_key_version:
         response.new_public_key = signing_key['public_key']
 
@@ -1353,8 +1359,8 @@
                 verification_sig)
 
         if client_key is not None:
-          response.new_public_key_signature = client_key['private_key'].sign(
-              response.new_public_key, padding.PKCS1v15(), hashes.SHA1())
+          response.new_public_key_signature = bytes(
+              client_key['private_key'].hashAndSign(response.new_public_key))
 
     return (200, response.SerializeToString())
 
@@ -1556,14 +1562,15 @@
           print('Failed to load private key from %s' % key_path)
           continue
         try:
-          key = serialization.load_pem_private_key(key_str, password=None)
-        except ValueError:
-          key = serialization.load_der_private_key(key_str, password=None)
+          # Decode with replacement characters to avoid decode errors if was
+          # actually DER.
+          key = tlslite.api.parsePEMKey(key_str.decode('utf-8', 'replace'),
+                                        private=True)
+        except SyntaxError:
+          key = tlslite.utils.python_rsakey.Python_RSAKey._parsePKCS8(
+              bytearray(key_str))
 
         assert key is not None
-        if not isinstance(key, rsa.RSAPrivateKey):
-          raise TypeError('Unexpected key type')
-
         key_info = { 'private_key' : key }
 
         # Now try to read in a signature, if one exists.
@@ -1578,9 +1585,9 @@
       # Use the canned private keys if none were passed from the command line.
       for signing_key in SIGNING_KEYS:
         decoded_key = base64.b64decode(signing_key['key']);
-        key = serialization.load_der_private_key(decoded_key, password=None)
+        key = tlslite.utils.python_rsakey.Python_RSAKey._parsePKCS8(
+            bytearray(decoded_key))
         assert key is not None
-        assert isinstance(key, rsa.RSAPrivateKey)
         # Grab the signature dictionary for this key and decode all of the
         # signatures.
         signature_dict = signing_key['signatures']
@@ -1592,9 +1599,15 @@
 
     # Derive the public keys from the private keys.
     for entry in self.keys:
-      entry['public_key'] = entry['private_key'].public_key().public_bytes(
-          encoding=serialization.Encoding.DER,
-          format=serialization.PublicFormat.SubjectPublicKeyInfo)
+      key = entry['private_key']
+
+      algorithm = asn1der.Sequence(
+          [ asn1der.Data(asn1der.OBJECT_IDENTIFIER, PKCS1_RSA_OID),
+            asn1der.Data(asn1der.NULL, b'') ])
+      rsa_pubkey = asn1der.Sequence([ asn1der.Integer(key.n),
+                                      asn1der.Integer(key.e) ])
+      pubkey = asn1der.Sequence([ algorithm, asn1der.Bitstring(rsa_pubkey) ])
+      entry['public_key'] = pubkey
 
     try:
       self.ReadClientStateFile()
diff --git a/components/security_interstitials/content/origin_policy_interstitial_page.cc b/components/security_interstitials/content/origin_policy_interstitial_page.cc
index d64ff95c..dc74b84f 100644
--- a/components/security_interstitials/content/origin_policy_interstitial_page.cc
+++ b/components/security_interstitials/content/origin_policy_interstitial_page.cc
@@ -22,12 +22,14 @@
 
 OriginPolicyInterstitialPage::OriginPolicyInterstitialPage(
     content::WebContents* web_contents,
+    content::StoragePartition* storage_partition,
     const GURL& request_url,
     std::unique_ptr<SecurityInterstitialControllerClient> controller,
     network::OriginPolicyState error_reason)
     : SecurityInterstitialPage(web_contents,
                                request_url,
                                std::move(controller)),
+      storage_partition_(storage_partition),
       error_reason_(error_reason) {}
 
 OriginPolicyInterstitialPage::~OriginPolicyInterstitialPage() = default;
@@ -116,8 +118,7 @@
 }
 
 void OriginPolicyInterstitialPage::Proceed() {
-  content::OriginPolicyAddExceptionFor(web_contents()->GetBrowserContext(),
-                                       request_url());
+  content::OriginPolicyAddExceptionFor(storage_partition_, request_url());
   web_contents()->GetController().Reload(content::ReloadType::NORMAL, true);
 }
 
diff --git a/components/security_interstitials/content/origin_policy_interstitial_page.h b/components/security_interstitials/content/origin_policy_interstitial_page.h
index c36d436..12dddb4 100644
--- a/components/security_interstitials/content/origin_policy_interstitial_page.h
+++ b/components/security_interstitials/content/origin_policy_interstitial_page.h
@@ -15,6 +15,7 @@
 #include "url/gurl.h"
 
 namespace content {
+class StoragePartition;
 class WebContents;
 }  // namespace content
 
@@ -25,6 +26,7 @@
  public:
   OriginPolicyInterstitialPage(
       content::WebContents* web_contents,
+      content::StoragePartition* storage_partition,
       const GURL& request_url,
       std::unique_ptr<SecurityInterstitialControllerClient> controller,
       network::OriginPolicyState error_reason);
@@ -43,6 +45,7 @@
   void PopulateInterstitialStrings(base::Value*) override;
 
  private:
+  content::StoragePartition* storage_partition_;
   network::OriginPolicyState error_reason_;
 
   void Proceed();
diff --git a/components/security_interstitials/content/origin_policy_ui.cc b/components/security_interstitials/content/origin_policy_ui.cc
index 241d2e4..343fd88 100644
--- a/components/security_interstitials/content/origin_policy_ui.cc
+++ b/components/security_interstitials/content/origin_policy_ui.cc
@@ -12,7 +12,9 @@
 #include "components/security_interstitials/content/security_interstitial_tab_helper.h"
 #include "components/security_interstitials/content/settings_page_helper.h"
 #include "components/security_interstitials/core/metrics_helper.h"
+#include "content/public/browser/browser_context.h"
 #include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/storage_partition.h"
 #include "services/network/public/cpp/origin_policy.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
@@ -24,6 +26,7 @@
 std::unique_ptr<SecurityInterstitialPage> GetErrorPageImpl(
     network::OriginPolicyState error_reason,
     content::WebContents* web_contents,
+    content::StoragePartition* storage_partition,
     const GURL& url) {
   MetricsHelper::ReportDetails report_details;
   report_details.metric_prefix = "origin_policy";
@@ -34,7 +37,8 @@
           nullptr, /* pref service: can be null */
           "", GURL(), /* settings_page_helper: not used */ nullptr);
   return std::make_unique<security_interstitials::OriginPolicyInterstitialPage>(
-      web_contents, url, std::move(controller), error_reason);
+      web_contents, storage_partition, url, std::move(controller),
+      error_reason);
 }
 
 }  // namespace
@@ -44,7 +48,8 @@
     content::NavigationHandle* handle) {
   DCHECK(handle);
   std::unique_ptr<SecurityInterstitialPage> page(GetErrorPageImpl(
-      error_reason, handle->GetWebContents(), handle->GetURL()));
+      error_reason, handle->GetWebContents(),
+      handle->GetRenderFrameHost()->GetStoragePartition(), handle->GetURL()));
   std::string html = page->GetHTMLContents();
 
   // The page object is "associated" with the web contents, and this is how
@@ -59,7 +64,11 @@
     network::OriginPolicyState error_reason,
     content::WebContents* web_contents,
     const GURL& url) {
-  return GetErrorPageImpl(error_reason, web_contents, url).release();
+  return GetErrorPageImpl(
+             error_reason, web_contents,
+             web_contents->GetBrowserContext()->GetDefaultStoragePartition(),
+             url)
+      .release();
 }
 
 }  // namespace security_interstitials
diff --git a/components/viz/test/test_gpu_service_holder.cc b/components/viz/test/test_gpu_service_holder.cc
index 71a9e395..42e39142 100644
--- a/components/viz/test/test_gpu_service_holder.cc
+++ b/components/viz/test/test_gpu_service_holder.cc
@@ -33,6 +33,7 @@
 #endif
 
 #if defined(USE_OZONE)
+#include "ui/ozone/public/gpu_platform_support_host.h"
 #include "ui/ozone/public/ozone_platform.h"
 #endif
 
@@ -40,6 +41,12 @@
 
 namespace {
 
+#if defined(USE_OZONE) && !defined(OS_FUCHSIA)
+namespace {
+constexpr int kGpuProcessHostId = 1;
+}  // namespace
+#endif
+
 base::Lock& GetLock() {
   static base::NoDestructor<base::Lock> lock;
   return *lock;
@@ -160,9 +167,26 @@
       base::BindOnce(&TestGpuServiceHolder::InitializeOnGpuThread,
                      base::Unretained(this), gpu_preferences, &completion));
   completion.Wait();
+
+#if defined(USE_OZONE) && !defined(OS_FUCHSIA)
+  if (auto* gpu_platform_support_host =
+          ui::OzonePlatform::GetInstance()->GetGpuPlatformSupportHost()) {
+    auto interface_binder = base::BindRepeating(
+        &TestGpuServiceHolder::BindInterface, base::Unretained(this));
+    gpu_platform_support_host->OnGpuServiceLaunched(
+        kGpuProcessHostId, interface_binder, base::DoNothing());
+  }
+#endif
 }
 
 TestGpuServiceHolder::~TestGpuServiceHolder() {
+#if defined(USE_OZONE) && !defined(OS_FUCHSIA)
+  if (auto* gpu_platform_support_host =
+          ui::OzonePlatform::GetInstance()->GetGpuPlatformSupportHost()) {
+    gpu_platform_support_host->OnChannelDestroyed(kGpuProcessHostId);
+  }
+#endif
+
   // Ensure members created on GPU thread are destroyed there too.
   gpu_thread_.task_runner()->PostTask(
       FROM_HERE, base::BindOnce(&TestGpuServiceHolder::DeleteOnGpuThread,
@@ -190,6 +214,10 @@
     base::WaitableEvent* completion) {
   DCHECK(gpu_thread_.task_runner()->BelongsToCurrentThread());
 
+#if defined(USE_OZONE) && !defined(OS_FUCHSIA)
+  ui::OzonePlatform::GetInstance()->AddInterfaces(&binders_);
+#endif
+
   if (gpu_preferences.use_vulkan != gpu::VulkanImplementationName::kNone) {
 #if BUILDFLAG(ENABLE_VULKAN)
     bool use_swiftshader = gpu_preferences.use_vulkan ==
@@ -270,4 +298,27 @@
   gpu_service_.reset();
 }
 
+#if defined(USE_OZONE) && !defined(OS_FUCHSIA)
+void TestGpuServiceHolder::BindInterface(
+    const std::string& interface_name,
+    mojo::ScopedMessagePipeHandle interface_pipe) {
+  // The interfaces must be bound on the gpu to ensure the mojo calls happen
+  // on the correct sequence (same happens when the browser runs with a real
+  // gpu service).
+  gpu_thread_.task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&TestGpuServiceHolder::BindInterfaceOnGpuThread,
+                                base::Unretained(this), interface_name,
+                                std::move(interface_pipe)));
+}
+
+void TestGpuServiceHolder::BindInterfaceOnGpuThread(
+    const std::string& interface_name,
+    mojo::ScopedMessagePipeHandle interface_pipe) {
+  mojo::GenericPendingReceiver receiver =
+      mojo::GenericPendingReceiver(interface_name, std::move(interface_pipe));
+  CHECK(binders_.TryBind(&receiver))
+      << "Unable to find mojo interface " << interface_name;
+}
+#endif  // defined(USE_OZONE) && !defined(OS_FUCHSIA)
+
 }  // namespace viz
diff --git a/components/viz/test/test_gpu_service_holder.h b/components/viz/test/test_gpu_service_holder.h
index 7b11108..6c32c617 100644
--- a/components/viz/test/test_gpu_service_holder.h
+++ b/components/viz/test/test_gpu_service_holder.h
@@ -6,13 +6,19 @@
 #define COMPONENTS_VIZ_TEST_TEST_GPU_SERVICE_HOLDER_H_
 
 #include <memory>
+#include <string>
 
 #include "base/feature_list.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/threading/thread.h"
+#include "build/build_config.h"
 #include "gpu/ipc/gpu_in_process_thread_service.h"
 #include "gpu/vulkan/buildflags.h"
 
+#if defined(USE_OZONE) && !defined(OS_FUCHSIA)
+#include "mojo/public/cpp/bindings/binder_map.h"
+#endif
+
 namespace gpu {
 class CommandBufferTaskExecutor;
 class SingleTaskSequence;
@@ -95,6 +101,14 @@
                              base::WaitableEvent* completion);
   void DeleteOnGpuThread();
 
+// TODO(crbug.com/1267788): Fuchsia crashes. See details in the crbug.
+#if defined(USE_OZONE) && !defined(OS_FUCHSIA)
+  void BindInterface(const std::string& interface_name,
+                     mojo::ScopedMessagePipeHandle interface_pipe);
+  void BindInterfaceOnGpuThread(const std::string& interface_name,
+                                mojo::ScopedMessagePipeHandle interface_pipe);
+#endif
+
 #if !defined(OS_CHROMEOS)
   // TODO(crbug.com/1241161): This is equally applicable to Chrome OS there are
   // just a number of tests that already override the feature list after it's no
@@ -115,6 +129,11 @@
 #if BUILDFLAG(ENABLE_VULKAN)
   std::unique_ptr<gpu::VulkanImplementation> vulkan_implementation_;
 #endif
+
+#if defined(USE_OZONE) && !defined(OS_FUCHSIA)
+  // Bound interfaces.
+  mojo::BinderMap binders_;
+#endif
 };
 
 }  // namespace viz
diff --git a/content/browser/devtools/protocol/input_handler.cc b/content/browser/devtools/protocol/input_handler.cc
index 7cc2fab0..18ccc68 100644
--- a/content/browser/devtools/protocol/input_handler.cc
+++ b/content/browser/devtools/protocol/input_handler.cc
@@ -133,6 +133,7 @@
 bool GetMouseEventButton(const std::string& button,
                          blink::WebPointerProperties::Button* event_button,
                          int* event_modifiers) {
+  *event_modifiers = blink::WebInputEvent::kFromDebugger;
   if (button.empty())
     return true;
 
@@ -140,19 +141,19 @@
     *event_button = blink::WebMouseEvent::Button::kNoButton;
   } else if (button == Input::MouseButtonEnum::Left) {
     *event_button = blink::WebMouseEvent::Button::kLeft;
-    *event_modifiers = blink::WebInputEvent::kLeftButtonDown;
+    *event_modifiers |= blink::WebInputEvent::kLeftButtonDown;
   } else if (button == Input::MouseButtonEnum::Middle) {
     *event_button = blink::WebMouseEvent::Button::kMiddle;
-    *event_modifiers = blink::WebInputEvent::kMiddleButtonDown;
+    *event_modifiers |= blink::WebInputEvent::kMiddleButtonDown;
   } else if (button == Input::MouseButtonEnum::Right) {
     *event_button = blink::WebMouseEvent::Button::kRight;
-    *event_modifiers = blink::WebInputEvent::kRightButtonDown;
+    *event_modifiers |= blink::WebInputEvent::kRightButtonDown;
   } else if (button == Input::MouseButtonEnum::Back) {
     *event_button = blink::WebMouseEvent::Button::kBack;
-    *event_modifiers = blink::WebInputEvent::kBackButtonDown;
+    *event_modifiers |= blink::WebInputEvent::kBackButtonDown;
   } else if (button == Input::MouseButtonEnum::Forward) {
     *event_button = blink::WebMouseEvent::Button::kForward;
-    *event_modifiers = blink::WebInputEvent::kForwardButtonDown;
+    *event_modifiers |= blink::WebInputEvent::kForwardButtonDown;
   } else {
     return false;
   }
@@ -1368,7 +1369,7 @@
 
   if (!synthetic_pointer_driver_) {
     synthetic_pointer_driver_ =
-        SyntheticPointerDriver::Create(gesture_source_type);
+        SyntheticPointerDriver::Create(gesture_source_type, true);
   }
   std::unique_ptr<SyntheticPointerAction> synthetic_gesture =
       std::make_unique<SyntheticPointerAction>(action_list_params);
@@ -1564,6 +1565,7 @@
   SyntheticPinchGestureParams gesture_params;
   const int kDefaultRelativeSpeed = 800;
 
+  gesture_params.from_devtools_debugger = true;
   gesture_params.scale_factor = scale_factor;
   gesture_params.anchor = CssPixelsToPointF(x, y, page_scale_factor_);
   if (!PointIsWithinContents(gesture_params.anchor)) {
@@ -1613,6 +1615,7 @@
   }
 
   SyntheticSmoothScrollGestureParams gesture_params;
+  gesture_params.from_devtools_debugger = true;
   const bool kDefaultPreventFling = true;
   const int kDefaultSpeed = 800;
 
@@ -1724,6 +1727,7 @@
   const int kDefaultTapCount = 1;
 
   gesture_params.position = CssPixelsToPointF(x, y, page_scale_factor_);
+  gesture_params.from_devtools_debugger = true;
   if (!PointIsWithinContents(gesture_params.position)) {
     callback->sendFailure(Response::InvalidParams("Position out of bounds"));
     return;
diff --git a/content/browser/devtools/protocol/network_handler.cc b/content/browser/devtools/protocol/network_handler.cc
index 4155f010..a3a918e 100644
--- a/content/browser/devtools/protocol/network_handler.cc
+++ b/content/browser/devtools/protocol/network_handler.cc
@@ -69,6 +69,7 @@
 #include "net/cert/x509_util.h"
 #include "net/cookies/canonical_cookie.h"
 #include "net/cookies/cookie_inclusion_status.h"
+#include "net/cookies/cookie_partition_key.h"
 #include "net/cookies/cookie_util.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_status_code.h"
@@ -190,7 +191,17 @@
   if (maybe_same_site) {
     devtools_cookie->SetSameSite(*maybe_same_site);
   }
-
+  absl::optional<net::CookiePartitionKey> partition_key = cookie.PartitionKey();
+  if (partition_key) {
+    std::string serialized_partition_key;
+    if (partition_key->IsSerializeable()) {
+      DCHECK(net::CookiePartitionKey::Serialize(partition_key,
+                                                serialized_partition_key));
+      devtools_cookie->SetPartitionKey(serialized_partition_key);
+    } else {
+      devtools_cookie->SetPartitionKeyOpaque(true);
+    }
+  }
   return devtools_cookie;
 }
 
@@ -199,13 +210,17 @@
  public:
   static void Retrieve(network::mojom::CookieManager* cookie_manager,
                        const std::vector<GURL> urls,
+                       const net::NetworkIsolationKey& network_isolation_key,
                        std::unique_ptr<GetCookiesCallback> callback) {
     scoped_refptr<CookieRetrieverNetworkService> self =
         new CookieRetrieverNetworkService(std::move(callback));
     net::CookieOptions cookie_options = net::CookieOptions::MakeAllInclusive();
     for (const auto& url : urls) {
       cookie_manager->GetCookieList(
-          url, cookie_options, net::CookiePartitionKeychain::Todo(),
+          url, cookie_options,
+          net::CookiePartitionKeychain::FromOptional(
+              net::CookiePartitionKey::FromNetworkIsolationKey(
+                  network_isolation_key)),
           base::BindOnce(&CookieRetrieverNetworkService::GotCookies, self));
     }
   }
@@ -317,7 +332,8 @@
                              const std::string& priority,
                              bool same_party,
                              const Maybe<std::string>& source_scheme,
-                             const Maybe<int>& source_port) {
+                             const Maybe<int>& source_port,
+                             const Maybe<std::string>& partition_key) {
   std::string normalized_domain = domain;
 
   if (url_spec.empty() && domain.empty()) {
@@ -373,12 +389,24 @@
   else if (priority == Network::CookiePriorityEnum::Low)
     cp = net::CookiePriority::COOKIE_PRIORITY_LOW;
 
+  absl::optional<net::CookiePartitionKey> deserialized_partition_key;
+  if (partition_key.isJust()) {
+    if (!base::FeatureList::IsEnabled(net::features::kPartitionedCookies)) {
+      return Response::InvalidParams(
+          "Partitioned cookies disabled. Cannot set cookie partition key");
+    }
+    if (!net::CookiePartitionKey::Deserialize(partition_key.fromJust(),
+                                              deserialized_partition_key)) {
+      return Response::InvalidParams(
+          "Deserializing cookie partition key failed");
+    }
+  }
   // TODO(crbug.com/1225444) Add Partitioned to DevTools cookie structures.
   std::unique_ptr<net::CanonicalCookie> cookie =
       net::CanonicalCookie::CreateSanitizedCookie(
           url, name, value, normalized_domain, path, base::Time(),
           expiration_date, base::Time(), secure, http_only, css, cp, same_party,
-          absl::nullopt);
+          deserialized_partition_key);
 
   if (!cookie)
     return Response::InvalidParams("Sanitizing cookie failed");
@@ -1418,7 +1446,7 @@
 
   CookieRetrieverNetworkService::Retrieve(
       storage_partition_->GetCookieManagerForBrowserProcess(), urls,
-      std::move(callback));
+      host_->GetNetworkIsolationKey(), std::move(callback));
 }
 
 void NetworkHandler::GetAllCookies(
@@ -1449,6 +1477,7 @@
                                Maybe<bool> same_party,
                                Maybe<std::string> source_scheme,
                                Maybe<int> source_port,
+                               Maybe<std::string> partition_key,
                                std::unique_ptr<SetCookieCallback> callback) {
   if (!storage_partition_) {
     callback->sendFailure(Response::InternalError());
@@ -1459,7 +1488,7 @@
       name, value, url.fromMaybe(""), domain.fromMaybe(""), path.fromMaybe(""),
       secure.fromMaybe(false), http_only.fromMaybe(false),
       same_site.fromMaybe(""), expires.fromMaybe(-1), priority.fromMaybe(""),
-      same_party.fromMaybe(false), source_scheme, source_port);
+      same_party.fromMaybe(false), source_scheme, source_port, partition_key);
 
   if (absl::holds_alternative<Response>(cookie_or_error)) {
     callback->sendFailure(absl::get<Response>(std::move(cookie_or_error)));
@@ -1497,13 +1526,17 @@
     const Maybe<int> source_port = cookie->HasSourcePort()
                                        ? Maybe<int>(cookie->GetSourcePort(0))
                                        : Maybe<int>();
+    const Maybe<std::string> partition_key =
+        cookie->HasPartitionKey()
+            ? Maybe<std::string>(cookie->GetPartitionKey(""))
+            : Maybe<std::string>();
 
     auto net_cookie_or_error = MakeCookieFromProtocolValues(
         cookie->GetName(), cookie->GetValue(), cookie->GetUrl(""),
         cookie->GetDomain(""), cookie->GetPath(""), cookie->GetSecure(false),
         cookie->GetHttpOnly(false), cookie->GetSameSite(""),
         cookie->GetExpires(-1), cookie->GetPriority(""),
-        cookie->GetSameParty(false), source_scheme, source_port);
+        cookie->GetSameParty(false), source_scheme, source_port, partition_key);
     if (absl::holds_alternative<Response>(net_cookie_or_error)) {
       // TODO: Investiage whether we can report the error as a protocol error
       // (this might be a breaking CDP change).
diff --git a/content/browser/devtools/protocol/network_handler.h b/content/browser/devtools/protocol/network_handler.h
index 024402d..5798a54e 100644
--- a/content/browser/devtools/protocol/network_handler.h
+++ b/content/browser/devtools/protocol/network_handler.h
@@ -145,6 +145,7 @@
                  Maybe<bool> same_party,
                  Maybe<std::string> source_scheme,
                  Maybe<int> source_port,
+                 Maybe<std::string> partition_key,
                  std::unique_ptr<SetCookieCallback> callback) override;
   void SetCookies(
       std::unique_ptr<protocol::Array<Network::CookieParam>> cookies,
diff --git a/content/browser/renderer_host/input/synthetic_gesture_target_aura.cc b/content/browser/renderer_host/input/synthetic_gesture_target_aura.cc
index db6bbafc..c5848f1 100644
--- a/content/browser/renderer_host/input/synthetic_gesture_target_aura.cc
+++ b/content/browser/renderer_host/input/synthetic_gesture_target_aura.cc
@@ -63,12 +63,19 @@
 
   aura::Window* window = GetWindow();
   aura::WindowTreeHost* host = window->GetHost();
-  for (const auto& event : events) {
-    event->ConvertLocationToTarget(window, host->window());
-    ui::EventDispatchDetails details =
-        event_injector_.Inject(host, event.get());
-    if (details.dispatcher_destroyed)
-      break;
+  for (auto& event : events) {
+    // Synthetic events from devtools debugger need to be dispatched explicitly
+    // to the target window. Otherwise they will end up in the active tab
+    // which might be different from the target.
+    if (web_touch.GetModifiers() & blink::WebInputEvent::kFromDebugger) {
+      window->delegate()->OnEvent(event.get());
+    } else {
+      event->ConvertLocationToTarget(window, host->window());
+      ui::EventDispatchDetails details =
+          event_injector_.Inject(host, event.get());
+      if (details.dispatcher_destroyed)
+        break;
+    }
   }
 }
 
@@ -97,11 +104,18 @@
   wheel_precision_y_ = delta_y - wheel_event.y_offset();
 
   aura::Window* window = GetWindow();
-  wheel_event.ConvertLocationToTarget(window, window->GetRootWindow());
-  ui::EventDispatchDetails details =
-      event_injector_.Inject(window->GetHost(), &wheel_event);
-  if (details.dispatcher_destroyed)
-    return;
+  // Synthetic events from devtools debugger need to be dispatched explicitly
+  // to the target window. Otherwise they will end up in the active tab
+  // which might be different from the target.
+  if (web_wheel.GetModifiers() & blink::WebInputEvent::kFromDebugger) {
+    window->delegate()->OnEvent(&wheel_event);
+  } else {
+    wheel_event.ConvertLocationToTarget(window, window->GetRootWindow());
+    ui::EventDispatchDetails details =
+        event_injector_.Inject(window->GetHost(), &wheel_event);
+    if (details.dispatcher_destroyed)
+      return;
+  }
 }
 
 void SyntheticGestureTargetAura::DispatchWebGestureEventToPlatform(
@@ -123,8 +137,15 @@
                                  web_gesture.PositionInWidget().y(), flags,
                                  web_gesture.TimeStamp(), pinch_details);
 
-    pinch_event.ConvertLocationToTarget(window, window->GetRootWindow());
-    event_injector_.Inject(window->GetHost(), &pinch_event);
+    // Synthetic events from devtools debugger need to be dispatched explicitly
+    // to the target window. Otherwise they will end up in the active tab
+    // which might be different from the target.
+    if (web_gesture.GetModifiers() & blink::WebInputEvent::kFromDebugger) {
+      window->delegate()->OnEvent(&pinch_event);
+    } else {
+      pinch_event.ConvertLocationToTarget(window, window->GetRootWindow());
+      event_injector_.Inject(window->GetHost(), &pinch_event);
+    }
     return;
   }
 
@@ -138,8 +159,15 @@
                                web_gesture.data.fling_start.velocity_x,
                                web_gesture.data.fling_start.velocity_y, 0, 0, 2,
                                momentum_phase, ui::ScrollEventPhase::kNone);
-  scroll_event.ConvertLocationToTarget(window, window->GetRootWindow());
-  event_injector_.Inject(window->GetHost(), &scroll_event);
+  // Synthetic events from devtools debugger need to be dispatched explicitly
+  // to the target window. Otherwise they will end up in the active tab
+  // which might be different from the target.
+  if (web_gesture.GetModifiers() & blink::WebInputEvent::kFromDebugger) {
+    window->delegate()->OnEvent(&scroll_event);
+  } else {
+    scroll_event.ConvertLocationToTarget(window, window->GetRootWindow());
+    event_injector_.Inject(window->GetHost(), &scroll_event);
+  }
 }
 
 void SyntheticGestureTargetAura::DispatchWebMouseEventToPlatform(
@@ -164,12 +192,19 @@
                              changed_button_flags, pointer_details);
 
   aura::Window* window = GetWindow();
-  mouse_event.ConvertLocationToTarget(window, window->GetRootWindow());
   mouse_event.SetClickCount(web_mouse_event.click_count);
-  ui::EventDispatchDetails details =
-      event_injector_.Inject(window->GetHost(), &mouse_event);
-  if (details.dispatcher_destroyed)
-    return;
+  // Synthetic events from devtools debugger need to be dispatched explicitly
+  // to the target window. Otherwise they will end up in the active tab
+  // which might be different from the target.
+  if (web_mouse_event.GetModifiers() & blink::WebInputEvent::kFromDebugger) {
+    window->delegate()->OnEvent(&mouse_event);
+  } else {
+    mouse_event.ConvertLocationToTarget(window, window->GetRootWindow());
+    ui::EventDispatchDetails details =
+        event_injector_.Inject(window->GetHost(), &mouse_event);
+    if (details.dispatcher_destroyed)
+      return;
+  }
 }
 
 content::mojom::GestureSourceType
diff --git a/content/browser/renderer_host/origin_policy_throttle.cc b/content/browser/renderer_host/origin_policy_throttle.cc
index 044b6e2..f20b26d 100644
--- a/content/browser/renderer_host/origin_policy_throttle.cc
+++ b/content/browser/renderer_host/origin_policy_throttle.cc
@@ -27,9 +27,9 @@
 
 // Implement the public "API" from
 // content/public/browser/origin_policy_commands.h:
-void OriginPolicyAddExceptionFor(BrowserContext* browser_context,
+void OriginPolicyAddExceptionFor(StoragePartition* storage_partition,
                                  const GURL& url) {
-  OriginPolicyThrottle::AddExceptionFor(browser_context, url);
+  OriginPolicyThrottle::AddExceptionFor(storage_partition, url);
 }
 
 // static
@@ -57,13 +57,12 @@
 }
 
 // static
-void OriginPolicyThrottle::AddExceptionFor(BrowserContext* browser_context,
+void OriginPolicyThrottle::AddExceptionFor(StoragePartition* storage_partition,
                                            const GURL& url) {
-  DCHECK(browser_context);
-  StoragePartitionImpl* storage_partition = static_cast<StoragePartitionImpl*>(
-      browser_context->GetStoragePartitionForUrl(url));
+  auto* storage_partition_impl =
+      static_cast<StoragePartitionImpl*>(storage_partition);
   network::mojom::OriginPolicyManager* origin_policy_manager =
-      storage_partition->GetOriginPolicyManagerForBrowserProcess();
+      storage_partition_impl->GetOriginPolicyManagerForBrowserProcess();
 
   origin_policy_manager->AddExceptionFor(url::Origin::Create(url));
 }
diff --git a/content/browser/renderer_host/origin_policy_throttle.h b/content/browser/renderer_host/origin_policy_throttle.h
index eb0cdd66..562afd3b 100644
--- a/content/browser/renderer_host/origin_policy_throttle.h
+++ b/content/browser/renderer_host/origin_policy_throttle.h
@@ -8,7 +8,6 @@
 #include <memory>
 
 #include "content/common/content_export.h"
-#include "content/public/browser/browser_context.h"
 #include "content/public/browser/navigation_throttle.h"
 #include "services/network/public/cpp/origin_policy.h"
 
@@ -16,6 +15,7 @@
 
 namespace content {
 class NavigationHandle;
+class StoragePartition;
 
 // The OriginPolicyThrottle is responsible for deciding whether an origin
 // policy should be fetched, and doing so when that is positive.
@@ -42,7 +42,8 @@
   // otherwise invalid) policy. This is meant to be called by the security
   // interstitial.
   // This will exempt the entire origin, rather than only the given URL.
-  static void AddExceptionFor(BrowserContext* browser_context, const GURL& url);
+  static void AddExceptionFor(StoragePartition* storage_partition,
+                              const GURL& url);
 
   OriginPolicyThrottle(const OriginPolicyThrottle&) = delete;
   OriginPolicyThrottle& operator=(const OriginPolicyThrottle&) = delete;
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 91347ffe..bbd9798 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -6091,17 +6091,6 @@
   delegate_->UnregisterProtocolHandler(source, protocol, url, user_gesture);
 }
 
-void WebContentsImpl::OnAppCacheAccessed(const GURL& manifest_url,
-                                         bool blocked_by_policy) {
-  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::OnAppCacheAccessed");
-  // TODO(nick): Should we consider |source| here? Should we call FilterURL on
-  // |manifest_url|?
-
-  // Notify observers about navigation.
-  observers_.NotifyObservers(&WebContentsObserver::AppCacheAccessed,
-                             manifest_url, blocked_by_policy);
-}
-
 void WebContentsImpl::DomOperationResponse(const std::string& json_string) {
   OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::DomOperationResponse",
                         "json_string", json_string);
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 7fb0c54..25a4066 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -1226,10 +1226,6 @@
   void DidActivatePortal(WebContentsImpl* predecessor_web_contents,
                          base::TimeTicks activation_time);
 
-  // Notifies observers that AppCache was accessed. Public so AppCache code can
-  // call this directly.
-  void OnAppCacheAccessed(const GURL& manifest_url, bool blocked_by_policy);
-
   void OnServiceWorkerAccessed(RenderFrameHost* render_frame_host,
                                const GURL& scope,
                                AllowServiceWorkerResult allowed);
diff --git a/content/public/browser/origin_policy_commands.h b/content/public/browser/origin_policy_commands.h
index 7533193..b77cdf1 100644
--- a/content/public/browser/origin_policy_commands.h
+++ b/content/public/browser/origin_policy_commands.h
@@ -11,14 +11,15 @@
 
 namespace content {
 
-class BrowserContext;
+class StoragePartition;
 
 // Instruct the Origin Policy throttle to disregard errors for the given URL.
 //
 // Intended use: This should be called by the browser when the user selects
 // "proceed" on the security interstitial page for the given URL.
-CONTENT_EXPORT void OriginPolicyAddExceptionFor(BrowserContext* browser_context,
-                                                const GURL& url);
+CONTENT_EXPORT void OriginPolicyAddExceptionFor(
+    StoragePartition* storage_partition,
+    const GURL& url);
 
 }  // namespace content
 
diff --git a/content/public/browser/web_contents_observer.h b/content/public/browser/web_contents_observer.h
index f2cc589..1447272 100644
--- a/content/public/browser/web_contents_observer.h
+++ b/content/public/browser/web_contents_observer.h
@@ -514,9 +514,6 @@
   // NavigationEntry assigned to it.
   virtual void TitleWasSet(NavigationEntry* entry) {}
 
-  virtual void AppCacheAccessed(const GURL& manifest_url,
-                                bool blocked_by_policy) {}
-
   // These methods are invoked when a Pepper plugin instance is created/deleted
   // in the DOM.
   virtual void PepperInstanceCreated() {}
diff --git a/content/public/test/mock_web_contents_observer.h b/content/public/test/mock_web_contents_observer.h
index 7e8bc6ed..53f17ed 100644
--- a/content/public/test/mock_web_contents_observer.h
+++ b/content/public/test/mock_web_contents_observer.h
@@ -194,11 +194,7 @@
               (RenderFrameHost* render_frame_host,
                const gfx::Size& frame_size),
               (override));
-  MOCK_METHOD(void, TitleWasSet, (NavigationEntry* entry), (override));
-  MOCK_METHOD(void,
-              AppCacheAccessed,
-              (const GURL& manifest_url, bool blocked_by_policy),
-              (override));
+  MOCK_METHOD(void, TitleWasSet, (NavigationEntry * entry), (override));
   MOCK_METHOD(void, PepperInstanceCreated, (), (override));
   MOCK_METHOD(void, PepperInstanceDeleted, (), (override));
   MOCK_METHOD(void,
diff --git a/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt
index 17aff586..3f6cec8 100644
--- a/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt
@@ -113,7 +113,6 @@
 
 # Flakily hits a DCHECK during shared image creation.
 crbug.com/1188437 [ linux intel display-server-wayland ] GpuProcess_webgl [ RetryOnFailure ]
-crbug.com/1253917 [ linux intel display-server-wayland ] GpuProcess_disable_gpu [ RetryOnFailure ]
 
 # Flakey/slow tests using clang_rt.
 crbug.com/1261260 [ android android-nexus-5x ] GpuProcess_webgpu_iframe_removed [ Failure ]
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h
index ee34c1c0..20b4ce4 100644
--- a/extensions/browser/extension_function_histogram_value.h
+++ b/extensions/browser/extension_function_histogram_value.h
@@ -1662,6 +1662,7 @@
   WEB_AUTHENTICATION_PROXY_DETACH = 1599,
   FILEMANAGERPRIVATE_CANCELIOTASK = 1600,
   AUTOTESTPRIVATE_GETDISPLAYSMOOTHNESS = 1601,
+  AUTOTESTPRIVATE_RESETHOLDINGSPACE = 1602,
   // Last entry: Add new entries above, then run:
   // python tools/metrics/histograms/update_extension_histograms.py
   ENUM_BOUNDARY
diff --git a/extensions/common/BUILD.gn b/extensions/common/BUILD.gn
index 3efbc39..0923a779 100644
--- a/extensions/common/BUILD.gn
+++ b/extensions/common/BUILD.gn
@@ -635,4 +635,12 @@
       "//base",
     ]
   }
+
+  fuzzer_test("extension_csp_validator_fuzzer") {
+    sources = [ "csp_validator_fuzzer.cc" ]
+    deps = [
+      ":common",
+      "//base",
+    ]
+  }
 }  # enable_extensions
diff --git a/extensions/common/csp_validator_fuzzer.cc b/extensions/common/csp_validator_fuzzer.cc
new file mode 100644
index 0000000..87907c9f
--- /dev/null
+++ b/extensions/common/csp_validator_fuzzer.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 <stddef.h>
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include "extensions/common/csp_validator.h"
+#include "extensions/common/install_warning.h"
+
+namespace extensions {
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  FuzzedDataProvider fuzzed_data_provider(data, size);
+
+  const std::string content_security_policy =
+      fuzzed_data_provider.ConsumeRandomLengthString();
+  const std::string manifest_key =
+      fuzzed_data_provider.ConsumeRandomLengthString();
+
+  std::vector<InstallWarning> install_warnings;
+  csp_validator::SanitizeContentSecurityPolicy(
+      content_security_policy, manifest_key,
+      /*options=*/fuzzed_data_provider.ConsumeIntegralInRange(0, 4),
+      &install_warnings);
+
+  csp_validator::GetEffectiveSandoxedPageCSP(content_security_policy,
+                                             manifest_key, &install_warnings);
+
+  std::u16string error;
+  csp_validator::DoesCSPDisallowRemoteCode(content_security_policy,
+                                           manifest_key, &error);
+
+  return 0;
+}
+
+}  // namespace extensions
diff --git a/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm b/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
index a2051507..6bd6dd70 100644
--- a/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
@@ -775,7 +775,8 @@
 
 // Test to ensure that feed can be enabled/disabled and that feed header changes
 // accordingly.
-- (void)testToggleFeedEnabled {
+// TODO(crbug.com/1194106): Flaky on ios-simulator-noncq.
+- (void)DISABLED_testToggleFeedEnabled {
   [self
       testNTPInitialPositionAndContent:[NewTabPageAppInterface collectionView]];
 
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
index 5d75a81..1269b467 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-5da94496a6036476405ee112b4f21a3a8d4555d3
\ No newline at end of file
+e8dac9ffd91084624d559f5fe177d4b06f23eee9
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
index 204655d..87fde24 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-c0e7d6582b005f1c730439bf070359f66542ab31
\ No newline at end of file
+d1b07f28175e9585551bc569ad4dadd70041ec43
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
index 398a177..b0691b9 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-66566dbfa7fe4bf2f10e70ef43cdf953a7b21b39
\ No newline at end of file
+6e4b73a34437bd9358edf6b0185d8f6f10a1632d
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
index f65c43f..3e2f5ff 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-3abdaac67d0fce6b572a1ee15ee5cfbea9f5b219
\ No newline at end of file
+9d298ae0267f8a533f975c27c31f5b9a7fb7a7a8
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
index dbd40612..bb3c2177 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-07d6c0312262f7e652325da872cf56afd2fdacfc
\ No newline at end of file
+d0f13a96ae528b85783e001b82be8b27f3bbd107
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
index c4ebc88..f4b00f5 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-da303077ec667dcf8d9b7bd4e593385a2a8ea598
\ No newline at end of file
+e957391a82885c99d392eadf4e0859c982e280c8
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
index 2d80f60..c9e4a70 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-a84f6df2e29c716e8dde6065e28c9fdd34ddbecb
\ No newline at end of file
+60f355cfd35b1af0bf7c1a73224c2b144e1c800b
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
index 4a274940..63ff913e 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-bb3157a65d7dc145c7a663bfd10139eeafe68288
\ No newline at end of file
+82bd1bde09f630d8f379811fa0eaa81e69b116f1
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
index a29a1b0..1a669bb4 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-b719b20698427db4a2350b0ccf208a5657879101
\ No newline at end of file
+826d8e147c7b1bfce295b132eaa8763221738e89
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
index aa3109c..e4e04c7 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-07eea62c1cdc6f8f568862fdf976ac632cbb8c94
\ No newline at end of file
+bac6df57a087e2692a151f1255b26387ced22a34
\ No newline at end of file
diff --git a/media/gpu/vaapi/vaapi_video_encode_accelerator.cc b/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
index 37cc281..427b54b 100644
--- a/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
@@ -431,31 +431,35 @@
   EncodePendingInputs();
 }
 
-void VaapiVideoEncodeAccelerator::TryToReturnBitstreamBuffer() {
+void VaapiVideoEncodeAccelerator::TryToReturnBitstreamBuffers() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(encoder_sequence_checker_);
 
   if (state_ != kEncoding)
     return;
 
-  while (!pending_encode_results_.empty() &&
-         pending_encode_results_.front() == nullptr) {
-    // A null job indicates a flush command.
+  TRACE_EVENT1("media,gpu", "VAVEA::TryToReturnBitstreamBuffers",
+               "pending encode results", pending_encode_results_.size());
+  while (!pending_encode_results_.empty()) {
+    if (pending_encode_results_.front() == nullptr) {
+      // A null job indicates a flush command.
+      pending_encode_results_.pop();
+      DVLOGF(2) << "FlushDone";
+      DCHECK(flush_callback_);
+      child_task_runner_->PostTask(
+          FROM_HERE, base::BindOnce(std::move(flush_callback_), true));
+      continue;
+    }
+
+    if (available_bitstream_buffers_.empty())
+      return;
+
+    auto buffer = std::move(available_bitstream_buffers_.front());
+    available_bitstream_buffers_.pop();
+    auto encode_result = std::move(pending_encode_results_.front());
     pending_encode_results_.pop();
-    DVLOGF(2) << "FlushDone";
-    DCHECK(flush_callback_);
-    child_task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(std::move(flush_callback_), true));
+
+    ReturnBitstreamBuffer(std::move(encode_result), std::move(buffer));
   }
-
-  if (pending_encode_results_.empty() || available_bitstream_buffers_.empty())
-    return;
-
-  auto buffer = std::move(available_bitstream_buffers_.front());
-  available_bitstream_buffers_.pop();
-  auto encode_result = std::move(pending_encode_results_.front());
-  pending_encode_results_.pop();
-
-  ReturnBitstreamBuffer(std::move(encode_result), std::move(buffer));
 }
 
 void VaapiVideoEncodeAccelerator::ReturnBitstreamBuffer(
@@ -568,7 +572,7 @@
   }
 
   // Create input and reconstructed surfaces.
-  TRACE_EVENT1("media,gpu", "VAVEA::ConstructSurfaces", "the number of layers",
+  TRACE_EVENT1("media,gpu", "VAVEA::ConstructSurfaces", "layers",
                spatial_layer_resolutions.size());
   input_surfaces->reserve(spatial_layer_resolutions.size());
   reconstructed_surfaces->reserve(spatial_layer_resolutions.size());
@@ -823,10 +827,12 @@
       // |pending_encode_results_| queue.
       pending_encode_results_.push(nullptr);
       input_queue_.pop();
-      TryToReturnBitstreamBuffer();
+      TryToReturnBitstreamBuffers();
       continue;
     }
 
+    TRACE_EVENT0("media,gpu",
+                 "VAVEA::EncodeOneInputFrameAndReturnEncodedChunks");
     const size_t num_spatial_layers = spatial_layer_resolutions.size();
     std::vector<scoped_refptr<VASurface>> input_surfaces;
     std::vector<scoped_refptr<VASurface>> reconstructed_surfaces;
@@ -865,7 +871,7 @@
     }
 
     for (auto&& job : jobs) {
-      TRACE_EVENT0("media,gpu", "VAVEA::FromEncodeToReturn");
+      TRACE_EVENT0("media,gpu", "VAVEA::Encode");
       std::unique_ptr<EncodeResult> result = encoder_->Encode(std::move(job));
       if (!result) {
         NOTIFY_ERROR(kPlatformFailureError, "Failed encoding job");
@@ -873,9 +879,9 @@
       }
 
       pending_encode_results_.push(std::move(result));
-      TryToReturnBitstreamBuffer();
     }
 
+    TryToReturnBitstreamBuffers();
     input_queue_.pop();
   }
 }
@@ -910,7 +916,7 @@
   }
 
   available_bitstream_buffers_.push(std::move(buffer_ref));
-  TryToReturnBitstreamBuffer();
+  TryToReturnBitstreamBuffers();
 }
 
 void VaapiVideoEncodeAccelerator::RequestEncodingParametersChange(
diff --git a/media/gpu/vaapi/vaapi_video_encode_accelerator.h b/media/gpu/vaapi/vaapi_video_encode_accelerator.h
index b7e1d90..8d77648 100644
--- a/media/gpu/vaapi/vaapi_video_encode_accelerator.h
+++ b/media/gpu/vaapi/vaapi_video_encode_accelerator.h
@@ -186,10 +186,10 @@
   scoped_refptr<VASurface> GetAvailableVASurfaceAsRefCounted(
       std::vector<std::unique_ptr<ScopedVASurface>>* va_surfaces);
 
-  // Returns a bitstream buffer to the client if we have both pending the
+  // Returns pending bitstream buffers to the client if we have both pending
   // encoded data to be completed and bitstream buffers available to download
   // the encoded data into.
-  void TryToReturnBitstreamBuffer();
+  void TryToReturnBitstreamBuffers();
 
   // Downloads encoded data produced as a result of running |encode_result| into
   // |buffer|, and returns it to the client.
diff --git a/media/gpu/vaapi/vaapi_video_encode_accelerator_unittest.cc b/media/gpu/vaapi/vaapi_video_encode_accelerator_unittest.cc
index 16c23dd..132e27d6 100644
--- a/media/gpu/vaapi/vaapi_video_encode_accelerator_unittest.cc
+++ b/media/gpu/vaapi/vaapi_video_encode_accelerator_unittest.cc
@@ -608,6 +608,7 @@
           .WillOnce(Return(kEncodedChunkSize));
       EXPECT_CALL(*mock_encoder_, BitrateControlUpdate(kEncodedChunkSize))
           .WillOnce(Return());
+
       EXPECT_CALL(*mock_encoder_, GetMetadata(_, _))
           .WillOnce(
               WithArgs<0, 1>([](const VaapiVideoEncoderDelegate::EncodeJob& job,
@@ -620,6 +621,11 @@
                                    ->metadata_for_encoding;
                 return metadata;
               }));
+    }
+
+    for (size_t i = 0; i < num_spatial_layers; ++i) {
+      const VABufferID kCodedBufferId = kCodedBufferIds[i];
+      const uint64_t kEncodedChunkSize = kEncodedChunkSizes[i];
       EXPECT_CALL(
           *mock_vaapi_wrapper_,
           DownloadFromVABuffer(kCodedBufferId, _, _, output_buffer_size_, _))
diff --git a/services/audio/sync_reader.cc b/services/audio/sync_reader.cc
index 9a990353..acc5a8a 100644
--- a/services/audio/sync_reader.cc
+++ b/services/audio/sync_reader.cc
@@ -12,7 +12,7 @@
 #include "base/command_line.h"
 #include "base/format_macros.h"
 #include "base/logging.h"
-#include "base/metrics/histogram_macros.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/trace_event.h"
@@ -22,19 +22,68 @@
 #include "media/base/audio_parameters.h"
 #include "media/base/media_switches.h"
 
+using media::AudioLatency;
+
 namespace {
 
 // Used to log if any audio glitches have been detected during an audio session.
 // Elements in this enum should not be added, deleted or rearranged.
-enum AudioGlitchResult {
-  AUDIO_RENDERER_NO_AUDIO_GLITCHES = 0,
-  AUDIO_RENDERER_AUDIO_GLITCHES = 1,
-  AUDIO_RENDERER_AUDIO_GLITCHES_MAX = AUDIO_RENDERER_AUDIO_GLITCHES
+enum class AudioGlitchResult {
+  kNoGlitches = 0,
+  kGlitches = 1,
+  kMaxValue = kGlitches
 };
 
-void LogAudioGlitchResult(AudioGlitchResult result) {
-  UMA_HISTOGRAM_ENUMERATION("Media.AudioRendererAudioGlitches", result,
-                            AUDIO_RENDERER_AUDIO_GLITCHES_MAX + 1);
+void LogPerLatencyGlitchUma(AudioLatency::LatencyType latency,
+                            int renderer_missed_callback_count,
+                            int renderer_callback_count) {
+  DCHECK_LE(renderer_missed_callback_count, renderer_callback_count);
+
+  auto LatencyToString = [](AudioLatency::LatencyType latency) {
+    switch (latency) {
+      case AudioLatency::LATENCY_EXACT_MS:
+        return "LatencyExactMs";
+      case AudioLatency::LATENCY_INTERACTIVE:
+        return "LatencyInteractive";
+      case AudioLatency::LATENCY_RTC:
+        return "LatencyRtc";
+      case AudioLatency::LATENCY_PLAYBACK:
+        return "LatencyPlayback";
+      default:
+        return "LatencyUnknown";
+    }
+  };
+
+  const std::string suffix = LatencyToString(latency);
+
+  base::UmaHistogramEnumeration("Media.AudioRendererAudioGlitches2." + suffix,
+                                (renderer_missed_callback_count > 0)
+                                    ? AudioGlitchResult::kGlitches
+                                    : AudioGlitchResult::kNoGlitches);
+
+  const int kPermilleScaling = 1000;
+  // 10%: if we have more that 10% of callbacks having issues, the details are
+  // not very interesting any more, so we just log all those cases together to
+  // have a better resolution for lower values.
+  const int kHistogramRange = kPermilleScaling / 10;
+
+  // 30 s for 10 ms buffers (RTC streams)/ 1 minute for 20 ms buffers (media
+  // playback).
+  const int kShortStreamMaxCallbackCount = 3000;
+
+  if (renderer_callback_count <= 0)
+    return;
+
+  int missed_permille = std::ceil(
+      kPermilleScaling * static_cast<double>(renderer_missed_callback_count) /
+      renderer_callback_count);
+
+  base::UmaHistogramCustomCounts(
+      ((renderer_callback_count < kShortStreamMaxCallbackCount)
+           ? "Media.AudioRendererMissedDeadline2.Short."
+           : "Media.AudioRendererMissedDeadline2.Long.") +
+          suffix,
+      std::min(missed_permille, kHistogramRange), 0, kHistogramRange + 1, 100);
 }
 
 }  // namespace
@@ -55,6 +104,7 @@
     const media::AudioParameters& params,
     base::CancelableSyncSocket* foreign_socket)
     : log_callback_(std::move(log_callback)),
+      latency_tag_(params.latency_tag()),
       mute_audio_for_testing_(base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kMuteAudio)),
       had_socket_error_(false),
@@ -114,22 +164,22 @@
   if (!renderer_callback_count_)
     return;
 
-  // Recording the percentage of deadline misses gives us a rough overview of
-  // how many users might be running into audio glitches.
+  base::UmaHistogramEnumeration("Media.AudioRendererAudioGlitches",
+                                (renderer_missed_callback_count_ > 0)
+                                    ? AudioGlitchResult::kGlitches
+                                    : AudioGlitchResult::kNoGlitches);
   int percentage_missed =
       100.0 * renderer_missed_callback_count_ / renderer_callback_count_;
-  UMA_HISTOGRAM_PERCENTAGE("Media.AudioRendererMissedDeadline",
-                           percentage_missed);
+
+  base::UmaHistogramPercentage("Media.AudioRendererMissedDeadline",
+                               percentage_missed);
+
+  LogPerLatencyGlitchUma(latency_tag_, renderer_missed_callback_count_,
+                         renderer_callback_count_);
 
   TRACE_EVENT_INSTANT1("audio", "~SyncReader", TRACE_EVENT_SCOPE_THREAD,
                        "Missed callback percentage", percentage_missed);
 
-  // Add more detailed information regarding detected audio glitches where
-  // a non-zero value of |renderer_missed_callback_count_| is added to the
-  // AUDIO_RENDERER_AUDIO_GLITCHES bin.
-  renderer_missed_callback_count_ > 0
-      ? LogAudioGlitchResult(AUDIO_RENDERER_AUDIO_GLITCHES)
-      : LogAudioGlitchResult(AUDIO_RENDERER_NO_AUDIO_GLITCHES);
   log_callback_.Run(base::StringPrintf(
       "ASR: number of detected audio glitches: %" PRIuS " out of %" PRIuS,
       renderer_missed_callback_count_, renderer_callback_count_));
@@ -291,9 +341,9 @@
                          TRACE_EVENT_SCOPE_THREAD);
 
     base::TimeDelta time_since_start = base::TimeTicks::Now() - start_time;
-    UMA_HISTOGRAM_CUSTOM_TIMES("Media.AudioOutputControllerDataNotReady",
-                               time_since_start, base::Milliseconds(1),
-                               base::Milliseconds(1000), 50);
+    base::UmaHistogramCustomTimes("Media.AudioOutputControllerDataNotReady",
+                                  time_since_start, base::Milliseconds(1),
+                                  base::Milliseconds(1000), 50);
     return false;
   }
 
diff --git a/services/audio/sync_reader.h b/services/audio/sync_reader.h
index 6f5c64d6..626c28ad 100644
--- a/services/audio/sync_reader.h
+++ b/services/audio/sync_reader.h
@@ -18,6 +18,7 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "media/base/audio_bus.h"
+#include "media/base/audio_latency.h"
 #include "services/audio/output_controller.h"
 
 #if defined(OS_POSIX)
@@ -73,6 +74,8 @@
   base::UnsafeSharedMemoryRegion shared_memory_region_;
   base::WritableSharedMemoryMapping shared_memory_mapping_;
 
+  const media::AudioLatency::LatencyType latency_tag_;
+
   // Mutes all incoming samples. This is used to prevent audible sound
   // during automated testing.
   const bool mute_audio_for_testing_;
diff --git a/services/network/websocket.cc b/services/network/websocket.cc
index 36abc985..7c6b9358 100644
--- a/services/network/websocket.cc
+++ b/services/network/websocket.cc
@@ -273,6 +273,10 @@
   if (payload.size() > 0) {
     impl_->pending_data_frames_.push(payload);
   }
+  if (impl_->incoming_frame_interceptor_ &&
+      impl_->incoming_frame_interceptor_->IsFrameStarted()) {
+    return;
+  }
   impl_->SendPendingDataFrames();
 }
 
@@ -511,8 +515,11 @@
 
   // Safe if ReadAndSendFromDataPipe() deletes |this| because this method is
   // only called from mojo.
-  if (!blocked_on_websocket_channel_)
+  if (!blocked_on_websocket_channel_ &&
+      (!outgoing_frame_interceptor_ ||
+       !outgoing_frame_interceptor_->IsFrameStarted())) {
     ReadAndSendFromDataPipe();
+  }
 }
 
 void WebSocket::StartReceiving() {
@@ -733,7 +740,10 @@
 
   // Safe if ReadAndSendFromDataPipe() deletes |this| because this method is
   // only called from mojo.
-  ReadAndSendFromDataPipe();
+  if (!outgoing_frame_interceptor_ ||
+      !outgoing_frame_interceptor_->IsFrameStarted()) {
+    ReadAndSendFromDataPipe();
+  }
 }
 
 void WebSocket::ReadAndSendFromDataPipe() {
diff --git a/services/network/websocket_interceptor.cc b/services/network/websocket_interceptor.cc
index 8c34ef6d..3eb2ee4d 100644
--- a/services/network/websocket_interceptor.cc
+++ b/services/network/websocket_interceptor.cc
@@ -44,13 +44,13 @@
   if (!throttling_interceptor)
     return kContinue;
 
-  frame_started_ = true;
   throttling_interceptor->SetSuspendWhenOffline(true);
   int start_throttle_result = throttling_interceptor->StartThrottle(
       /*result=*/0, size, /*send_end=*/base::TimeTicks(), /*start=*/false,
       /*is_upload=*/direction_ == kOutgoing, throttle_callback_);
   if (start_throttle_result == net::ERR_IO_PENDING) {
     pending_callback_ = std::move(retry_callback);
+    frame_started_ = true;
     return kShouldWait;
   }
   return kContinue;
diff --git a/services/tracing/public/cpp/perfetto/trace_string_lookup.cc b/services/tracing/public/cpp/perfetto/trace_string_lookup.cc
index 70d92a86..5a86991 100644
--- a/services/tracing/public/cpp/perfetto/trace_string_lookup.cc
+++ b/services/tracing/public/cpp/perfetto/trace_string_lookup.cc
@@ -148,6 +148,7 @@
     {"NetworkConfigWatcher",
      ChromeThreadDescriptor::THREAD_NETWORKCONFIGWATCHER},
     {"wasapi_render_thread", ChromeThreadDescriptor::THREAD_WASAPI_RENDER},
+    {"LoaderLockSampler", ChromeThreadDescriptor::THREAD_LOADER_LOCK_SAMPLER},
 };
 
 ChromeProcessDescriptor::ProcessType GetProcessType(const std::string& name) {
diff --git a/storage/browser/quota/quota_database.cc b/storage/browser/quota/quota_database.cc
index 9f52786..84db81d 100644
--- a/storage/browser/quota/quota_database.cc
+++ b/storage/browser/quota/quota_database.cc
@@ -129,13 +129,12 @@
   }
 }
 
-bool QuotaDatabase::GetHostQuota(const std::string& host,
-                                 StorageType type,
-                                 int64_t* quota) {
+QuotaErrorOr<int64_t> QuotaDatabase::GetHostQuota(const std::string& host,
+                                                  StorageType type) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(quota);
-  if (EnsureOpened(EnsureOpenedMode::kFailIfNotFound) != QuotaError::kNone)
-    return false;
+  QuotaError open_error = EnsureOpened(EnsureOpenedMode::kFailIfNotFound);
+  if (open_error != QuotaError::kNone)
+    return open_error;
 
   static constexpr char kSql[] =
       "SELECT quota FROM quota WHERE host = ? AND type = ?";
@@ -143,27 +142,36 @@
   statement.BindString(0, host);
   statement.BindInt(1, static_cast<int>(type));
 
-  if (!statement.Step())
-    return false;
-
-  *quota = statement.ColumnInt64(0);
-  return true;
+  if (!statement.Step()) {
+    return statement.Succeeded() ? QuotaError::kNotFound
+                                 : QuotaError::kDatabaseError;
+  }
+  return statement.ColumnInt64(0);
 }
 
-bool QuotaDatabase::SetHostQuota(const std::string& host,
-                                 StorageType type,
-                                 int64_t quota) {
+QuotaError QuotaDatabase::SetHostQuota(const std::string& host,
+                                       StorageType type,
+                                       int64_t quota) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_GE(quota, 0);
-  if (EnsureOpened(EnsureOpenedMode::kCreateIfNotFound) != QuotaError::kNone)
-    return false;
+  QuotaError open_error = EnsureOpened(EnsureOpenedMode::kCreateIfNotFound);
+  if (open_error != QuotaError::kNone)
+    return open_error;
 
   if (quota == 0)
     return DeleteHostQuota(host, type);
-  if (!InsertOrReplaceHostQuota(host, type, quota))
-    return false;
+
+  static constexpr char kSql[] =
+      "INSERT OR REPLACE INTO quota(quota, host, type) VALUES (?, ?, ?)";
+  sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+  statement.BindInt64(0, quota);
+  statement.BindString(1, host);
+  statement.BindInt(2, static_cast<int>(type));
+  if (!statement.Run())
+    return QuotaError::kDatabaseError;
+
   ScheduleCommit();
-  return true;
+  return QuotaError::kNone;
 }
 
 QuotaErrorOr<BucketInfo> QuotaDatabase::GetOrCreateBucket(
@@ -519,11 +527,12 @@
   return true;
 }
 
-bool QuotaDatabase::DeleteHostQuota(
-    const std::string& host, StorageType type) {
+QuotaError QuotaDatabase::DeleteHostQuota(const std::string& host,
+                                          StorageType type) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (EnsureOpened(EnsureOpenedMode::kFailIfNotFound) != QuotaError::kNone)
-    return false;
+  QuotaError open_error = EnsureOpened(EnsureOpenedMode::kFailIfNotFound);
+  if (open_error != QuotaError::kNone)
+    return open_error;
 
   static constexpr char kSql[] =
       "DELETE FROM quota WHERE host = ? AND type = ?";
@@ -532,10 +541,10 @@
   statement.BindInt(1, static_cast<int>(type));
 
   if (!statement.Run())
-    return false;
+    return QuotaError::kDatabaseError;
 
   ScheduleCommit();
-  return true;
+  return QuotaError::kNone;
 }
 
 bool QuotaDatabase::DeleteStorageKeyInfo(const StorageKey& storage_key,
@@ -892,23 +901,6 @@
   return EnsureOpened(EnsureOpenedMode::kCreateIfNotFound) == QuotaError::kNone;
 }
 
-bool QuotaDatabase::InsertOrReplaceHostQuota(const std::string& host,
-                                             StorageType type,
-                                             int64_t quota) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(db_.get());
-  static constexpr char kSql[] =
-      // clang-format off
-      "INSERT OR REPLACE INTO quota(quota, host, type)"
-        "VALUES (?, ?, ?)";
-  // clang-format on
-  sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
-  statement.BindInt64(0, quota);
-  statement.BindString(1, host);
-  statement.BindInt(2, static_cast<int>(type));
-  return statement.Run();
-}
-
 QuotaError QuotaDatabase::DumpQuotaTable(const QuotaTableCallback& callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   QuotaError open_error = EnsureOpened(EnsureOpenedMode::kCreateIfNotFound);
diff --git a/storage/browser/quota/quota_database.h b/storage/browser/quota/quota_database.h
index d54cb27..0c90bf4 100644
--- a/storage/browser/quota/quota_database.h
+++ b/storage/browser/quota/quota_database.h
@@ -90,16 +90,17 @@
 
   ~QuotaDatabase();
 
-  // Returns whether the record could be found.
-  bool GetHostQuota(const std::string& host,
-                    blink::mojom::StorageType type,
-                    int64_t* quota);
+  // Returns quota if entry is found. Returns QuotaError::kNotFound no entry if
+  // found.
+  QuotaErrorOr<int64_t> GetHostQuota(const std::string& host,
+                                     blink::mojom::StorageType type);
 
   // Returns whether the operation succeeded.
-  bool SetHostQuota(const std::string& host,
-                    blink::mojom::StorageType type,
-                    int64_t quota);
-  bool DeleteHostQuota(const std::string& host, blink::mojom::StorageType type);
+  QuotaError SetHostQuota(const std::string& host,
+                          blink::mojom::StorageType type,
+                          int64_t quota);
+  QuotaError DeleteHostQuota(const std::string& host,
+                             blink::mojom::StorageType type);
 
   // Gets the bucket with `bucket_name` for the `storage_key` for StorageType
   // kTemporary and returns the BucketInfo. If one doesn't exist, it creates
@@ -262,9 +263,6 @@
   bool EnsureDatabaseVersion();
   bool ResetSchema();
   bool UpgradeSchema(int current_version);
-  bool InsertOrReplaceHostQuota(const std::string& host,
-                                blink::mojom::StorageType type,
-                                int64_t quota);
 
   bool CreateSchema();
   bool CreateTable(const TableSchema& table);
diff --git a/storage/browser/quota/quota_database_unittest.cc b/storage/browser/quota/quota_database_unittest.cc
index 7fefcdf..7aa9ba16 100644
--- a/storage/browser/quota/quota_database_unittest.cc
+++ b/storage/browser/quota/quota_database_unittest.cc
@@ -185,30 +185,41 @@
   const int kQuota1 = 13579;
   const int kQuota2 = kQuota1 + 1024;
 
-  int64_t quota = -1;
-  EXPECT_FALSE(db.GetHostQuota(kHost, kTemp, &quota));
-  EXPECT_FALSE(db.GetHostQuota(kHost, kPerm, &quota));
+  QuotaErrorOr<int64_t> result = db.GetHostQuota(kHost, kTemp);
+  EXPECT_FALSE(result.ok());
+  EXPECT_EQ(result.error(), QuotaError::kNotFound);
+  result = db.GetHostQuota(kHost, kPerm);
+  EXPECT_FALSE(result.ok());
+  EXPECT_EQ(result.error(), QuotaError::kNotFound);
 
   // Insert quota for temporary.
-  EXPECT_TRUE(db.SetHostQuota(kHost, kTemp, kQuota1));
-  EXPECT_TRUE(db.GetHostQuota(kHost, kTemp, &quota));
-  EXPECT_EQ(kQuota1, quota);
+  EXPECT_EQ(db.SetHostQuota(kHost, kTemp, kQuota1), QuotaError::kNone);
+  result = db.GetHostQuota(kHost, kTemp);
+  EXPECT_TRUE(result.ok());
+  EXPECT_EQ(kQuota1, result.value());
 
   // Update quota for temporary.
-  EXPECT_TRUE(db.SetHostQuota(kHost, kTemp, kQuota2));
-  EXPECT_TRUE(db.GetHostQuota(kHost, kTemp, &quota));
-  EXPECT_EQ(kQuota2, quota);
+  EXPECT_EQ(db.SetHostQuota(kHost, kTemp, kQuota2), QuotaError::kNone);
+  result = db.GetHostQuota(kHost, kTemp);
+  EXPECT_TRUE(result.ok());
+  EXPECT_EQ(kQuota2, result.value());
 
   // Quota for persistent must not be updated.
-  EXPECT_FALSE(db.GetHostQuota(kHost, kPerm, &quota));
+  result = db.GetHostQuota(kHost, kPerm);
+  EXPECT_FALSE(result.ok());
+  EXPECT_EQ(result.error(), QuotaError::kNotFound);
 
   // Delete temporary storage quota.
-  EXPECT_TRUE(db.DeleteHostQuota(kHost, kTemp));
-  EXPECT_FALSE(db.GetHostQuota(kHost, kTemp, &quota));
+  EXPECT_EQ(db.DeleteHostQuota(kHost, kTemp), QuotaError::kNone);
+  result = db.GetHostQuota(kHost, kTemp);
+  EXPECT_FALSE(result.ok());
+  EXPECT_EQ(result.error(), QuotaError::kNotFound);
 
   // Delete persistent quota by setting it to zero.
-  EXPECT_TRUE(db.SetHostQuota(kHost, kPerm, 0));
-  EXPECT_FALSE(db.GetHostQuota(kHost, kPerm, &quota));
+  EXPECT_EQ(db.SetHostQuota(kHost, kPerm, 0), QuotaError::kNone);
+  result = db.GetHostQuota(kHost, kPerm);
+  EXPECT_FALSE(result.ok());
+  EXPECT_EQ(result.error(), QuotaError::kNotFound);
 }
 
 TEST_P(QuotaDatabaseTest, GetOrCreateBucket) {
diff --git a/storage/browser/quota/quota_manager_impl.cc b/storage/browser/quota/quota_manager_impl.cc
index 95e0559..42338ce 100644
--- a/storage/browser/quota/quota_manager_impl.cc
+++ b/storage/browser/quota/quota_manager_impl.cc
@@ -159,22 +159,18 @@
   return database->GetBucketsModifiedBetween(type, begin, end);
 }
 
-bool GetPersistentHostQuotaOnDBThread(const std::string& host,
-                                      int64_t* quota,
-                                      QuotaDatabase* database) {
+QuotaErrorOr<int64_t> GetPersistentHostQuotaOnDBThread(
+    const std::string& host,
+    QuotaDatabase* database) {
   DCHECK(database);
-  database->GetHostQuota(host, StorageType::kPersistent, quota);
-  return true;
+  return database->GetHostQuota(host, StorageType::kPersistent);
 }
 
-bool SetPersistentHostQuotaOnDBThread(const std::string& host,
-                                      int64_t* new_quota,
-                                      QuotaDatabase* database) {
+QuotaError SetPersistentHostQuotaOnDBThread(const std::string& host,
+                                            int64_t* new_quota,
+                                            QuotaDatabase* database) {
   DCHECK(database);
-  if (database->SetHostQuota(host, StorageType::kPersistent, *new_quota))
-    return true;
-  *new_quota = 0;
-  return false;
+  return database->SetHostQuota(host, StorageType::kPersistent, *new_quota);
 }
 
 QuotaErrorOr<BucketLocator> GetLRUBucketOnDBThread(
@@ -1381,13 +1377,10 @@
   if (!persistent_host_quota_callbacks_.Add(host, std::move(callback)))
     return;
 
-  int64_t* quota_ptr = new int64_t(0);
   PostTaskAndReplyWithResultForDBThread(
-      FROM_HERE,
-      base::BindOnce(&GetPersistentHostQuotaOnDBThread, host,
-                     base::Unretained(quota_ptr)),
+      base::BindOnce(&GetPersistentHostQuotaOnDBThread, host),
       base::BindOnce(&QuotaManagerImpl::DidGetPersistentHostQuota,
-                     weak_factory_.GetWeakPtr(), host, base::Owned(quota_ptr)));
+                     weak_factory_.GetWeakPtr(), host));
 }
 
 void QuotaManagerImpl::SetPersistentHostQuota(const std::string& host,
@@ -1419,7 +1412,6 @@
 
   int64_t* new_quota_ptr = new int64_t(new_quota);
   PostTaskAndReplyWithResultForDBThread(
-      FROM_HERE,
       base::BindOnce(&SetPersistentHostQuotaOnDBThread, host,
                      base::Unretained(new_quota_ptr)),
       base::BindOnce(&QuotaManagerImpl::DidSetPersistentHostQuota,
@@ -2114,25 +2106,33 @@
 }
 
 void QuotaManagerImpl::DidGetPersistentHostQuota(const std::string& host,
-                                                 const int64_t* quota,
-                                                 bool success) {
+                                                 QuotaErrorOr<int64_t> result) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DidDatabaseWork(success);
+  DidDatabaseWork(result.ok() || result.error() != QuotaError::kDatabaseError);
+
+  if (!result.ok() && result.error() != QuotaError::kNotFound) {
+    persistent_host_quota_callbacks_.Run(
+        host, blink::mojom::QuotaStatusCode::kErrorInvalidAccess, /*quota=*/0);
+    return;
+  }
   persistent_host_quota_callbacks_.Run(
       host, blink::mojom::QuotaStatusCode::kOk,
-      std::min(*quota, kPerHostPersistentQuotaLimit));
+      std::min(result.ok() ? result.value() : 0, kPerHostPersistentQuotaLimit));
 }
 
 void QuotaManagerImpl::DidSetPersistentHostQuota(const std::string& host,
                                                  QuotaCallback callback,
                                                  const int64_t* new_quota,
-                                                 bool success) {
+                                                 QuotaError error) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DidDatabaseWork(success);
-  std::move(callback).Run(
-      success ? blink::mojom::QuotaStatusCode::kOk
-              : blink::mojom::QuotaStatusCode::kErrorInvalidAccess,
-      *new_quota);
+  DidDatabaseWork(error != QuotaError::kDatabaseError);
+
+  if (error == QuotaError::kNone) {
+    std::move(callback).Run(blink::mojom::QuotaStatusCode::kOk, *new_quota);
+    return;
+  }
+  std::move(callback).Run(blink::mojom::QuotaStatusCode::kErrorInvalidAccess,
+                          /*new_quota=*/0);
 }
 
 void QuotaManagerImpl::DidGetLRUBucket(QuotaErrorOr<BucketLocator> result) {
diff --git a/storage/browser/quota/quota_manager_impl.h b/storage/browser/quota/quota_manager_impl.h
index 266d392..3f3ecde2 100644
--- a/storage/browser/quota/quota_manager_impl.h
+++ b/storage/browser/quota/quota_manager_impl.h
@@ -546,12 +546,11 @@
   void GetLRUBucket(blink::mojom::StorageType type, GetBucketCallback callback);
 
   void DidGetPersistentHostQuota(const std::string& host,
-                                 const int64_t* quota,
-                                 bool success);
+                                 QuotaErrorOr<int64_t> result);
   void DidSetPersistentHostQuota(const std::string& host,
                                  QuotaCallback callback,
                                  const int64_t* new_quota,
-                                 bool success);
+                                 QuotaError error);
   void DidGetLRUBucket(QuotaErrorOr<BucketLocator> result);
   void GetQuotaSettings(QuotaSettingsCallback callback);
   void DidGetSettings(absl::optional<QuotaSettings> settings);
diff --git a/testing/scripts/run_performance_tests.py b/testing/scripts/run_performance_tests.py
index 00a51f9..b687be8 100755
--- a/testing/scripts/run_performance_tests.py
+++ b/testing/scripts/run_performance_tests.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env vpython3
 # Copyright 2017 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/testing/test_env.py b/testing/test_env.py
index 95f5741..8a65b1b 100755
--- a/testing/test_env.py
+++ b/testing/test_env.py
@@ -204,12 +204,12 @@
                      stderr=subprocess.STDOUT)
     forward_signals([process])
     while process.poll() is None:
-      sys.stdout.write(reader.read())
+      sys.stdout.write(reader.read().decode('utf-8'))
       # This sleep is needed for signal propagation. See the
       # wait_with_signals() docstring.
       time.sleep(0.1)
     # Read the remaining.
-    sys.stdout.write(reader.read())
+    sys.stdout.write(reader.read().decode('utf-8'))
     print('Command %r returned exit code %d' % (argv, process.returncode))
     return process.returncode
 
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index a28d517..d298808b 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1762,24 +1762,6 @@
             ]
         }
     ],
-    "ButterForPaymentsIOS": [
-        {
-            "platforms": [
-                "ios"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_Butter_IndicationForAllUsers",
-                    "enable_features": [
-                        "AutofillEnableAccountWalletStorage",
-                        "AutofillEnableInfoBarAccountIndicationFooterForSingleAccountUsers",
-                        "AutofillEnableInfoBarAccountIndicationFooterForSyncUsers",
-                        "AutofillEnableSaveCardInfoBarAccountIndicationFooter"
-                    ]
-                }
-            ]
-        }
-    ],
     "CPSS": [
         {
             "platforms": [
@@ -6508,6 +6490,28 @@
             ]
         }
     ],
+    "RTCDisallowPlanBOutsideDeprecationTrial": [
+        {
+            "platforms": [
+                "android",
+                "android_webview",
+                "chromeos",
+                "chromeos_lacros",
+                "ios",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "RTCDisallowPlanBOutsideDeprecationTrial"
+                    ]
+                }
+            ]
+        }
+    ],
     "RadioThrottleOnWeakSignalAndroid": [
         {
             "platforms": [
@@ -9013,29 +9017,6 @@
             ]
         }
     ],
-    "WebRTCDisallowPlanBOutsideDeprecationTrial": [
-        {
-            "platforms": [
-                "windows",
-                "mac",
-                "chromeos",
-                "chromeos_lacros",
-                "linux",
-                "ios",
-                "android",
-                "android_weblayer",
-                "android_webview"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "RTCDisallowPlanBOutsideDeprecationTrial"
-                    ]
-                }
-            ]
-        }
-    ],
     "WebRtcDistinctWorkerThread": [
         {
             "platforms": [
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 7c14f5e..12f353f 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -34,7 +34,7 @@
                                          base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kCOLRV1Fonts{"COLRV1Fonts",
-                                 base::FEATURE_DISABLED_BY_DEFAULT};
+                                 base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enable CSS Container Queries. Also implies LayoutNGGrid and CSSContainSize1D.
 const base::Feature kCSSContainerQueries{"CSSContainerQueries",
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
index af319b2..f393dbbc 100644
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -5117,6 +5117,11 @@
       # An unspecified port value allows protocol clients to emulate legacy cookie scope for the port.
       # This is a temporary ability and it will be removed in the future.
       experimental integer sourcePort
+      # Cookie partition key. The site of the top-level URL the browser was visiting at the start
+      # of the request to the endpoint that set the cookie.
+      experimental optional string partitionKey
+      # True if cookie partition key is opaque.
+      experimental optional boolean partitionKeyOpaque
 
   # Types of reasons why a cookie may not be stored from a response.
   experimental type SetCookieBlockedReason extends string
@@ -5275,6 +5280,10 @@
       # An unspecified port value allows protocol clients to emulate legacy cookie scope for the port.
       # This is a temporary ability and it will be removed in the future.
       experimental optional integer sourcePort
+      # Cookie partition key. The site of the top-level URL the browser was visiting at the start
+      # of the request to the endpoint that set the cookie.
+      # If not set, the cookie will be set as not partitioned.
+      experimental optional string partitionKey
 
   # Authorization challenge for HTTP status code 401 or 407.
   experimental type AuthChallenge extends object
@@ -5645,6 +5654,10 @@
       # An unspecified port value allows protocol clients to emulate legacy cookie scope for the port.
       # This is a temporary ability and it will be removed in the future.
       experimental optional integer sourcePort
+      # Cookie partition key. The site of the top-level URL the browser was visiting at the start
+      # of the request to the endpoint that set the cookie.
+      # If not set, the cookie will be set as not partitioned.
+      experimental optional string partitionKey
     returns
       # Always set to true. If an error occurs, the response indicates protocol error.
       deprecated boolean success
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc
index 4d9c9236..1baa74e 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc
@@ -638,9 +638,15 @@
 v8::MaybeLocal<v8::Value> V8ScriptRunner::CompileAndRunInternalScript(
     v8::Isolate* isolate,
     ScriptState* script_state,
-    const ScriptSourceCode& source_code) {
+    const ClassicScript& classic_script) {
   DCHECK_EQ(isolate, script_state->GetIsolate());
 
+  const ScriptSourceCode& source_code = classic_script.GetScriptSourceCode();
+  const ReferrerScriptInfo referrer_info(classic_script.BaseURL(),
+                                         classic_script.FetchOptions());
+  v8::Local<v8::Data> host_defined_options =
+      referrer_info.ToV8HostDefinedOptions(isolate, source_code.Url());
+
   v8::ScriptCompiler::CompileOptions compile_options;
   V8CodeCache::ProduceCacheOptions produce_cache_options;
   v8::ScriptCompiler::NoCacheReason no_cache_reason;
@@ -651,13 +657,9 @@
   // produce cache for them.
   DCHECK_EQ(produce_cache_options,
             V8CodeCache::ProduceCacheOptions::kNoProduceCache);
-  v8::Local<v8::Data> host_defined_options;
   v8::Local<v8::Script> script;
-  // Use default ScriptReferrerInfo here:
-  // - nonce: empty for internal script, and
-  // - parser_state: always "not parser inserted" for internal scripts.
   if (!V8ScriptRunner::CompileScript(
-           script_state, source_code, SanitizeScriptErrors::kDoNotSanitize,
+           script_state, source_code, classic_script.GetSanitizeScriptErrors(),
            compile_options, no_cache_reason, host_defined_options)
            .ToLocal(&script))
     return v8::MaybeLocal<v8::Value>();
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.h b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.h
index 64d830e..5438dc79 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.h
+++ b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.h
@@ -127,10 +127,8 @@
                                                     ClassicScript*,
                                                     ExecuteScriptPolicy,
                                                     RethrowErrorsOption);
-  static v8::MaybeLocal<v8::Value> CompileAndRunInternalScript(
-      v8::Isolate*,
-      ScriptState*,
-      const ScriptSourceCode&);
+  static v8::MaybeLocal<v8::Value>
+  CompileAndRunInternalScript(v8::Isolate*, ScriptState*, const ClassicScript&);
   static v8::MaybeLocal<v8::Value> CallAsConstructor(
       v8::Isolate*,
       v8::Local<v8::Object>,
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/observable_array.py b/third_party/blink/renderer/bindings/scripts/bind_gen/observable_array.py
index 1898c69..71ef0b7 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/observable_array.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/observable_array.py
@@ -209,9 +209,12 @@
     bind_local_vars(body, cg_context)
 
     body.extend([
-        T("static const void* kTemplateKey = &kTemplateKey;"),
+        T("// Make `template_key` unique for `FindV8Template`."),
+        T("static const char kTemplateKeyTag = 0;"),
+        T("const void* const template_key = &kTemplateKeyTag;"),
+        EmptyNode(),
         T("v8::Local<v8::Template> v8_template = "
-          "${per_isolate_data}->FindV8Template(${world}, kTemplateKey);"),
+          "${per_isolate_data}->FindV8Template(${world}, template_key);"),
         CxxLikelyIfNode(
             cond="!v8_template.IsEmpty()",
             body=T("return v8_template.As<v8::FunctionTemplate>();")),
@@ -245,7 +248,7 @@
     body.extend([
         EmptyNode(),
         T("${per_isolate_data}->AddV8Template("
-          "${world}, kTemplateKey, constructor_template);"),
+          "${world}, template_key, constructor_template);"),
         T("return constructor_template;"),
     ])
 
diff --git a/third_party/blink/renderer/core/css/style_recalc_test.cc b/third_party/blink/renderer/core/css/style_recalc_test.cc
index 780f340..d5c7c72 100644
--- a/third_party/blink/renderer/core/css/style_recalc_test.cc
+++ b/third_party/blink/renderer/core/css/style_recalc_test.cc
@@ -174,4 +174,37 @@
                 GetCSSPropertyColor()));
 }
 
+TEST_F(StyleRecalcTest, SkipStyleRecalcForContainerCleanSubtree) {
+  ScopedCSSContainerQueriesForTest scoped_cq(true);
+  ScopedCSSContainerSkipStyleRecalcForTest scoped_skip(true);
+
+  UpdateAllLifecyclePhasesForTest();
+
+  ASSERT_TRUE(GetDocument().body());
+
+  GetDocument().body()->setInnerHTML(R"HTML(
+    <style>
+      #container { container-type: inline-size; }
+      #container.narrow { width: 100px; }
+      @container (max-width: 100px) {
+        #affected { color: green; }
+      }
+    </style>
+    <div id="container">
+      <span id="affected"></span>
+    </div>
+  )HTML",
+                                     ASSERT_NO_EXCEPTION);
+
+  UpdateAllLifecyclePhasesForTest();
+
+  Element* container = GetDocument().getElementById("container");
+  ASSERT_TRUE(container);
+  container->classList().Add("narrow");
+  GetDocument().UpdateStyleAndLayoutTreeForThisDocument();
+
+  ASSERT_TRUE(container->GetContainerQueryData());
+  EXPECT_FALSE(container->GetContainerQueryData()->SkippedStyleRecalc());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index be13f27..2dd8c127 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -2890,6 +2890,12 @@
     const ComputedStyle& style,
     const StyleRecalcChange& child_change) {
   DCHECK(RuntimeEnabledFeatures::CSSContainerSkipStyleRecalcEnabled());
+  if (!child_change.TraversePseudoElements(*this)) {
+    // If none of the children or pseudo elements need to be traversed for style
+    // recalc, there is no point in marking the subtree as skipped.
+    DCHECK(!child_change.TraverseChildren(*this));
+    return false;
+  }
   if (child_change.ReattachLayoutTree()) {
     if (!LayoutObjectIsNeeded(style) || style.Display() == EDisplay::kInline ||
         style.IsDisplayTableType()) {
diff --git a/third_party/blink/renderer/core/html/forms/html_button_element.cc b/third_party/blink/renderer/core/html/forms/html_button_element.cc
index 61135f0..2098d13 100644
--- a/third_party/blink/renderer/core/html/forms/html_button_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_button_element.cc
@@ -40,9 +40,7 @@
 namespace blink {
 
 HTMLButtonElement::HTMLButtonElement(Document& document)
-    : HTMLFormControlElement(html_names::kButtonTag, document),
-      type_(SUBMIT),
-      is_activated_submit_(false) {}
+    : HTMLFormControlElement(html_names::kButtonTag, document) {}
 
 void HTMLButtonElement::setType(const AtomicString& type) {
   setAttribute(html_names::kTypeAttr, type);
@@ -62,15 +60,15 @@
 
 const AtomicString& HTMLButtonElement::FormControlType() const {
   switch (type_) {
-    case SUBMIT: {
+    case kSubmit: {
       DEFINE_STATIC_LOCAL(const AtomicString, submit, ("submit"));
       return submit;
     }
-    case BUTTON: {
+    case kButton: {
       DEFINE_STATIC_LOCAL(const AtomicString, button, ("button"));
       return button;
     }
-    case RESET: {
+    case kReset: {
       DEFINE_STATIC_LOCAL(const AtomicString, reset, ("reset"));
       return reset;
     }
@@ -95,11 +93,11 @@
     const AttributeModificationParams& params) {
   if (params.name == html_names::kTypeAttr) {
     if (EqualIgnoringASCIICase(params.new_value, "reset"))
-      type_ = RESET;
+      type_ = kReset;
     else if (EqualIgnoringASCIICase(params.new_value, "button"))
-      type_ = BUTTON;
+      type_ = kButton;
     else
-      type_ = SUBMIT;
+      type_ = kSubmit;
     UpdateWillValidateCache();
     if (formOwner() && isConnected())
       formOwner()->InvalidateDefaultButtonStyle();
@@ -118,11 +116,11 @@
       To<HTMLPopupElement>(popupElement)->Invoke(this);
     }
     if (!IsDisabledFormControl()) {
-      if (Form() && type_ == SUBMIT) {
+      if (Form() && type_ == kSubmit) {
         Form()->PrepareForSubmission(&event, this);
         event.SetDefaultHandled();
       }
-      if (Form() && type_ == RESET) {
+      if (Form() && type_ == kReset) {
         Form()->reset();
         event.SetDefaultHandled();
       }
@@ -140,13 +138,14 @@
 }
 
 bool HTMLButtonElement::WillRespondToMouseClickEvents() {
-  if (!IsDisabledFormControl() && Form() && (type_ == SUBMIT || type_ == RESET))
+  if (!IsDisabledFormControl() && Form() &&
+      (type_ == kSubmit || type_ == kReset))
     return true;
   return HTMLFormControlElement::WillRespondToMouseClickEvents();
 }
 
 bool HTMLButtonElement::CanBeSuccessfulSubmitButton() const {
-  return type_ == SUBMIT;
+  return type_ == kSubmit;
 }
 
 bool HTMLButtonElement::IsActivatedSubmit() const {
@@ -158,7 +157,7 @@
 }
 
 void HTMLButtonElement::AppendToFormData(FormData& form_data) {
-  if (type_ == SUBMIT && !GetName().IsEmpty() && is_activated_submit_)
+  if (type_ == kSubmit && !GetName().IsEmpty() && is_activated_submit_)
     form_data.AppendFromElement(GetName(), Value());
 }
 
@@ -178,7 +177,7 @@
 }
 
 bool HTMLButtonElement::RecalcWillValidate() const {
-  return type_ == SUBMIT && HTMLFormControlElement::RecalcWillValidate();
+  return type_ == kSubmit && HTMLFormControlElement::RecalcWillValidate();
 }
 
 int HTMLButtonElement::DefaultTabIndex() const {
diff --git a/third_party/blink/renderer/core/html/forms/html_button_element.h b/third_party/blink/renderer/core/html/forms/html_button_element.h
index 7080b9e..8d2b0948 100644
--- a/third_party/blink/renderer/core/html/forms/html_button_element.h
+++ b/third_party/blink/renderer/core/html/forms/html_button_element.h
@@ -46,7 +46,7 @@
                          InputDeviceCapabilities*) override;
 
  private:
-  enum Type { SUBMIT, RESET, BUTTON };
+  enum Type { kSubmit, kReset, kButton };
 
   const AtomicString& FormControlType() const override;
 
@@ -82,8 +82,8 @@
 
   int DefaultTabIndex() const override;
 
-  Type type_;
-  bool is_activated_submit_;
+  Type type_ = kSubmit;
+  bool is_activated_submit_ = false;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/media/media_remoting_interstitial.cc b/third_party/blink/renderer/core/html/media/media_remoting_interstitial.cc
index cc50744..aadbd5f1 100644
--- a/third_party/blink/renderer/core/html/media/media_remoting_interstitial.cc
+++ b/third_party/blink/renderer/core/html/media/media_remoting_interstitial.cc
@@ -75,7 +75,7 @@
   }
   if (toggle_interstitial_timer_.IsActive())
     toggle_interstitial_timer_.Stop();
-  state_ = VISIBLE;
+  state_ = kVisible;
   RemoveInlineStyleProperty(CSSPropertyID::kDisplay);
   SetInlineStyleProperty(CSSPropertyID::kOpacity, 0,
                          CSSPrimitiveValue::UnitType::kNumber);
@@ -89,7 +89,7 @@
   if (toggle_interstitial_timer_.IsActive())
     toggle_interstitial_timer_.Stop();
   if (error_code == WebMediaPlayerClient::kMediaRemotingStopNoText) {
-    state_ = HIDDEN;
+    state_ = kHidden;
   } else {
     String stop_text =
         GetVideoElement().GetLocale().QueryString(IDS_MEDIA_REMOTING_STOP_TEXT);
@@ -98,7 +98,7 @@
                   stop_text;
     }
     toast_message_->setInnerText(stop_text, ASSERT_NO_EXCEPTION);
-    state_ = TOAST;
+    state_ = kToast;
   }
   SetInlineStyleProperty(CSSPropertyID::kOpacity, 0,
                          CSSPrimitiveValue::UnitType::kNumber);
@@ -117,7 +117,7 @@
     SetInlineStyleProperty(CSSPropertyID::kBackgroundColor, CSSValueID::kBlack);
     SetInlineStyleProperty(CSSPropertyID::kOpacity, 1,
                            CSSPrimitiveValue::UnitType::kNumber);
-  } else if (state_ == HIDDEN) {
+  } else if (state_ == kHidden) {
     SetInlineStyleProperty(CSSPropertyID::kDisplay, CSSValueID::kNone);
     toast_message_->setInnerText(WebString(), ASSERT_NO_EXCEPTION);
   } else {
@@ -135,7 +135,7 @@
                                                CSSValueID::kNone);
     toast_message_->SetInlineStyleProperty(
         CSSPropertyID::kOpacity, 1, CSSPrimitiveValue::UnitType::kNumber);
-    state_ = HIDDEN;
+    state_ = kHidden;
     toggle_interstitial_timer_.StartOneShot(kShowToastDuration, FROM_HERE);
   }
 }
diff --git a/third_party/blink/renderer/core/html/media/media_remoting_interstitial.h b/third_party/blink/renderer/core/html/media/media_remoting_interstitial.h
index 466719b..24efeb3c 100644
--- a/third_party/blink/renderer/core/html/media/media_remoting_interstitial.h
+++ b/third_party/blink/renderer/core/html/media/media_remoting_interstitial.h
@@ -43,7 +43,7 @@
   void OnPosterImageChanged();
 
   // Query for whether the remoting interstitial is visible.
-  bool IsVisible() const { return state_ == VISIBLE; }
+  bool IsVisible() const { return state_ == kVisible; }
 
   HTMLVideoElement& GetVideoElement() const { return *video_element_; }
 
@@ -59,11 +59,11 @@
   // Indicates whether the interstitial should be visible. It is set/changed
   // when Show()/Hide() is called.
   enum State {
-    HIDDEN,   // The interstitial is currently not showing.
-    VISIBLE,  // The interstitial is currently visible except the toast.
-    TOAST,    // Only the toast is visible.
+    kHidden,   // The interstitial is currently not showing.
+    kVisible,  // The interstitial is currently visible except the toast.
+    kToast,    // Only the toast is visible.
   };
-  State state_ = HIDDEN;
+  State state_ = kHidden;
 
   HeapTaskRunnerTimer<MediaRemotingInterstitial> toggle_interstitial_timer_;
   Member<HTMLVideoElement> video_element_;
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser.cc b/third_party/blink/renderer/core/html/parser/html_document_parser.cc
index 392759c2..15497e4 100644
--- a/third_party/blink/renderer/core/html/parser/html_document_parser.cc
+++ b/third_party/blink/renderer/core/html/parser/html_document_parser.cc
@@ -664,17 +664,17 @@
 
 HTMLDocumentParser::NextTokenStatus HTMLDocumentParser::CanTakeNextToken() {
   if (IsStopped())
-    return NoTokens;
+    return kNoTokens;
 
   // If we're paused waiting for a script, we try to execute scripts before
   // continuing.
-  auto ret = HaveTokens;
+  auto ret = kHaveTokens;
   if (tree_builder_->HasParserBlockingScript()) {
     RunScriptsForPausedTreeBuilder();
-    ret = HaveTokensAfterScript;
+    ret = kHaveTokensAfterScript;
   }
   if (IsStopped() || IsPaused())
-    return NoTokens;
+    return kNoTokens;
   return ret;
 }
 
@@ -1020,10 +1020,10 @@
   unsigned tokens_parsed = 0;
   while (!should_yield) {
     const auto next_token_status = CanTakeNextToken();
-    if (next_token_status == NoTokens) {
+    if (next_token_status == kNoTokens) {
       // No tokens left to process in this pump, so break
       break;
-    } else if (next_token_status == HaveTokensAfterScript &&
+    } else if (next_token_status == kHaveTokensAfterScript &&
                task_runner_state_->HaveExitedHeader()) {
       // Just executed a parser-blocking script in the body. We'd probably like
       // to yield at some point soon, especially if we're in "extended budget"
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser.h b/third_party/blink/renderer/core/html/parser/html_document_parser.h
index bbdfde4b..9ddb4f40 100644
--- a/third_party/blink/renderer/core/html/parser/html_document_parser.h
+++ b/third_party/blink/renderer/core/html/parser/html_document_parser.h
@@ -166,7 +166,7 @@
                      ParserSynchronizationPolicy,
                      ParserPrefetchPolicy);
 
-  enum NextTokenStatus { NoTokens, HaveTokens, HaveTokensAfterScript };
+  enum NextTokenStatus { kNoTokens, kHaveTokens, kHaveTokensAfterScript };
 
   // DocumentParser
   void Detach() final;
diff --git a/third_party/blink/renderer/core/html/parser/html_tree_builder_simulator.cc b/third_party/blink/renderer/core/html/parser/html_tree_builder_simulator.cc
index a1e7cd7..54e29b4 100644
--- a/third_party/blink/renderer/core/html/parser/html_tree_builder_simulator.cc
+++ b/third_party/blink/renderer/core/html/parser/html_tree_builder_simulator.cc
@@ -112,7 +112,7 @@
 HTMLTreeBuilderSimulator::HTMLTreeBuilderSimulator(
     const HTMLParserOptions& options)
     : options_(options), in_select_insertion_mode_(false) {
-  namespace_stack_.push_back(HTML);
+  namespace_stack_.push_back(kHtml);
 }
 
 HTMLTreeBuilderSimulator::State HTMLTreeBuilderSimulator::StateFor(
@@ -122,9 +122,9 @@
   for (HTMLElementStack::ElementRecord* record =
            tree_builder->OpenElements()->TopRecord();
        record; record = record->Next()) {
-    Namespace current_namespace = HTML;
+    Namespace current_namespace = kHtml;
     if (record->NamespaceURI() == svg_names::kNamespaceURI)
-      current_namespace = SVG;
+      current_namespace = kSvg;
     else if (record->NamespaceURI() == mathml_names::kNamespaceURI)
       current_namespace = kMathML;
 
@@ -144,14 +144,14 @@
   if (token.GetType() == HTMLToken::kStartTag) {
     const String& tag_name = token.Data();
     if (ThreadSafeMatch(tag_name, svg_names::kSVGTag))
-      namespace_stack_.push_back(SVG);
+      namespace_stack_.push_back(kSvg);
     if (ThreadSafeMatch(tag_name, mathml_names::kMathTag))
       namespace_stack_.push_back(kMathML);
     if (InForeignContent() && TokenExitsForeignContent(token))
       namespace_stack_.pop_back();
     if (IsHTMLIntegrationPointForStartTag(token) ||
         (namespace_stack_.back() == kMathML && TokenExitsMath(token))) {
-      namespace_stack_.push_back(HTML);
+      namespace_stack_.push_back(kHtml);
     } else if (!InForeignContent()) {
       // FIXME: This is just a copy of Tokenizer::updateStateFor which uses
       // threadSafeMatches.
@@ -232,13 +232,13 @@
       (token.GetType() == HTMLToken::kStartTag && token.SelfClosing() &&
        InForeignContent())) {
     const String& tag_name = token.Data();
-    if ((namespace_stack_.back() == SVG &&
+    if ((namespace_stack_.back() == kSvg &&
          ThreadSafeMatch(tag_name, svg_names::kSVGTag)) ||
         (namespace_stack_.back() == kMathML &&
          ThreadSafeMatch(tag_name, mathml_names::kMathTag)) ||
         IsHTMLIntegrationPointForEndTag(token) ||
         (namespace_stack_.Contains(kMathML) &&
-         namespace_stack_.back() == HTML && TokenExitsMath(token))) {
+         namespace_stack_.back() == kHtml && TokenExitsMath(token))) {
       namespace_stack_.pop_back();
     }
     if (ThreadSafeMatch(tag_name, html_names::kScriptTag)) {
@@ -289,7 +289,7 @@
       return EqualIgnoringASCIICase(encoding->Value(), "text/html") ||
              EqualIgnoringASCIICase(encoding->Value(), "application/xhtml+xml");
     }
-  } else if (tokens_ns == SVG) {
+  } else if (tokens_ns == kSvg) {
     // FIXME: It's very fragile that we special case foreignObject here to be
     // ASCII case-insensitive.
     if (EqualIgnoringASCIICase(tag_name,
@@ -309,7 +309,7 @@
 
   // If it's inside an HTML integration point, the top namespace is
   // HTML, and its next namespace is not HTML.
-  if (namespace_stack_.back() != HTML)
+  if (namespace_stack_.back() != kHtml)
     return false;
   if (namespace_stack_.size() < 2)
     return false;
@@ -318,7 +318,7 @@
   const String& tag_name = token.Data();
   if (tokens_ns == kMathML)
     return ThreadSafeMatch(tag_name, mathml_names::kAnnotationXmlTag);
-  if (tokens_ns == SVG) {
+  if (tokens_ns == kSvg) {
     // FIXME: It's very fragile that we special case foreignObject here to be
     // ASCII case-insensitive.
     if (EqualIgnoringASCIICase(tag_name,
diff --git a/third_party/blink/renderer/core/html/parser/html_tree_builder_simulator.h b/third_party/blink/renderer/core/html/parser/html_tree_builder_simulator.h
index 184f58f..ec0565d 100644
--- a/third_party/blink/renderer/core/html/parser/html_tree_builder_simulator.h
+++ b/third_party/blink/renderer/core/html/parser/html_tree_builder_simulator.h
@@ -40,7 +40,7 @@
   USING_FAST_MALLOC(HTMLTreeBuilderSimulator);
 
  private:
-  enum Namespace { HTML, SVG, kMathML };
+  enum Namespace { kHtml, kSvg, kMathML };
 
   enum class TemplateType { kRegular, kShadow };
 
@@ -67,7 +67,7 @@
   SimulatedToken Simulate(const CompactHTMLToken&, HTMLTokenizer*);
 
  private:
-  bool InForeignContent() const { return namespace_stack_.back() != HTML; }
+  bool InForeignContent() const { return namespace_stack_.back() != kHtml; }
   bool IsHTMLIntegrationPointForStartTag(const CompactHTMLToken&) const;
   bool IsHTMLIntegrationPointForEndTag(const CompactHTMLToken&) const;
 
diff --git a/third_party/blink/renderer/core/inspector/dev_tools_host.cc b/third_party/blink/renderer/core/inspector/dev_tools_host.cc
index 244afef..090b8f0 100644
--- a/third_party/blink/renderer/core/inspector/dev_tools_host.cc
+++ b/third_party/blink/renderer/core/inspector/dev_tools_host.cc
@@ -31,7 +31,6 @@
 
 #include "base/json/json_reader.h"
 #include "third_party/blink/public/common/context_menu_data/menu_item_info.h"
-#include "third_party/blink/renderer/bindings/core/v8/script_source_code.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_script_runner.h"
 #include "third_party/blink/renderer/core/clipboard/system_clipboard.h"
@@ -49,6 +48,7 @@
 #include "third_party/blink/renderer/core/page/context_menu_controller.h"
 #include "third_party/blink/renderer/core/page/context_menu_provider.h"
 #include "third_party/blink/renderer/core/page/page.h"
+#include "third_party/blink/renderer/core/script/classic_script.h"
 #include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_error.h"
@@ -126,11 +126,13 @@
   ScriptState::Scope scope(script_state);
   v8::MicrotasksScope microtasks(script_state->GetIsolate(),
                                  v8::MicrotasksScope::kRunMicrotasks);
-  ScriptSourceCode source_code(expression, ScriptSourceLocationType::kInternal,
-                               nullptr, KURL(),
-                               TextPosition::MinimumPosition());
-  V8ScriptRunner::CompileAndRunInternalScript(script_state->GetIsolate(),
-                                              script_state, source_code);
+  // `kDoNotSanitize` is used for internal scripts for keeping the existing
+  // behavior.
+  V8ScriptRunner::CompileAndRunInternalScript(
+      script_state->GetIsolate(), script_state,
+      *ClassicScript::CreateUnspecifiedScript(
+          ScriptSourceCode(expression, ScriptSourceLocationType::kInternal),
+          SanitizeScriptErrors::kDoNotSanitize));
 }
 
 void DevToolsHost::DisconnectClient() {
diff --git a/third_party/blink/renderer/core/inspector/thread_debugger.cc b/third_party/blink/renderer/core/inspector/thread_debugger.cc
index e1dc5b4..7e4372a 100644
--- a/third_party/blink/renderer/core/inspector/thread_debugger.cc
+++ b/third_party/blink/renderer/core/inspector/thread_debugger.cc
@@ -8,7 +8,6 @@
 
 #include "base/rand_util.h"
 #include "third_party/blink/public/mojom/frame/user_activation_notification_type.mojom-blink.h"
-#include "third_party/blink/renderer/bindings/core/v8/script_source_code.h"
 #include "third_party/blink/renderer/bindings/core/v8/source_location.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_blob.h"
@@ -33,6 +32,7 @@
 #include "third_party/blink/renderer/core/inspector/inspector_trace_events.h"
 #include "third_party/blink/renderer/core/inspector/v8_inspector_string.h"
 #include "third_party/blink/renderer/core/probe/core_probes.h"
+#include "third_party/blink/renderer/core/script/classic_script.h"
 #include "third_party/blink/renderer/core/trustedtypes/trusted_html.h"
 #include "third_party/blink/renderer/core/trustedtypes/trusted_script.h"
 #include "third_party/blink/renderer/core/trustedtypes/trusted_script_url.h"
@@ -361,12 +361,15 @@
       v8::SideEffectType::kHasNoSideEffect);
 
   v8::Local<v8::Value> function_value;
+  // `kDoNotSanitize` is used for internal scripts for keeping the existing
+  // behavior.
   bool success =
       V8ScriptRunner::CompileAndRunInternalScript(
           isolate_, ScriptState::From(context),
-          ScriptSourceCode("(function(e) { console.log(e.type, e); })",
-                           ScriptSourceLocationType::kInternal, nullptr, KURL(),
-                           TextPosition::MinimumPosition()))
+          *ClassicScript::CreateUnspecifiedScript(
+              ScriptSourceCode("(function(e) { console.log(e.type, e); })",
+                               ScriptSourceLocationType::kInternal),
+              SanitizeScriptErrors::kDoNotSanitize))
           .ToLocal(&function_value) &&
       function_value->IsFunction();
   DCHECK(success);
diff --git a/third_party/blink/renderer/core/layout/layout_block.cc b/third_party/blink/renderer/core/layout/layout_block.cc
index 843f857..71881b0 100644
--- a/third_party/blink/renderer/core/layout/layout_block.cc
+++ b/third_party/blink/renderer/core/layout/layout_block.cc
@@ -64,6 +64,7 @@
 #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
+#include "third_party/blink/renderer/core/layout/ng/svg/layout_ng_svg_text.h"
 #include "third_party/blink/renderer/core/layout/text_autosizer.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/page/scrolling/root_scroller_controller.h"
@@ -118,6 +119,16 @@
   return *map;
 }
 
+// This map keeps track of SVG <text> descendants.
+// LayoutNGSVGText needs to do re-layout on transform changes of any ancestor
+// because LayoutNGSVGText's layout result depends on scaling factors computed
+// with ancestor transforms.
+TrackedDescendantsMap& GetSvgTextDescendantsMap() {
+  DEFINE_STATIC_LOCAL(Persistent<TrackedDescendantsMap>, map,
+                      (MakeGarbageCollected<TrackedDescendantsMap>()));
+  return *map;
+}
+
 LayoutBlock::LayoutBlock(ContainerNode* node)
     : LayoutBox(node),
       has_margin_before_quirk_(false),
@@ -129,6 +140,7 @@
       descendants_with_floats_marked_for_layout_(false),
       has_positioned_objects_(false),
       has_percent_height_descendants_(false),
+      has_svg_text_descendants_(false),
       pagination_state_changed_(false),
       is_legacy_initiated_out_of_flow_layout_(false) {
   if (node)
@@ -163,6 +175,10 @@
       descendant->SetPercentHeightContainer(nullptr);
     }
   }
+  if (has_svg_text_descendants_) {
+    GetSvgTextDescendantsMap().erase(this);
+    has_svg_text_descendants_ = false;
+  }
 }
 
 void LayoutBlock::WillBeDestroyed() {
@@ -259,6 +275,14 @@
       old_style && diff.NeedsFullLayout() && NeedsLayout() &&
       BorderOrPaddingLogicalDimensionChanged(*old_style, new_style,
                                              kLogicalHeight);
+
+  if (diff.TransformChanged() && has_svg_text_descendants_) {
+    for (LayoutBox* box : *GetSvgTextDescendantsMap().at(this)) {
+      box->SetNeedsLayout(layout_invalidation_reason::kStyleChange,
+                          kMarkContainerChain);
+      To<LayoutNGSVGText>(box)->SetNeedsTextMetricsUpdate();
+    }
+  }
 }
 
 void LayoutBlock::UpdateFromStyle() {
@@ -1227,6 +1251,33 @@
   }
 }
 
+void LayoutBlock::AddSvgTextDescendant(LayoutBox& svg_text) {
+  NOT_DESTROYED();
+  DCHECK(IsA<LayoutNGSVGText>(svg_text));
+  auto result = GetSvgTextDescendantsMap().insert(this, nullptr);
+  if (result.is_new_entry) {
+    result.stored_value->value =
+        MakeGarbageCollected<TrackedLayoutBoxLinkedHashSet>();
+  }
+  result.stored_value->value->insert(&svg_text);
+  has_svg_text_descendants_ = true;
+}
+
+void LayoutBlock::RemoveSvgTextDescendant(LayoutBox& svg_text) {
+  NOT_DESTROYED();
+  DCHECK(IsA<LayoutNGSVGText>(svg_text));
+  TrackedDescendantsMap& map = GetSvgTextDescendantsMap();
+  auto it = map.find(this);
+  if (it == map.end())
+    return;
+  TrackedLayoutBoxLinkedHashSet* descendants = &*it->value;
+  descendants->erase(&svg_text);
+  if (descendants->IsEmpty()) {
+    map.erase(this);
+    has_svg_text_descendants_ = false;
+  }
+}
+
 TrackedLayoutBoxLinkedHashSet* LayoutBlock::PercentHeightDescendantsInternal()
     const {
   NOT_DESTROYED();
diff --git a/third_party/blink/renderer/core/layout/layout_block.h b/third_party/blink/renderer/core/layout/layout_block.h
index 4ef5517..0f0eaeb 100644
--- a/third_party/blink/renderer/core/layout/layout_block.h
+++ b/third_party/blink/renderer/core/layout/layout_block.h
@@ -212,6 +212,9 @@
     return has_percent_height_descendants_;
   }
 
+  void AddSvgTextDescendant(LayoutBox& svg_text);
+  void RemoveSvgTextDescendant(LayoutBox& svg_text);
+
   void NotifyScrollbarThicknessChanged() {
     NOT_DESTROYED();
     width_available_to_children_changed_ = true;
@@ -633,6 +636,7 @@
 
   unsigned has_positioned_objects_ : 1;
   unsigned has_percent_height_descendants_ : 1;
+  unsigned has_svg_text_descendants_ : 1;
 
   // When an object ceases to establish a fragmentation context (e.g. the
   // LayoutView when we're no longer printing), we need a deep layout
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h
index f58efaa..599babb 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h
@@ -123,13 +123,14 @@
   void SetSvgLineLocalRect(const PhysicalRect& unscaled_rect);
 
   // A sequence number of fragments generated from a |LayoutObject|.
-  // For line boxes, please see |kInitialLineFragmentId|.
+  // For line boxes, this is a sequence number for the containing
+  // |LayoutBlockFlow|, starting at |kInitialLineFragmentId|.
   wtf_size_t FragmentId() const {
-    DCHECK_NE(Type(), kLine);
+    DCHECK(Type() != kLine || fragment_id_ >= kInitialLineFragmentId);
     return fragment_id_;
   }
   void SetFragmentId(wtf_size_t id) const {
-    DCHECK_NE(Type(), kLine);
+    DCHECK(Type() != kLine || id >= kInitialLineFragmentId);
     fragment_id_ = id;
   }
   // The initial framgent_id for line boxes.
@@ -137,8 +138,6 @@
   // its |LayoutBlockFlow| as their |DisplayItemClient|, but multicol also uses
   // fragment id for |LayoutBlockFlow| today. The plan is to make |FragmentData|
   // a |DisplayItemClient| instead.
-  // TODO(kojii): The fragment id for line boxes must be unique across NG block
-  // fragmentation. This is not implemented yet.
   static constexpr wtf_size_t kInitialLineFragmentId = 0x80000000;
 
   // Return true if this is the first fragment generated from a node.
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item_test.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item_test.cc
index 982c01c..b9f09f9 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item_test.cc
@@ -428,6 +428,41 @@
   EXPECT_TRUE(cursor.Current()->IsLastForNode());
 }
 
+TEST_F(NGFragmentItemTest, LineFragmentId) {
+  ScopedLayoutNGBlockFragmentationForTest ng_block_frag(true);
+  SetBodyInnerHTML(R"HTML(
+    <style>
+    #columns {
+      columns: 2;
+      column-fill: auto;
+      line-height: 1em;
+      height: 3em;
+    }
+    </style>
+    <body>
+      <div id="columns">
+        <div id="target">
+          1<br>
+          2<br>
+          3<br>
+          4<br>
+          5<br>
+          6
+        </div>
+      </div>
+    </body>
+  )HTML");
+  auto* target = To<LayoutBlockFlow>(GetLayoutObjectByElementId("target"));
+  NGInlineCursor cursor(*target);
+  wtf_size_t line_index = 0;
+  for (cursor.MoveToFirstLine(); cursor;
+       cursor.MoveToNextLineIncludingFragmentainer(), ++line_index) {
+    EXPECT_EQ(cursor.Current()->FragmentId(),
+              line_index + NGFragmentItem::kInitialLineFragmentId);
+  }
+  EXPECT_EQ(line_index, 6u);
+}
+
 // Various nodes/elements to test insertions.
 using CreateNode = Node* (*)(Document&);
 static CreateNode node_creators[] = {
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.cc
index fe29950..8b8696e 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.cc
@@ -99,6 +99,7 @@
   ClearCollectionScope<HeapHashMap<Member<const LayoutObject>, LastItem>>
       clear_scope(&last_items);
   wtf_size_t item_index = 0;
+  wtf_size_t line_fragment_id = NGFragmentItem::kInitialLineFragmentId;
   for (const auto& result : results) {
     const auto& fragment =
         To<NGPhysicalBoxFragment>(result->PhysicalFragment());
@@ -112,6 +113,7 @@
       ++item_index;
       if (item.Type() == NGFragmentItem::kLine) {
         DCHECK_EQ(item.DeltaToNextForSameLayoutObject(), 0u);
+        item.SetFragmentId(line_fragment_id++);
         continue;
       }
       LayoutObject* const layout_object = item.GetMutableLayoutObject();
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc
index 48a63f2..3cde28f9 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc
@@ -1192,6 +1192,15 @@
   NOTREACHED();
 }
 
+void NGInlineCursor::MoveToNextLineIncludingFragmentainer() {
+  MoveToNextLine();
+  if (!Current() && max_fragment_index_ && CanMoveAcrossFragmentainer()) {
+    MoveToNextFragmentainer();
+    if (Current() && !Current().IsLineBox())
+      MoveToFirstLine();
+  }
+}
+
 void NGInlineCursor::MoveToPreviousInlineLeaf() {
   if (Current() && Current().IsInlineLeaf())
     MoveToPrevious();
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h
index 82b938a..a1374d79 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h
@@ -451,6 +451,7 @@
   // Move the current position to next line. It is error to call other than line
   // box.
   void MoveToNextLine();
+  void MoveToNextLineIncludingFragmentainer();
 
   // Same as |MoveToNext| except that this skips children even if they exist.
   void MoveToNextSkippingChildren();
diff --git a/third_party/blink/renderer/core/layout/ng/svg/layout_ng_svg_text.cc b/third_party/blink/renderer/core/layout/ng/svg/layout_ng_svg_text.cc
index 20ed0861..ec880117 100644
--- a/third_party/blink/renderer/core/layout/ng/svg/layout_ng_svg_text.cc
+++ b/third_party/blink/renderer/core/layout/ng/svg/layout_ng_svg_text.cc
@@ -83,6 +83,20 @@
   LayoutSVGBlock::RemoveChild(child);
 }
 
+void LayoutNGSVGText::InsertedIntoTree() {
+  NOT_DESTROYED();
+  LayoutNGBlockFlowMixin<LayoutSVGBlock>::InsertedIntoTree();
+  for (LayoutBlock* cb = ContainingBlock(); cb; cb = cb->ContainingBlock())
+    cb->AddSvgTextDescendant(*this);
+}
+
+void LayoutNGSVGText::WillBeRemovedFromTree() {
+  NOT_DESTROYED();
+  for (LayoutBlock* cb = ContainingBlock(); cb; cb = cb->ContainingBlock())
+    cb->RemoveSvgTextDescendant(*this);
+  LayoutNGBlockFlowMixin<LayoutSVGBlock>::WillBeRemovedFromTree();
+}
+
 void LayoutNGSVGText::SubtreeStructureChanged(
     LayoutInvalidationReasonForTracing) {
   NOT_DESTROYED();
diff --git a/third_party/blink/renderer/core/layout/ng/svg/layout_ng_svg_text.h b/third_party/blink/renderer/core/layout/ng/svg/layout_ng_svg_text.h
index d59ee38..6534a7eb 100644
--- a/third_party/blink/renderer/core/layout/ng/svg/layout_ng_svg_text.h
+++ b/third_party/blink/renderer/core/layout/ng/svg/layout_ng_svg_text.h
@@ -34,6 +34,8 @@
   bool IsChildAllowed(LayoutObject* child, const ComputedStyle&) const override;
   void AddChild(LayoutObject* child, LayoutObject* before_child) override;
   void RemoveChild(LayoutObject* child) override;
+  void InsertedIntoTree() override;
+  void WillBeRemovedFromTree() override;
   gfx::RectF ObjectBoundingBox() const override;
   gfx::RectF StrokeBoundingBox() const override;
   gfx::RectF VisualRectInLocalSVGCoordinates() const override;
diff --git a/third_party/blink/renderer/core/loader/mixed_content_checker.cc b/third_party/blink/renderer/core/loader/mixed_content_checker.cc
index 040dadd..0713299 100644
--- a/third_party/blink/renderer/core/loader/mixed_content_checker.cc
+++ b/third_party/blink/renderer/core/loader/mixed_content_checker.cc
@@ -234,7 +234,7 @@
           source->GetDocument(),
           WebFeature::kMixedContentInNonHTTPSFrameThatRestrictsMixedContent);
     }
-  } else if (network::IsUrlPotentiallyTrustworthy(url) &&
+  } else if (!network::IsUrlPotentiallyTrustworthy(url) &&
              base::Contains(url::GetSecureSchemes(),
                             origin->Protocol().Ascii())) {
     UseCounter::Count(
diff --git a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
index fae49fa..515992e 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
@@ -1472,7 +1472,6 @@
     const NGPhysicalFragment& line_box_fragment,
     const DisplayItemClient& display_item_client,
     const NGFragmentItem& line_box_item,
-    wtf_size_t line_fragment_id,
     const PaintInfo& paint_info,
     const PhysicalOffset& child_offset) {
   if (paint_info.phase != PaintPhase::kForeground)
@@ -1481,6 +1480,8 @@
   PhysicalRect border_box = line_box_fragment.LocalRect();
   border_box.offset += child_offset;
   absl::optional<ScopedDisplayItemFragment> display_item_fragment;
+  const wtf_size_t line_fragment_id = line_box_item.FragmentId();
+  DCHECK_GE(line_fragment_id, NGFragmentItem::kInitialLineFragmentId);
   if (ShouldRecordHitTestData(paint_info)) {
     display_item_fragment.emplace(paint_info.context, line_fragment_id);
     paint_info.context.GetPaintController().RecordHitTestData(
@@ -1556,7 +1557,6 @@
     const PaintInfo& paint_info,
     const PhysicalOffset& paint_offset) {
   const bool is_horizontal = box_fragment_.Style().IsHorizontalWritingMode();
-  wtf_size_t line_fragment_id = NGFragmentItem::kInitialLineFragmentId;
   for (; *children; children->MoveToNextSkippingChildren()) {
     const NGFragmentItem* child_item = children->CurrentItem();
     DCHECK(child_item);
@@ -1586,7 +1586,7 @@
           child_item->LineBoxFragment();
       DCHECK(line_box_fragment);
       PaintLineBox(*line_box_fragment, *child_item->GetDisplayItemClient(),
-                   *child_item, line_fragment_id++, paint_info, child_offset);
+                   *child_item, paint_info, child_offset);
       NGInlineCursor line_box_cursor = children->CursorForDescendants();
       PaintInlineItems(paint_info, paint_offset,
                        child_item->OffsetInContainerFragment(),
diff --git a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h
index 365be0f..0a32869 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h
+++ b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h
@@ -142,7 +142,6 @@
   void PaintLineBox(const NGPhysicalFragment& line_box_fragment,
                     const DisplayItemClient& display_item_client,
                     const NGFragmentItem& line_box_item,
-                    wtf_size_t line_fragment_id,
                     const PaintInfo&,
                     const PhysicalOffset& paint_offset);
   void PaintBackplate(NGInlineCursor* descendants,
diff --git a/third_party/blink/renderer/core/scroll/DEPS b/third_party/blink/renderer/core/scroll/DEPS
new file mode 100644
index 0000000..7f381c0
--- /dev/null
+++ b/third_party/blink/renderer/core/scroll/DEPS
@@ -0,0 +1,5 @@
+specific_include_rules = {
+  "mac_scrollbar_animator_impl.h": [
+    "+ui/native_theme/scrollbar_animator_mac.h",
+  ],
+}
diff --git a/third_party/blink/renderer/core/scroll/mac_scrollbar_animator_impl.h b/third_party/blink/renderer/core/scroll/mac_scrollbar_animator_impl.h
index 84971bb..1d95cf34 100644
--- a/third_party/blink/renderer/core/scroll/mac_scrollbar_animator_impl.h
+++ b/third_party/blink/renderer/core/scroll/mac_scrollbar_animator_impl.h
@@ -15,6 +15,7 @@
 #include "third_party/blink/renderer/platform/scheduler/public/post_cancellable_task.h"
 #include "third_party/blink/renderer/platform/timer.h"
 #include "ui/gfx/geometry/point_f.h"
+#include "ui/native_theme/scrollbar_animator_mac.h"
 
 @class BlinkScrollbarObserver;
 @class BlinkScrollbarPainterControllerDelegate;
@@ -27,7 +28,7 @@
 
 class ScrollbarThemeMac;
 
-class PLATFORM_EXPORT MacScrollbarImpl : public MacScrollbar {
+class CORE_EXPORT MacScrollbarImpl : public MacScrollbar {
  public:
   MacScrollbarImpl(Scrollbar&,
                    base::scoped_nsobject<ScrollbarPainterController>,
@@ -111,7 +112,7 @@
 // If the scroller is composited, the opacity value stored on the scrollbar
 // painter is subsequently read out through ScrollbarThemeMac::ThumbOpacity and
 // plumbed into PaintedScrollbarLayerImpl::thumb_opacity_.
-class PLATFORM_EXPORT MacScrollbarAnimatorImpl : public MacScrollbarAnimator {
+class CORE_EXPORT MacScrollbarAnimatorImpl : public MacScrollbarAnimator {
  public:
   MacScrollbarAnimatorImpl(ScrollableArea*);
   virtual ~MacScrollbarAnimatorImpl() = default;
@@ -168,6 +169,80 @@
 
   Member<ScrollableArea> scrollable_area_;
 };
+// Implementation of the MacScrollbarV2::Client interface to talk to a Scrollbar
+// instance (the only other implementations are mocks for testing).
+class CORE_EXPORT MacScrollbarImplV2
+    : public ui::OverlayScrollbarAnimatorMac::Client,
+      public MacScrollbar {
+ public:
+  MacScrollbarImplV2(Scrollbar& scrollbar,
+                     scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+  ~MacScrollbarImplV2() override;
+
+  // Return true if `this` is the animator for `scrollbar`.
+  bool IsAnimatorFor(Scrollbar& scrollbar) const;
+
+  // Function to call upon interaction with this scrollbar.
+  void MouseDidEnter();
+  void MouseDidExit();
+  void DidScroll();
+
+  // MacScrollbar:
+  void SetEnabled(bool) final {}
+  void SetOverlayColorTheme(ScrollbarOverlayColorTheme) final {}
+  float GetKnobAlpha() final;
+  float GetTrackAlpha() final;
+  int GetTrackBoxWidth() final;
+
+  // ui::OverlayScrollbarAnimatorMac::Client:
+  bool IsMouseInScrollbarFrameRect() const override;
+  void SetHidden(bool hidden) override;
+  void SetThumbNeedsDisplay() override;
+  void SetTrackNeedsDisplay() override;
+
+ private:
+  std::unique_ptr<ui::OverlayScrollbarAnimatorMac> overlay_animator_;
+  Persistent<Scrollbar> scrollbar_;
+};
+
+// A non-Cocoa-based implementation of the MacScrollbarAnimator interface.
+class CORE_EXPORT MacScrollbarAnimatorV2 : public MacScrollbarAnimator {
+ public:
+  MacScrollbarAnimatorV2(ScrollableArea*);
+  virtual ~MacScrollbarAnimatorV2();
+
+  // MacScrollbarAnimator:
+  void Trace(Visitor* visitor) const final {
+    MacScrollbarAnimator::Trace(visitor);
+  }
+  void ContentAreaWillPaint() const final {}
+  void MouseEnteredContentArea() const final {}
+  void MouseExitedContentArea() const final {}
+  void MouseMovedInContentArea() const final {}
+  void MouseEnteredScrollbar(Scrollbar&) const final;
+  void MouseExitedScrollbar(Scrollbar&) const final;
+  void ContentsResized() const final {}
+  void DidAddVerticalScrollbar(Scrollbar&) final;
+  void WillRemoveVerticalScrollbar(Scrollbar&) final;
+  void DidAddHorizontalScrollbar(Scrollbar&) final;
+  void WillRemoveHorizontalScrollbar(Scrollbar&) final;
+  bool SetScrollbarsVisibleForTesting(bool) final { return true; }
+  void DidChangeUserVisibleScrollOffset(const ScrollOffset&) final;
+  void UpdateScrollerStyle() final { NOTREACHED(); }
+  bool ScrollbarPaintTimerIsActive() const final {
+    NOTREACHED();
+    return false;
+  }
+  void StartScrollbarPaintTimer() final { NOTREACHED(); }
+  void StopScrollbarPaintTimer() final { NOTREACHED(); }
+  void Dispose() final;
+
+ private:
+  std::unique_ptr<MacScrollbarImplV2> horizontal_scrollbar_;
+  std::unique_ptr<MacScrollbarImplV2> vertical_scrollbar_;
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+};
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_SCROLL_MAC_SCROLLBAR_ANIMATOR_IMPL_H_
diff --git a/third_party/blink/renderer/core/scroll/mac_scrollbar_animator_impl.mm b/third_party/blink/renderer/core/scroll/mac_scrollbar_animator_impl.mm
index d69bfe84..bd61947 100644
--- a/third_party/blink/renderer/core/scroll/mac_scrollbar_animator_impl.mm
+++ b/third_party/blink/renderer/core/scroll/mac_scrollbar_animator_impl.mm
@@ -6,6 +6,7 @@
 
 #import <AppKit/AppKit.h>
 
+#include "base/feature_list.h"
 #include "base/mac/scoped_nsobject.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/core/scroll/ns_scroller_imp_details.h"
@@ -28,6 +29,15 @@
   return *map;
 }
 
+typedef HeapHashMap<WeakMember<const Scrollbar>, MacScrollbarImplV2*>
+    ScrollbarToAnimatorV2Map;
+
+ScrollbarToAnimatorV2Map& GetScrollbarToAnimatorV2Map() {
+  DEFINE_STATIC_LOCAL(Persistent<ScrollbarToAnimatorV2Map>, map,
+                      (MakeGarbageCollected<ScrollbarToAnimatorV2Map>()));
+  return *map;
+}
+
 }  // namespace
 }  // namespace blink
 
@@ -82,6 +92,9 @@
 
 }  // namespace
 
+///////////////////////////////////////////////////////////////////////////////
+// BlinkScrollbarObserver
+
 @interface BlinkScrollbarObserver : NSObject {
   blink::Scrollbar* _scrollbar;
   base::scoped_nsobject<ScrollbarPainter> _scrollbarPainter;
@@ -149,6 +162,9 @@
 
 @end
 
+///////////////////////////////////////////////////////////////////////////////
+// BlinkScrollbarPainterControllerDelegate
+//
 // This class is a delegator of ScrollbarPainterController to ScrollableArea
 // that has the scrollbars of a ScrollbarPainter.
 @interface BlinkScrollbarPainterControllerDelegate : NSObject {
@@ -257,73 +273,12 @@
 };
 
 @class BlinkScrollbarPartAnimation;
-namespace blink {
-// This class is used to drive the animation timer for
-// BlinkScrollbarPartAnimation
-// objects. This is used instead of NSAnimation because CoreAnimation
-// establishes connections to the WindowServer, which should not be done in a
-// sandboxed renderer process.
-class BlinkScrollbarPartAnimationTimer {
- public:
-  BlinkScrollbarPartAnimationTimer(
-      BlinkScrollbarPartAnimation* animation,
-      CFTimeInterval duration,
-      scoped_refptr<base::SingleThreadTaskRunner> task_runner)
-      : timer_(std::move(task_runner),
-               this,
-               &BlinkScrollbarPartAnimationTimer::TimerFired),
-        start_time_(0.0),
-        duration_(duration),
-        animation_(animation),
-        timing_function_(CubicBezierTimingFunction::Preset(
-            CubicBezierTimingFunction::EaseType::EASE_IN_OUT)) {}
-
-  ~BlinkScrollbarPartAnimationTimer() {}
-
-  void Start() {
-    start_time_ = base::Time::Now().ToDoubleT();
-    // Set the framerate of the animation. NSAnimation uses a default
-    // framerate of 60 Hz, so use that here.
-    timer_.StartRepeating(base::Seconds(1.0 / 60.0), FROM_HERE);
-  }
-
-  void Stop() { timer_.Stop(); }
-
-  void SetDuration(CFTimeInterval duration) { duration_ = duration; }
-
- private:
-  void TimerFired(TimerBase*) {
-    double current_time = base::Time::Now().ToDoubleT();
-    double delta = current_time - start_time_;
-
-    if (delta >= duration_)
-      timer_.Stop();
-
-    double fraction = delta / duration_;
-    fraction = ClampTo(fraction, 0.0, 1.0);
-    double progress = timing_function_->Evaluate(fraction);
-    // In some scenarios, animation_ gets released during the call to
-    // setCurrentProgress. Because BlinkScrollbarPartAnimationTimer is a
-    // member variable of BlinkScrollbarPartAnimation animation_ the timer
-    // gets freed at the same time with animation_. In that case, it will
-    // not be safe to call any other code after animation_ setCurrentProgress.
-    [animation_ setCurrentProgress:progress];
-  }
-
-  TaskRunnerTimer<BlinkScrollbarPartAnimationTimer> timer_;
-  double start_time_;                       // In seconds.
-  double duration_;                         // In seconds.
-  BlinkScrollbarPartAnimation* animation_;  // Weak, owns this.
-  scoped_refptr<CubicBezierTimingFunction> timing_function_;
-};
-
-}  // namespace blink
 
 // This class handles the animation of a |_featureToAnimate| part of
 // |_scrollbar|.
 @interface BlinkScrollbarPartAnimation : NSObject {
   blink::Scrollbar* _scrollbar;
-  std::unique_ptr<blink::BlinkScrollbarPartAnimationTimer> _timer;
+  std::unique_ptr<ui::ScrollbarAnimationTimerMac> _timer;
   base::scoped_nsobject<ScrollbarPainter> _scrollbarPainter;
   FeatureToAnimate _featureToAnimate;
   CGFloat _startValue;
@@ -351,8 +306,14 @@
   if (!self)
     return nil;
 
-  _timer = std::make_unique<blink::BlinkScrollbarPartAnimationTimer>(
-      self, duration, std::move(taskRunner));
+  base::scoped_nsobject<BlinkScrollbarPartAnimation> scoped_self(
+      self, base::scoped_policy::RETAIN);
+  auto animation_callback = WTF::BindRepeating(
+      [](base::scoped_nsobject<BlinkScrollbarPartAnimation> animation,
+         double progress) { [animation setCurrentProgress:progress]; },
+      std::move(scoped_self));
+  _timer = std::make_unique<ui::ScrollbarAnimationTimerMac>(
+      std::move(animation_callback), duration, std::move(taskRunner));
   _scrollbar = scrollbar;
   _featureToAnimate = featureToAnimate;
   _startValue = startValue;
@@ -433,6 +394,9 @@
 }
 @end
 
+///////////////////////////////////////////////////////////////////////////////
+// BlinkScrollbarPainterDelegate
+//
 // This class is a delegator of ScrollbarPainter to the 4 types of animation
 // it can run. The animations are run through BlinkScrollbarPartAnimation.
 @interface BlinkScrollbarPainterDelegate : NSObject <NSAnimationDelegate> {
@@ -692,6 +656,9 @@
 
 namespace blink {
 
+///////////////////////////////////////////////////////////////////////////////
+// MacScrollbarImpl
+
 MacScrollbarImpl::MacScrollbarImpl(
     Scrollbar& scrollbar,
     base::scoped_nsobject<ScrollbarPainterController> painter_controller,
@@ -839,6 +806,9 @@
   return [observer_ painter];
 }
 
+///////////////////////////////////////////////////////////////////////////////
+// MacScrollbarAnimatorImpl
+
 MacScrollbarAnimatorImpl::MacScrollbarAnimatorImpl(
     ScrollableArea* scrollable_area)
     : task_runner_(scrollable_area->GetCompositorTaskRunner()),
@@ -1060,18 +1030,189 @@
     [scrollbar_painter_controller_ contentAreaScrolled];
 }
 
+///////////////////////////////////////////////////////////////////////////////
+// MacScrollbarImplV2
+
+MacScrollbarImplV2::MacScrollbarImplV2(
+    Scrollbar& scrollbar,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+    : scrollbar_(scrollbar) {
+  if (ScrollbarThemeMac::PreferOverlayScrollerStyle()) {
+    int track_box_width_expanded = 0;
+    int track_box_width_unexpanded = 0;
+    switch (scrollbar_->CSSScrollbarWidth()) {
+      case EScrollbarWidth::kNone:
+        break;
+      case EScrollbarWidth::kThin:
+        track_box_width_expanded = 14;
+        track_box_width_unexpanded = 10;
+        break;
+      case EScrollbarWidth::kAuto:
+        track_box_width_expanded = 16;
+        track_box_width_unexpanded = 12;
+        break;
+    }
+    overlay_animator_ = std::make_unique<ui::OverlayScrollbarAnimatorMac>(
+        this, track_box_width_expanded, track_box_width_unexpanded,
+        task_runner);
+  }
+  GetScrollbarToAnimatorV2Map().Set(scrollbar_, this);
+}
+
+MacScrollbarImplV2::~MacScrollbarImplV2() {
+  auto it = GetScrollbarToAnimatorV2Map().find(scrollbar_);
+  GetScrollbarToAnimatorV2Map().erase(it);
+}
+
+bool MacScrollbarImplV2::IsAnimatorFor(Scrollbar& scrollbar) const {
+  return &scrollbar == scrollbar_;
+}
+
+void MacScrollbarImplV2::MouseDidEnter() {
+  if (overlay_animator_)
+    overlay_animator_->MouseDidEnter();
+}
+void MacScrollbarImplV2::MouseDidExit() {
+  if (overlay_animator_)
+    overlay_animator_->MouseDidExit();
+}
+
+void MacScrollbarImplV2::DidScroll() {
+  if (overlay_animator_)
+    overlay_animator_->DidScroll();
+}
+
+float MacScrollbarImplV2::GetKnobAlpha() {
+  if (overlay_animator_)
+    return overlay_animator_->GetThumbAlpha();
+  return 1.f;
+}
+
+float MacScrollbarImplV2::GetTrackAlpha() {
+  if (overlay_animator_)
+    return overlay_animator_->GetTrackAlpha();
+  return 1.f;
+}
+
+int MacScrollbarImplV2::GetTrackBoxWidth() {
+  if (overlay_animator_)
+    return overlay_animator_->GetThumbWidth();
+
+  switch (scrollbar_->CSSScrollbarWidth()) {
+    case EScrollbarWidth::kNone:
+      return 0;
+    case EScrollbarWidth::kThin:
+      return 11;
+    case EScrollbarWidth::kAuto:
+      return 15;
+  }
+}
+
+bool MacScrollbarImplV2::IsMouseInScrollbarFrameRect() const {
+  if (auto* area = scrollbar_->GetScrollableArea())
+    return scrollbar_->FrameRect().Contains(area->LastKnownMousePosition());
+  return false;
+}
+void MacScrollbarImplV2::SetHidden(bool hidden) {
+  scrollbar_->SetScrollbarsHiddenFromExternalAnimator(hidden);
+}
+void MacScrollbarImplV2::SetThumbNeedsDisplay() {
+  scrollbar_->SetNeedsPaintInvalidation(kThumbPart);
+}
+void MacScrollbarImplV2::SetTrackNeedsDisplay() {
+  scrollbar_->SetNeedsPaintInvalidation(kTrackBGPart);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// MacScrollbarAnimatorV2
+
+MacScrollbarAnimatorV2::MacScrollbarAnimatorV2(ScrollableArea* scrollable_area)
+    : task_runner_(scrollable_area->GetCompositorTaskRunner()) {}
+MacScrollbarAnimatorV2::~MacScrollbarAnimatorV2() = default;
+
+void MacScrollbarAnimatorV2::MouseEnteredScrollbar(Scrollbar& scrollbar) const {
+  if (horizontal_scrollbar_ && horizontal_scrollbar_->IsAnimatorFor(scrollbar))
+    horizontal_scrollbar_->MouseDidEnter();
+  if (vertical_scrollbar_ && vertical_scrollbar_->IsAnimatorFor(scrollbar))
+    vertical_scrollbar_->MouseDidEnter();
+}
+
+void MacScrollbarAnimatorV2::MouseExitedScrollbar(Scrollbar& scrollbar) const {
+  if (horizontal_scrollbar_ && horizontal_scrollbar_->IsAnimatorFor(scrollbar))
+    horizontal_scrollbar_->MouseDidExit();
+  if (vertical_scrollbar_ && vertical_scrollbar_->IsAnimatorFor(scrollbar))
+    vertical_scrollbar_->MouseDidExit();
+}
+
+void MacScrollbarAnimatorV2::DidAddVerticalScrollbar(Scrollbar& scrollbar) {
+  if (!IsScrollbarRegistered(scrollbar))
+    return;
+  DCHECK(!vertical_scrollbar_);
+  vertical_scrollbar_ =
+      std::make_unique<MacScrollbarImplV2>(scrollbar, task_runner_);
+}
+
+void MacScrollbarAnimatorV2::WillRemoveVerticalScrollbar(Scrollbar& scrollbar) {
+  vertical_scrollbar_.reset();
+}
+
+void MacScrollbarAnimatorV2::DidAddHorizontalScrollbar(Scrollbar& scrollbar) {
+  if (!IsScrollbarRegistered(scrollbar))
+    return;
+  DCHECK(!horizontal_scrollbar_);
+  horizontal_scrollbar_ =
+      std::make_unique<MacScrollbarImplV2>(scrollbar, task_runner_);
+}
+
+void MacScrollbarAnimatorV2::WillRemoveHorizontalScrollbar(
+    Scrollbar& scrollbar) {
+  horizontal_scrollbar_.reset();
+}
+
+void MacScrollbarAnimatorV2::DidChangeUserVisibleScrollOffset(
+    const ScrollOffset& new_offset) {
+  if (horizontal_scrollbar_ && new_offset.width() != 0)
+    horizontal_scrollbar_->DidScroll();
+  if (vertical_scrollbar_ && new_offset.height() != 0)
+    vertical_scrollbar_->DidScroll();
+}
+
+void MacScrollbarAnimatorV2::Dispose() {
+  vertical_scrollbar_.reset();
+  horizontal_scrollbar_.reset();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// MacScrollbarAnimator
+
+const base::Feature kMacScrollbarsV2{"MacScrollbarsV2",
+                                     base::FEATURE_ENABLED_BY_DEFAULT};
+
 // static
 MacScrollbarAnimator* MacScrollbarAnimator::Create(
     ScrollableArea* scrollable_area) {
+  if (base::FeatureList::IsEnabled(kMacScrollbarsV2)) {
+    return MakeGarbageCollected<MacScrollbarAnimatorV2>(
+        const_cast<ScrollableArea*>(scrollable_area));
+  }
   return MakeGarbageCollected<MacScrollbarAnimatorImpl>(
       const_cast<ScrollableArea*>(scrollable_area));
 }
 
+///////////////////////////////////////////////////////////////////////////////
+// MacScrollbar
+
 // static
 MacScrollbar* MacScrollbar::GetForScrollbar(const Scrollbar& scrollbar) {
-  auto it = GetScrollbarToAnimatorMap().find(&scrollbar);
-  if (it != GetScrollbarToAnimatorMap().end())
-    return it->value;
+  if (base::FeatureList::IsEnabled(kMacScrollbarsV2)) {
+    auto found = GetScrollbarToAnimatorV2Map().find(&scrollbar);
+    if (found != GetScrollbarToAnimatorV2Map().end())
+      return found->value;
+  } else {
+    auto found = GetScrollbarToAnimatorMap().find(&scrollbar);
+    if (found != GetScrollbarToAnimatorMap().end())
+      return found->value;
+  }
   return nullptr;
 }
 
diff --git a/third_party/blink/renderer/core/scroll/scrollbar_theme_mac.cc b/third_party/blink/renderer/core/scroll/scrollbar_theme_mac.cc
index 0f82f19..17cc3cf 100644
--- a/third_party/blink/renderer/core/scroll/scrollbar_theme_mac.cc
+++ b/third_party/blink/renderer/core/scroll/scrollbar_theme_mac.cc
@@ -87,6 +87,7 @@
                                              : legacy_regular_values;
   }
 }
+
 const NSScrollerImpValues& GetScrollbarPainterValues(
     const Scrollbar& scrollbar) {
   return GetScrollbarPainterValues(
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc
index d12f85cf..7d428e3 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc
@@ -222,10 +222,12 @@
 
 MediaStreamAudioProcessor::MediaStreamAudioProcessor(
     DeliverProcessedAudioCallback deliver_processed_audio_callback,
+    LogCallback log_callback,
     const AudioProcessingProperties& properties,
     bool use_capture_multi_channel_processing,
     scoped_refptr<WebRtcAudioDeviceImpl> playout_data_source)
     : deliver_processed_audio_callback_(deliver_processed_audio_callback),
+      log_callback_(log_callback),
       render_delay_(base::TimeDelta()),
       audio_delay_stats_reporter_(kBuffersPerSecond),
       playout_data_source_(std::move(playout_data_source)),
@@ -235,12 +237,14 @@
       stopped_(false),
       use_capture_multi_channel_processing_(
           use_capture_multi_channel_processing) {
+  DCHECK(deliver_processed_audio_callback_);
+  DCHECK(log_callback_);
   DCHECK(main_thread_runner_);
   DETACH_FROM_THREAD(capture_thread_checker_);
   DETACH_FROM_THREAD(render_thread_checker_);
-  SendLogMessage(
-      String::Format("%s({use_capture_multi_channel_processing=%s})", __func__,
-                     use_capture_multi_channel_processing ? "true" : "false"));
+  SendLogMessage(base::StringPrintf(
+      "%s({use_capture_multi_channel_processing=%s})", __func__,
+      use_capture_multi_channel_processing ? "true" : "false"));
 
   InitializeAudioProcessingModule(properties);
 }
@@ -346,7 +350,7 @@
   DCHECK(base::FeatureList::IsEnabled(
       features::kMinimizeAudioProcessingForUnusedOutput));
   SendLogMessage(
-      String::Format("%s({muted=%s})", __func__, muted ? "true" : "false"));
+      base::StringPrintf("%s({muted=%s})", __func__, muted ? "true" : "false"));
   if (audio_processing_) {
     audio_processing_->set_output_will_be_muted(muted);
   }
@@ -482,7 +486,7 @@
     const AudioProcessingProperties& properties) {
   DCHECK(main_thread_runner_->BelongsToCurrentThread());
   DCHECK(!audio_processing_);
-  SendLogMessage(String::Format("%s()", __func__));
+  SendLogMessage(base::StringPrintf("%s()", __func__));
 
   // Note: The audio mirroring constraint (i.e., swap left and right channels)
   // is handled within this MediaStreamAudioProcessor and does not, by itself,
@@ -526,8 +530,9 @@
     const media::AudioParameters& input_format) {
   DCHECK(main_thread_runner_->BelongsToCurrentThread());
   DCHECK(input_format.IsValid());
-  SendLogMessage(String::Format("%s({input_format=[%s]})", __func__,
-                                input_format.AsHumanReadableString().c_str()));
+  SendLogMessage(
+      base::StringPrintf("%s({input_format=[%s]})", __func__,
+                         input_format.AsHumanReadableString().c_str()));
 
   input_format_ = input_format;
 
@@ -610,11 +615,11 @@
     output_format_.set_channels_for_discrete(input_format.channels());
   }
   SendLogMessage(
-      String::Format("%s => (output_format=[%s])", __func__,
-                     output_format_.AsHumanReadableString().c_str()));
-  SendLogMessage(
-      String::Format("%s => (FIFO: processing_frames=%d, output_channels=%d)",
-                     __func__, processing_frames, fifo_output_channels));
+      base::StringPrintf("%s => (output_format=[%s])", __func__,
+                         output_format_.AsHumanReadableString().c_str()));
+  SendLogMessage(base::StringPrintf(
+      "%s => (FIFO: processing_frames=%d, output_channels=%d)", __func__,
+      processing_frames, fifo_output_channels));
 
   capture_fifo_ = std::make_unique<MediaStreamAudioFifo>(
       input_format.channels(), fifo_output_channels,
@@ -743,11 +748,10 @@
   DCHECK(main_thread_runner_->BelongsToCurrentThread());
 }
 
-void MediaStreamAudioProcessor::SendLogMessage(const WTF::String& message) {
-  WebRtcLogMessage(String::Format("MSAP::%s [this=0x%" PRIXPTR "]",
-                                  message.Utf8().c_str(),
-                                  reinterpret_cast<uintptr_t>(this))
-                       .Utf8());
+void MediaStreamAudioProcessor::SendLogMessage(const std::string& message) {
+  log_callback_.Run(base::StringPrintf("MSAP::%s [this=0x%" PRIXPTR "]",
+                                       message.c_str(),
+                                       reinterpret_cast<uintptr_t>(this)));
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.h b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.h
index 6301273..1234555 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.h
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.h
@@ -56,16 +56,24 @@
                                    base::TimeTicks audio_capture_time,
                                    absl::optional<double> new_volume)>;
 
+  using LogCallback = base::RepeatingCallback<void(const std::string&)>;
+
   // |deliver_processed_audio_callback| is used to deliver frames of processed
   // capture audio, from ProcessCapturedAudio(), and has to be valid until
-  // Stop() is called. |playout_data_source| is used to register this class as a
-  // sink to the WebRtc playout data for processing AEC. If clients do not
-  // enable AEC, |playout_data_source| won't be used.
+  // Stop() is called. |log_callback| is used for logging messages.
+  // |playout_data_source| is used to register this class as a sink to the
+  // WebRtc playout data for processing AEC. If clients do not enable AEC,
+  // |playout_data_source| won't be used.
+
+  // |playout_data_source| is used to register this class as a sink to the
+  // WebRtc playout data for processing AEC. If clients do not enable AEC,
+  // |playout_data_source| won't be used.
   //
   // Threading note: The constructor assumes it is being run on the main render
   // thread.
   MediaStreamAudioProcessor(
       DeliverProcessedAudioCallback deliver_processed_audio_callback,
+      LogCallback log_callback,
       const AudioProcessingProperties& properties,
       bool use_capture_multi_channel_processing,
       scoped_refptr<WebRtcAudioDeviceImpl> playout_data_source);
@@ -195,11 +203,14 @@
   // Update AEC stats. Called on the main render thread.
   void UpdateAecStats();
 
-  void SendLogMessage(const WTF::String& message);
+  void SendLogMessage(const std::string& message);
 
   // Consumer of processed capture audio in ProcessCapturedAudio().
   DeliverProcessedAudioCallback deliver_processed_audio_callback_;
 
+  // Used by SendLogMessage.
+  LogCallback log_callback_;
+
   // Cached value for the render delay latency. This member is accessed by
   // both the capture audio thread and the render audio thread.
   std::atomic<base::TimeDelta> render_delay_;
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor_test.cc b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor_test.cc
index d6f1aff..4cdd2ce 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor_test.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor_test.cc
@@ -69,6 +69,11 @@
   DCHECK(data_file_size64 > length);
 }
 
+MediaStreamAudioProcessor::LogCallback LogCallbackForTest() {
+  return base::BindRepeating(
+      [](const std::string& message) { VLOG(1) << message; });
+}
+
 }  // namespace
 
 class MediaStreamAudioProcessorTest : public ::testing::Test {
@@ -205,8 +210,8 @@
   blink::AudioProcessingProperties properties;
   scoped_refptr<MediaStreamAudioProcessor> audio_processor(
       new rtc::RefCountedObject<MediaStreamAudioProcessor>(
-          mock_capture_callback_.Get(), properties, use_multichannel_processing,
-          webrtc_audio_device));
+          mock_capture_callback_.Get(), LogCallbackForTest(), properties,
+          use_multichannel_processing, webrtc_audio_device));
   EXPECT_TRUE(audio_processor->has_audio_processing());
   audio_processor->OnCaptureFormatChanged(params_);
   VerifyDefaultComponents(*audio_processor);
@@ -231,7 +236,7 @@
       new rtc::RefCountedObject<WebRtcAudioDeviceImpl>());
   scoped_refptr<MediaStreamAudioProcessor> audio_processor(
       new rtc::RefCountedObject<MediaStreamAudioProcessor>(
-          mock_capture_callback_.Get(), properties,
+          mock_capture_callback_.Get(), LogCallbackForTest(), properties,
           /*use_capture_multi_channel_processing=*/true, webrtc_audio_device));
   EXPECT_FALSE(audio_processor->has_audio_processing());
   audio_processor->OnCaptureFormatChanged(params_);
@@ -258,8 +263,8 @@
   blink::AudioProcessingProperties properties;
   scoped_refptr<MediaStreamAudioProcessor> audio_processor(
       new rtc::RefCountedObject<MediaStreamAudioProcessor>(
-          mock_capture_callback_.Get(), properties, use_multichannel_processing,
-          webrtc_audio_device));
+          mock_capture_callback_.Get(), LogCallbackForTest(), properties,
+          use_multichannel_processing, webrtc_audio_device));
   EXPECT_TRUE(audio_processor->has_audio_processing());
 
   static const int kSupportedSampleRates[] = {
@@ -313,7 +318,7 @@
   {
     scoped_refptr<MediaStreamAudioProcessor> audio_processor(
         new rtc::RefCountedObject<MediaStreamAudioProcessor>(
-            mock_capture_callback_.Get(), properties,
+            mock_capture_callback_.Get(), LogCallbackForTest(), properties,
             /*use_capture_multi_channel_processing=*/true,
             webrtc_audio_device));
 
@@ -378,7 +383,7 @@
     properties.goog_audio_mirroring = true;
     scoped_refptr<MediaStreamAudioProcessor> audio_processor(
         new rtc::RefCountedObject<MediaStreamAudioProcessor>(
-            mock_capture_callback_.Get(), properties,
+            mock_capture_callback_.Get(), LogCallbackForTest(), properties,
             use_multichannel_processing, webrtc_audio_device));
     EXPECT_EQ(audio_processor->has_audio_processing(), use_apm);
     audio_processor->OnCaptureFormatChanged(source_params);
@@ -449,8 +454,8 @@
   blink::AudioProcessingProperties properties;
   scoped_refptr<MediaStreamAudioProcessor> audio_processor(
       new rtc::RefCountedObject<MediaStreamAudioProcessor>(
-          mock_capture_callback_.Get(), properties, use_multichannel_processing,
-          webrtc_audio_device));
+          mock_capture_callback_.Get(), LogCallbackForTest(), properties,
+          use_multichannel_processing, webrtc_audio_device));
   EXPECT_TRUE(audio_processor->has_audio_processing());
 
   media::AudioParameters params(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
@@ -479,7 +484,7 @@
       new rtc::RefCountedObject<WebRtcAudioDeviceImpl>());
   scoped_refptr<MediaStreamAudioProcessor> audio_processor(
       new rtc::RefCountedObject<MediaStreamAudioProcessor>(
-          mock_capture_callback_.Get(), properties,
+          mock_capture_callback_.Get(), LogCallbackForTest(), properties,
           /*use_capture_multi_channel_processing=*/true, webrtc_audio_device));
   EXPECT_TRUE(audio_processor->has_audio_processing());
 
@@ -508,7 +513,7 @@
       new rtc::RefCountedObject<WebRtcAudioDeviceImpl>());
   scoped_refptr<MediaStreamAudioProcessor> audio_processor(
       new rtc::RefCountedObject<MediaStreamAudioProcessor>(
-          mock_capture_callback.Get(), properties,
+          mock_capture_callback.Get(), LogCallbackForTest(), properties,
           /*use_capture_multi_channel_processing=*/true, webrtc_audio_device));
   ASSERT_TRUE(audio_processor->has_audio_processing());
 
@@ -565,7 +570,7 @@
       new rtc::RefCountedObject<WebRtcAudioDeviceImpl>());
   scoped_refptr<MediaStreamAudioProcessor> audio_processor(
       new rtc::RefCountedObject<MediaStreamAudioProcessor>(
-          mock_capture_callback.Get(), properties,
+          mock_capture_callback.Get(), LogCallbackForTest(), properties,
           /*use_capture_multi_channel_processing=*/true, webrtc_audio_device));
   ASSERT_TRUE(audio_processor->has_audio_processing());
 
@@ -612,7 +617,7 @@
       new rtc::RefCountedObject<WebRtcAudioDeviceImpl>());
   scoped_refptr<MediaStreamAudioProcessor> audio_processor(
       new rtc::RefCountedObject<MediaStreamAudioProcessor>(
-          mock_capture_callback.Get(), properties,
+          mock_capture_callback.Get(), LogCallbackForTest(), properties,
           /*use_capture_multi_channel_processing=*/true, webrtc_audio_device));
   ASSERT_FALSE(audio_processor->has_audio_processing());
 
@@ -656,7 +661,7 @@
       new rtc::RefCountedObject<WebRtcAudioDeviceImpl>());
   scoped_refptr<MediaStreamAudioProcessor> audio_processor(
       new rtc::RefCountedObject<MediaStreamAudioProcessor>(
-          mock_capture_callback.Get(), properties,
+          mock_capture_callback.Get(), LogCallbackForTest(), properties,
           /*use_capture_multi_channel_processing=*/true, webrtc_audio_device));
   ASSERT_FALSE(audio_processor->has_audio_processing());
 
diff --git a/third_party/blink/renderer/modules/mediastream/processed_local_audio_source.cc b/third_party/blink/renderer/modules/mediastream/processed_local_audio_source.cc
index b8b6ccb..81d6e34 100644
--- a/third_party/blink/renderer/modules/mediastream/processed_local_audio_source.cc
+++ b/third_party/blink/renderer/modules/mediastream/processed_local_audio_source.cc
@@ -380,9 +380,13 @@
       ConvertToBaseRepeatingCallback(CrossThreadBindRepeating(
           &ProcessedLocalAudioSource::DeliverProcessedAudio,
           CrossThreadUnretained(this)));
+  MediaStreamAudioProcessor::LogCallback log_callback =
+      ConvertToBaseRepeatingCallback(
+          CrossThreadBindRepeating(&WebRtcLogMessage));
   audio_processor_ = new rtc::RefCountedObject<MediaStreamAudioProcessor>(
-      std::move(processing_callback), audio_processing_properties_,
-      use_multichannel_processing, rtc_audio_device);
+      std::move(processing_callback), std::move(log_callback),
+      audio_processing_properties_, use_multichannel_processing,
+      rtc_audio_device);
   params.set_frames_per_buffer(GetBufferSize(device().input.sample_rate()));
   audio_processor_->OnCaptureFormatChanged(params);
   SetFormat(audio_processor_->OutputFormat());
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 4cae866..b2bd2b6b 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -404,7 +404,7 @@
     },
     {
       name: "COLRV1Fonts",
-      status: "experimental",
+      status: "stable",
     },
     {
       name: "CompositeBGColorAnimation",
diff --git a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
index 59574aa2..e27bf06 100755
--- a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
+++ b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
@@ -1410,6 +1410,16 @@
     },
     {
         'paths': [
+            'third_party/blink/renderer/core/scroll/mac_scrollbar_animator_impl.h',
+            'third_party/blink/renderer/core/scroll/mac_scrollbar_animator_impl.mm',
+        ],
+        'allowed': [
+            'ui::ScrollbarAnimationTimerMac',
+            'ui::OverlayScrollbarAnimatorMac',
+        ],
+    },
+    {
+        'paths': [
             'third_party/blink/renderer/modules/crypto/',
         ],
         'allowed': ['crypto::.+'],
diff --git a/third_party/blink/web_tests/LeakExpectations b/third_party/blink/web_tests/LeakExpectations
index 2a9d6da..657079a 100644
--- a/third_party/blink/web_tests/LeakExpectations
+++ b/third_party/blink/web_tests/LeakExpectations
@@ -161,6 +161,9 @@
 crbug.com/1219915 [ Linux ] touchadjustment/focusout-on-touch.html [ Failure ]
 crbug.com/1219915 [ Linux ] touchadjustment/touch-links-longpress.html [ Failure ]
 
+# Sheriff 2021-11-18
+crbug.com/1271392 [ Linux ] external/wpt/service-workers/service-worker/navigation-headers.https.html [ Failure Pass ]
+
 ###########################################################################
 # WARNING: Memory leaks must be fixed asap. Sheriff is expected to revert #
 # culprit CLs instead of suppressing the leaks. If you have any question, #
diff --git a/third_party/blink/web_tests/MSANExpectations b/third_party/blink/web_tests/MSANExpectations
index bdd9c1e..ee94cf4c 100644
--- a/third_party/blink/web_tests/MSANExpectations
+++ b/third_party/blink/web_tests/MSANExpectations
@@ -116,7 +116,6 @@
 crbug.com/856601 [ Linux ] external/wpt/IndexedDB/nested-cloning-large.any.sharedworker.html [ Pass Timeout ]
 crbug.com/856601 [ Linux ] external/wpt/IndexedDB/nested-cloning-large.any.worker.html [ Pass Timeout ]
 crbug.com/856601 [ Linux ] external/wpt/IndexedDB/nested-cloning-small.any.serviceworker.html [ Pass Timeout ]
-crbug.com/856601 [ Linux ] http/tests/devtools/a11y-axe-core/console/console-a11y-test.js [ Pass Timeout ]
 
 # Sheriff 2019-06-28
 crbug.com/856601 [ Linux ] http/tests/devtools/indexeddb/live-update-indexeddb-content.js [ Pass Timeout ]
@@ -171,9 +170,6 @@
 crbug.com/1179829 [ Linux ] http/tests/devtools/console/console-viewport-stick-to-bottom-onload.js [ Pass Timeout ]
 crbug.com/1179829 [ Linux ] http/tests/devtools/isolated-code-cache/stale-revalidation-test.js [ Pass Timeout ]
 
-# Sheriff 2021-03-23
-crbug.com/1179829 [ Linux ] http/tests/devtools/console/viewport-testing/console-stick-to-bottom-with-large-prompt.js [ Pass Timeout ]
-
 # Sheriff 2021-04-22
 crbug.com/1201807 [ Linux ] virtual/feature-policy-permissions/external/wpt/mediacapture-streams/MediaStreamTrack-MediaElement-disabled-video-is-black.https.html [ Pass Timeout ]
 
diff --git a/third_party/blink/web_tests/NeverFixTests b/third_party/blink/web_tests/NeverFixTests
index 8b508c1..34a26f0 100644
--- a/third_party/blink/web_tests/NeverFixTests
+++ b/third_party/blink/web_tests/NeverFixTests
@@ -1687,17 +1687,6 @@
 virtual/not-site-per-process/external/wpt/app-history/navigate-event/cross-window/submit-samedocument-crossorigin-sameorigindomain.sub.html [ Pass ]
 virtual/wasm-module-sharing-enabled-not-site-per-process/external/wpt/wasm/serialization/module/window-similar-but-cross-origin-success.sub.html [ Pass ]
 virtual/wasm-module-sharing-enabled-not-site-per-process/external/wpt/wasm/serialization/module/window-domain-success.sub.html [ Pass ]
-# -------------------- origin-vs-url in console messages -----------------
-# Process hosting https://foo.example.com should not get any data from
-# https://bar.other-site-example.com.  This includes things like cookies
-# and localStorage, but also full URLs (which may include some "secrets"
-# and [unlike origins] are not exposed to other origins through web APIs).
-# To properly enforce security boundary described above, console messages
-# cannot include full cross-origin URLs (i.e. the messages need to strip
-# cross-origin URLs down to just the origin - for example,
-# https://foo.example.com/path?token=secret needs to be replaced with just
-# https://foo.example.com).
-crbug.com/669083 http/tests/security/frameNavigation/xss-DENIED-top-navigation-without-user-gesture.html [ Skip ]
 # ----------------------- origin-keyed agent clusters --------------------
 # Origin-keyed agent clusters web platform tests are for the feature at
 # https://html.spec.whatwg.org/#origin-keyed-agent-clusters. It doesn't make
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 42eb496..6ebe2d0a 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -536,6 +536,7 @@
 
 crbug.com/898394 virtual/android/url-bar/bottom-and-top-fixed-sticks-to-top.html [ Failure Timeout ]
 
+crbug.com/1024151 http/tests/devtools/tracing/timeline-style/timeline-style-recalc-all-invalidator-types.js [ Failure Pass ]
 
 # Subpixel rounding differences that are incorrect.
 crbug.com/997202 compositing/overflow/scaled-overflow.html [ Failure ]
@@ -3000,6 +3001,7 @@
 crbug.com/626703 virtual/plz-dedicated-worker/external/wpt/service-workers/service-worker/navigation-timing-extended.https.html [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 [ Mac10.15 ] virtual/without-coep-for-shared-worker/external/wpt/html/cross-origin-embedder-policy/reporting-subresource-corp.https.html [ Skip Timeout ]
 crbug.com/626703 [ Mac10.15 ] external/wpt/media-capabilities/encodingInfo.any.worker.html [ Crash ]
 crbug.com/626703 [ Mac11 ] virtual/css-calc-infinity-and-nan-disabled/external/wpt/css/css-transforms/crashtests/w-negative-002.html [ Timeout ]
 crbug.com/626703 [ Mac11 ] virtual/dialogfocus-old-behavior/external/wpt/html/semantics/interactive-elements/the-dialog-element/backdrop-does-not-inherit.html [ Timeout ]
@@ -3446,8 +3448,6 @@
 crbug.com/626703 [ Mac11 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDataChannel-iceRestart.html [ Skip Timeout ]
 crbug.com/626703 [ Mac10.14 ] external/wpt/webrtc/RTCDtlsTransport-state.html [ Timeout ]
 crbug.com/626703 [ Mac11 ] external/wpt/webrtc/RTCDtlsTransport-state.html [ Timeout ]
-crbug.com/626703 [ Mac10.14 ] external/wpt/mediacapture-record/passthrough/MediaRecorder-passthrough.https.html [ Timeout ]
-crbug.com/626703 [ Mac11-arm64 ] external/wpt/mediacapture-record/passthrough/MediaRecorder-passthrough.https.html [ Failure Pass Timeout ]
 crbug.com/626703 [ Mac10.14 ] external/wpt/webrtc-encoded-transform/RTCPeerConnection-insertable-streams-simulcast.https.html [ Timeout ]
 crbug.com/626703 [ Mac11 ] external/wpt/webrtc-encoded-transform/RTCPeerConnection-insertable-streams-simulcast.https.html [ Timeout ]
 crbug.com/626703 [ Mac10.14 ] external/wpt/webrtc/RTCIceTransport.html [ Timeout ]
@@ -4053,6 +4053,7 @@
 crbug.com/614667 external/wpt/css/css-break/grid/grid-item-fragmentation-029.html [ Failure ]
 crbug.com/614667 external/wpt/css/css-break/grid/grid-item-fragmentation-030.html [ Failure ]
 crbug.com/614667 external/wpt/css/css-break/grid/grid-item-fragmentation-031.html [ Failure ]
+crbug.com/1066629 external/wpt/css/css-break/hit-test-transformed.html [ Failure ]
 crbug.com/829028 external/wpt/css/css-break/monolithic-with-overflow-lr.html [ Failure ]
 crbug.com/829028 external/wpt/css/css-break/monolithic-with-overflow-rl.html [ Failure ]
 crbug.com/829028 external/wpt/css/css-break/monolithic-with-overflow.html [ Failure ]
@@ -6806,7 +6807,6 @@
 crbug.com/1230534 external/wpt/webrtc/simulcast/getStats.https.html [ Failure Pass Skip Timeout ]
 
 # Sheriff 2021-08-10
-crbug.com/1233840 external/wpt/html/cross-origin-opener-policy/historical/coep-navigate-popup-unsafe-inherit.https.html [ Pass Skip Timeout ]
 crbug.com/1230534 external/wpt/webrtc/simulcast/basic.https.html [ Pass Skip Timeout ]
 crbug.com/1230534 external/wpt/webrtc/simulcast/setParameters-active.https.html [ Failure Pass Skip Timeout ]
 
@@ -7164,9 +7164,7 @@
 crbug.com/1222126 http/tests/devtools/domdebugger/domdebugger-getEventListeners.js [ Skip ]
 crbug.com/1222126 http/tests/devtools/cache-storage/cache-live-update-list.js [ Skip ]
 crbug.com/1222126 http/tests/devtools/runtime/evaluate-timeout.js [ Skip ]
-crbug.com/1222126 http/tests/devtools/resource-har-headers.js [ Skip ]
 crbug.com/1222126 http/tests/devtools/resource-har-conversion.js [ Skip ]
-crbug.com/1222126 http/tests/devtools/network/network-choose-preview-view.js [ Skip ]
 crbug.com/1222126 http/tests/devtools/network/json-preview.js [ Skip ]
 crbug.com/1215072 http/tests/devtools/elements/copy-styles.js [ Skip ]
 
@@ -7300,3 +7298,5 @@
 # Security OWP fixit week
 crbug.com/1270355 external/wpt/html/browsers/browsing-the-web/navigating-across-documents/009.html [ Timeout ]
 
+# Sheriff 2021-11-18
+crbug.com/1180993 external/wpt/mediacapture-record/passthrough/MediaRecorder-passthrough.https.html [ Failure Pass Timeout ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index d115ce0..a99a9ea 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -961,7 +961,7 @@
   },
   {
     "prefix": "partitioned-cookies",
-    "bases": ["external/wpt/cookies/partitioned-cookies/"],
+    "bases": ["external/wpt/cookies/partitioned-cookies/", "http/tests/inspector-protocol/network/"],
     "args": ["--enable-features=PartitionedCookies"]
   },
   {
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 5d84259..cf43388 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
@@ -214623,6 +214623,19 @@
         {}
        ]
       ],
+      "transform-dynamic-change.html": [
+       "964a7d31f69397ecc5a06cac4cf0e4efd890ebeb",
+       [
+        null,
+        [
+         [
+          "/svg/text/reftests/transform-dynamic-change-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
       "tspan-opacity-mixed-direction.svg": [
        "fa153250e79ab6a60bf167c659b4e2d777609a30",
        [
@@ -303993,6 +304006,10 @@
        "0e2008c328aeb06071efa6086786a273c6cafe7d",
        []
       ],
+      "transform-dynamic-change-ref.html": [
+       "c7e238d2bf957491c5a1f041db0b0c19bd8269bd",
+       []
+      ],
       "tspan-opacity-mixed-direction-ref.svg": [
        "f07911627fed87722b41ddb7a006eb1b7a65a63a",
        []
@@ -477950,6 +477967,13 @@
       }
      ]
     ],
+    "prompt-in-detached-iframe.html": [
+     "501471755ab8130e0799a9c2c82960eb096ae5a3",
+     [
+      null,
+      {}
+     ]
+    ],
     "watch-availability-initial-callback.html": [
      "352321b06ae66c5a3e31c1d3d7cf40a5e3b4c460",
      [
@@ -500049,7 +500073,7 @@
      ]
     ],
     "request-video-frame-callback-dom.html": [
-     "0277d7297883c4cd4f472b08c4399806280fe8eb",
+     "c1804b4edd0c986aa161dfc13179983c04ace404",
      [
       null,
       {}
@@ -500063,14 +500087,14 @@
      ]
     ],
     "request-video-frame-callback-parallel.html": [
-     "14c5de1adf55ba0016ab9985776c568e49f5956d",
+     "682fd0ac8f6a22006e015c2f9b621da84fe53e04",
      [
       null,
       {}
      ]
     ],
     "request-video-frame-callback-repeating.html": [
-     "e637a0872cb0d0c151e0a80a6a569449fd222a1b",
+     "38e4abafd4afe963232ec7427afa5cbd57bc734d",
      [
       null,
       {}
@@ -500084,7 +500108,7 @@
      ]
     ],
     "request-video-frame-callback.html": [
-     "a4404143fa408ebbb8f49cc3180e065d200ffa4a",
+     "256216e8fc098482baf267d95fc5f2ceca430c53",
      [
       null,
       {}
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/hit-test-transformed.html b/third_party/blink/web_tests/external/wpt/css/css-break/hit-test-transformed.html
new file mode 100644
index 0000000..99365c9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/hit-test-transformed.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<link rel="help" href="https://www.w3.org/TR/css-break-3/#transforms">
+<style>
+  body { margin: 0; }
+  *:hover { background: lime !important; }
+</style>
+<div id="multicol" style="columns:4; column-gap:0; column-fill:auto; width:400px; height:100px; background: yellow">
+  <div id="before" style="height: 50px; background: gray"></div>
+  <div id="transform" style="transform: translateY(20px)">
+    <div id="target1" style="height: 100px; background: green"></div>
+    <div id="target2" style="height: 200px; background: blue"></div>
+  </div>
+  <div id="after" style="height: 50px; background: gray"></div>
+</div>
+<div id="log" style="margin-top:100px;"></div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+  test(()=> { assert_equals(document.elementFromPoint(50, 40), before); }, "before");
+  test(()=> { assert_equals(document.elementFromPoint(50, 60), multicol); }, "between before and transform");
+  test(()=> { assert_equals(document.elementFromPoint(50, 80), target1); }, "target1");
+  test(()=> { assert_equals(document.elementFromPoint(50, 110), target1); }, "target1 overflow");
+  test(()=> { assert_equals(document.elementFromPoint(150, 10), multicol); }, "top gap in column 2");
+  test(()=> { assert_equals(document.elementFromPoint(150, 30), target1); }, "target1 in column 2 top");
+  test(()=> { assert_equals(document.elementFromPoint(150, 60), target1); }, "target1 in column 2 bottom");
+  test(()=> { assert_equals(document.elementFromPoint(150, 80), target2); }, "target2 in column 2");
+  test(()=> { assert_equals(document.elementFromPoint(150, 110), target2); }, "target2 in column 2 overflow");
+  test(()=> { assert_equals(document.elementFromPoint(250, 10), multicol); }, "top gap in column 3");
+  test(()=> { assert_equals(document.elementFromPoint(250, 30), target2); }, "target2 in column 3 top");
+  test(()=> { assert_equals(document.elementFromPoint(250, 110), target2); }, "target2 in column 3 bottom");
+  test(()=> { assert_equals(document.elementFromPoint(350, 10), multicol); }, "top gap in column 4");
+  test(()=> { assert_equals(document.elementFromPoint(350, 30), target2); }, "target2 in column 4 top");
+  test(()=> { assert_equals(document.elementFromPoint(350, 60), target2); }, "target2 in column 4 bottom");
+  test(()=> { assert_equals(document.elementFromPoint(350, 80), after); }, "after");
+  test(()=> { assert_equals(document.elementFromPoint(350, 110), document.documentElement); }, "below");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/navigating-across-documents/cross-origin-top-navigation-with-user-activation-in-parent.window.js b/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/navigating-across-documents/cross-origin-top-navigation-with-user-activation-in-parent.window.js
new file mode 100644
index 0000000..8dbe667
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/navigating-across-documents/cross-origin-top-navigation-with-user-activation-in-parent.window.js
@@ -0,0 +1,11 @@
+// META: script=/resources/testharness.js
+// META: script=/resources/testharnessreport.js
+
+async_test(t => {
+  addEventListener('message', t.step_func_done(e => {
+    assert_equals(e.data, 'Denied');
+  }));
+  const w = open("resources/page-with-top-navigating-iframe.html?parent_user_gesture=true");
+  t.add_cleanup(() => {w.close()});
+
+}, "Cross-origin top navigation is blocked without user activation, even if the parent has user activation");
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/navigating-across-documents/cross-origin-top-navigation-without-user-activation.window.js b/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/navigating-across-documents/cross-origin-top-navigation-without-user-activation.window.js
new file mode 100644
index 0000000..bd7140f4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/navigating-across-documents/cross-origin-top-navigation-without-user-activation.window.js
@@ -0,0 +1,11 @@
+// META: script=/resources/testharness.js
+// META: script=/resources/testharnessreport.js
+
+async_test(t => {
+  addEventListener('message', t.step_func_done(e => {
+    assert_equals(e.data, 'Denied');
+  }));
+  const w = open("resources/page-with-top-navigating-iframe.html");
+  t.add_cleanup(() => {w.close()});
+
+}, "Cross-origin top navigation is blocked without user activation");
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/navigating-across-documents/resources/page-that-post-message-to-opener.html b/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/navigating-across-documents/resources/page-that-post-message-to-opener.html
new file mode 100644
index 0000000..65a0c82
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/navigating-across-documents/resources/page-that-post-message-to-opener.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Page that postMessage to its opener</title>
+<script>
+  opener.postMessage('Allowed', '*');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/navigating-across-documents/resources/page-with-top-navigating-iframe.html b/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/navigating-across-documents/resources/page-with-top-navigating-iframe.html
new file mode 100644
index 0000000..568e442
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/navigating-across-documents/resources/page-with-top-navigating-iframe.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src=/common/get-host-info.sub.js></script>
+<script src=/resources/testdriver.js></script>
+<script src=/resources/testdriver-vendor.js></script>
+<title>Page that embeds an iframe that navigates its top</title>
+<script>
+function addIframe() {
+  const iframe = document.createElement('iframe');
+  const path = new URL("top-navigating-page.html", window.location).pathname;
+  iframe.src = get_host_info().HTTP_NOTSAMESITE_ORIGIN + path;
+  document.body.appendChild(iframe);
+}
+
+addEventListener('load', () => {
+  const urlParams = new URLSearchParams(location.search);
+  const parentUserGesture = urlParams.get('parent_user_gesture') === 'true';
+  if (parentUserGesture)
+    test_driver.bless("Giving parent frame user activation").then(addIframe);
+  else
+    addIframe();
+});
+</script>
+
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/navigating-across-documents/resources/top-navigating-page.html b/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/navigating-across-documents/resources/top-navigating-page.html
new file mode 100644
index 0000000..557f408
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/navigating-across-documents/resources/top-navigating-page.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Page that navigates its top</title>
+<script src=/common/get-host-info.sub.js></script>
+<script>
+
+let path = new URL("page-that-post-message-to-opener.html", window.location).pathname;
+let fullUrl = get_host_info().HTTP_NOTSAMESITE_ORIGIN + path;
+try {
+  top.location = fullUrl;
+} catch {
+  top.opener.postMessage('Denied', '*');
+}
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/historical/coep-navigate-popup-unsafe-inherit.https.html b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/historical/coep-navigate-popup-unsafe-inherit.https.html
index 21feef73..8756cb6 100644
--- a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/historical/coep-navigate-popup-unsafe-inherit.https.html
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/historical/coep-navigate-popup-unsafe-inherit.https.html
@@ -1,10 +1,15 @@
 <!doctype html>
 <title>Historical: Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy: a navigating popup</title>
 <meta name=timeout content=long>
+<meta name=variant content=?0-0>
+<meta name=variant content=?1-1>
+<meta name=variant content=?2-2>
+<meta name=variant content=?3-3>
 <script src=/resources/testharness.js></script>
 <script src=/resources/testharnessreport.js></script>
 <script src="/common/get-host-info.sub.js"></script>
 <script src="../resources/common.js"></script>
+<script src="/common/subset-tests.js"></script>
 <script>
 [
   {
@@ -19,8 +24,13 @@
     "coep": "",
     "opener": false
   }
-].forEach(variant => {
-  ["same-origin", "same-site"].forEach(site => {
+].forEach((variant, i) => {
+  ["same-origin", "same-site"].forEach((site, j) => {
+
+    // Only run specified variants
+    if (!shouldRunSubTest(2*i + j)) {
+      return;
+    }
     const title = `Popup navigating to ${site} with ${variant.title}`;
     const channel = title.replace(/ /g,"-");
     const navigateHost = site === "same-origin" ? SAME_ORIGIN : SAME_SITE;
diff --git a/third_party/blink/web_tests/external/wpt/svg/text/reftests/transform-dynamic-change-ref.html b/third_party/blink/web_tests/external/wpt/svg/text/reftests/transform-dynamic-change-ref.html
new file mode 100644
index 0000000..c7e238d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/svg/text/reftests/transform-dynamic-change-ref.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<body>
+<div style="position:absolute; transform-origin: 0px 0px; transform: matrix(0.9, 0, 0, 0.9, -210, -777);">
+  <svg width="2384" height="1684" style="position:absolute; left:0; top:0;">
+    <circle cx="475" cy="975" r="40" stroke="black" stroke-width="2" fill="none" />
+    <text fill="red" style="font-size: 40px;" transform="matrix(1, 0, 0, 1, 468, 988)">A</text>
+  </svg>
+</div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/svg/text/reftests/transform-dynamic-change.html b/third_party/blink/web_tests/external/wpt/svg/text/reftests/transform-dynamic-change.html
new file mode 100644
index 0000000..964a7d31
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/svg/text/reftests/transform-dynamic-change.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1270713">
+<link rel="match" href="transform-dynamic-change-ref.html">
+<body>
+
+<div id="moveme" style="position:absolute; transform-origin: 0px 0px; transform: matrix(0.3, 0, 0, 0.3, 136, 13);">
+  <svg width="2384" height="1684" style="position:absolute; left:0; top:0;">
+    <circle cx="475" cy="975" r="40" stroke="black" stroke-width="2" fill="none" />
+    <text id="txt" style="font-size: 40px;" transform="matrix(1, 0, 0, 1, 468, 988)">A</text>
+  </svg>
+</div>
+
+<script>
+requestAnimationFrame(() => {
+  requestAnimationFrame(() => {
+    document.getElementById('moveme').style.transform = 'matrix(0.9, 0, 0, 0.9, -210, -777)';
+    document.getElementById('txt').style.fill = 'red';
+    document.documentElement.classList.remove('reftest-wait');
+  });
+});
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/video-rvfc/request-video-frame-callback-dom.html b/third_party/blink/web_tests/external/wpt/video-rvfc/request-video-frame-callback-dom.html
index 0277d72..c1804b4 100644
--- a/third_party/blink/web_tests/external/wpt/video-rvfc/request-video-frame-callback-dom.html
+++ b/third_party/blink/web_tests/external/wpt/video-rvfc/request-video-frame-callback-dom.html
@@ -13,17 +13,24 @@
   width: 320,
 }
 
-async_test(function(t) {
+promise_test(async function(t) {
+    let done;
+    const promise = new Promise(resolve => done = resolve);
+
     let video = document.createElement('video');
 
-    video.requestVideoFrameCallback(t.step_func_done());
+    video.requestVideoFrameCallback(done);
     video.src = testVideo.url;
-    video.play();
+    await video.play();
 
+    return promise;
 }, 'Test a video outside of the DOM can still use video.rVFC.');
 
 function rvfcStyleTest(applyStyle, description) {
-    async_test(function(t) {
+    promise_test(async function(t) {
+      let done;
+      const promise = new Promise(resolve => done = resolve);
+
       let video = document.createElement('video');
       document.body.appendChild(video);
       applyStyle(video);
@@ -31,12 +38,14 @@
       video.requestVideoFrameCallback(
         t.step_func( _ => {
           // Make sure we can receive more than one callback.
-          video.requestVideoFrameCallback(t.step_func_done());
+          video.requestVideoFrameCallback(done);
         })
       );
 
       video.src = testVideo.url;
-      video.play();
+      await video.play();
+
+      return promise;
     }, description);
 }
 
diff --git a/third_party/blink/web_tests/external/wpt/video-rvfc/request-video-frame-callback-parallel.html b/third_party/blink/web_tests/external/wpt/video-rvfc/request-video-frame-callback-parallel.html
index 14c5de1..682fd0a 100644
--- a/third_party/blink/web_tests/external/wpt/video-rvfc/request-video-frame-callback-parallel.html
+++ b/third_party/blink/web_tests/external/wpt/video-rvfc/request-video-frame-callback-parallel.html
@@ -7,7 +7,10 @@
 <script src="/common/media.js"></script>
 <script>
 
-async_test(function(t) {
+promise_test(async function(t) {
+    let done;
+    const promise = new Promise(resolve => done = resolve);
+
     let video = document.createElement('video');
     document.body.appendChild(video);
 
@@ -19,17 +22,22 @@
       firstMetadata = metadata;
     }));
 
-    video.requestVideoFrameCallback(t.step_func_done((time, metadata) => {
+    video.requestVideoFrameCallback(t.step_func((time, metadata) => {
       assert_equals(firstTime, time);
       assert_object_equals(firstMetadata, metadata);
+      done();
     }));
 
     video.src = getVideoURI('/media/movie_5');
     video.play();
 
+    return promise;
 }, 'Test callbacks get the same information.');
 
-async_test(function(t) {
+promise_test(async function(t) {
+    let done;
+    const promise = new Promise(resolve => done = resolve);
+
     let video = document.createElement('video');
     document.body.appendChild(video);
 
@@ -46,12 +54,12 @@
     );
 
     // NOTE: This callback should be executed last.
-    video.requestVideoFrameCallback(
-      t.step_func_done()
-    );
+    video.requestVideoFrameCallback(done);
 
     video.src = getVideoURI('/media/movie_5');
     video.play();
+
+    return promise;
 }, 'Test we can cancel callbacks from callbacks.');
 </script>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/video-rvfc/request-video-frame-callback-repeating.html b/third_party/blink/web_tests/external/wpt/video-rvfc/request-video-frame-callback-repeating.html
index e637a08..38e4aba 100644
--- a/third_party/blink/web_tests/external/wpt/video-rvfc/request-video-frame-callback-repeating.html
+++ b/third_party/blink/web_tests/external/wpt/video-rvfc/request-video-frame-callback-repeating.html
@@ -7,7 +7,10 @@
 <script src="/common/media.js"></script>
 <script>
 
-async_test(function(t) {
+promise_test(async function(t) {
+    let done;
+    const promise = new Promise(resolve => done = resolve);
+
     let video = document.createElement('video');
     document.body.appendChild(video);
 
@@ -24,18 +27,23 @@
 
       // Queue up a second callback, and make sure it's called at the same time
       // as the one we just queued up.
-      video.requestVideoFrameCallback(t.step_func_done((time) => {
+      video.requestVideoFrameCallback(t.step_func((time) => {
         assert_equals(time, secondTime, "Callbacks queued together should be called at the same time");
+        done();
       }))
 
     }));
 
     video.src = getVideoURI('/media/movie_5');
-    video.play();
+    await video.play();
 
+    return promise;
 }, 'Test new callbacks are only called on the next frame.');
 
-async_test(function(t) {
+promise_test(async function(t) {
+    let done;
+    const promise = new Promise(resolve => done = resolve);
+
     let video = document.createElement('video');
     document.body.appendChild(video);
 
@@ -64,7 +72,7 @@
       lastMetadata = metadata;
 
       if (++currentCallNumber > maxNumberOfCalls) {
-        t.done()
+        done()
       } else {
         video.requestVideoFrameCallback(t.step_func(repeatingCallback));
       }
@@ -73,8 +81,9 @@
     video.requestVideoFrameCallback(t.step_func(repeatingCallback));
 
     video.src = getVideoURI('/media/movie_5');
-    video.play();
+    await video.play();
 
+    return promise;
 }, 'Test chaining calls to video.rVFC, and verify the required parameters.');
 </script>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/video-rvfc/request-video-frame-callback.html b/third_party/blink/web_tests/external/wpt/video-rvfc/request-video-frame-callback.html
index a4404143..256216e 100644
--- a/third_party/blink/web_tests/external/wpt/video-rvfc/request-video-frame-callback.html
+++ b/third_party/blink/web_tests/external/wpt/video-rvfc/request-video-frame-callback.html
@@ -18,26 +18,34 @@
   width: 320,
 }
 
-async_test(function(t) {
+promise_test(async function(t) {
+    let done;
+    const promise = new Promise(resolve => done = resolve);
+
     let video = document.createElement('video');
     document.body.appendChild(video);
 
     let id = video.requestVideoFrameCallback(
-      t.step_func_done((time, metadata) => {
+      t.step_func((time, metadata) => {
         assert_true(time > 0);
         assert_equals(metadata.height, testVideo.height);
         assert_equals(metadata.width, testVideo.width);
+        done();
       })
     );
 
     assert_true(id > 0);
 
     video.src = testVideo.url;
-    video.play();
+    await video.play();
 
+    return promise;
 }, 'Test we can register a video.rVFC callback.');
 
-async_test(function(t) {
+promise_test(async function(t) {
+    let done;
+    const promise = new Promise(resolve => done = resolve);
+
     let video = document.createElement('video');
     document.body.appendChild(video);
 
@@ -45,19 +53,24 @@
       t.step_func(video_now => {
         // Queue a call to window.rAF, and make sure it is executed within the
         // same turn of the event loop (with the same 'time' parameter).
-        window.requestAnimationFrame( t.step_func_done( window_now => {
+        window.requestAnimationFrame( t.step_func( window_now => {
           assert_equals(video_now, window_now);
+          done();
         }));
       })
     );
 
     video.src = testVideo.url;
-    video.play();
+    await video.play();
 
+    return promise;
 }, 'Test video.rVFC callbacks run before window.rAF callbacks.');
 
 
-async_test(function(t) {
+promise_test(async function(t) {
+    let done;
+    const promise = new Promise(resolve => done = resolve);
+
     let video = document.createElement('video');
     document.body.appendChild(video);
 
@@ -74,12 +87,14 @@
         // At this point, the other callback shouldn't have fired, but
         // give it some more time and really make sure it doesn't, by going
         // throught the event loop once more.
-        t.step_timeout(() => { t.done(); }, 0);
+        t.step_timeout(() => { done(); }, 0);
       })
     );
 
     video.src = testVideo.url;
-    video.play();
+    await video.play();
+
+    return promise;
 }, 'Test we can cancel a video.rVFC request.');
 
 test(function(t) {
@@ -105,6 +120,7 @@
 
 promise_test(async function(t) {
     let video = document.createElement('video');
+    video.autoplay = true;
     document.body.appendChild(video);
 
     let first_width = 0;
diff --git a/third_party/blink/web_tests/flag-specific/highdpi/virtual/text-antialias/colrv1-samples-visual-expected.png b/third_party/blink/web_tests/flag-specific/highdpi/virtual/text-antialias/colrv1-samples-visual-expected.png
new file mode 100644
index 0000000..000c60e
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/highdpi/virtual/text-antialias/colrv1-samples-visual-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/console/console-a11y-test-expected.txt b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/console/console-a11y-test-expected.txt
deleted file mode 100644
index 41fdf3b..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/console/console-a11y-test-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-Tests accessibility in the console using the axe-core linter.
-aXe violations: []
-
-
diff --git a/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/console/console-a11y-test.js b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/console/console-a11y-test.js
deleted file mode 100644
index 0ac4fce..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/console/console-a11y-test.js
+++ /dev/null
@@ -1,15 +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.
-
-(async function() {
-  await TestRunner.loadTestModule('axe_core_test_runner');
-  TestRunner.addResult(
-      'Tests accessibility in the console using the axe-core linter.');
-
-  await UI.viewManager.showView('console');
-  const widget = await UI.viewManager.view('console').widget();
-
-  await AxeCoreTestRunner.runValidation(widget.element);
-  TestRunner.completeTest();
-})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/console/console-error-a11y-test-expected.txt b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/console/console-error-a11y-test-expected.txt
deleted file mode 100644
index d4398902..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/console/console-error-a11y-test-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-Tests accessibility of console containing an error message using the axe-core linter.
-invalidVar123
-VM:1 Uncaught ReferenceError: invalidVar123 is not defined
-    at <anonymous>:1:1
-(anonymous) @ VM:1
-aXe violations: []
-
-
diff --git a/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/console/console-error-a11y-test.js b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/console/console-error-a11y-test.js
deleted file mode 100644
index 3297c72..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/console/console-error-a11y-test.js
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-(async function() {
-  await TestRunner.loadTestModule('axe_core_test_runner');
-  TestRunner.addResult(
-      'Tests accessibility of console containing an error message using the axe-core linter.');
-
-  await TestRunner.loadLegacyModule('console'); await TestRunner.loadTestModule('console_test_runner');
-  await TestRunner.showPanel('console');
-  const widget = await UI.viewManager.view('console').widget();
-
-  async function callback() {
-    ConsoleTestRunner.dumpConsoleMessages();
-    await AxeCoreTestRunner.runValidation(widget.element);
-    TestRunner.completeTest();
-  }
-
-  ConsoleTestRunner.evaluateInConsole('invalidVar123', callback);
-})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/console/argument-hints-expected.txt b/third_party/blink/web_tests/http/tests/devtools/console/argument-hints-expected.txt
deleted file mode 100644
index 49ba4e1..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/console/argument-hints-expected.txt
+++ /dev/null
@@ -1,113 +0,0 @@
-Tests function argument hints.
-
-open(
-?url,?target,?features
-
-Math.log(
-x
-
-console.log(1,
-...data
-
-const process = window.setTimeout(
-callback,ms,...args
-
-document.body.addEventListener(
-type,listener,?options
-
-boundAddClickListener(x => x,
-listener,?options
-
-userFunctionWithDestructuring(
-obj,arr
-
-originalFunction(a,b,c,d,e,f,g,
-a,b,c,...more
-
-secondFunction(
-b,c,...more
-
-thirdFunction(
-c,...more
-
-fourthFunction(
-...more
-
-fifthFunction(
-...more
-
-aLotBound(
-...more
-
-boundMathLog(
-x
-
-CSS.supports(
-null
-
-Uint8Array.from(
-arrayLike,?mapfn,?thisArg
-
-ctx.drawImage(image, x, y,
-image,x,y,?width,?height
-image,sx,sy,sw,sh,dx,dy,dw,dh
-
-window.open(document.getElementsByName(|));
-elementName
-
-window.open(|document.getElementsByName());
-?url,?target,?features
-
-"asdf".indexOf(
-searchString,?position
-
-[].indexOf(
-searchElement,?fromIndex
-
-new Thing(
-initialValue
-
-(new Thing).setT(
-t
-
-(x => x)(
-x
-
-(function(y){})(
-y
-
-Thing.myStaticMethod(
-a,b,c
-
-aString.toString(
-null
-
-aNumber.toString(
-?radix
-
-[1,2,3].splice(
-start,?deleteCount,...items
-
-URL.createObjectURL(
-blob
-source
-
-(() => window)().URL["revokeObjectURL"](
-url
-
-var notInAfunction
-null
-
-some gibberish $@#)(*^@#
-null
-
-Date.parse(
-s
-
-JSON.parse(
-text,?reviver
-
-CSSNumericValue.parse(
-cssText
-
-
diff --git a/third_party/blink/web_tests/http/tests/devtools/console/argument-hints.js b/third_party/blink/web_tests/http/tests/devtools/console/argument-hints.js
deleted file mode 100644
index 27f4341e..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/console/argument-hints.js
+++ /dev/null
@@ -1,129 +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.
-
-(async function() {
-  TestRunner.addResult(`Tests function argument hints.\n`);
-
-  await TestRunner.loadLegacyModule('console'); await TestRunner.loadTestModule('console_test_runner');
-  await TestRunner.showPanel('console');
-
-  await TestRunner.evaluateInPagePromise(`
-     const boundAddClickListener = document.addEventListener.bind(document, 'click');
-     function userFunctionWithDefault(required, notRequired = 5) {
-
-     }
-
-     function userFunctionWithDestructuring({something1}, [sommething2, something3]) {
-
-     }
-
-     function originalFunction(a,b,c,...more) {
-
-     }
-
-     const secondFunction = originalFunction.bind(null, 'a');
-     const thirdFunction = secondFunction.bind(null, 'b');
-     const fourthFunction = thirdFunction.bind(null, 'c');
-     const fifthFunction = fourthFunction.bind(null, 'more');
-     const aLotBound = originalFunction.bind(null, 'a','b','c','d','e','f');
-
-     const ctx = document.createElement('canvas').getContext('2d');
-     const boundMathLog = Math.log.bind(Math);
-
-     class Thing {
-       constructor(initialValue) {
-
-       }
-
-       static myStaticMethod(a,b,c) {
-
-       }
-
-       setT(t) {
-
-       }
-     }
-
-     const aString = "aString";
-     const aNumber = 4;
-  `);
-  const consoleEditor = await ConsoleTestRunner.waitUntilConsoleEditorLoaded();
-  await testHints('open(');
-  await testHints('Math.log(');
-  await testHints('console.log(1,');
-  await testHints('const process = window.setTimeout(');
-  await testHints('document.body.addEventListener(');
-  await testHints('boundAddClickListener(x => x,');
-  await testHints('userFunctionWithDestructuring(');
-  await testHints('originalFunction(a,b,c,d,e,f,g,');
-  await testHints('secondFunction(');
-  await testHints('thirdFunction(');
-  await testHints('fourthFunction(');
-  await testHints('fifthFunction(');
-  await testHints('aLotBound(');
-  await testHints('boundMathLog(');
-  await testHints('CSS.supports(');
-  await testHints('Uint8Array.from(');
-  await testHints('ctx.drawImage(image, x, y,');
-  await testHints('window.open(document.getElementsByName(|));');
-  await testHints('window.open(|document.getElementsByName());');
-  await testHints('"asdf".indexOf(');
-  await testHints('[].indexOf(');
-  await testHints('new Thing(');
-  await testHints('(new Thing).setT(');
-  await testHints('(x => x)(');
-  await testHints('(function(y){})(');
-  await testHints('Thing.myStaticMethod(');
-  await testHints('aString.toString(');
-  await testHints('aNumber.toString(');
-  await testHints('[1,2,3].splice(');
-  await testHints('URL.createObjectURL(');
-  await testHints('(() => window)().URL["revokeObjectURL"](');
-  await testHints('var notInAfunction');
-  await testHints('some gibberish $@#)(*^@#');
-  await testHints('Date.parse(');
-  await testHints('JSON.parse(');
-  await testHints('CSSNumericValue.parse(');
-  TestRunner.completeTest();
-
-  /**
-   * @param {string} text
-   * @param {!Array<string>} expected
-   */
-  async function testHints(text, expected) {
-    var cursorPosition = text.indexOf('|');
-
-    if (cursorPosition < 0)
-      cursorPosition = Infinity;
-
-    consoleEditor.setText(text.replace('|', ''));
-    consoleEditor.setSelection(
-        TextUtils.TextRange.createFromLocation(0, cursorPosition));
-    const signaturesPromise = new Promise(
-        x => TestRunner.addSniffer(
-            ObjectUI.javaScriptAutocomplete, 'argumentsHint',
-            (text, retVal) => x(retVal)));
-    consoleEditor.autocompleteController.onCursorActivity();
-    var message = 'Checking \'' + +'\'';
-
-    const signatures = await signaturesPromise;
-    TestRunner.addResult(`${
-                            text.replace('\n', '\\n').replace('\r', '\\r')
-                          }\n${printSignatures()}`);
-    TestRunner.addResult('');
-    function printSignatures() {
-      if (!signatures)
-        return 'null';
-      return signatures.args
-          .map(args => {
-            return args
-                .map((arg, index) => {
-                  return arg;
-                })
-                .join(',');
-          })
-          .join('\n')
-    }
-  }
-})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/console/console-focus-expected.txt b/third_party/blink/web_tests/http/tests/devtools/console/console-focus-expected.txt
deleted file mode 100644
index be43b83e..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/console/console-focus-expected.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-Tests that interacting with the console gives appropriate focus.
-
-Message count: 2
-
-Running: testClickingWithSelectedTextShouldNotFocusPrompt
-Prompt has focus: false
-Viewport scrolled to top: true
-Clicking message 0
-Prompt has focus: false
-Viewport scrolled to top: true
-
-Running: testKeypressWithFocusedMessageShouldFocusPrompt
-Prompt has focus: false
-Viewport scrolled to top: true
-Clicking message 0
-Prompt has focus: true
-Viewport scrolled to top: false
-
-Running: testClickOutsideMessageListShouldFocusPromptAndMoveCaretToEnd
-Prompt has focus: false
-Viewport scrolled to top: true
-Selection before: {"startLine":0,"startColumn":1,"endLine":0,"endColumn":1}
-Clicking on container
-Prompt has focus: true
-Viewport scrolled to top: false
-Selection after: {"startLine":0,"startColumn":6,"endLine":0,"endColumn":6}
-
diff --git a/third_party/blink/web_tests/http/tests/devtools/console/console-focus.js b/third_party/blink/web_tests/http/tests/devtools/console/console-focus.js
deleted file mode 100644
index 3e47d443f..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/console/console-focus.js
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-(async function() {
-  TestRunner.addResult(`Tests that interacting with the console gives appropriate focus.\n`);
-
-  await TestRunner.loadLegacyModule('console'); await TestRunner.loadTestModule('console_test_runner');
-  await TestRunner.showPanel('console');
-
-  var consoleView = Console.ConsoleView.instance();
-  var viewport = consoleView.viewport;
-  var prompt = consoleView.prompt;
-  var consoleEditor;
-  ConsoleTestRunner.waitUntilConsoleEditorLoaded().then(e => consoleEditor = e).then(logMessages);
-
-  // Ensure that the body is focusable.
-  document.body.tabIndex = -1;
-  function resetAndDumpFocusAndScrollTop() {
-    document.body.focus();
-    viewport.element.scrollTop = 0;
-    dumpFocusAndScrollInfo();
-  }
-
-  function logMessages() {
-    ConsoleTestRunner.waitForConsoleMessages(2, async () => {
-      await ConsoleTestRunner.waitForPendingViewportUpdates();
-      TestRunner.runTestSuite(testSuite);
-    });
-    ConsoleTestRunner.evaluateInConsole(
-        '\'foo ' +
-        '\n'.repeat(50) + 'bar\'');
-  }
-
-  var testSuite = [
-    function testClickingWithSelectedTextShouldNotFocusPrompt(next) {
-      resetAndDumpFocusAndScrollTop();
-
-      // Make a selection.
-      var messageElement = consoleView.itemElement(0).element();
-      var firstTextNode = messageElement.traverseNextTextNode();
-      window.getSelection().setBaseAndExtent(firstTextNode, 0, firstTextNode, 1);
-
-      focusMessage(0);
-      dumpFocusAndScrollInfo();
-      window.getSelection().removeAllRanges();
-      next();
-    },
-
-    function testKeypressWithFocusedMessageShouldFocusPrompt(next) {
-      resetAndDumpFocusAndScrollTop();
-
-      focusMessage(0);
-
-      eventSender.keyDown('A');
-      dumpFocusAndScrollInfo();
-      next();
-    },
-
-    function testClickOutsideMessageListShouldFocusPromptAndMoveCaretToEnd(next) {
-      prompt.setText('foobar');
-      consoleEditor.setSelection(TextUtils.TextRange.createFromLocation(0, 1));
-      resetAndDumpFocusAndScrollTop();
-      TestRunner.addResult('Selection before: ' + consoleEditor.selection().toString());
-
-      TestRunner.addResult('Clicking on container');
-      consoleView.messagesElement.click();
-
-      dumpFocusAndScrollInfo();
-      TestRunner.addResult('Selection after: ' + consoleEditor.selection().toString());
-      next();
-    }
-  ];
-
-  function focusMessage(index) {
-    var previewElement = consoleView.visibleViewMessages[index].element().querySelector('.source-code');
-    var previewRect = previewElement.getBoundingClientRect();
-    var clientX = previewRect.left + previewRect.width / 2;
-    var clientY = previewRect.top + previewRect.height / 2;
-
-    TestRunner.addResult('Clicking message ' + index);
-    previewElement.dispatchEvent(new MouseEvent('click', {clientX: clientX, clientY: clientY, bubbles: true}));
-    consoleView.visibleViewMessages[index].element().focus();
-  }
-
-  function dumpFocusAndScrollInfo() {
-    var focusedElement = document.deepActiveElement();
-    if (focusedElement)
-      TestRunner.addResult('Prompt has focus: ' + consoleView.prompt.hasFocus());
-    else
-      TestRunner.addResult('No focus');
-    TestRunner.addResult('Viewport scrolled to top: ' + String(viewport.element.scrollTop === 0));
-  }
-})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/console/console-prompt-keyboard-expected.txt b/third_party/blink/web_tests/http/tests/devtools/console/console-prompt-keyboard-expected.txt
deleted file mode 100644
index 5456d37..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/console/console-prompt-keyboard-expected.txt
+++ /dev/null
@@ -1,39 +0,0 @@
-Tests that console prompt keyboard events work.
-
-Adding first message: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-Setting prompt text: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
-
-Test that arrow Up stays in the same command
-{"startLine":1,"startColumn":0,"endLine":1,"endColumn":0}
-Prompt is displaying SECOND message
-
-Test that ArrowUp+shift stays in the same command
-{"startLine":0,"startColumn":0,"endLine":0,"endColumn":1}
-Prompt is displaying SECOND message
-
-Test that arrow Up on the first line, second visual row stays in the same command
-{"startLine":0,"startColumn":100,"endLine":0,"endColumn":100}
-Prompt is displaying SECOND message
-
-Test that arrow Up from the first line loads previous command
-{"startLine":0,"startColumn":0,"endLine":0,"endColumn":0}
-Prompt is displaying FIRST message
-
-Test that arrow Down stays in the same command
-{"startLine":0,"startColumn":0,"endLine":0,"endColumn":0}
-Prompt is displaying FIRST message
-
-Test that ArrowDown+shift stays in the same command
-{"startLine":1,"startColumn":0,"endLine":1,"endColumn":1}
-Prompt is displaying FIRST message
-
-Test that arrow Down on the last line, first visual row stays in the same command
-{"startLine":1,"startColumn":0,"endLine":1,"endColumn":0}
-Prompt is displaying FIRST message
-
-Test that arrow Down from the first line loads next command
-{"startLine":1,"startColumn":100,"endLine":1,"endColumn":100}
-Prompt is displaying SECOND message
-
diff --git a/third_party/blink/web_tests/http/tests/devtools/console/console-prompt-keyboard.js b/third_party/blink/web_tests/http/tests/devtools/console/console-prompt-keyboard.js
deleted file mode 100644
index 907c8f5..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/console/console-prompt-keyboard.js
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-(async function() {
-  TestRunner.addResult('Tests that console prompt keyboard events work.\n');
-
-  await TestRunner.loadLegacyModule('console'); await TestRunner.loadTestModule('console_test_runner');
-  await TestRunner.showPanel('console');
-  await ConsoleTestRunner.waitUntilConsoleEditorLoaded();
-  // Make sure that `singleLineCharCount` wraps onto multiple lines.
-  var singleLineCharCount = 100;
-  ConsoleTestRunner.fixConsoleViewportDimensions(300, 200);
-
-  var firstCommand = 'a'.repeat(singleLineCharCount) + '\n' + 'b'.repeat(singleLineCharCount);
-  TestRunner.addResult('Adding first message: ' + firstCommand);
-  await ConsoleTestRunner.evaluateInConsolePromise(firstCommand);
-
-  var secondCommand = 'x'.repeat(singleLineCharCount) + '\n' + 'y'.repeat(singleLineCharCount);
-  TestRunner.addResult('Setting prompt text: ' + secondCommand);
-  var prompt = Console.ConsoleView.instance().prompt;
-  prompt.setText(secondCommand);
-
-  TestRunner.addResult('\nTest that arrow Up stays in the same command');
-  prompt.editor.setSelection(TextUtils.TextRange.createFromLocation(1, 0));
-  dumpSelection();
-  sendKeyUpToPrompt();
-  printSelectedCommand();
-
-  TestRunner.addResult('\nTest that ArrowUp+shift stays in the same command');
-  prompt.editor.setSelection(new TextUtils.TextRange(0, 0, 0, 1));
-  dumpSelection();
-  sendKeyUpToPrompt(true);
-  printSelectedCommand();
-
-  TestRunner.addResult('\nTest that arrow Up on the first line, second visual row stays in the same command');
-  prompt.editor.setSelection(TextUtils.TextRange.createFromLocation(0, singleLineCharCount));
-  dumpSelection();
-  sendKeyUpToPrompt();
-  printSelectedCommand();
-
-  TestRunner.addResult('\nTest that arrow Up from the first line loads previous command');
-  prompt.editor.setSelection(TextUtils.TextRange.createFromLocation(0, 0));
-  dumpSelection();
-  sendKeyUpToPrompt();
-  printSelectedCommand();
-
-
-  TestRunner.addResult('\nTest that arrow Down stays in the same command');
-  prompt.editor.setSelection(TextUtils.TextRange.createFromLocation(0, 0));
-  dumpSelection();
-  sendKeyDownToPrompt();
-  printSelectedCommand();
-
-  TestRunner.addResult('\nTest that ArrowDown+shift stays in the same command');
-  prompt.editor.setSelection(new TextUtils.TextRange(1, 0, 1, 1));
-  dumpSelection();
-  sendKeyDownToPrompt(true);
-  printSelectedCommand();
-
-  TestRunner.addResult('\nTest that arrow Down on the last line, first visual row stays in the same command');
-  prompt.editor.setSelection(TextUtils.TextRange.createFromLocation(1, 0));
-  dumpSelection();
-  sendKeyDownToPrompt();
-  printSelectedCommand();
-
-  TestRunner.addResult('\nTest that arrow Down from the first line loads next command');
-  prompt.editor.setSelection(TextUtils.TextRange.createFromLocation(1, singleLineCharCount));
-  dumpSelection();
-  sendKeyDownToPrompt();
-  printSelectedCommand();
-
-  TestRunner.completeTest();
-
-  function printSelectedCommand() {
-    if (prompt.text().startsWith('a'))
-      TestRunner.addResult('Prompt is displaying FIRST message');
-    else if (prompt.text().startsWith('x'))
-      TestRunner.addResult('Prompt is displaying SECOND message');
-    else
-      TestRunner.addResult('TEST FAILURE');
-  }
-
-  /**
-   * @param {boolean} shiftKey
-   */
-  function sendKeyUpToPrompt(shiftKey) {
-    prompt.editor.element.focus();
-    if (shiftKey)
-      eventSender.keyDown('ArrowUp', ['shiftKey']);
-    else
-      eventSender.keyDown('ArrowUp');
-  }
-
-  /**
-   * @param {boolean} shiftKey
-   */
-  function sendKeyDownToPrompt(shiftKey) {
-    prompt.editor.element.focus();
-    if (shiftKey)
-      eventSender.keyDown('ArrowDown', ['shiftKey']);
-    else
-      eventSender.keyDown('ArrowDown');
-  }
-
-  function dumpSelection() {
-    TestRunner.addResult(JSON.stringify(prompt.editor.selection()));
-  }
-})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/console/console-retain-autocomplete-on-typing-expected.txt b/third_party/blink/web_tests/http/tests/devtools/console/console-retain-autocomplete-on-typing-expected.txt
deleted file mode 100644
index bb1ad6b..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/console/console-retain-autocomplete-on-typing-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-Verify that console does not hide autocomplete during typing.
-
-
-Running: testSummonSuggestBox
-Suggestions shown.
-
-Running: testTypeText
-SUCCESS: suggestbox is not hidden during typing.
-
diff --git a/third_party/blink/web_tests/http/tests/devtools/console/console-retain-autocomplete-on-typing.js b/third_party/blink/web_tests/http/tests/devtools/console/console-retain-autocomplete-on-typing.js
deleted file mode 100644
index 9dd3a6d..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/console/console-retain-autocomplete-on-typing.js
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-(async function() {
-  TestRunner.addResult(`Verify that console does not hide autocomplete during typing.\n`);
-  await TestRunner.loadLegacyModule('console'); await TestRunner.loadTestModule('console_test_runner');
-  await TestRunner.loadLegacyModule('sources'); await TestRunner.loadTestModule('sources_test_runner');
-  await TestRunner.loadLegacyModule('text_editor');
-  await TestRunner.showPanel('console');
-  await TestRunner.evaluateInPagePromise(`
-      window.foobar = "foobar";
-      window.foobaz = "foobaz";
-  `);
-
-  ConsoleTestRunner.waitUntilConsoleEditorLoaded().then(onConsoleEditorLoaded);
-
-  var consoleEditor;
-  function onConsoleEditorLoaded(editor) {
-    consoleEditor = editor;
-    TestRunner.runTestSuite(testSuite);
-  }
-
-  var testSuite = [
-    function testSummonSuggestBox(next) {
-      TestRunner.addSniffer(
-          TextEditor.TextEditorAutocompleteController.prototype, 'onSuggestionsShownForTest', onSuggestionsShown);
-      SourcesTestRunner.typeIn(consoleEditor, 'f');
-
-      function onSuggestionsShown() {
-        TestRunner.addResult('Suggestions shown.');
-        next();
-      }
-    },
-
-    function testTypeText(next) {
-      TestRunner.addSniffer(
-          TextEditor.TextEditorAutocompleteController.prototype, 'onSuggestionsHiddenForTest', onSuggestionsHidden);
-      TestRunner.addSniffer(
-          TextEditor.TextEditorAutocompleteController.prototype, 'onCursorActivityHandledForTest',
-          onCursorActivityHandled);
-      SourcesTestRunner.typeIn(consoleEditor, 'o');
-
-      var activityHandled = false;
-
-      function onSuggestionsHidden() {
-        if (activityHandled)
-          return;
-        TestRunner.addResult('FAIL: suggestbox is hidden during typing.');
-        TestRunner.completeTest();
-      }
-
-      function onCursorActivityHandled() {
-        TestRunner.addResult('SUCCESS: suggestbox is not hidden during typing.');
-        activityHandled = true;
-        next();
-      }
-    }
-  ];
-})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/console/console-smart-enter-expected.txt b/third_party/blink/web_tests/http/tests/devtools/console/console-smart-enter-expected.txt
deleted file mode 100644
index 6001ba2..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/console/console-smart-enter-expected.txt
+++ /dev/null
@@ -1,128 +0,0 @@
-Tests that the console enters a newline instead of running a command if the command is incomplete.
-
-Text Before Enter:
-window
-Text After Enter:
-<empty>
-
-Text Before Enter:
-window.
-Text After Enter:
-window.
-
-
-Text Before Enter:
-if.(1.===.2)
-Text After Enter:
-if.(1.===.2)
-....
-
-Text Before Enter:
-if.(1.===.2).{
-Text After Enter:
-if.(1.===.2).{
-....
-
-Text Before Enter:
-if.(1.===.2).{}
-Text After Enter:
-<empty>
-
-Text Before Enter:
-[1,2,
-Text After Enter:
-[1,2,
-.
-
-Text Before Enter:
-[1,2,3]
-Text After Enter:
-<empty>
-
-Text Before Enter:
-{abc:
-Text After Enter:
-{abc:
-.
-
-Text Before Enter:
-{abc:123}
-Text After Enter:
-<empty>
-
-Text Before Enter:
-function.incomplete().{
-....if.(1)
-........5;
-Text After Enter:
-function.incomplete().{
-....if.(1)
-........5;
-....
-
-Text Before Enter:
-function.bad().{
-....if.(1)
-}
-Text After Enter:
-<empty>
-
-Text Before Enter:
-function.good().{
-....if.(1).{
-........5;
-....}
-}
-Text After Enter:
-<empty>
-
-Text Before Enter:
-1,
-Text After Enter:
-1,
-....
-
-Text Before Enter:
-label:
-Text After Enter:
-label:
-
-
-Text Before Enter:
-for.(var.i.=.0;.i.<.100;.i++)
-Text After Enter:
-for.(var.i.=.0;.i.<.100;.i++)
-....
-
-Text Before Enter:
-for.(var.i.=.0;.i.<.100;.i++).i
-Text After Enter:
-<empty>
-
-Text Before Enter:
-var.templateStr.=.`
-Text After Enter:
-var.templateStr.=.`
-
-
-Text Before Enter:
-var.templateStr.=.`str`
-Text After Enter:
-<empty>
-
-Text Before Enter:
-var.doubleQ.=."
-Text After Enter:
-<empty>
-
-Text Before Enter:
-var.singleQ.=.'
-Text After Enter:
-<empty>
-
-Text Before Enter:
-var.singleQ.=.'str'
-Text After Enter:
-<empty>
-
-
diff --git a/third_party/blink/web_tests/http/tests/devtools/console/console-smart-enter.js b/third_party/blink/web_tests/http/tests/devtools/console/console-smart-enter.js
deleted file mode 100644
index a71a237..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/console/console-smart-enter.js
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-(async function() {
-  TestRunner.addResult(
-      `Tests that the console enters a newline instead of running a command if the command is incomplete.\n`);
-  await TestRunner.loadLegacyModule('console'); await TestRunner.loadTestModule('console_test_runner');
-  await TestRunner.showPanel('console');
-
-  var prompt = Console.ConsoleView.instance().prompt;
-  ConsoleTestRunner.waitUntilConsoleEditorLoaded().then(step1);
-
-  function step1() {
-    sequential([
-      () => pressEnterAfter('window'),
-      () => pressEnterAfter('window.'),
-      () => pressEnterAfter('if (1 === 2)'),
-      () => pressEnterAfter('if (1 === 2) {'),
-      () => pressEnterAfter('if (1 === 2) {}'),
-      () => pressEnterAfter('[1,2,'),
-      () => pressEnterAfter('[1,2,3]'),
-      () => pressEnterAfter('{abc:'),
-      () => pressEnterAfter('{abc:123}'),
-      () => pressEnterAfter(`function incomplete() {
-    if (1)
-        5;`),
-      () => pressEnterAfter(`function bad() {
-    if (1)
-}`),
-      () => pressEnterAfter(`function good() {
-    if (1) {
-        5;
-    }
-}`),
-      () => pressEnterAfter('1,'),
-      () => pressEnterAfter('label:'),
-      () => pressEnterAfter('for (var i = 0; i < 100; i++)'),
-      () => pressEnterAfter('for (var i = 0; i < 100; i++) i'),
-      () => pressEnterAfter('var templateStr = `'),
-      () => pressEnterAfter('var templateStr = `str`'),
-      () => pressEnterAfter('var doubleQ = "'),
-      () => pressEnterAfter('var singleQ = \''),
-      () => pressEnterAfter('var singleQ = \'str\'')
-    ]).then(TestRunner.completeTest);
-  }
-
-  function sequential(tests) {
-    var promise = Promise.resolve();
-    for (var i = 0; i < tests.length; i++)
-      promise = promise.then(tests[i]);
-    return promise;
-  }
-
-  function pressEnterAfter(text) {
-    var fulfill;
-    var promise = new Promise(x => fulfill = x);
-    TestRunner.addSniffer(Console.ConsolePrompt.prototype, 'enterProcessedForTest', enterProcessed);
-
-    prompt.setText(text);
-    prompt.moveCaretToEndOfPrompt();
-    prompt.enterKeyPressed(TestRunner.createKeyEvent('Enter'));
-    return promise;
-
-    function enterProcessed() {
-      TestRunner.addResult('Text Before Enter:');
-      TestRunner.addResult(text.replace(/ /g, '.'));
-      TestRunner.addResult('Text After Enter:');
-      TestRunner.addResult(prompt.text().replace(/ /g, '.') || '<empty>');
-      TestRunner.addResult('');
-      fulfill();
-    }
-  }
-})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/console/viewport-testing/console-key-expand-expected.txt b/third_party/blink/web_tests/http/tests/devtools/console/viewport-testing/console-key-expand-expected.txt
deleted file mode 100644
index 6dfcc8aa..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/console/viewport-testing/console-key-expand-expected.txt
+++ /dev/null
@@ -1,322 +0,0 @@
-Tests that console artifacts can be expanded, collapsed via keyboard.
-
-
-Running: testExpandingTraces
-Evaluating: console.warn("warning")
-Message count: 1
-
-Force selecting index 0
-Viewport virtual selection: 0
-Is trace expanded: NO
-
-ArrowRight:
-Viewport virtual selection: 0
-Is trace expanded: YES
-
-ArrowLeft:
-Viewport virtual selection: 0
-Is trace expanded: NO
-
-Running: testExpandingGroups
-Evaluating: console.group("group"); console.log("log child");
-Message count: 2
-
-Force selecting index 0
-Viewport virtual selection: 0
-Is group expanded: YES
-console-key-expand.js:35 group
-console-key-expand.js:35 log child
-
-ArrowLeft:
-Viewport virtual selection: 0
-Is group expanded: NO
-console-key-expand.js:35 group
-
-ArrowRight:
-Viewport virtual selection: 0
-Is group expanded: YES
-console-key-expand.js:35 group
-console-key-expand.js:35 log child
-
-Running: testNavigateBetweenObjectsAndLogs
-Evaluating: console.log("before");console.log("text", obj1, obj2);console.log("after");
-Message count: 3
-
-Force selecting index 1
-Viewport virtual selection: 1
-activeElement: DIV.console-message-wrapper.console-from-api.console-info-level.console-selected
-active text: console-key-expand.js:51 text {x: 1} {y: 2}
-
-ArrowRight:
-Viewport virtual selection: 1
-activeElement: LI.parent.object-properties-section-root-element.selected
-active text: {x: 1}
-
-ArrowDown:
-Viewport virtual selection: 1
-activeElement: LI.parent.object-properties-section-root-element.selected
-active text: {y: 2}
-
-ArrowDown:
-Viewport virtual selection: 1
-activeElement: SPAN.devtools-link
-active text: console-key-expand.js:51
-
-ArrowDown:
-Viewport virtual selection: 2
-activeElement: DIV.console-message-wrapper.console-from-api.console-info-level.console-selected
-active text: console-key-expand.js:51 after
-
-ArrowUp:
-Viewport virtual selection: 1
-activeElement: SPAN.devtools-link
-active text: console-key-expand.js:51
-
-ArrowUp:
-Viewport virtual selection: 1
-activeElement: LI.parent.object-properties-section-root-element.selected
-active text: {y: 2}
-
-ArrowLeft:
-Viewport virtual selection: 1
-activeElement: DIV.console-message-wrapper.console-from-api.console-info-level.console-selected
-active text: console-key-expand.js:51 text {x: 1} {y: 2}
-
-Running: testExpandingObjects
-Evaluating: console.log("before");console.log("text", obj1, obj2);console.log("after");
-Message count: 3
-
-Force selecting index 1
-Viewport virtual selection: 1
-activeElement: DIV.console-message-wrapper.console-from-api.console-info-level.console-selected
-active text: console-key-expand.js:74 text {x: 1} {y: 2}
-
-ArrowRight:
-Viewport virtual selection: 1
-activeElement: LI.parent.object-properties-section-root-element.selected
-active text: {x: 1}
-
-ArrowRight:
-Viewport virtual selection: 1
-activeElement: LI.parent.object-properties-section-root-element.selected.expanded
-active text: {x: 1}
-
-ArrowDown:
-Viewport virtual selection: 1
-activeElement: LI.selected
-active text: x: 1
-
-ArrowDown:
-Viewport virtual selection: 1
-activeElement: LI.parent.object-properties-section-root-element.selected
-active text: {y: 2}
-
-ArrowRight:
-Viewport virtual selection: 1
-activeElement: LI.parent.object-properties-section-root-element.selected.expanded
-active text: {y: 2}
-
-ArrowDown:
-
-ArrowDown:
-
-ArrowDown:
-Viewport virtual selection: 2
-activeElement: DIV.console-message-wrapper.console-from-api.console-info-level.console-selected
-active text: console-key-expand.js:74 after
-
-ArrowUp:
-
-ArrowUp:
-Viewport virtual selection: 1
-activeElement: LI.parent.object-properties-section-root-element.expanded.selected
-active text: {y: 2}
-
-ArrowLeft:
-Viewport virtual selection: 1
-activeElement: LI.parent.object-properties-section-root-element.selected
-active text: {y: 2}
-
-ArrowLeft:
-Viewport virtual selection: 1
-activeElement: DIV.console-message-wrapper.console-from-api.console-info-level.console-selected
-active text: console-key-expand.js:74 text {x: 1}x: 1 {y: 2}y: 2
-
-Running: testExpandingObjectInTrace
-Evaluating: console.log("before");console.warn("warning", obj1);console.log("after");
-Message count: 3
-
-Force selecting index 1
-Viewport virtual selection: 1
-Has object: collapsed
-Is trace expanded: NO
-activeElement: DIV.console-message-wrapper.console-from-api.console-warning-level.console-selected
-active text: console-key-expand.js:117 warning {x: 1}
-(anonymous) @ console-key-expand.js:117
-
-ArrowRight:
-Viewport virtual selection: 1
-Has object: collapsed
-Is trace expanded: YES
-activeElement: DIV.console-message-wrapper.console-from-api.console-warning-level.console-selected
-active text: console-key-expand.js:117 warning {x: 1}
-(anonymous) @ console-key-expand.js:117
-
-ArrowRight:
-Viewport virtual selection: 1
-Has object: collapsed
-Is trace expanded: YES
-activeElement: LI.parent.object-properties-section-root-element.selected
-active text: {x: 1}
-
-ArrowRight:
-Viewport virtual selection: 1
-Has object: expanded
-Is trace expanded: YES
-activeElement: LI.parent.object-properties-section-root-element.selected.expanded
-active text: {x: 1}
-
-ArrowDown:
-Viewport virtual selection: 1
-Has object: expanded
-Is trace expanded: YES
-activeElement: LI.selected
-active text: x: 1
-
-ArrowDown:
-
-ArrowDown:
-
-ArrowDown:
-Viewport virtual selection: 2
-Has object: expanded
-Is trace expanded: YES
-activeElement: DIV.console-message-wrapper.console-from-api.console-info-level.console-selected
-active text: console-key-expand.js:117 after
-
-ArrowUp:
-
-ArrowUp:
-Viewport virtual selection: 1
-Has object: expanded
-Is trace expanded: YES
-activeElement: SPAN.devtools-link
-active text: console-key-expand.js:117
-
-ArrowUp:
-Viewport virtual selection: 1
-Has object: expanded
-Is trace expanded: YES
-activeElement: LI.parent.object-properties-section-root-element.expanded.selected
-active text: {x: 1}
-
-ArrowUp:
-Viewport virtual selection: 1
-Has object: expanded
-Is trace expanded: YES
-activeElement: DIV.console-message-wrapper.console-from-api.console-warning-level.console-selected
-active text: console-key-expand.js:117 warning {x: 1}x: 1
-(anonymous) @ console-key-expand.js:117
-
-ArrowLeft:
-Viewport virtual selection: 1
-Has object: expanded
-Is trace expanded: NO
-activeElement: DIV.console-message-wrapper.console-from-api.console-warning-level.console-selected
-active text: console-key-expand.js:117 warning {x: 1}x: 1
-(anonymous) @ console-key-expand.js:117
-
-ArrowLeft:
-Viewport virtual selection: 1
-Has object: expanded
-Is trace expanded: NO
-activeElement: DIV.console-message-wrapper.console-from-api.console-warning-level.console-selected
-active text: console-key-expand.js:117 warning {x: 1}x: 1
-(anonymous) @ console-key-expand.js:117
-
-Running: testExpandingElement
-Evaluating: console.log("before");console.log(el);console.log("after");
-Message count: 3
-
-Force selecting index 1
-Viewport virtual selection: 1
-activeElement: DIV.console-message-wrapper.console-from-api.console-info-level.console-selected
-active text: console-key-expand.js:167 <div>​…​</div>​
-
-ArrowDown:
-
-ArrowDown:
-Viewport virtual selection: 1
-activeElement: LI.parent.selected
-active text: <div>​…​</div>​
-
-ArrowRight:
-Viewport virtual selection: 1
-activeElement: LI.parent.selected.expanded
-active text: <div>​
-
-Running: testShiftTabShouldSelectLastObject
-Evaluating: console.log("before");console.log(obj1);
-Message count: 2
-Setting focus in prompt:
-
-Shift+Tab:
-
-ArrowUp:
-Viewport virtual selection: 1
-Has object: collapsed
-activeElement: LI.parent.object-properties-section-root-element.selected
-active text: {x: 1}
-
-ArrowRight:
-Viewport virtual selection: 1
-Has object: expanded
-activeElement: LI.parent.object-properties-section-root-element.selected.expanded
-active text: {x: 1}
-
-Running: testArrowUpToFirstVisibleMessageShouldSelectLastObject
-Evaluating: console.log(obj1);console.log("after");
-Message count: 2
-Setting focus in prompt:
-
-Shift+Tab:
-
-ArrowUp:
-Viewport virtual selection: 1
-Has object: collapsed
-activeElement: DIV.console-message-wrapper.console-from-api.console-info-level.console-selected
-active text: console-key-expand.js:199 after
-
-ArrowUp:
-
-ArrowUp:
-Viewport virtual selection: 0
-Has object: collapsed
-activeElement: LI.parent.object-properties-section-root-element.selected
-active text: {x: 1}
-
-Running: testFocusLastChildInBigObjectShouldScrollIntoView
-Evaluating: console.log(bigObj);
-Message count: 1
-Setting focus in prompt:
-
-Shift+Tab:
-
-ArrowUp:
-
-ArrowRight:
-
-Tab:
-Viewport virtual selection: -1
-Has object: expanded
-activeElement: TEXTAREA
-
-Shift+Tab:
-
-ArrowUp:
-Viewport virtual selection: 0
-Has object: expanded
-activeElement: LI.parent.object-properties-section-root-element.expanded.selected
-active text: {a0: 0, a1: 1, a2: 2, a3: 3, a4: 4, …}
-Is at bottom: false, should stick: false
-
diff --git a/third_party/blink/web_tests/http/tests/devtools/console/viewport-testing/console-key-expand.js b/third_party/blink/web_tests/http/tests/devtools/console/viewport-testing/console-key-expand.js
deleted file mode 100644
index 37c5b0e8..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/console/viewport-testing/console-key-expand.js
+++ /dev/null
@@ -1,328 +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.
-
-(async function() {
-  TestRunner.addResult(`Tests that console artifacts can be expanded, collapsed via keyboard.\n`);
-  await TestRunner.loadLegacyModule('console'); await TestRunner.loadTestModule('console_test_runner');
-  await TestRunner.showPanel('console');
-  ConsoleTestRunner.fixConsoleViewportDimensions(600, 200);
-  await ConsoleTestRunner.waitUntilConsoleEditorLoaded();
-
-  const consoleView = Console.ConsoleView.instance();
-  const viewport = consoleView.viewport;
-  const prompt = consoleView.prompt;
-
-  await TestRunner.evaluateInPagePromise(`
-    var obj1 = Object.create(null);
-    obj1.x = 1;
-
-    var obj2 = Object.create(null);
-    obj2.y = 2;
-  `);
-
-  TestRunner.runTestSuite([
-    async function testExpandingTraces(next) {
-      await clearAndLog(`console.warn("warning")`);
-      forceSelect(0);
-
-      await dumpFocus();
-      press('ArrowRight');
-      await dumpFocus();
-      press('ArrowLeft');
-      await dumpFocus();
-
-      next();
-    },
-
-    async function testExpandingGroups(next) {
-      await clearAndLog(`console.group("group"); console.log("log child");`, 2);
-      forceSelect(0);
-
-      await dumpFocus();
-      await ConsoleTestRunner.dumpConsoleMessages();
-      press('ArrowLeft');
-      await dumpFocus();
-      await ConsoleTestRunner.dumpConsoleMessages();
-      press('ArrowRight');
-      await dumpFocus();
-      await ConsoleTestRunner.dumpConsoleMessages();
-
-      next();
-    },
-
-    async function testNavigateBetweenObjectsAndLogs(next) {
-      await clearAndLog(`console.log("before");console.log("text", obj1, obj2);console.log("after");`, 3);
-      forceSelect(1);
-
-      await dumpFocus(true, 1, true /* skipObjectCheck */);
-      press('ArrowRight');
-      await dumpFocus(true, 1, true /* skipObjectCheck */);
-      press('ArrowDown');
-      await dumpFocus(true, 1, true /* skipObjectCheck */);
-      press('ArrowDown');
-      await dumpFocus(true, 1, true /* skipObjectCheck */);
-      press('ArrowDown');
-      await dumpFocus(true, 1, true /* skipObjectCheck */);
-      press('ArrowUp');
-      await dumpFocus(true, 1, true /* skipObjectCheck */);
-      press('ArrowUp');
-      await dumpFocus(true, 1, true /* skipObjectCheck */);
-      press('ArrowLeft');
-      await dumpFocus(true, 1, true /* skipObjectCheck */);
-
-      next();
-    },
-
-    // Note: During this test expanded objects
-    // do not include the __proto__ property.
-    async function testExpandingObjects(next) {
-      await clearAndLog(`console.log("before");console.log("text", obj1, obj2);console.log("after");`, 3);
-      forceSelect(1);
-
-      await dumpFocus(true, 1, true /* skipObjectCheck */);
-      press('ArrowRight');
-      await dumpFocus(true, 1, true /* skipObjectCheck */);
-
-      // Expand obj1.
-      press('ArrowRight');
-      await dumpFocus(true, 1, true /* skipObjectCheck */);
-      await ConsoleTestRunner.waitForRemoteObjectsConsoleMessagesPromise();
-      press('ArrowDown');
-      await dumpFocus(true, 1, true /* skipObjectCheck */);
-      press('ArrowDown');
-      await dumpFocus(true, 1, true /* skipObjectCheck */);
-
-      // Expand obj2.
-      press('ArrowRight');
-      await dumpFocus(true, 1, true /* skipObjectCheck */);
-      await ConsoleTestRunner.waitForRemoteObjectsConsoleMessagesPromise();
-      press('ArrowDown');
-      press('ArrowDown');
-      press('ArrowDown');
-      await dumpFocus(true, 1, true /* skipObjectCheck */);
-
-      press('ArrowUp');
-      press('ArrowUp');
-      await dumpFocus(true, 1, true /* skipObjectCheck */);
-
-      // Collapse object.
-      press('ArrowLeft');
-      await dumpFocus(true, 1, true /* skipObjectCheck */);
-
-      // Select message.
-      press('ArrowLeft');
-      await dumpFocus(true, 1, true /* skipObjectCheck */);
-
-      next();
-    },
-
-    async function testExpandingObjectInTrace(next) {
-      await clearAndLog(`console.log("before");console.warn("warning", obj1);console.log("after");`, 3);
-      forceSelect(1);
-
-      await dumpFocus(true, 1);
-      press('ArrowRight');
-      await dumpFocus(true, 1);
-      press('ArrowRight');
-      await dumpFocus(true, 1);
-
-      // Expand object.
-      press('ArrowRight');
-      await dumpFocus(true, 1);
-      await ConsoleTestRunner.waitForRemoteObjectsConsoleMessagesPromise();
-      press('ArrowDown');
-      await dumpFocus(true, 1);
-      press('ArrowDown');
-      press('ArrowDown');
-      press('ArrowDown');
-      await dumpFocus(true, 1);
-
-      press('ArrowUp');
-      press('ArrowUp');
-      await dumpFocus(true, 1);
-      press('ArrowUp');
-      await dumpFocus(true, 1);
-      press('ArrowUp');
-      await dumpFocus(true, 1);
-
-      // Collapse trace.
-      press('ArrowLeft');
-      await dumpFocus(true, 1);
-
-      // ArrowLeft on message does not collapse object.
-      press('ArrowLeft');
-      await dumpFocus(true, 1);
-
-      next();
-    },
-
-    async function testExpandingElement(next) {
-      await TestRunner.evaluateInPagePromise(`
-        var el = document.createElement('div');
-        var child = document.createElement('span');
-        el.appendChild(child); undefined;
-      `);
-      const nodePromise = TestRunner.addSnifferPromise(Console.ConsoleViewMessage.prototype, 'formattedParameterAsNodeForTest');
-      await clearAndLog(`console.log("before");console.log(el);console.log("after");`, 3);
-      await nodePromise;
-      forceSelect(1);
-
-      await dumpFocus(true, 1);
-      press('ArrowDown');
-      press('ArrowDown');
-      await dumpFocus(true, 1);
-
-      // Expand object.
-      press('ArrowRight');
-      await ConsoleTestRunner.waitForRemoteObjectsConsoleMessagesPromise();
-      await dumpFocus(true, 1);
-
-      next();
-    },
-
-    async function testShiftTabShouldSelectLastObject(next) {
-      await clearAndLog(`console.log("before");console.log(obj1);`, 2);
-      await ConsoleTestRunner.waitForRemoteObjectsConsoleMessagesPromise();
-
-      TestRunner.addResult(`Setting focus in prompt:`);
-      prompt.focus();
-      shiftPress('Tab');
-      press('ArrowUp');  // Move from source link to object.
-      await dumpFocus(true, 1);
-
-      // Expand object.
-      press('ArrowRight');
-      await ConsoleTestRunner.waitForRemoteObjectsConsoleMessagesPromise();
-      await dumpFocus(true, 1);
-
-      next();
-    },
-
-    async function testArrowUpToFirstVisibleMessageShouldSelectLastObject(
-        next) {
-      await clearAndLog(`console.log(obj1);console.log("after");`, 2);
-      await ConsoleTestRunner.waitForRemoteObjectsConsoleMessagesPromise();
-
-      TestRunner.addResult(`Setting focus in prompt:`);
-      prompt.focus();
-      shiftPress('Tab');
-      press('ArrowUp');  // Move from source link to "after".
-      await dumpFocus(true);
-
-      press('ArrowUp');  // Move from source link to object.
-      press('ArrowUp');
-      await dumpFocus(true);
-
-      next();
-    },
-
-    async function testFocusLastChildInBigObjectShouldScrollIntoView(next) {
-      await TestRunner.evaluateInPagePromise(`
-        var bigObj = Object.create(null);
-          for (var i = 0; i < 100; i++)
-            bigObj['a' + i] = i;
-      `);
-      await clearAndLog(`console.log(bigObj);`, 1);
-      await ConsoleTestRunner.waitForRemoteObjectsConsoleMessagesPromise();
-
-      TestRunner.addResult(`Setting focus in prompt:`);
-      prompt.focus();
-      shiftPress('Tab');
-      press('ArrowUp');  // Move from source link to object.
-      press('ArrowRight');
-      await ConsoleTestRunner.waitForRemoteObjectsConsoleMessagesPromise();
-      press('Tab');
-
-      await dumpFocus(true);
-      shiftPress('Tab');
-      press('ArrowUp');  // Move from source link to object.
-
-      await dumpFocus(true);
-      dumpScrollInfo();
-
-      next();
-    },
-  ]);
-
-
-  // Utilities.
-  async function clearAndLog(expression, expectedCount = 1) {
-    consoleView.consoleCleared();
-    TestRunner.addResult(`Evaluating: ${expression}`);
-    await TestRunner.evaluateInPagePromise(expression);
-    await ConsoleTestRunner.waitForConsoleMessagesPromise(expectedCount);
-    await ConsoleTestRunner.waitForPendingViewportUpdates();
-  }
-
-  function forceSelect(index) {
-    TestRunner.addResult(`\nForce selecting index ${index}`);
-    viewport.virtualSelectedIndex = index;
-    viewport.contentElement().focus();
-    viewport.updateFocusedItem();
-  }
-
-  function press(key) {
-    TestRunner.addResult(`\n${key}:`);
-    eventSender.keyDown(key);
-  }
-
-  function shiftPress(key) {
-    TestRunner.addResult(`\nShift+${key}:`);
-    eventSender.keyDown(key, ['shiftKey']);
-  }
-
-  function dumpScrollInfo() {
-    viewport.refresh();
-    let infoText =
-      'Is at bottom: ' + TestRunner.isScrolledToBottom(viewport.element) + ', should stick: ' + viewport.stickToBottom();
-    TestRunner.addResult(infoText);
-  }
-
-  async function dumpFocus(activeElement, messageIndex = 0, skipObjectCheck) {
-    const firstMessage = consoleView.visibleViewMessages[messageIndex]
-    // Ordering here is important. Retrieving the element triggers the creation of a LiveLocation.
-    // Wait for pending updates to settle as updates usually cause more rendering.
-    const firstMessageElement = firstMessage.element();
-    await TestRunner.waitForPendingLiveLocationUpdates();
-
-    const hasTrace = !!firstMessageElement.querySelector('.console-message-stack-trace-toggle');
-    const hasHiddenStackTrace = firstMessageElement.querySelector('.console-message-stack-trace-wrapper > div.hidden');
-    const hasCollapsedObject = firstMessageElement.querySelector('.console-view-object-properties-section:not(.expanded)');
-    const hasExpandedObject = firstMessageElement.querySelector('.console-view-object-properties-section.expanded');
-
-    TestRunner.addResult(`Viewport virtual selection: ${viewport.virtualSelectedIndex}`);
-
-    if (!skipObjectCheck) {
-      if (hasCollapsedObject) {
-        TestRunner.addResult(`Has object: collapsed`);
-      } else if (hasExpandedObject) {
-        TestRunner.addResult(`Has object: expanded`);
-      }
-    }
-
-    if (hasTrace) {
-      TestRunner.addResult(`Is trace expanded: ${!hasHiddenStackTrace ? 'YES' : 'NO'}`);
-    }
-    if (firstMessage instanceof Console.ConsoleGroupViewMessage) {
-      const expanded = !firstMessage.collapsed();
-      TestRunner.addResult(`Is group expanded: ${expanded ? 'YES' : 'NO'}`);
-    }
-
-    if (!activeElement)
-      return;
-    var element = document.deepActiveElement();
-    if (!element) {
-      TestRunner.addResult('null');
-      return;
-    }
-    var name = `activeElement: ${element.tagName}`;
-    if (element.id)
-      name += '#' + element.id;
-    else if (element.className)
-      name += '.' + element.className.split(' ').join('.');
-    if (element.deepTextContent())
-      name += '\nactive text: ' + element.deepTextContent();
-    TestRunner.addResult(name);
-  }
-})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/console/viewport-testing/console-runtime-result-below-prompt-expected.txt b/third_party/blink/web_tests/http/tests/devtools/console/viewport-testing/console-runtime-result-below-prompt-expected.txt
deleted file mode 100644
index 5735d66..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/console/viewport-testing/console-runtime-result-below-prompt-expected.txt
+++ /dev/null
@@ -1,60 +0,0 @@
-Tests that console fills the empty element below the prompt editor.
-
-
-Running: testUnsafeExpressions
-Expression: "var should_not_be_defined" yielded preview: ""
-Expression: "window.prop_should_not_be_set = 1" yielded preview: ""
-Evaluating "should_not_be_defined"
-value: undefined
-exceptionDetails: {
-    columnNumber : 0
-    exception : {
-        className : "ReferenceError"
-        description : "ReferenceError: should_not_be_defined is not defined
-    at test://evaluations/0/console-runtime-result-below-prompt.js:20:1"
-        objectId : <string>
-        subtype : "error"
-        type : "object"
-    }
-    exceptionId : <number>
-    lineNumber : 19
-    scriptId : <string>
-    stackTrace : {
-        callFrames : [
-            {
-                columnNumber : 0
-                functionName : ""
-                lineNumber : 19
-                scriptId : <string>
-                url : "test://evaluations/0/console-runtime-result-below-prompt.js"
-            }
-        ]
-    }
-    text : "Uncaught"
-}
-Evaluating "window.prop_should_not_be_set"
-value: undefined
-exceptionDetails: undefined
-
-Running: testSafeExpressions
-Expression: "1 + 2" yielded preview: "3"
-Expression: "123" yielded preview: ""
-
-Running: testNoOpForLongText
-Setting max length for evaluation to 0
-Expression: "1 + 2" yielded preview: ""
-
-Running: testClickingPreviewFocusesEditor
-Prompt text set to `1 + 2`
-Selection before: {"startLine":0,"startColumn":0,"endLine":0,"endColumn":0}
-Clicking preview element
-Selection after: {"startLine":0,"startColumn":5,"endLine":0,"endColumn":5}
-Editor has focus: true
-
-Running: testClickWithSelectionDoesNotFocusEditor
-Prompt text set to `1 + 2`
-Selection before: 3
-Clicking preview element
-Selection after: 3
-Editor has focus: false
-
diff --git a/third_party/blink/web_tests/http/tests/devtools/console/viewport-testing/console-runtime-result-below-prompt.js b/third_party/blink/web_tests/http/tests/devtools/console/viewport-testing/console-runtime-result-below-prompt.js
deleted file mode 100644
index 3a680391..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/console/viewport-testing/console-runtime-result-below-prompt.js
+++ /dev/null
@@ -1,100 +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.
-
-(async function() {
-  TestRunner.addResult(`Tests that console fills the empty element below the prompt editor.\n`);
-  await TestRunner.loadLegacyModule('console'); await TestRunner.loadTestModule('console_test_runner');
-  await TestRunner.showPanel('console');
-  await ConsoleTestRunner.waitForPendingViewportUpdates();
-  const consoleView = Console.ConsoleView.instance();
-  const prompt = consoleView.prompt;
-  const editor = await ConsoleTestRunner.waitUntilConsoleEditorLoaded();
-  Common.settings.moduleSetting('consoleEagerEval').set(true);
-
-  TestRunner.runTestSuite([
-    async function testUnsafeExpressions(next) {
-      await checkExpression(`var should_not_be_defined`);
-      await checkExpression(`window.prop_should_not_be_set = 1`);
-
-      await evaluateAndDumpResult(`should_not_be_defined`);
-      await evaluateAndDumpResult(`window.prop_should_not_be_set`);
-
-      next();
-    },
-
-    async function testSafeExpressions(next) {
-      await checkExpression(`1 + 2`);
-      await checkExpression(`123`);
-
-      next();
-    },
-
-    async function testNoOpForLongText(next) {
-      TestRunner.addResult('Setting max length for evaluation to 0');
-      const originalMaxLength = ObjectUI.JavaScriptREPL._MaxLengthForEvaluation;
-      ObjectUI.JavaScriptREPL._MaxLengthForEvaluation = 0;
-      await checkExpression(`1 + 2`);
-      ObjectUI.JavaScriptREPL._MaxLengthForEvaluation = originalMaxLength;
-
-      next();
-    },
-
-    async function testClickingPreviewFocusesEditor(next) {
-      const expression = `1 + 2`;
-      TestRunner.addResult(`Prompt text set to \`${expression}\``);
-      prompt.setText(expression);
-      await prompt.previewRequestForTest;
-
-      editor.setSelection(TextUtils.TextRange.createFromLocation(0, 0));
-      TestRunner.addResult('Selection before: ' + editor.selection().toString());
-
-      TestRunner.addResult(`Clicking preview element`);
-      prompt.eagerPreviewElement.click();
-
-      TestRunner.addResult('Selection after: ' + editor.selection().toString());
-      TestRunner.addResult(`Editor has focus: ${editor.element.hasFocus()}`);
-
-      next();
-    },
-
-    async function testClickWithSelectionDoesNotFocusEditor(next) {
-      const expression = `1 + 2`;
-      TestRunner.addResult(`Prompt text set to \`${expression}\``);
-      prompt.setText(expression);
-      await prompt.previewRequestForTest;
-      document.activeElement.blur();
-
-      var firstTextNode = prompt.belowEditorElement().traverseNextTextNode();
-      window.getSelection().setBaseAndExtent(firstTextNode, 0, firstTextNode, 1);
-      TestRunner.addResult('Selection before: ' + window.getSelection().toString());
-
-      TestRunner.addResult(`Clicking preview element`);
-      prompt.eagerPreviewElement.click();
-
-      TestRunner.addResult('Selection after: ' + window.getSelection().toString());
-      TestRunner.addResult(`Editor has focus: ${editor.element.hasFocus()}`);
-
-      next();
-    },
-  ]);
-
-  async function checkExpression(expression) {
-    prompt.setText(expression);
-    await prompt.previewRequestForTest;
-    const previewText = prompt.innerPreviewElement.deepTextContent();
-    TestRunner.addResult(`Expression: "${expression}" yielded preview: "${previewText}"`);
-  }
-
-  async function evaluateAndDumpResult(expression) {
-    const customFormatters = {};
-    for (let name of ['objectId', 'scriptId', 'exceptionId'])
-      customFormatters[name] = 'formatAsTypeNameOrNull';
-
-    TestRunner.addResult(`Evaluating "${expression}"`);
-    await TestRunner.evaluateInPage(expression, (value, exceptionDetails) => {
-      TestRunner.dump(value, customFormatters, '', 'value: ');
-      TestRunner.dump(exceptionDetails, customFormatters, '', 'exceptionDetails: ');
-    });
-  }
-})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/console/viewport-testing/console-stick-to-bottom-with-large-prompt-expected.txt b/third_party/blink/web_tests/http/tests/devtools/console/viewport-testing/console-stick-to-bottom-with-large-prompt-expected.txt
deleted file mode 100644
index 1566bbe1..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/console/viewport-testing/console-stick-to-bottom-with-large-prompt-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-Verifies viewport stick-to-bottom behavior when prompt has space below editable area.
-
-
-Running: testStickToBottomWhenAddingMessages
-Is at bottom: true, should stick: true
-
-Running: testScrollViewportToBottom
-Is at bottom: true, should stick: true
-
-Running: testJumpToBottomWhenTypingOnLastPromptLine
-Is at bottom: true, should stick: true
-
-Running: testDoNotJumpToBottomWhenTypingAboveLastPromptLine
-Is at bottom: false, should stick: false
-
diff --git a/third_party/blink/web_tests/http/tests/devtools/console/viewport-testing/console-stick-to-bottom-with-large-prompt.js b/third_party/blink/web_tests/http/tests/devtools/console/viewport-testing/console-stick-to-bottom-with-large-prompt.js
deleted file mode 100644
index 743b8cc..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/console/viewport-testing/console-stick-to-bottom-with-large-prompt.js
+++ /dev/null
@@ -1,89 +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.
-
-(async function() {
-  TestRunner.addResult(`Verifies viewport stick-to-bottom behavior when prompt has space below editable area.\n`);
-  await TestRunner.loadLegacyModule('console'); await TestRunner.loadTestModule('console_test_runner');
-  await TestRunner.showPanel('console');
-  await TestRunner.evaluateInPagePromise(`
-      function populateConsoleWithMessages(count)
-      {
-          for (var i = 0; i < count; ++i)
-              console.log("Multiline\\nMessage #" + i);
-      }
-
-      //# sourceURL=console-viewport-stick-to-bottom-with-large-prompt.js
-    `);
-  await ConsoleTestRunner.waitUntilConsoleEditorLoaded();
-  ConsoleTestRunner.fixConsoleViewportDimensions(600, 200);
-  await ConsoleTestRunner.waitForPendingViewportUpdates();
-
-  const consoleView = Console.ConsoleView.instance();
-  const viewport = consoleView.viewport;
-  const heightBelowPromptEditor = consoleView.prompt.belowEditorElement().offsetHeight;
-  const messagesCount = 150;
-
-  TestRunner.runTestSuite([
-    async function testStickToBottomWhenAddingMessages(next) {
-      await logMessagesToConsole(messagesCount);
-      await ConsoleTestRunner.waitForPendingViewportUpdates();
-      dumpAndContinue(next);
-    },
-
-    async function testScrollViewportToBottom(next) {
-      viewport.element.scrollTop = 0;
-      consoleView.immediatelyScrollToBottom();
-      await ConsoleTestRunner.waitForPendingViewportUpdates();
-      dumpAndContinue(next);
-    },
-
-    async function testJumpToBottomWhenTypingOnLastPromptLine(next) {
-      viewport.element.scrollTop = 0;
-      await ConsoleTestRunner.waitForPendingViewportUpdates();
-
-      // Simulate scroll on type.
-      viewport.element.scrollTop = viewport.element.scrollHeight - viewport.element.clientHeight - heightBelowPromptEditor;
-      consoleView.prompt.setText('a');
-
-      await ConsoleTestRunner.waitForPendingViewportUpdates();
-      dumpAndContinue(next);
-    },
-
-    async function testDoNotJumpToBottomWhenTypingAboveLastPromptLine(next) {
-      const multilineText = `Multiline text\n\n\nfoo`;
-      consoleView.prompt.setText(multilineText);
-      viewport.element.scrollTop = 0;
-      await ConsoleTestRunner.waitForPendingViewportUpdates();
-
-      // Simulate scroll on type.
-      viewport.element.scrollTop = viewport.element.scrollHeight - viewport.element.clientHeight - heightBelowPromptEditor - 3;
-      consoleView.prompt.setText(multilineText + 'a');
-
-      await ConsoleTestRunner.waitForPendingViewportUpdates();
-      dumpAndContinue(next);
-    }
-  ]);
-
-  function dumpAndContinue(callback) {
-    viewport.refresh();
-    TestRunner.addResult(
-      'Is at bottom: ' + TestRunner.isScrolledToBottom(viewport.element) + ', should stick: ' + viewport.stickToBottom());
-    callback();
-  }
-
-  function logMessagesToConsole(count) {
-    return new Promise(resolve => {
-      var awaitingMessagesCount = count;
-      function messageAdded() {
-        if (!--awaitingMessagesCount)
-          resolve();
-        else
-          ConsoleTestRunner.addConsoleSniffer(messageAdded, false);
-      }
-
-      ConsoleTestRunner.addConsoleSniffer(messageAdded, false);
-      TestRunner.evaluateInPage(String.sprintf('populateConsoleWithMessages(%d)', count));
-    })
-  }
-})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/network/network-choose-preview-view.js b/third_party/blink/web_tests/http/tests/devtools/network/network-choose-preview-view.js
index b11bfca0..ea007b1 100644
--- a/third_party/blink/web_tests/http/tests/devtools/network/network-choose-preview-view.js
+++ b/third_party/blink/web_tests/http/tests/devtools/network/network-choose-preview-view.js
@@ -11,9 +11,9 @@
 
   function createNetworkRequest(mimeType, content, statusCode, resourceType) {
     var request = SDK.NetworkRequest.create(0, 'http://localhost');
-    request.resourceType = resourceType;
+    request.setResourceType(resourceType);
     request.mimeType = mimeType;
-    request.contentDataInternal = Promise.resolve({error: null, content: content, encoded: false});
+    request.setContentDataProvider(() => Promise.resolve({error: null, content: content, encoded: false}));
     if (statusCode !== undefined)
       request.statusCode = statusCode;
     return request;
diff --git a/third_party/blink/web_tests/http/tests/devtools/resource-har-headers.js b/third_party/blink/web_tests/http/tests/devtools/resource-har-headers.js
index 22f73343..8b89bcd 100644
--- a/third_party/blink/web_tests/http/tests/devtools/resource-har-headers.js
+++ b/third_party/blink/web_tests/http/tests/devtools/resource-har-headers.js
@@ -24,7 +24,7 @@
     request.statusCode = 200;
     request.statusText = 'OK';
     request.resourceSize = 1000;
-    request.transferSizeInternal = 539;  // 39 = header size at the end of the day
+    request.setTransferSize(539);  // 39 = header size at the end of the day
     request.setPriority('VeryHigh');
     request.setResourceType(Common.resourceTypes.Fetch);
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-style/timeline-style-recalc-all-invalidator-types-expected.txt b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-style/timeline-style-recalc-all-invalidator-types-expected.txt
index ccaa6b3a..c9d520c 100644
--- a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-style/timeline-style-recalc-all-invalidator-types-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-style/timeline-style-recalc-all-invalidator-types-expected.txt
@@ -84,17 +84,6 @@
 Running: testAttributeChange
 [
     {
-        cause : {reason: PseudoClass, stackTrace: test://evaluations/0/timeline-style-recalc-all-invalidator-types.js:68}
-        changedAttribute : undefined
-        changedClass : undefined
-        changedId : undefined
-        changedPseudo : undefined
-        extraData : ""
-        nodeName : "DIV id='testElementFour' class='red'"
-        selectorPart : undefined
-        type : "StyleRecalcInvalidationTracking"
-    }
-    {
         cause : {reason: Attribute, stackTrace: test://evaluations/0/timeline-style-recalc-all-invalidator-types.js:68}
         changedAttribute : undefined
         changedClass : undefined
@@ -106,17 +95,6 @@
         type : "StyleRecalcInvalidationTracking"
     }
     {
-        cause : {reason: PseudoClass, stackTrace: test://evaluations/0/timeline-style-recalc-all-invalidator-types.js:69}
-        changedAttribute : undefined
-        changedClass : undefined
-        changedId : undefined
-        changedPseudo : undefined
-        extraData : ""
-        nodeName : "DIV id='testElementFive' class='red'"
-        selectorPart : undefined
-        type : "StyleRecalcInvalidationTracking"
-    }
-    {
         cause : {reason: Attribute, stackTrace: test://evaluations/0/timeline-style-recalc-all-invalidator-types.js:69}
         changedAttribute : undefined
         changedClass : undefined
@@ -127,6 +105,28 @@
         selectorPart : undefined
         type : "StyleRecalcInvalidationTracking"
     }
+    {
+        cause : {reason: Element has pending invalidation list, stackTrace: test://evaluations/0/timeline-style-recalc-all-invalidator-types.js:68}
+        changedAttribute : "dir"
+        changedClass : undefined
+        changedId : undefined
+        changedPseudo : undefined
+        extraData : undefined
+        nodeName : "DIV id='testElementFour' class='red'"
+        selectorPart : undefined
+        type : "StyleRecalcInvalidationTracking"
+    }
+    {
+        cause : {reason: Element has pending invalidation list, stackTrace: test://evaluations/0/timeline-style-recalc-all-invalidator-types.js:69}
+        changedAttribute : "dir"
+        changedClass : undefined
+        changedId : undefined
+        changedPseudo : undefined
+        extraData : undefined
+        nodeName : "DIV id='testElementFive' class='red'"
+        selectorPart : undefined
+        type : "StyleRecalcInvalidationTracking"
+    }
 ]
 
 Running: testPseudoChange
diff --git a/third_party/blink/web_tests/http/tests/devtools/unit/binary-resource-view.js b/third_party/blink/web_tests/http/tests/devtools/unit/binary-resource-view.js
index 34d8ca4c..75cf8b8b 100644
--- a/third_party/blink/web_tests/http/tests/devtools/unit/binary-resource-view.js
+++ b/third_party/blink/web_tests/http/tests/devtools/unit/binary-resource-view.js
@@ -3,7 +3,6 @@
       'Verifies that BinaryResourceViewFactory interprets base64 data correctly');
   TestRunner.addResult('');
 
-  await TestRunner.loadModule('source_frame');
   await TestRunner.loadLegacyModule('source_frame');
   const base64content =
       'c2VuZGluZyB0aGlzIHV0Zi04IHN0cmluZyBhcyBhIGJpbmFyeSBtZXNzYWdlLi4u';
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/cookies-protocol-test-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/network/cookies-protocol-test-expected.txt
index 12d35dc..6c8aa4a 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/network/cookies-protocol-test-expected.txt
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/cookies-protocol-test-expected.txt
@@ -182,3 +182,32 @@
 
 Running test: deleteAllCookies
 
+Running test: setPartitionedCookie
+Setting Cookie
+setCookie failed: Partitioned cookies disabled. Cannot set cookie partition key
+Num of cookies 0
+Setting Cookie
+setCookie failed: Partitioned cookies disabled. Cannot set cookie partition key
+Num of cookies 0
+
+Running test: deleteAllCookies
+
+Running test: logCookies
+Num of cookies 0
+
+Running test: setPartitionedCookies
+Adding multiple cookies
+Num of cookies 0
+
+Running test: getPartitionedCookie
+Num of cookies 1
+name: __Host-foo, value: bar, domain: example.test, path: /, secure, session, None
+
+Running test: deleteAllCookies
+
+Running test: getPartitionedCookieFromOpaqueOrigin
+Num of cookies 1
+name: __Host-foo, value: bar, domain: example.test, path: /, secure, session, None
+
+Running test: deleteAllCookies
+
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/cookies-protocol-test.js b/third_party/blink/web_tests/http/tests/inspector-protocol/network/cookies-protocol-test.js
index 51d043c..d045fdf 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/network/cookies-protocol-test.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/cookies-protocol-test.js
@@ -1,13 +1,18 @@
 (async function(testRunner) {
+
   var {page, session, dp} = await testRunner.startBlank(
       `Tests that cookies are set, updated and removed.`);
 
-  async function logCookies() {
-    var data = (await dp.Network.getAllCookies()).result;
+  async function logCookies(opt_data) {
+    var data = opt_data || (await dp.Network.getAllCookies()).result;
     testRunner.log('Num of cookies ' + data.cookies.length);
     data.cookies.sort((a, b) => a.name.localeCompare(b.name));
     for (var cookie of data.cookies) {
       var suffix = ''
+      if (cookie.partitionKeyOpaque)
+        suffix += `, partitionKey: <opaque>`;
+      else if (cookie.partitionKey)
+        suffix += `, partitionKey: ${cookie.partitionKey}`;
       if (cookie.secure)
         suffix += `, secure`;
       if (cookie.httpOnly)
@@ -226,5 +231,34 @@
     printCookieViaFetch,
 
     deleteAllCookies,
+
+    async function setPartitionedCookie() {
+      await setCookie({url: 'https://devtools.test:8443', secure: true, name: '__Host-foo', value: 'bar', partitionKey: 'https://example.test:8443'});
+      await setCookie({url: 'https://example.test:8443', secure: true, name: '__Host-foo', value: 'bar', partitionKey: 'https://devtools.test:8443'});
+    },
+
+    deleteAllCookies,
+    logCookies,
+
+    async function setPartitionedCookies() {
+      await setCookies([
+        {url: 'https://devtools.test:8443', secure: true, name: '__Host-foo', value: 'bar', partitionKey: 'https://example.test:8443'},
+        {url: 'https://example.test:8443', secure: true, name: '__Host-foo', value: 'bar', partitionKey: 'https://devtools.test:8443'}
+      ]);
+    },
+
+    async function getPartitionedCookie() {
+      await page.navigate('https://devtools.test:8443/inspector-protocol/resources/iframe-third-party-cookie-parent.php');
+      logCookies((await dp.Network.getCookies()).result);
+    },
+
+    deleteAllCookies,
+
+    async function getPartitionedCookieFromOpaqueOrigin() {
+      await page.navigate('https://devtools.test:8443/inspector-protocol/resources/iframe-third-party-cookie-parent.php?opaque');
+      logCookies((await dp.Network.getCookies()).result);
+    },
+
+    deleteAllCookies,
   ]);
 })
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/websocket/slow-network.js b/third_party/blink/web_tests/http/tests/inspector-protocol/network/websocket/slow-network.js
index d19b9b6..1871a0d 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/network/websocket/slow-network.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/websocket/slow-network.js
@@ -17,6 +17,7 @@
           ws.onopen = () => {
             startTime = performance.now();
             ws.send('x'.repeat(2000));
+            ws.send('x'.repeat(2000));
           };
           ws.onmessage = () => { resolve(performance.now() - startTime); }
           ws.onerror = () => log += 'onerror ';
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/resources/iframe-third-party-cookie-child.php b/third_party/blink/web_tests/http/tests/inspector-protocol/resources/iframe-third-party-cookie-child.php
new file mode 100644
index 0000000..7477d1b3
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/resources/iframe-third-party-cookie-child.php
@@ -0,0 +1,4 @@
+<?php
+header('Content-Type: text/html; charset=UTF-8');
+header('Set-Cookie: __Host-foo=bar; Secure; Path=/; SameSite=None; Partitioned');
+?>
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/resources/iframe-third-party-cookie-parent.php b/third_party/blink/web_tests/http/tests/inspector-protocol/resources/iframe-third-party-cookie-parent.php
new file mode 100644
index 0000000..7d43e3d
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/resources/iframe-third-party-cookie-parent.php
@@ -0,0 +1,6 @@
+<?php
+if(isset($_GET['opaque'])) {
+  header("Content-Security-Policy: sandbox;");
+}
+?>
+<iframe src="https://example.test:8443/inspector-protocol/resources/iframe-third-party-cookie-child.php"></iframe>
diff --git a/third_party/blink/web_tests/http/tests/security/frameNavigation/xss-DENIED-top-navigation-user-gesture-in-parent-expected.txt b/third_party/blink/web_tests/http/tests/security/frameNavigation/xss-DENIED-top-navigation-user-gesture-in-parent-expected.txt
deleted file mode 100644
index ee18088..0000000
--- a/third_party/blink/web_tests/http/tests/security/frameNavigation/xss-DENIED-top-navigation-user-gesture-in-parent-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
---------
-Frame: '<!--framePath //<!--frame0-->-->'
---------
-The navigation should fail. This text should be visible.
diff --git a/third_party/blink/web_tests/http/tests/security/frameNavigation/xss-DENIED-top-navigation-user-gesture-in-parent.html b/third_party/blink/web_tests/http/tests/security/frameNavigation/xss-DENIED-top-navigation-user-gesture-in-parent.html
deleted file mode 100644
index f473720..0000000
--- a/third_party/blink/web_tests/http/tests/security/frameNavigation/xss-DENIED-top-navigation-user-gesture-in-parent.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<html>
-<body>
-<script>
-if (window.testRunner) {
-    testRunner.dumpAsText();
-    testRunner.dumpChildFrames();
-    testRunner.setDumpConsoleMessages(false);
-    testRunner.waitUntilDone();
-}
-
-// Ensure a user gesture happened in the main frame, but not in the iframe.
-if (window.eventSender) {
-    eventSender.mouseMoveTo(0, 0);
-    eventSender.mouseDown(0, 0);
-    eventSender.mouseUp(0, 0);
-}
-
-window.addEventListener("message", e => {
-  if (e.data == "PASS")
-    setTimeout(_ => { testRunner.notifyDone(); }, 500); // Give the schedule blocking navigation time to fire.
-  else
-    testRunner.testFailed("'top.location' didn't throw.");
-});
-</script>
-<iframe src="http://localhost:8000/security/frameNavigation/resources/iframe-that-performs-top-navigation-without-user-gesture.html"></iframe>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/http/tests/security/frameNavigation/xss-DENIED-top-navigation-without-user-gesture-expected.txt b/third_party/blink/web_tests/http/tests/security/frameNavigation/xss-DENIED-top-navigation-without-user-gesture-expected.txt
deleted file mode 100644
index b6161c3..0000000
--- a/third_party/blink/web_tests/http/tests/security/frameNavigation/xss-DENIED-top-navigation-without-user-gesture-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-CONSOLE ERROR: line 8: Unsafe attempt to initiate navigation for frame with URL 'http://127.0.0.1:8000/security/frameNavigation/xss-DENIED-top-navigation-without-user-gesture.html' from frame with URL 'http://localhost:8000/security/frameNavigation/resources/iframe-that-performs-top-navigation-without-user-gesture.html'. The frame attempting navigation is targeting its top-level window, but is neither same-origin with its target nor has it received a user gesture. See https://www.chromestatus.com/feature/5851021045661696.
-
-
-
---------
-Frame: '<!--framePath //<!--frame0-->-->'
---------
-The navigation should fail. This text should be visible.
diff --git a/third_party/blink/web_tests/http/tests/security/frameNavigation/xss-DENIED-top-navigation-without-user-gesture.html b/third_party/blink/web_tests/http/tests/security/frameNavigation/xss-DENIED-top-navigation-without-user-gesture.html
deleted file mode 100644
index 379a7b7..0000000
--- a/third_party/blink/web_tests/http/tests/security/frameNavigation/xss-DENIED-top-navigation-without-user-gesture.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<html>
-<head>
-<script>
-if (window.testRunner) {
-    testRunner.dumpAsText();
-    testRunner.dumpChildFrames();
-    testRunner.setDumpConsoleMessages(true);
-    testRunner.waitUntilDone();
-}
-
-window.addEventListener("message", e => {
-  if (e.data == "PASS")
-    testRunner.notifyDone();
-  else
-    testRunner.testFailed("'top.location' didn't throw.");
-});
-</script>
-</head>
-<body>
-<iframe src="http://localhost:8000/security/frameNavigation/resources/iframe-that-performs-top-navigation-without-user-gesture.html"></iframe>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/http/tests/security/isolatedWorld/window-setTimeout-function.html b/third_party/blink/web_tests/http/tests/security/isolatedWorld/window-setTimeout-function.html
index f35ae5e..c68ed92 100644
--- a/third_party/blink/web_tests/http/tests/security/isolatedWorld/window-setTimeout-function.html
+++ b/third_party/blink/web_tests/http/tests/security/isolatedWorld/window-setTimeout-function.html
@@ -7,11 +7,19 @@
   if (window.testRunner)
     testRunner.notifyDone();
 }
+
+addEventListener('message', (msg) => {
+  if (msg.data === 'done')
+    done();
+});
+
 window.func = function () {
   alert("FAIL: Wrong function.");
   done();
 };
+
 document.foo = "FAIL: Wrong wrappers.";
+
 if (window.testRunner) {
   testRunner.dumpAsText();
   testRunner.waitUntilDone();
@@ -20,7 +28,7 @@
     "document.foo = 'PASS';\n" +
     "window.func = function () {\n" +
     "  alert(document.foo);\n" +
-    "  window.location = 'javascript:done()';\n" +
+    "  window.postMessage('done', '*');\n" +
     "};\n" +
     "window.setTimeout(func, 0);");
 }
diff --git a/third_party/blink/web_tests/http/tests/security/isolatedWorld/window-setTimeout-string.html b/third_party/blink/web_tests/http/tests/security/isolatedWorld/window-setTimeout-string.html
index 9e33827..3f44aabf 100644
--- a/third_party/blink/web_tests/http/tests/security/isolatedWorld/window-setTimeout-string.html
+++ b/third_party/blink/web_tests/http/tests/security/isolatedWorld/window-setTimeout-string.html
@@ -7,11 +7,19 @@
   if (window.testRunner)
     testRunner.notifyDone();
 }
+
+addEventListener('message', (msg) => {
+  if (msg.data === 'done')
+    done();
+});
+
 window.func = function () {
   alert("FAIL: Wrong function.");
   done();
 };
+
 document.foo = "FAIL: Wrong wrappers.";
+
 if (window.testRunner) {
   testRunner.dumpAsText();
   testRunner.waitUntilDone();
@@ -20,7 +28,7 @@
     "document.foo = 'PASS';\n" +
     "window.func = function () {\n" +
     "  alert(document.foo);\n" +
-    "  window.location = 'javascript:done()';\n" +
+    "  window.postMessage('done', '*');\n" +
     "};\n" +
     "window.setTimeout('func()', 0);");
 }
diff --git a/third_party/blink/web_tests/platform/linux/virtual/text-antialias/colrv1-samples-visual-expected.png b/third_party/blink/web_tests/platform/linux/virtual/text-antialias/colrv1-samples-visual-expected.png
new file mode 100644
index 0000000..99093e16
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/text-antialias/colrv1-samples-visual-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/text-antialias/colrv1-samples-visual-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/text-antialias/colrv1-samples-visual-expected.png
new file mode 100644
index 0000000..aa42e1c
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/text-antialias/colrv1-samples-visual-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/text-antialias/colrv1-samples-visual-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/text-antialias/colrv1-samples-visual-expected.png
new file mode 100644
index 0000000..3c0dc1d
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/text-antialias/colrv1-samples-visual-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/text-antialias/colrv1-samples-visual-expected.png b/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/text-antialias/colrv1-samples-visual-expected.png
new file mode 100644
index 0000000..0611f9e
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/text-antialias/colrv1-samples-visual-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/text-antialias/colrv1-samples-visual-expected.png b/third_party/blink/web_tests/platform/mac/virtual/text-antialias/colrv1-samples-visual-expected.png
new file mode 100644
index 0000000..73e6dc1
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/text-antialias/colrv1-samples-visual-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/text-antialias/colrv1-samples-visual-expected.png b/third_party/blink/web_tests/platform/win/virtual/text-antialias/colrv1-samples-visual-expected.png
new file mode 100644
index 0000000..2976a17
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/text-antialias/colrv1-samples-visual-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/text-antialias/colrv1-samples-visual-expected.png b/third_party/blink/web_tests/platform/win7/virtual/text-antialias/colrv1-samples-visual-expected.png
new file mode 100644
index 0000000..bdb4559
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/virtual/text-antialias/colrv1-samples-visual-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/partitioned-cookies/http/tests/inspector-protocol/network/cookies-protocol-test-expected.txt b/third_party/blink/web_tests/virtual/partitioned-cookies/http/tests/inspector-protocol/network/cookies-protocol-test-expected.txt
new file mode 100644
index 0000000..6a214b0
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/partitioned-cookies/http/tests/inspector-protocol/network/cookies-protocol-test-expected.txt
@@ -0,0 +1,216 @@
+Tests that cookies are set, updated and removed.
+Test started
+Enabling network
+
+Running test: simpleCookieAdd
+Setting Cookie
+Num of cookies 1
+name: foo, value: bar1, domain: 127.0.0.1, path: /, session
+
+Running test: simpleCookieChange
+Setting Cookie
+Num of cookies 1
+name: foo, value: second bar2, domain: 127.0.0.1, path: /, session
+
+Running test: anotherSimpleCookieAdd
+Setting Cookie
+Num of cookies 2
+name: foo, value: second bar2, domain: 127.0.0.1, path: /, session
+name: foo2, value: bar1, domain: 127.0.0.1, path: /, session
+
+Running test: simpleCookieDelete
+Deleting Cookie
+Num of cookies 1
+name: foo2, value: bar1, domain: 127.0.0.1, path: /, session
+
+Running test: deleteAllCookies
+
+Running test: sessionCookieAdd
+Setting Cookie
+Num of cookies 1
+name: foo, value: bar4, domain: 127.0.0.1, path: /, session
+
+Running test: deleteAllCookies
+
+Running test: nonSessionCookieZeroAdd
+Setting Cookie
+Num of cookies 0
+
+Running test: deleteAllCookies
+
+Running test: nonSessionCookieAdd
+Setting Cookie
+Num of cookies 1
+name: foo, value: bar6, domain: 127.0.0.1, path: /, expires
+
+Running test: deleteAllCookies
+
+Running test: differentOriginCookieAdd
+Setting Cookie
+Num of cookies 1
+name: foo, value: bar7, domain: example.com, path: /, session
+
+Running test: deleteAllCookies
+
+Running test: invalidCookieAddDomain
+Setting Cookie
+setCookie failed: URL must have scheme http or https
+Num of cookies 0
+
+Running test: deleteAllCookies
+
+Running test: invalidCookieAddName
+Setting Cookie
+setCookie failed: Sanitizing cookie failed
+Num of cookies 0
+
+Running test: deleteAllCookies
+
+Running test: invalidCookieSourceScheme
+Setting Cookie
+setCookie failed: Invalid cookie source scheme
+Num of cookies 0
+
+Running test: deleteAllCookies
+
+Running test: invalidCookieSourcePort
+Setting Cookie
+setCookie failed: Invalid source port
+Num of cookies 0
+
+Running test: deleteAllCookies
+
+Running test: secureCookieAdd
+Setting Cookie
+Num of cookies 1
+name: foo, value: bar, domain: 127.0.0.1, path: /, secure, session
+
+Running test: deleteAllCookies
+
+Running test: cookieAddHttpOnly
+Setting Cookie
+Num of cookies 1
+name: foo, value: bar, domain: 127.0.0.1, path: /, httpOnly, session
+
+Running test: deleteAllCookies
+
+Running test: cookieAddSameSiteLax
+Setting Cookie
+Num of cookies 1
+name: foo, value: bar, domain: 127.0.0.1, path: /, session, Lax
+
+Running test: deleteAllCookies
+
+Running test: cookieAddSameSiteLax
+Setting Cookie
+Num of cookies 1
+name: foo, value: bar, domain: 127.0.0.1, path: /, session, Strict
+
+Running test: deleteAllCookies
+
+Running test: setCookiesBasic
+Adding multiple cookies
+Num of cookies 8
+name: cookie1, value: session, domain: localhost, path: /, session
+name: cookie2, value: httpOnly, domain: localhost, path: /, httpOnly, session
+name: cookie3, value: secure, domain: localhost, path: /, secure, session
+name: cookie4, value: lax, domain: localhost, path: /, session, Lax
+name: cookie5, value: expires, domain: localhost, path: /, expires
+name: cookie6, value: .domain, domain: .chromium.org, path: /path, session
+name: cookie7, value: domain, domain: www.chromium.org, path: /path, session
+name: cookie8, value: url-based, domain: www.chromium.org, path: /, secure, session
+
+Running test: deleteAllCookies
+
+Running test: setCookiesWithInvalidCookie
+Adding multiple cookies
+Num of cookies 0
+
+Running test: deleteAllCookies
+
+Running test: deleteCookieByURL
+Adding multiple cookies
+Num of cookies 2
+name: cookie1, value: .domain, domain: www.chromium.org, path: /, session
+name: cookie2, value: .domain, domain: www.chromium.org, path: /, expires
+Deleting Cookie
+Num of cookies 1
+name: cookie2, value: .domain, domain: www.chromium.org, path: /, expires
+
+Running test: deleteAllCookies
+
+Running test: deleteCookieByDomain
+Adding multiple cookies
+Num of cookies 2
+name: cookie1, value: .domain, domain: .chromium.org, path: /path, session
+name: cookie2, value: .domain, domain: .chromium.org, path: /path, expires
+Deleting Cookie
+Num of cookies 1
+name: cookie2, value: .domain, domain: .chromium.org, path: /path, expires
+Deleting Cookie
+Num of cookies 0
+
+Running test: deleteAllCookies
+
+Running test: deleteCookieByDomainAndPath
+Adding multiple cookies
+Num of cookies 1
+name: cookie1, value: .domain, domain: .chromium.org, path: /path, session
+Deleting Cookie
+Num of cookies 1
+name: cookie1, value: .domain, domain: .chromium.org, path: /path, session
+Deleting Cookie
+Num of cookies 0
+
+Running test: deleteAllCookies
+
+Running test: nonUnicodeCookie
+Adding multiple cookies
+Num of cookies 1
+name: cookie1, value: привет, domain: .chromium.org, path: /path, session
+
+Running test: deleteAllCookies
+
+Running test: setCookieViaFetch
+Num of cookies 1
+name: name, value: value, domain: 127.0.0.1, path: /inspector-protocol/network/resources, session
+
+Running test: deleteAllCookies
+
+Running test: printCookieViaFetch
+Cookies as seen on server: "HTTP_COOKIE: foo=bar1\n"
+
+Running test: deleteAllCookies
+
+Running test: setPartitionedCookie
+Setting Cookie
+Num of cookies 1
+name: __Host-foo, value: bar, domain: devtools.test, path: /, partitionKey: https://example.test, secure, session
+Setting Cookie
+Num of cookies 2
+name: __Host-foo, value: bar, domain: devtools.test, path: /, partitionKey: https://example.test, secure, session
+name: __Host-foo, value: bar, domain: example.test, path: /, partitionKey: https://devtools.test, secure, session
+
+Running test: deleteAllCookies
+
+Running test: logCookies
+Num of cookies 0
+
+Running test: setPartitionedCookies
+Adding multiple cookies
+Num of cookies 2
+name: __Host-foo, value: bar, domain: devtools.test, path: /, partitionKey: https://example.test, secure, session
+name: __Host-foo, value: bar, domain: example.test, path: /, partitionKey: https://devtools.test, secure, session
+
+Running test: getPartitionedCookie
+Num of cookies 1
+name: __Host-foo, value: bar, domain: example.test, path: /, partitionKey: https://devtools.test, secure, session, None
+
+Running test: deleteAllCookies
+
+Running test: getPartitionedCookieFromOpaqueOrigin
+Num of cookies 1
+name: __Host-foo, value: bar, domain: example.test, path: /, partitionKey: <opaque>, secure, session, None
+
+Running test: deleteAllCookies
+
diff --git a/third_party/blink/web_tests/virtual/text-antialias/colrv1-samples-visual.html b/third_party/blink/web_tests/virtual/text-antialias/colrv1-samples-visual.html
new file mode 100644
index 0000000..91691199
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/text-antialias/colrv1-samples-visual.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<title>COLRv1 test font visual rendering test</title>
+<link rel="help" href="https://www.w3.org/TR/css-fonts-4/#font-face-src-parsing" />
+<link rel="author" href="drott@chromium.org" />
+<meta name="assert" content="Checks that COLRv1 glyphs are displayed in color." />
+<style>
+@font-face {
+  font-family: colrv1_samples;
+  src: url(resources/more_samples-glyf_colr_1.ttf);
+}
+
+.colrv1 {
+  font: 40px/1 colrv1_samples, monospace;
+  word-break: break-all;
+}
+</style>
+Test passes if only color glyphs are seen below. Basic sanity check for COLRv1 rendering, additional testing is done in
+the Skia GM test for COLRv1, see Skia's gm/colrv1.cpp for details.
+
+<div class="colrv1">adefg<br>hijklm<br>nopqrs<br>tuvw<br>xyzABC<br>DEFG<br>HIJKL<br>MNOPQRS</div>
+
+The following two rows must feature purple (first row) and green (second row) in the glyph gradients.
+<div class="colrv1" style="color: purple;">TUVWXYZ</div>
+<div class="colrv1" style="color: green;">TUVWXYZ</div>
diff --git a/third_party/blink/web_tests/virtual/text-antialias/resources/more_samples-glyf_colr_1.ttf b/third_party/blink/web_tests/virtual/text-antialias/resources/more_samples-glyf_colr_1.ttf
new file mode 100644
index 0000000..55d4415
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/text-antialias/resources/more_samples-glyf_colr_1.ttf
Binary files differ
diff --git a/third_party/private_membership/BUILD.gn b/third_party/private_membership/BUILD.gn
index 34c76a02..b022f3d 100644
--- a/third_party/private_membership/BUILD.gn
+++ b/third_party/private_membership/BUILD.gn
@@ -9,6 +9,16 @@
 config("private_membership_config") {
   # TODO(crbug.com/1072732) Reenable the deprecated warning, after updating the upstream.
   cflags = [ "-Wno-deprecated-declarations" ]
+  defines = []
+  if (is_component_build) {
+    defines = [
+      # Build targets inside private_membership will need this macro to
+      # be defined in order to correctly export symbols when is_component_build
+      # is true.
+      # For more info see: base/private_membership_export.h.
+      "PRIVATE_MEMBERSHIP_ENABLE_SYMBOL_EXPORT",
+    ]
+  }
   include_dirs = [
     # Allow includes to be prefixed with shell-encryption/src/ in case it is not an
     # immediate subdirectory of the top-level.
@@ -31,16 +41,24 @@
     "src/private_membership_rlwe.proto",
   ]
   import_dirs = [ "//third_party/shell-encryption/src" ]
+  extra_configs = [ ":private_membership_config" ]
+
   proto_in_dir = "src/"
+  cc_generator_options = "dllexport_decl=PRIVATE_MEMBERSHIP_EXPORT:"
+  cc_include = "third_party/private_membership/base/private_membership_export.h"
+  component_build_force_source_set = true
+
   link_deps = [ "//third_party/shell-encryption:serialization_proto" ]
 }
 
 if (is_chromeos_ash) {
-  source_set("private_membership") {
+  component("private_membership") {
     public_configs = [ ":private_membership_config" ]
     configs -= [ "//build/config/compiler:chromium_code" ]
     configs += [ "//build/config/compiler:no_chromium_code" ]
+
     public = [
+      "base/private_membership_export.h",
       "src/internal/aes_ctr_256_with_fixed_iv.h",
       "src/internal/constants.h",
       "src/internal/crypto_utils.h",
diff --git a/third_party/private_membership/base/private_membership_export.h b/third_party/private_membership/base/private_membership_export.h
new file mode 100644
index 0000000..58ff63f
--- /dev/null
+++ b/third_party/private_membership/base/private_membership_export.h
@@ -0,0 +1,28 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRIVATE_MEMBERSHIP_BASE_PRIVATE_MEMBERSHIP_EXPORT_H_
+#define PRIVATE_MEMBERSHIP_BASE_PRIVATE_MEMBERSHIP_EXPORT_H_
+
+// PRIVATE_MEMBERSHIP_EXPORT is used to mark symbols as imported or
+// exported when private_membership is built or used as a shared library.
+// When private_membership is built as a static library the
+// PRIVATE_MEMBERSHIP_EXPORT macro expands to nothing.
+//
+// This export macros doesn't support Windows. There will be additional
+// component build work to support Windows (see crbug.com/1269714).
+
+#ifdef PRIVATE_MEMBERSHIP_ENABLE_SYMBOL_EXPORT
+
+#if __has_attribute(visibility)
+#define PRIVATE_MEMBERSHIP_EXPORT __attribute__((visibility("default")))
+#endif
+
+#endif  // PRIVATE_MEMBERSHIP_ENABLE_SYMBOL_EXPORT
+
+#ifndef PRIVATE_MEMBERSHIP_EXPORT
+#define PRIVATE_MEMBERSHIP_EXPORT
+#endif
+
+#endif  // PRIVATE_MEMBERSHIP_BASE_PRIVATE_MEMBERSHIP_EXPORT_H_
diff --git a/third_party/private_membership/patches/0002-Support-PSM-component-build.patch b/third_party/private_membership/patches/0002-Support-PSM-component-build.patch
new file mode 100644
index 0000000..4e42b83
--- /dev/null
+++ b/third_party/private_membership/patches/0002-Support-PSM-component-build.patch
@@ -0,0 +1,69 @@
+diff --git a/membership_response_map.h b/membership_response_map.h
+index dc56c4bbe148a..d8233d94e63c3 100644
+--- a/membership_response_map.h
++++ b/membership_response_map.h
+@@ -15,6 +15,7 @@
+ #ifndef THIRD_PARTY_PRIVATE_MEMBERSHIP_SRC_MEMBERSHIP_RESPONSE_MAP_H_
+ #define THIRD_PARTY_PRIVATE_MEMBERSHIP_SRC_MEMBERSHIP_RESPONSE_MAP_H_
+ 
++#include "third_party/private_membership/base/private_membership_export.h"
+ #include "third_party/private_membership/src/private_membership.pb.h"
+ #include "third_party/private_membership/src/private_membership_rlwe.pb.h"
+ #include "absl/container/flat_hash_map.h"
+@@ -22,7 +23,7 @@
+ namespace private_membership {
+ namespace rlwe {
+ 
+-class MembershipResponseMap {
++class PRIVATE_MEMBERSHIP_EXPORT MembershipResponseMap {
+  public:
+   MembershipResponseMap() = default;
+ 
+diff --git a/private_membership_rlwe_client.h b/private_membership_rlwe_client.h
+index 888fe190094ac..533a517c1a155 100644
+--- a/private_membership_rlwe_client.h
++++ b/private_membership_rlwe_client.h
+@@ -15,6 +15,7 @@
+ #ifndef THIRD_PARTY_PRIVATE_MEMBERSHIP_SRC_PRIVATE_MEMBERSHIP_RLWE_CLIENT_H_
+ #define THIRD_PARTY_PRIVATE_MEMBERSHIP_SRC_PRIVATE_MEMBERSHIP_RLWE_CLIENT_H_
+ 
++#include "third_party/private_membership/base/private_membership_export.h"
+ #include "third_party/private-join-and-compute/src/crypto/ec_commutative_cipher.h"
+ #include "third_party/private_membership/src/private_membership.pb.h"
+ #include "third_party/private_membership/src/membership_response_map.h"
+@@ -30,7 +31,7 @@ namespace rlwe {
+ namespace internal {
+ 
+ // PRNG seed generator which supports deterministic seed generation.
+-class PrngSeedGenerator {
++class PRIVATE_MEMBERSHIP_EXPORT PrngSeedGenerator {
+  public:
+   // Creates a non deterministic PRNG seed generator.
+   static std::unique_ptr<PrngSeedGenerator> Create();
+@@ -54,7 +55,7 @@ class PrngSeedGenerator {
+ 
+ // Lightweight wrapper for processing PIR related requests and responses.
+ // Thread safe.
+-class PirClient {
++class PRIVATE_MEMBERSHIP_EXPORT PirClient {
+  public:
+   virtual ~PirClient() = default;
+ 
+@@ -79,7 +80,7 @@ class PirClient {
+ 
+ // Thread safe.
+ template <typename ModularInt>
+-class PirClientImpl : public PirClient {
++class PRIVATE_MEMBERSHIP_EXPORT PirClientImpl : public PirClient {
+  public:
+   static ::rlwe::StatusOr<std::unique_ptr<PirClientImpl<ModularInt>>> Create(
+       const RlweParameters& rlwe_params, int total_entry_count,
+@@ -142,7 +143,7 @@ class PirClientImpl : public PirClient {
+ }  // namespace internal
+ 
+ // Client for the Private Membership RLWE protocol.
+-class PrivateMembershipRlweClient {
++class PRIVATE_MEMBERSHIP_EXPORT PrivateMembershipRlweClient {
+  public:
+   // PrivateMembershipRlweClient is neither copyable nor copy assignable.
+   PrivateMembershipRlweClient(const PrivateMembershipRlweClient&) = delete;
diff --git a/third_party/private_membership/src/membership_response_map.h b/third_party/private_membership/src/membership_response_map.h
index dc56c4b..d8233d9 100644
--- a/third_party/private_membership/src/membership_response_map.h
+++ b/third_party/private_membership/src/membership_response_map.h
@@ -15,6 +15,7 @@
 #ifndef THIRD_PARTY_PRIVATE_MEMBERSHIP_SRC_MEMBERSHIP_RESPONSE_MAP_H_
 #define THIRD_PARTY_PRIVATE_MEMBERSHIP_SRC_MEMBERSHIP_RESPONSE_MAP_H_
 
+#include "third_party/private_membership/base/private_membership_export.h"
 #include "third_party/private_membership/src/private_membership.pb.h"
 #include "third_party/private_membership/src/private_membership_rlwe.pb.h"
 #include "absl/container/flat_hash_map.h"
@@ -22,7 +23,7 @@
 namespace private_membership {
 namespace rlwe {
 
-class MembershipResponseMap {
+class PRIVATE_MEMBERSHIP_EXPORT MembershipResponseMap {
  public:
   MembershipResponseMap() = default;
 
diff --git a/third_party/private_membership/src/private_membership_rlwe_client.h b/third_party/private_membership/src/private_membership_rlwe_client.h
index 888fe19..533a517 100644
--- a/third_party/private_membership/src/private_membership_rlwe_client.h
+++ b/third_party/private_membership/src/private_membership_rlwe_client.h
@@ -15,6 +15,7 @@
 #ifndef THIRD_PARTY_PRIVATE_MEMBERSHIP_SRC_PRIVATE_MEMBERSHIP_RLWE_CLIENT_H_
 #define THIRD_PARTY_PRIVATE_MEMBERSHIP_SRC_PRIVATE_MEMBERSHIP_RLWE_CLIENT_H_
 
+#include "third_party/private_membership/base/private_membership_export.h"
 #include "third_party/private-join-and-compute/src/crypto/ec_commutative_cipher.h"
 #include "third_party/private_membership/src/private_membership.pb.h"
 #include "third_party/private_membership/src/membership_response_map.h"
@@ -30,7 +31,7 @@
 namespace internal {
 
 // PRNG seed generator which supports deterministic seed generation.
-class PrngSeedGenerator {
+class PRIVATE_MEMBERSHIP_EXPORT PrngSeedGenerator {
  public:
   // Creates a non deterministic PRNG seed generator.
   static std::unique_ptr<PrngSeedGenerator> Create();
@@ -54,7 +55,7 @@
 
 // Lightweight wrapper for processing PIR related requests and responses.
 // Thread safe.
-class PirClient {
+class PRIVATE_MEMBERSHIP_EXPORT PirClient {
  public:
   virtual ~PirClient() = default;
 
@@ -79,7 +80,7 @@
 
 // Thread safe.
 template <typename ModularInt>
-class PirClientImpl : public PirClient {
+class PRIVATE_MEMBERSHIP_EXPORT PirClientImpl : public PirClient {
  public:
   static ::rlwe::StatusOr<std::unique_ptr<PirClientImpl<ModularInt>>> Create(
       const RlweParameters& rlwe_params, int total_entry_count,
@@ -142,7 +143,7 @@
 }  // namespace internal
 
 // Client for the Private Membership RLWE protocol.
-class PrivateMembershipRlweClient {
+class PRIVATE_MEMBERSHIP_EXPORT PrivateMembershipRlweClient {
  public:
   // PrivateMembershipRlweClient is neither copyable nor copy assignable.
   PrivateMembershipRlweClient(const PrivateMembershipRlweClient&) = delete;
diff --git a/third_party/shell-encryption/BUILD.gn b/third_party/shell-encryption/BUILD.gn
index 91095005..2f92906 100644
--- a/third_party/shell-encryption/BUILD.gn
+++ b/third_party/shell-encryption/BUILD.gn
@@ -15,6 +15,17 @@
     "-Wno-ignored-qualifiers",
   ]
 
+  defines = []
+  if (is_component_build) {
+    defines = [
+      # Build targets inside shell-encryption will need this macro to
+      # be defined in order to correctly export symbols when is_component_build
+      # is true.
+      # For more info see: base/shell_encryption_export.h.
+      "SHELL_ENCRYPTION_ENABLE_SYMBOL_EXPORT",
+    ]
+  }
+
   include_dirs = [
     # Allow includes to be prefixed with shell-encryption/src/ in case it is not an
     # immediate subdirectory of the top-level.
@@ -39,20 +50,30 @@
 proto_library("serialization_proto") {
   sources = [ "src/serialization.proto" ]
   proto_in_dir = "src/"
+
+  cc_generator_options = "dllexport_decl=SHELL_ENCRYPTION_EXPORT:"
+  cc_include = "third_party/shell-encryption/base/shell_encryption_export.h"
+  component_build_force_source_set = true
 }
 
 proto_library("coefficient_polynomial_proto") {
   sources = [ "src/testing/coefficient_polynomial.proto" ]
   proto_in_dir = "src/testing/"
+
+  cc_generator_options = "dllexport_decl=SHELL_ENCRYPTION_EXPORT:"
+  cc_include = "third_party/shell-encryption/base/shell_encryption_export.h"
+  component_build_force_source_set = true
 }
 
 # SHELL lib.
 if (is_chromeos_ash) {
-  source_set("shell_encryption") {
+  component("shell_encryption") {
     public_configs = [ ":shell_encryption_config1" ]
     configs -= [ "//build/config/compiler:chromium_code" ]
     configs += [ "//build/config/compiler:no_chromium_code" ]
     public = [
+      "base/shell_encryption_export.h",
+      "base/shell_encryption_export_template.h",
       "glog/logging.h",
       "src/bits_util.h",
       "src/constants.h",
@@ -116,6 +137,7 @@
     ]
     public_deps = [
       ":coefficient_polynomial_proto",
+      ":serialization_proto",
       ":shell_encryption",
       "//testing/gmock:gmock",
       "//testing/gtest:gtest",
diff --git a/third_party/shell-encryption/base/shell_encryption_export.h b/third_party/shell-encryption/base/shell_encryption_export.h
new file mode 100644
index 0000000..72670c3
--- /dev/null
+++ b/third_party/shell-encryption/base/shell_encryption_export.h
@@ -0,0 +1,28 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_ENCRYPTION_BASE_SHELL_ENCRYPTION_EXPORT_H_
+#define SHELL_ENCRYPTION_BASE_SHELL_ENCRYPTION_EXPORT_H_
+
+// SHELL_ENCRYPTION_EXPORT is used to mark symbols as imported or
+// exported when shell-encryption is built or used as a shared library.
+// When shell-encryption is built as a static library the
+// SHELL_ENCRYPTION_EXPORT macro expands to nothing.
+//
+// This export macros doesn't support Windows. There will be additional
+// component build work to support Windows (see crbug.com/1269714).
+
+#ifdef SHELL_ENCRYPTION_ENABLE_SYMBOL_EXPORT
+
+#if __has_attribute(visibility)
+#define SHELL_ENCRYPTION_EXPORT __attribute__((visibility("default")))
+#endif
+
+#endif  // SHELL_ENCRYPTION_ENABLE_SYMBOL_EXPORT
+
+#ifndef SHELL_ENCRYPTION_EXPORT
+#define SHELL_ENCRYPTION_EXPORT
+#endif
+
+#endif  // SHELL_ENCRYPTION_BASE_SHELL_ENCRYPTION_EXPORT_H_
diff --git a/third_party/shell-encryption/base/shell_encryption_export_template.h b/third_party/shell-encryption/base/shell_encryption_export_template.h
new file mode 100644
index 0000000..1feb536
--- /dev/null
+++ b/third_party/shell-encryption/base/shell_encryption_export_template.h
@@ -0,0 +1,152 @@
+// 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 SHELL_ENCRYPTION_BASE_SHELL_ENCRYPTION_EXPORT_TEMPLATE_H_
+#define SHELL_ENCRYPTION_BASE_SHELL_ENCRYPTION_EXPORT_TEMPLATE_H_
+
+// This was borrowed from Chromium's base/export_template.h.
+//
+// TODO(crbug.com/1271458): Use shell-encryption template macro's name
+// instead of generic Chromium macro names.
+
+// Synopsis
+//
+// This header provides macros for using FOO_EXPORT macros with explicit
+// template instantiation declarations and definitions.
+// Generally, the FOO_EXPORT macros are used at declarations,
+// and GCC requires them to be used at explicit instantiation declarations,
+// but MSVC requires __declspec(dllexport) to be used at the explicit
+// instantiation definitions instead.
+
+// Usage
+//
+// In a header file, write:
+//
+//   extern template class EXPORT_TEMPLATE_DECLARE(FOO_EXPORT) foo<bar>;
+//
+// In a source file, write:
+//
+//   template class EXPORT_TEMPLATE_DEFINE(FOO_EXPORT) foo<bar>;
+
+// Implementation notes
+//
+// On Windows, when building the FOO library (that is, when FOO_EXPORT expands
+// to __declspec(dllexport)), we want the two lines to expand to:
+//
+//     extern template class foo<bar>;
+//     template class FOO_EXPORT foo<bar>;
+//
+// In all other cases (non-Windows, and Windows when using the FOO library (that
+// is when FOO_EXPORT expands to __declspec(dllimport)), we want:
+//
+//     extern template class FOO_EXPORT foo<bar>;
+//     template class foo<bar>;
+//
+// The implementation of this header uses some subtle macro semantics to
+// detect what the provided FOO_EXPORT value was defined as and then
+// to dispatch to appropriate macro definitions.
+
+#define EXPORT_TEMPLATE_DECLARE(foo_export) \
+  EXPORT_TEMPLATE_INVOKE(DECLARE, EXPORT_TEMPLATE_STYLE(foo_export), foo_export)
+#define EXPORT_TEMPLATE_DEFINE(foo_export) \
+  EXPORT_TEMPLATE_INVOKE(DEFINE, EXPORT_TEMPLATE_STYLE(foo_export), foo_export)
+
+// INVOKE is an internal helper macro to perform parameter replacements
+// and token pasting to chain invoke another macro.  E.g.,
+//     EXPORT_TEMPLATE_INVOKE(DECLARE, DEFAULT, FOO_EXPORT)
+// will expand to call
+//     EXPORT_TEMPLATE_DECLARE_DEFAULT(FOO_EXPORT)
+// (but with FOO_EXPORT expanded too).
+#define EXPORT_TEMPLATE_INVOKE(which, style, foo_export) \
+  EXPORT_TEMPLATE_INVOKE_2(which, style, foo_export)
+#define EXPORT_TEMPLATE_INVOKE_2(which, style, foo_export) \
+  EXPORT_TEMPLATE_##which##_##style(foo_export)
+
+// Default style is to apply the FOO_EXPORT macro at declaration sites.
+#define EXPORT_TEMPLATE_DECLARE_DEFAULT(foo_export) foo_export
+#define EXPORT_TEMPLATE_DEFINE_DEFAULT(foo_export)
+
+// The "declspec" style is used when FOO_EXPORT is defined
+// as __declspec(dllexport), which MSVC requires to be used at
+// definition sites instead.
+#define EXPORT_TEMPLATE_DECLARE_EXPORT_DLLEXPORT(foo_export)
+#define EXPORT_TEMPLATE_DEFINE_EXPORT_DLLEXPORT(foo_export) foo_export
+
+// EXPORT_TEMPLATE_STYLE is an internal helper macro that identifies which
+// export style needs to be used for the provided FOO_EXPORT macro definition.
+// "", "__attribute__(...)", and "__declspec(dllimport)" are mapped
+// to "DEFAULT"; while "__declspec(dllexport)" is mapped to "EXPORT_DLLEXPORT".
+// (NaCl headers define "DLLEXPORT" already, else we'd use that.
+// TODO(thakis): Rename once nacl is gone.)
+//
+// It's implemented with token pasting to transform the __attribute__ and
+// __declspec annotations into macro invocations.  E.g., if FOO_EXPORT is
+// defined as "__declspec(dllimport)", it undergoes the following sequence of
+// macro substitutions:
+//     EXPORT_TEMPLATE_STYLE(FOO_EXPORT)
+//     EXPORT_TEMPLATE_STYLE_2(__declspec(dllimport))
+//     EXPORT_TEMPLATE_STYLE_MATCH__declspec(dllimport)
+//     EXPORT_TEMPLATE_STYLE_MATCH_DECLSPEC_dllimport
+//     DEFAULT
+#define EXPORT_TEMPLATE_STYLE(foo_export) EXPORT_TEMPLATE_STYLE_2(foo_export)
+#define EXPORT_TEMPLATE_STYLE_2(foo_export) \
+  EXPORT_TEMPLATE_STYLE_MATCH_foj3FJo5StF0OvIzl7oMxA##foo_export
+
+// Internal helper macros for EXPORT_TEMPLATE_STYLE.
+//
+// XXX: C++ reserves all identifiers containing "__" for the implementation,
+// but "__attribute__" and "__declspec" already contain "__" and the token-paste
+// operator can only add characters; not remove them.  To minimize the risk of
+// conflict with implementations, we include "foj3FJo5StF0OvIzl7oMxA" (a random
+// 128-bit string, encoded in Base64) in the macro name.
+#define EXPORT_TEMPLATE_STYLE_MATCH_foj3FJo5StF0OvIzl7oMxA DEFAULT
+#define EXPORT_TEMPLATE_STYLE_MATCH_foj3FJo5StF0OvIzl7oMxA__attribute__(...) \
+  DEFAULT
+#define EXPORT_TEMPLATE_STYLE_MATCH_foj3FJo5StF0OvIzl7oMxA__declspec(arg) \
+  EXPORT_TEMPLATE_STYLE_MATCH_DECLSPEC_##arg
+
+// Internal helper macros for EXPORT_TEMPLATE_STYLE.
+#define EXPORT_TEMPLATE_STYLE_MATCH_DECLSPEC_dllexport EXPORT_DLLEXPORT
+#define EXPORT_TEMPLATE_STYLE_MATCH_DECLSPEC_dllimport DEFAULT
+
+// Sanity checks.
+//
+// EXPORT_TEMPLATE_TEST uses the same macro invocation pattern as
+// EXPORT_TEMPLATE_DECLARE and EXPORT_TEMPLATE_DEFINE do to check that they're
+// working correctly.  When they're working correctly, the sequence of macro
+// replacements should go something like:
+//
+//     EXPORT_TEMPLATE_TEST(DEFAULT, __declspec(dllimport));
+//
+//     static_assert(EXPORT_TEMPLATE_INVOKE(TEST_DEFAULT,
+//         EXPORT_TEMPLATE_STYLE(__declspec(dllimport)),
+//         __declspec(dllimport)), "__declspec(dllimport)");
+//
+//     static_assert(EXPORT_TEMPLATE_INVOKE(TEST_DEFAULT,
+//         DEFAULT, __declspec(dllimport)), "__declspec(dllimport)");
+//
+//     static_assert(EXPORT_TEMPLATE_TEST_DEFAULT_DEFAULT(
+//         __declspec(dllimport)), "__declspec(dllimport)");
+//
+//     static_assert(true, "__declspec(dllimport)");
+//
+// When they're not working correctly, a syntax error should occur instead.
+#define EXPORT_TEMPLATE_TEST(want, foo_export)                               \
+  static_assert(                                                             \
+      EXPORT_TEMPLATE_INVOKE(TEST_##want, EXPORT_TEMPLATE_STYLE(foo_export), \
+                             foo_export),                                    \
+      #foo_export)
+#define EXPORT_TEMPLATE_TEST_DEFAULT_DEFAULT(...) true
+#define EXPORT_TEMPLATE_TEST_EXPORT_DLLEXPORT_EXPORT_DLLEXPORT(...) true
+
+EXPORT_TEMPLATE_TEST(DEFAULT, );
+EXPORT_TEMPLATE_TEST(DEFAULT, __attribute__((visibility("default"))));
+EXPORT_TEMPLATE_TEST(EXPORT_DLLEXPORT, __declspec(dllexport));
+EXPORT_TEMPLATE_TEST(DEFAULT, __declspec(dllimport));
+
+#undef EXPORT_TEMPLATE_TEST
+#undef EXPORT_TEMPLATE_TEST_DEFAULT_DEFAULT
+#undef EXPORT_TEMPLATE_TEST_EXPORT_DLLEXPORT_EXPORT_DLLEXPORT
+
+#endif  // SHELL_ENCRYPTION_BASE_SHELL_ENCRYPTION_EXPORT_TEMPLATE_H_
diff --git a/third_party/shell-encryption/patches/0004-Support-SHELL-component-build.patch b/third_party/shell-encryption/patches/0004-Support-SHELL-component-build.patch
new file mode 100644
index 0000000..55072fa3
--- /dev/null
+++ b/third_party/shell-encryption/patches/0004-Support-SHELL-component-build.patch
@@ -0,0 +1,655 @@
+diff --git a/galois_key.h b/galois_key.h
+index 0b73a63eff4d0..e6295f1082748 100644
+--- a/galois_key.h
++++ b/galois_key.h
+@@ -23,6 +23,8 @@
+ #include "relinearization_key.h"
+ #include "status_macros.h"
+ #include "statusor.h"
++#include "third_party/shell-encryption/base/shell_encryption_export.h"
++#include "third_party/shell-encryption/base/shell_encryption_export_template.h"
+ 
+ namespace rlwe {
+ 
+@@ -41,7 +43,7 @@ namespace rlwe {
+ //
+ // Details can be found in Appendix D.2 of https://eprint.iacr.org/2011/566.pdf
+ template <typename ModularInt>
+-class GaloisKey {
++class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) GaloisKey {
+  public:
+   // Initializes a GaloisKey based on a SymmetricRlweKey key that can key-switch
+   // two component ciphertexts. A positive log_decomposition_modulus corresponds
+@@ -49,7 +51,7 @@ class GaloisKey {
+   // power of x in the secret key polynomial s(x^substitution_power) that the
+   // ciphertext is encrypted with. The prng_seed is used to generate and encode
+   // the bottom row of the matrix, which consists of random entries.
+-  static rlwe::StatusOr<GaloisKey> Create(
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<GaloisKey> Create(
+       const SymmetricRlweKey<ModularInt>& key, absl::string_view prng_seed,
+       Uint64 substitution_power, Uint64 log_decomposition_modulus) {
+     RLWE_ASSIGN_OR_RETURN(auto relinearization_key,
+@@ -88,7 +90,7 @@ class GaloisKey {
+   // SerializedGaloisKey is (2 * num_parts * dimension) where dimension is the
+   // number of digits needed to represent the modulus in base
+   // 2^{log_decomposition_modulus}. Crashes for non-valid input parameters.
+-  static rlwe::StatusOr<GaloisKey> Deserialize(
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<GaloisKey> Deserialize(
+       const SerializedGaloisKey& serialized,
+       const typename ModularInt::Params* modulus_params,
+       const NttParameters<ModularInt>* ntt_params) {
+@@ -110,6 +112,14 @@ class GaloisKey {
+   RelinearizationKey<ModularInt> relinearization_key_;
+ };
+ 
++template class EXPORT_TEMPLATE_DECLARE(
++    SHELL_ENCRYPTION_EXPORT) GaloisKey<rlwe::MontgomeryInt<Uint16>>;
++template class EXPORT_TEMPLATE_DECLARE(
++    SHELL_ENCRYPTION_EXPORT) GaloisKey<rlwe::MontgomeryInt<Uint32>>;
++template class EXPORT_TEMPLATE_DECLARE(
++    SHELL_ENCRYPTION_EXPORT) GaloisKey<rlwe::MontgomeryInt<Uint64>>;
++template class EXPORT_TEMPLATE_DECLARE(
++    SHELL_ENCRYPTION_EXPORT) GaloisKey<rlwe::MontgomeryInt<absl::uint128>>;
+ }  //  namespace rlwe
+ 
+ #endif  // RLWE_GALOIS_KEY_H_
+diff --git a/int256.h b/int256.h
+index 540dce2d765b1..4c464a4978412 100644
+--- a/int256.h
++++ b/int256.h
+@@ -18,13 +18,15 @@
+ 
+ #include "absl/numeric/int128.h"
+ #include "integral_types.h"
++#include "third_party/shell-encryption/base/shell_encryption_export.h"
++#include "third_party/shell-encryption/base/shell_encryption_export_template.h"
+ 
+ namespace rlwe {
+ 
+ struct uint256_pod;
+ 
+ // An unsigned 256-bit integer type. Thread-compatible.
+-class uint256 {
++class SHELL_ENCRYPTION_EXPORT uint256 {
+  public:
+   constexpr uint256();
+   constexpr uint256(absl::uint128 top, absl::uint128 bottom);
+@@ -74,11 +76,11 @@ class uint256 {
+   uint256& operator-=(const uint256& b);
+   uint256& operator*=(const uint256& b);
+   // Long division/modulo for uint256.
+-  uint256& operator/=(const uint256& b);
+-  uint256& operator%=(const uint256& b);
++  SHELL_ENCRYPTION_EXPORT uint256& operator/=(const uint256& b);
++  SHELL_ENCRYPTION_EXPORT uint256& operator%=(const uint256& b);
+   uint256 operator++(int);
+   uint256 operator--(int);
+-  uint256& operator<<=(int);
++  SHELL_ENCRYPTION_EXPORT uint256& operator<<=(int);
+   uint256& operator>>=(int);
+   uint256& operator&=(const uint256& b);
+   uint256& operator|=(const uint256& b);
+@@ -90,7 +92,7 @@ class uint256 {
+   friend absl::uint128 Uint256High128(const uint256& v);
+ 
+   // We add "std::" to avoid including all of port.h.
+-  friend std::ostream& operator<<(std::ostream& o, const uint256& b);
++  friend SHELL_ENCRYPTION_EXPORT std::ostream& operator<<(std::ostream& o, const uint256& b);
+ 
+  private:
+   static void DivModImpl(uint256 dividend, uint256 divisor,
+@@ -121,7 +123,7 @@ constexpr uint256 Uint256Max() {
+ 
+ // This is a POD form of uint256 which can be used for static variables which
+ // need to be operated on as uint256.
+-struct uint256_pod {
++struct SHELL_ENCRYPTION_EXPORT uint256_pod {
+   // Note: The ordering of fields is different than 'class uint256' but the
+   // same as its 2-arg constructor.  This enables more obvious initialization
+   // of static instances, which is the primary reason for this struct in the
+diff --git a/montgomery.cc b/montgomery.cc
+index 9fbc5dabee18c..4bd03362420fc 100644
+--- a/montgomery.cc
++++ b/montgomery.cc
+@@ -14,6 +14,8 @@
+ 
+ #include "montgomery.h"
+ 
++#include "third_party/shell-encryption/base/shell_encryption_export.h"
++#include "third_party/shell-encryption/base/shell_encryption_export_template.h"
+ #include "transcription.h"
+ 
+ namespace rlwe {
+@@ -416,12 +418,12 @@ MontgomeryInt<T> MontgomeryInt<T>::MultiplicativeInverse(
+ 
+ // Instantiations of MontgomeryInt and MontgomeryIntParams with specific
+ // integral types.
+-template struct MontgomeryIntParams<Uint16>;
+-template struct MontgomeryIntParams<Uint32>;
+-template struct MontgomeryIntParams<Uint64>;
+-template struct MontgomeryIntParams<absl::uint128>;
+-template class MontgomeryInt<Uint16>;
+-template class MontgomeryInt<Uint32>;
+-template class MontgomeryInt<Uint64>;
+-template class MontgomeryInt<absl::uint128>;
++template struct EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams<Uint16>;
++template struct EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams<Uint32>;
++template struct EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams<Uint64>;
++template struct EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams<absl::uint128>;
++template class EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) MontgomeryInt<Uint16>;
++template class EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) MontgomeryInt<Uint32>;
++template class EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) MontgomeryInt<Uint64>;
++template class EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) MontgomeryInt<absl::uint128>;
+ }  // namespace rlwe
+diff --git a/montgomery.h b/montgomery.h
+index 4f0e2eafb815f..c3988ff34da34 100644
+--- a/montgomery.h
++++ b/montgomery.h
+@@ -34,6 +34,8 @@
+ #include "serialization.pb.h"
+ #include "status_macros.h"
+ #include "statusor.h"
++#include "third_party/shell-encryption/base/shell_encryption_export.h"
++#include "third_party/shell-encryption/base/shell_encryption_export_template.h"
+ 
+ namespace rlwe {
+ 
+@@ -43,24 +45,24 @@ namespace internal {
+ template <typename T>
+ struct BigInt;
+ // Specialization for uint8, uint16, uint32, uint64, and uint128.
+-template <>
+-struct BigInt<Uint8> {
++template <> 
++struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) BigInt<Uint8> {
+   typedef Uint16 value_type;
+ };
+-template <>
+-struct BigInt<Uint16> {
++template <> 
++struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) BigInt<Uint16> {
+   typedef Uint32 value_type;
+ };
+-template <>
+-struct BigInt<Uint32> {
++template <> 
++struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) BigInt<Uint32> {
+   typedef Uint64 value_type;
+ };
+-template <>
+-struct BigInt<Uint64> {
++template <> 
++struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) BigInt<Uint64> {
+   typedef absl::uint128 value_type;
+ };
+-template <>
+-struct BigInt<absl::uint128> {
++template <> 
++struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) BigInt<absl::uint128> {
+   typedef uint256 value_type;
+ };
+ 
+@@ -69,7 +71,7 @@ struct BigInt<absl::uint128> {
+ // The parameters necessary for a Montgomery integer. Note that the template
+ // parameters ensure that T is an unsigned integral of at least 8 bits.
+ template <typename T>
+-struct MontgomeryIntParams {
++struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams{
+   // Expose Int and its greater type. BigInt is required in order to multiply
+   // two Int and ensure that no overflow occurs.
+   //
+@@ -159,7 +161,7 @@ struct MontgomeryIntParams {
+   // modulus must be odd.
+   // Returns a tuple of (inv_r, inv_modulus) such that:
+   //     r * inv_r - modulus * inv_modulus = 1
+-  static std::tuple<Int, Int> Inverses(BigInt modulus_bigint, BigInt r);
++  static SHELL_ENCRYPTION_EXPORT std::tuple<Int, Int> Inverses(BigInt modulus_bigint, BigInt r);
+ };
+ 
+ // Stores an integer in Montgomery representation. The goal of this
+@@ -170,7 +172,7 @@ struct MontgomeryIntParams {
+ // The underlying integer type T must be unsigned and must not be bool.
+ // This class is thread safe.
+ template <typename T>
+-class ABSL_MUST_USE_RESULT MontgomeryInt {
++class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) ABSL_MUST_USE_RESULT MontgomeryInt {
+  public:
+   // Expose Int and its greater type. BigInt is required in order to multiply
+   // two Int and ensure that no overflow occurs. This should also be used by
+@@ -184,16 +186,16 @@ class ABSL_MUST_USE_RESULT MontgomeryInt {
+   // Static factory that converts a non-Montgomery representation integer, the
+   // underlying integer type, into a Montgomery representation integer. Does not
+   // take ownership of params. i.e., import "a".
+-  static rlwe::StatusOr<MontgomeryInt> ImportInt(Int n, const Params* params);
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<MontgomeryInt> ImportInt(Int n, const Params* params);
+ 
+   // Static functions to create a MontgomeryInt of 0 and 1.
+-  static MontgomeryInt ImportZero(const Params* params);
+-  static MontgomeryInt ImportOne(const Params* params);
++  static SHELL_ENCRYPTION_EXPORT MontgomeryInt ImportZero(const Params* params);
++  static SHELL_ENCRYPTION_EXPORT MontgomeryInt ImportOne(const Params* params);
+ 
+   // Import a random integer using entropy from specified prng. Does not take
+   // ownership of params or prng.
+   template <typename Prng = rlwe::SecurePrng>
+-  static rlwe::StatusOr<MontgomeryInt> ImportRandom(Prng* prng,
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<MontgomeryInt> ImportRandom(Prng* prng,
+                                                     const Params* params) {
+     // In order to generate unbiased randomness, we uniformly and randomly
+     // sample integers in [0, 2^params->log_modulus) until the generated integer
+@@ -234,13 +236,13 @@ class ABSL_MUST_USE_RESULT MontgomeryInt {
+ 
+   // Serialization.
+   rlwe::StatusOr<std::string> Serialize(const Params* params) const;
+-  static rlwe::StatusOr<std::string> SerializeVector(
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::string> SerializeVector(
+       const std::vector<MontgomeryInt>& coeffs, const Params* params);
+ 
+   // Deserialization.
+-  static rlwe::StatusOr<MontgomeryInt> Deserialize(absl::string_view payload,
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<MontgomeryInt> Deserialize(absl::string_view payload,
+                                                    const Params* params);
+-  static rlwe::StatusOr<std::vector<MontgomeryInt>> DeserializeVector(
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> DeserializeVector(
+       int num_coeffs, absl::string_view serialized, const Params* params);
+ 
+   // Modular multiplication.
+@@ -353,7 +355,7 @@ class ABSL_MUST_USE_RESULT MontgomeryInt {
+   // size.
+ 
+   // Batch addition of two vectors.
+-  static rlwe::StatusOr<std::vector<MontgomeryInt>> BatchAdd(
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> BatchAdd(
+       const std::vector<MontgomeryInt>& in1,
+       const std::vector<MontgomeryInt>& in2, const Params* params);
+   static absl::Status BatchAddInPlace(std::vector<MontgomeryInt>* in1,
+@@ -361,7 +363,7 @@ class ABSL_MUST_USE_RESULT MontgomeryInt {
+                                       const Params* params);
+ 
+   // Batch addition of one vector with a scalar.
+-  static rlwe::StatusOr<std::vector<MontgomeryInt>> BatchAdd(
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> BatchAdd(
+       const std::vector<MontgomeryInt>& in1, const MontgomeryInt& in2,
+       const Params* params);
+   static absl::Status BatchAddInPlace(std::vector<MontgomeryInt>* in1,
+@@ -369,51 +371,51 @@ class ABSL_MUST_USE_RESULT MontgomeryInt {
+                                       const Params* params);
+ 
+   // Batch subtraction of two vectors.
+-  static rlwe::StatusOr<std::vector<MontgomeryInt>> BatchSub(
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> BatchSub(
+       const std::vector<MontgomeryInt>& in1,
+       const std::vector<MontgomeryInt>& in2, const Params* params);
+-  static absl::Status BatchSubInPlace(std::vector<MontgomeryInt>* in1,
++  static SHELL_ENCRYPTION_EXPORT absl::Status BatchSubInPlace(std::vector<MontgomeryInt>* in1,
+                                       const std::vector<MontgomeryInt>& in2,
+                                       const Params* params);
+ 
+   // Batch subtraction of one vector with a scalar.
+-  static rlwe::StatusOr<std::vector<MontgomeryInt>> BatchSub(
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> BatchSub(
+       const std::vector<MontgomeryInt>& in1, const MontgomeryInt& in2,
+       const Params* params);
+-  static absl::Status BatchSubInPlace(std::vector<MontgomeryInt>* in1,
++  static SHELL_ENCRYPTION_EXPORT absl::Status BatchSubInPlace(std::vector<MontgomeryInt>* in1,
+                                       const MontgomeryInt& in2,
+                                       const Params* params);
+ 
+   // Batch multiplication of two vectors.
+-  static rlwe::StatusOr<std::vector<MontgomeryInt>> BatchMul(
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> BatchMul(
+       const std::vector<MontgomeryInt>& in1,
+       const std::vector<MontgomeryInt>& in2, const Params* params);
+-  static absl::Status BatchMulInPlace(std::vector<MontgomeryInt>* in1,
++  static SHELL_ENCRYPTION_EXPORT absl::Status BatchMulInPlace(std::vector<MontgomeryInt>* in1,
+                                       const std::vector<MontgomeryInt>& in2,
+                                       const Params* params);
+ 
+   // Batch multiplication of two vectors, where the second vector is a constant.
+-  static rlwe::StatusOr<std::vector<MontgomeryInt>> BatchMulConstant(
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> BatchMulConstant(
+       const std::vector<MontgomeryInt>& in1,
+       const std::vector<Int>& in2_constant,
+       const std::vector<Int>& in2_constant_barrett, const Params* params);
+-  static absl::Status BatchMulConstantInPlace(
++  static SHELL_ENCRYPTION_EXPORT absl::Status BatchMulConstantInPlace(
+       std::vector<MontgomeryInt>* in1, const std::vector<Int>& in2_constant,
+       const std::vector<Int>& in2_constant_barrett, const Params* params);
+ 
+   // Batch multiplication of a vector with a scalar.
+-  static rlwe::StatusOr<std::vector<MontgomeryInt>> BatchMul(
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> BatchMul(
+       const std::vector<MontgomeryInt>& in1, const MontgomeryInt& in2,
+       const Params* params);
+-  static absl::Status BatchMulInPlace(std::vector<MontgomeryInt>* in1,
++  static SHELL_ENCRYPTION_EXPORT absl::Status BatchMulInPlace(std::vector<MontgomeryInt>* in1,
+                                       const MontgomeryInt& in2,
+                                       const Params* params);
+ 
+   // Batch multiplication of a vector with a constant scalar.
+-  static rlwe::StatusOr<std::vector<MontgomeryInt>> BatchMulConstant(
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> BatchMulConstant(
+       const std::vector<MontgomeryInt>& in1, const Int& constant,
+       const Int& constant_barrett, const Params* params);
+-  static absl::Status BatchMulConstantInPlace(std::vector<MontgomeryInt>* in1,
++  static SHELL_ENCRYPTION_EXPORT absl::Status BatchMulConstantInPlace(std::vector<MontgomeryInt>* in1,
+                                               const Int& constant,
+                                               const Int& constant_barrett,
+                                               const Params* params);
+@@ -423,14 +425,14 @@ class ABSL_MUST_USE_RESULT MontgomeryInt {
+   bool operator!=(const MontgomeryInt& that) const { return !(*this == that); }
+ 
+   // Modular exponentiation.
+-  MontgomeryInt ModExp(Int exponent, const Params* params) const;
++  SHELL_ENCRYPTION_EXPORT MontgomeryInt ModExp(Int exponent, const Params* params) const;
+ 
+   // Inverse.
+-  MontgomeryInt MultiplicativeInverse(const Params* params) const;
++  SHELL_ENCRYPTION_EXPORT  MontgomeryInt MultiplicativeInverse(const Params* params) const;
+ 
+  private:
+   template <typename Prng = rlwe::SecurePrng>
+-  static rlwe::StatusOr<Int> GenerateRandomInt(int log_modulus, Prng* prng) {
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<Int> GenerateRandomInt(int log_modulus, Prng* prng) {
+     // Generate a random Int. As the modulus is always smaller than max(Int),
+     // there will be no issues with overflow.
+     int max_bits_per_step = std::min((int)Params::bitsize_int, (int)64);
+@@ -467,6 +469,17 @@ class ABSL_MUST_USE_RESULT MontgomeryInt {
+   Int n_;
+ };
+ 
++// Instantiations of MontgomeryInt and MontgomeryIntParams with specific
++// integral types.
++extern template struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams<Uint16>;
++extern template struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams<Uint32>;
++extern template struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams<Uint64>;
++extern template struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams<absl::uint128>;
++extern template class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryInt<Uint16>;
++extern template class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryInt<Uint32>;
++extern template class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryInt<Uint64>;
++extern template class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryInt<absl::uint128>;
++
+ }  // namespace rlwe
+ 
+ #endif  // RLWE_MONTGOMERY_H_
+diff --git a/montgomery_test.cc b/montgomery_test.cc
+index 7caf459a4c08b..2d952ddfbdad4 100644
+--- a/montgomery_test.cc
++++ b/montgomery_test.cc
+@@ -30,6 +30,8 @@
+ #include "testing/status_matchers.h"
+ #include "testing/status_testing.h"
+ #include "testing/testing_prng.h"
++#include "third_party/shell-encryption/base/shell_encryption_export.h"
++#include "third_party/shell-encryption/base/shell_encryption_export_template.h"
+ 
+ namespace rlwe {
+ namespace {
+@@ -65,7 +67,7 @@ uint256 GenerateRandom(unsigned int* seed) {
+ }
+ 
+ template <typename T>
+-class MontgomeryTest : public ::testing::Test {};
++class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryTest : public ::testing::Test {};
+ TYPED_TEST_SUITE(MontgomeryTest, testing::ModularIntTypes);
+ 
+ TYPED_TEST(MontgomeryTest, ModulusTooLarge) {
+diff --git a/ntt_parameters.h b/ntt_parameters.h
+index 55671ec5e65de..eba9579ab87ca 100644
+--- a/ntt_parameters.h
++++ b/ntt_parameters.h
+@@ -24,6 +24,7 @@
+ #include "constants.h"
+ #include "status_macros.h"
+ #include "statusor.h"
++#include "third_party/shell-encryption/base/shell_encryption_export.h"
+ 
+ namespace rlwe {
+ namespace internal {
+@@ -94,7 +95,7 @@ rlwe::StatusOr<std::vector<ModularInt>> NttPsis(
+ // Creates a vector containing the indices necessary to perform the NTT bit
+ // reversal operation. Index i of the returned vector contains an integer with
+ // the rightmost log_n bits of i reversed.
+-std::vector<unsigned int> BitrevArray(unsigned int log_n);
++SHELL_ENCRYPTION_EXPORT std::vector<unsigned int> BitrevArray(unsigned int log_n);
+ 
+ // Helper function: Perform the bit-reversal operation in-place on coeffs_.
+ template <typename ModularInt>
+diff --git a/prng/chacha_prng.h b/prng/chacha_prng.h
+index 27940fd82c646..7e4f719fedaf6 100644
+--- a/prng/chacha_prng.h
++++ b/prng/chacha_prng.h
+@@ -26,6 +26,7 @@
+ #include "prng/chacha_prng_util.h"
+ #include "prng/prng.h"
+ #include "statusor.h"
++#include "third_party/shell-encryption/base/shell_encryption_export.h"
+ 
+ namespace rlwe {
+ 
+@@ -56,7 +57,7 @@ class ChaChaPrng : public SecurePrng {
+   // errors.
+   //
+   // Thread safe.
+-  static rlwe::StatusOr<std::unique_ptr<ChaChaPrng>> Create(
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::unique_ptr<ChaChaPrng>> Create(
+       absl::string_view in_key);
+ 
+   // Returns 8 bits of randomness.
+@@ -72,12 +73,12 @@ class ChaChaPrng : public SecurePrng {
+   // Generate a valid seed for the Prng.
+   //
+   // Fails on internal cryptographic errors.
+-  static rlwe::StatusOr<std::string> GenerateSeed() {
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::string> GenerateSeed() {
+     return internal::ChaChaPrngGenerateKey();
+   }
+ 
+   // Output the size of the expected generated seed.
+-  static int SeedLength() { return internal::kChaChaKeyBytesSize; }
++  static SHELL_ENCRYPTION_EXPORT int SeedLength() { return internal::kChaChaKeyBytesSize; }
+ 
+  private:
+   explicit ChaChaPrng(absl::string_view in_key, int position_in_buffer,
+diff --git a/prng/chacha_prng_util.h b/prng/chacha_prng_util.h
+index 8eb8118fe26f9..80f0cedf4a703 100644
+--- a/prng/chacha_prng_util.h
++++ b/prng/chacha_prng_util.h
+@@ -27,6 +27,7 @@
+ #include "absl/strings/string_view.h"
+ #include "integral_types.h"
+ #include "statusor.h"
++#include "third_party/shell-encryption/base/shell_encryption_export.h"
+ 
+ namespace rlwe {
+ namespace internal {
+@@ -37,28 +38,33 @@ const int kChaChaOutputBytes = 255 * 32;
+ 
+ // Once pseudorandom output is exhausted, the salt is updated to construct
+ // new pseudorandom output.
+-absl::Status ChaChaPrngResalt(absl::string_view key, int buffer_size,
+-                              int* salt_counter, int* position_in_buffer,
+-                              std::vector<Uint8>* buffer);
++SHELL_ENCRYPTION_EXPORT absl::Status ChaChaPrngResalt(
++    absl::string_view key,
++    int buffer_size,
++    int* salt_counter,
++    int* position_in_buffer,
++    std::vector<Uint8>* buffer);
+ 
+ // Generates a secure key for instantiating an CHACHA.
+-rlwe::StatusOr<std::string> ChaChaPrngGenerateKey();
++SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::string> ChaChaPrngGenerateKey();
+ 
+ // Returns 8 bits of randomness.
+ //
+ // Fails on internal cryptographic errors.
+-rlwe::StatusOr<Uint8> ChaChaPrngRand8(absl::string_view key,
+-                                      int* position_in_buffer,
+-                                      int* salt_counter,
+-                                      std::vector<Uint8>* buffer);
++SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<Uint8> ChaChaPrngRand8(
++    absl::string_view key,
++    int* position_in_buffer,
++    int* salt_counter,
++    std::vector<Uint8>* buffer);
+ 
+ // Returns 64 bits of randomness.
+ //
+ // Fails on internal cryptographic errors.
+-rlwe::StatusOr<Uint64> ChaChaPrngRand64(absl::string_view key,
+-                                        int* position_in_buffer,
+-                                        int* salt_counter,
+-                                        std::vector<Uint8>* buffer);
++SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<Uint64> ChaChaPrngRand64(
++    absl::string_view key,
++    int* position_in_buffer,
++    int* salt_counter,
++    std::vector<Uint8>* buffer);
+ 
+ }  // namespace internal
+ }  // namespace rlwe
+diff --git a/prng/single_thread_chacha_prng.h b/prng/single_thread_chacha_prng.h
+index fcaff827be355..2fddf6f3530c4 100644
+--- a/prng/single_thread_chacha_prng.h
++++ b/prng/single_thread_chacha_prng.h
+@@ -24,6 +24,7 @@
+ #include "prng/chacha_prng_util.h"
+ #include "prng/prng.h"
+ #include "statusor.h"
++#include "third_party/shell-encryption/base/shell_encryption_export.h"
+ 
+ namespace rlwe {
+ 
+@@ -52,7 +53,7 @@ class SingleThreadChaChaPrng : public SecurePrng {
+   //
+   // Fails if the key is not the expected size or on internal cryptographic
+   // errors.
+-  static rlwe::StatusOr<std::unique_ptr<SingleThreadChaChaPrng>> Create(
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::unique_ptr<SingleThreadChaChaPrng>> Create(
+       absl::string_view in_key);
+ 
+   // Returns 8 bits of randomness.
+diff --git a/relinearization_key.cc b/relinearization_key.cc
+index 7982a2f71a74c..0b22a50e0abb7 100644
+--- a/relinearization_key.cc
++++ b/relinearization_key.cc
+@@ -22,6 +22,8 @@
+ #include "status_macros.h"
+ #include "statusor.h"
+ #include "symmetric_encryption_with_prng.h"
++#include "third_party/shell-encryption/base/shell_encryption_export.h"
++#include "third_party/shell-encryption/base/shell_encryption_export_template.h"
+ 
+ namespace rlwe {
+ namespace {
+@@ -432,9 +434,9 @@ RelinearizationKey<ModularInt>::Deserialize(
+ // Instantiations of RelinearizationKey with specific MontgomeryInt classes.
+ // If any new types are added, montgomery.h should be updated accordingly (such
+ // as ensuring BigInt is correctly specialized, etc.).
+-template class RelinearizationKey<MontgomeryInt<Uint16>>;
+-template class RelinearizationKey<MontgomeryInt<Uint32>>;
+-template class RelinearizationKey<MontgomeryInt<Uint64>>;
+-template class RelinearizationKey<MontgomeryInt<absl::uint128>>;
++template class EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey<MontgomeryInt<Uint16>>;
++template class EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey<MontgomeryInt<Uint32>>;
++template class EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey<MontgomeryInt<Uint64>>;
++template class EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey<MontgomeryInt<absl::uint128>>;
+ 
+ }  //  namespace rlwe
+diff --git a/relinearization_key.h b/relinearization_key.h
+index 265da33e5af15..1aff179b9ed39 100644
+--- a/relinearization_key.h
++++ b/relinearization_key.h
+@@ -22,6 +22,8 @@
+ #include "sample_error.h"
+ #include "statusor.h"
+ #include "symmetric_encryption.h"
++#include "third_party/shell-encryption/base/shell_encryption_export.h"
++#include "third_party/shell-encryption/base/shell_encryption_export_template.h"
+ 
+ namespace rlwe {
+ // Represents a RelinearizationKey constructed from a symmetric-key. Applying a
+@@ -57,7 +59,7 @@ namespace rlwe {
+ // length (k - 1), where k is the number of parts of the ciphertext it applies
+ // to.
+ template <typename ModularInt>
+-class RelinearizationKey {
++class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey {
+   using ModularIntParams = typename ModularInt::Params;
+ 
+  public:
+@@ -73,7 +75,7 @@ class RelinearizationKey {
+   // with (1, s^k). In that case, we would use a relinearization key with
+   // substition_power = k to return the ciphertext to be encrypted with (1,s).
+   // See GaloisKey for an explicit wrapper around RelinearizationKey.
+-  static rlwe::StatusOr<RelinearizationKey> Create(
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<RelinearizationKey> Create(
+       const SymmetricRlweKey<ModularInt>& key, absl::string_view prng_seed,
+       ssize_t num_parts, Uint64 log_decomposition_modulus,
+       Uint64 substitution_power = 1);
+@@ -192,6 +194,13 @@ class RelinearizationKey {
+   std::string prng_seed_;
+ };
+ 
++// Instantiations of RelinearizationKey with specific MontgomeryInt classes.
++// If any new types are added, montgomery.h should be updated accordingly (such
++// as ensuring BigInt is correctly specialized, etc.).
++extern template class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey<MontgomeryInt<Uint16>>;
++extern template class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey<MontgomeryInt<Uint32>>;
++extern template class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey<MontgomeryInt<Uint64>>;
++extern template class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey<MontgomeryInt<absl::uint128>>;
+ }  // namespace rlwe
+ 
+ #endif  // RLWE_RELINEARIZATION_KEY_H_
+diff --git a/statusor.h b/statusor.h
+index 200f62d917d0f..b7ada09372c9f 100644
+--- a/statusor.h
++++ b/statusor.h
+@@ -22,11 +22,12 @@
+ #include "absl/base/attributes.h"
+ #include "absl/status/status.h"
+ #include "absl/types/optional.h"
++#include "third_party/shell-encryption/base/shell_encryption_export.h"
+ 
+ namespace rlwe {
+ 
+ template <typename T>
+-class StatusOr {
++class SHELL_ENCRYPTION_EXPORT StatusOr {
+  public:
+   // Construct a new StatusOr with Status::UNKNOWN status
+   StatusOr();
+@@ -112,12 +113,12 @@ class StatusOr {
+ 
+ namespace internal {
+ 
+-class StatusOrHelper {
++class SHELL_ENCRYPTION_EXPORT StatusOrHelper {
+  public:
+   // Move type-agnostic error handling to the .cc.
+-  static absl::Status HandleInvalidStatusCtorArg();
+-  static absl::Status HandleNullObjectCtorArg();
+-  static void Crash(const absl::Status& status);
++  static SHELL_ENCRYPTION_EXPORT absl::Status HandleInvalidStatusCtorArg();
++  static SHELL_ENCRYPTION_EXPORT absl::Status HandleNullObjectCtorArg();
++  static SHELL_ENCRYPTION_EXPORT void Crash(const absl::Status& status);
+ 
+   // Customized behavior for StatusOr<T> vs. StatusOr<T*>
+   template <typename T>
+@@ -125,13 +126,13 @@ class StatusOrHelper {
+ };
+ 
+ template <typename T>
+-struct StatusOrHelper::Specialize {
++struct SHELL_ENCRYPTION_EXPORT StatusOrHelper::Specialize {
+   // For non-pointer T, a reference can never be NULL.
+   static inline bool IsValueNull(const T& t) { return false; }
+ };
+ 
+ template <typename T>
+-struct StatusOrHelper::Specialize<T*> {
++struct SHELL_ENCRYPTION_EXPORT StatusOrHelper::Specialize<T*> {
+   static inline bool IsValueNull(const T* t) { return t == nullptr; }
+ };
+ 
diff --git a/third_party/shell-encryption/src/galois_key.h b/third_party/shell-encryption/src/galois_key.h
index 0b73a63..e6295f1 100644
--- a/third_party/shell-encryption/src/galois_key.h
+++ b/third_party/shell-encryption/src/galois_key.h
@@ -23,6 +23,8 @@
 #include "relinearization_key.h"
 #include "status_macros.h"
 #include "statusor.h"
+#include "third_party/shell-encryption/base/shell_encryption_export.h"
+#include "third_party/shell-encryption/base/shell_encryption_export_template.h"
 
 namespace rlwe {
 
@@ -41,7 +43,7 @@
 //
 // Details can be found in Appendix D.2 of https://eprint.iacr.org/2011/566.pdf
 template <typename ModularInt>
-class GaloisKey {
+class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) GaloisKey {
  public:
   // Initializes a GaloisKey based on a SymmetricRlweKey key that can key-switch
   // two component ciphertexts. A positive log_decomposition_modulus corresponds
@@ -49,7 +51,7 @@
   // power of x in the secret key polynomial s(x^substitution_power) that the
   // ciphertext is encrypted with. The prng_seed is used to generate and encode
   // the bottom row of the matrix, which consists of random entries.
-  static rlwe::StatusOr<GaloisKey> Create(
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<GaloisKey> Create(
       const SymmetricRlweKey<ModularInt>& key, absl::string_view prng_seed,
       Uint64 substitution_power, Uint64 log_decomposition_modulus) {
     RLWE_ASSIGN_OR_RETURN(auto relinearization_key,
@@ -88,7 +90,7 @@
   // SerializedGaloisKey is (2 * num_parts * dimension) where dimension is the
   // number of digits needed to represent the modulus in base
   // 2^{log_decomposition_modulus}. Crashes for non-valid input parameters.
-  static rlwe::StatusOr<GaloisKey> Deserialize(
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<GaloisKey> Deserialize(
       const SerializedGaloisKey& serialized,
       const typename ModularInt::Params* modulus_params,
       const NttParameters<ModularInt>* ntt_params) {
@@ -110,6 +112,14 @@
   RelinearizationKey<ModularInt> relinearization_key_;
 };
 
+template class EXPORT_TEMPLATE_DECLARE(
+    SHELL_ENCRYPTION_EXPORT) GaloisKey<rlwe::MontgomeryInt<Uint16>>;
+template class EXPORT_TEMPLATE_DECLARE(
+    SHELL_ENCRYPTION_EXPORT) GaloisKey<rlwe::MontgomeryInt<Uint32>>;
+template class EXPORT_TEMPLATE_DECLARE(
+    SHELL_ENCRYPTION_EXPORT) GaloisKey<rlwe::MontgomeryInt<Uint64>>;
+template class EXPORT_TEMPLATE_DECLARE(
+    SHELL_ENCRYPTION_EXPORT) GaloisKey<rlwe::MontgomeryInt<absl::uint128>>;
 }  //  namespace rlwe
 
 #endif  // RLWE_GALOIS_KEY_H_
diff --git a/third_party/shell-encryption/src/int256.h b/third_party/shell-encryption/src/int256.h
index 540dce2d..4c464a4 100644
--- a/third_party/shell-encryption/src/int256.h
+++ b/third_party/shell-encryption/src/int256.h
@@ -18,13 +18,15 @@
 
 #include "absl/numeric/int128.h"
 #include "integral_types.h"
+#include "third_party/shell-encryption/base/shell_encryption_export.h"
+#include "third_party/shell-encryption/base/shell_encryption_export_template.h"
 
 namespace rlwe {
 
 struct uint256_pod;
 
 // An unsigned 256-bit integer type. Thread-compatible.
-class uint256 {
+class SHELL_ENCRYPTION_EXPORT uint256 {
  public:
   constexpr uint256();
   constexpr uint256(absl::uint128 top, absl::uint128 bottom);
@@ -74,11 +76,11 @@
   uint256& operator-=(const uint256& b);
   uint256& operator*=(const uint256& b);
   // Long division/modulo for uint256.
-  uint256& operator/=(const uint256& b);
-  uint256& operator%=(const uint256& b);
+  SHELL_ENCRYPTION_EXPORT uint256& operator/=(const uint256& b);
+  SHELL_ENCRYPTION_EXPORT uint256& operator%=(const uint256& b);
   uint256 operator++(int);
   uint256 operator--(int);
-  uint256& operator<<=(int);
+  SHELL_ENCRYPTION_EXPORT uint256& operator<<=(int);
   uint256& operator>>=(int);
   uint256& operator&=(const uint256& b);
   uint256& operator|=(const uint256& b);
@@ -90,7 +92,7 @@
   friend absl::uint128 Uint256High128(const uint256& v);
 
   // We add "std::" to avoid including all of port.h.
-  friend std::ostream& operator<<(std::ostream& o, const uint256& b);
+  friend SHELL_ENCRYPTION_EXPORT std::ostream& operator<<(std::ostream& o, const uint256& b);
 
  private:
   static void DivModImpl(uint256 dividend, uint256 divisor,
@@ -121,7 +123,7 @@
 
 // This is a POD form of uint256 which can be used for static variables which
 // need to be operated on as uint256.
-struct uint256_pod {
+struct SHELL_ENCRYPTION_EXPORT uint256_pod {
   // Note: The ordering of fields is different than 'class uint256' but the
   // same as its 2-arg constructor.  This enables more obvious initialization
   // of static instances, which is the primary reason for this struct in the
diff --git a/third_party/shell-encryption/src/montgomery.cc b/third_party/shell-encryption/src/montgomery.cc
index 9fbc5dab..4bd0336 100644
--- a/third_party/shell-encryption/src/montgomery.cc
+++ b/third_party/shell-encryption/src/montgomery.cc
@@ -14,6 +14,8 @@
 
 #include "montgomery.h"
 
+#include "third_party/shell-encryption/base/shell_encryption_export.h"
+#include "third_party/shell-encryption/base/shell_encryption_export_template.h"
 #include "transcription.h"
 
 namespace rlwe {
@@ -416,12 +418,12 @@
 
 // Instantiations of MontgomeryInt and MontgomeryIntParams with specific
 // integral types.
-template struct MontgomeryIntParams<Uint16>;
-template struct MontgomeryIntParams<Uint32>;
-template struct MontgomeryIntParams<Uint64>;
-template struct MontgomeryIntParams<absl::uint128>;
-template class MontgomeryInt<Uint16>;
-template class MontgomeryInt<Uint32>;
-template class MontgomeryInt<Uint64>;
-template class MontgomeryInt<absl::uint128>;
+template struct EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams<Uint16>;
+template struct EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams<Uint32>;
+template struct EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams<Uint64>;
+template struct EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams<absl::uint128>;
+template class EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) MontgomeryInt<Uint16>;
+template class EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) MontgomeryInt<Uint32>;
+template class EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) MontgomeryInt<Uint64>;
+template class EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) MontgomeryInt<absl::uint128>;
 }  // namespace rlwe
diff --git a/third_party/shell-encryption/src/montgomery.h b/third_party/shell-encryption/src/montgomery.h
index 4f0e2eaf..c3988ff3 100644
--- a/third_party/shell-encryption/src/montgomery.h
+++ b/third_party/shell-encryption/src/montgomery.h
@@ -34,6 +34,8 @@
 #include "serialization.pb.h"
 #include "status_macros.h"
 #include "statusor.h"
+#include "third_party/shell-encryption/base/shell_encryption_export.h"
+#include "third_party/shell-encryption/base/shell_encryption_export_template.h"
 
 namespace rlwe {
 
@@ -43,24 +45,24 @@
 template <typename T>
 struct BigInt;
 // Specialization for uint8, uint16, uint32, uint64, and uint128.
-template <>
-struct BigInt<Uint8> {
+template <> 
+struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) BigInt<Uint8> {
   typedef Uint16 value_type;
 };
-template <>
-struct BigInt<Uint16> {
+template <> 
+struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) BigInt<Uint16> {
   typedef Uint32 value_type;
 };
-template <>
-struct BigInt<Uint32> {
+template <> 
+struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) BigInt<Uint32> {
   typedef Uint64 value_type;
 };
-template <>
-struct BigInt<Uint64> {
+template <> 
+struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) BigInt<Uint64> {
   typedef absl::uint128 value_type;
 };
-template <>
-struct BigInt<absl::uint128> {
+template <> 
+struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) BigInt<absl::uint128> {
   typedef uint256 value_type;
 };
 
@@ -69,7 +71,7 @@
 // The parameters necessary for a Montgomery integer. Note that the template
 // parameters ensure that T is an unsigned integral of at least 8 bits.
 template <typename T>
-struct MontgomeryIntParams {
+struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams{
   // Expose Int and its greater type. BigInt is required in order to multiply
   // two Int and ensure that no overflow occurs.
   //
@@ -159,7 +161,7 @@
   // modulus must be odd.
   // Returns a tuple of (inv_r, inv_modulus) such that:
   //     r * inv_r - modulus * inv_modulus = 1
-  static std::tuple<Int, Int> Inverses(BigInt modulus_bigint, BigInt r);
+  static SHELL_ENCRYPTION_EXPORT std::tuple<Int, Int> Inverses(BigInt modulus_bigint, BigInt r);
 };
 
 // Stores an integer in Montgomery representation. The goal of this
@@ -170,7 +172,7 @@
 // The underlying integer type T must be unsigned and must not be bool.
 // This class is thread safe.
 template <typename T>
-class ABSL_MUST_USE_RESULT MontgomeryInt {
+class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) ABSL_MUST_USE_RESULT MontgomeryInt {
  public:
   // Expose Int and its greater type. BigInt is required in order to multiply
   // two Int and ensure that no overflow occurs. This should also be used by
@@ -184,16 +186,16 @@
   // Static factory that converts a non-Montgomery representation integer, the
   // underlying integer type, into a Montgomery representation integer. Does not
   // take ownership of params. i.e., import "a".
-  static rlwe::StatusOr<MontgomeryInt> ImportInt(Int n, const Params* params);
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<MontgomeryInt> ImportInt(Int n, const Params* params);
 
   // Static functions to create a MontgomeryInt of 0 and 1.
-  static MontgomeryInt ImportZero(const Params* params);
-  static MontgomeryInt ImportOne(const Params* params);
+  static SHELL_ENCRYPTION_EXPORT MontgomeryInt ImportZero(const Params* params);
+  static SHELL_ENCRYPTION_EXPORT MontgomeryInt ImportOne(const Params* params);
 
   // Import a random integer using entropy from specified prng. Does not take
   // ownership of params or prng.
   template <typename Prng = rlwe::SecurePrng>
-  static rlwe::StatusOr<MontgomeryInt> ImportRandom(Prng* prng,
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<MontgomeryInt> ImportRandom(Prng* prng,
                                                     const Params* params) {
     // In order to generate unbiased randomness, we uniformly and randomly
     // sample integers in [0, 2^params->log_modulus) until the generated integer
@@ -234,13 +236,13 @@
 
   // Serialization.
   rlwe::StatusOr<std::string> Serialize(const Params* params) const;
-  static rlwe::StatusOr<std::string> SerializeVector(
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::string> SerializeVector(
       const std::vector<MontgomeryInt>& coeffs, const Params* params);
 
   // Deserialization.
-  static rlwe::StatusOr<MontgomeryInt> Deserialize(absl::string_view payload,
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<MontgomeryInt> Deserialize(absl::string_view payload,
                                                    const Params* params);
-  static rlwe::StatusOr<std::vector<MontgomeryInt>> DeserializeVector(
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> DeserializeVector(
       int num_coeffs, absl::string_view serialized, const Params* params);
 
   // Modular multiplication.
@@ -353,7 +355,7 @@
   // size.
 
   // Batch addition of two vectors.
-  static rlwe::StatusOr<std::vector<MontgomeryInt>> BatchAdd(
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> BatchAdd(
       const std::vector<MontgomeryInt>& in1,
       const std::vector<MontgomeryInt>& in2, const Params* params);
   static absl::Status BatchAddInPlace(std::vector<MontgomeryInt>* in1,
@@ -361,7 +363,7 @@
                                       const Params* params);
 
   // Batch addition of one vector with a scalar.
-  static rlwe::StatusOr<std::vector<MontgomeryInt>> BatchAdd(
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> BatchAdd(
       const std::vector<MontgomeryInt>& in1, const MontgomeryInt& in2,
       const Params* params);
   static absl::Status BatchAddInPlace(std::vector<MontgomeryInt>* in1,
@@ -369,51 +371,51 @@
                                       const Params* params);
 
   // Batch subtraction of two vectors.
-  static rlwe::StatusOr<std::vector<MontgomeryInt>> BatchSub(
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> BatchSub(
       const std::vector<MontgomeryInt>& in1,
       const std::vector<MontgomeryInt>& in2, const Params* params);
-  static absl::Status BatchSubInPlace(std::vector<MontgomeryInt>* in1,
+  static SHELL_ENCRYPTION_EXPORT absl::Status BatchSubInPlace(std::vector<MontgomeryInt>* in1,
                                       const std::vector<MontgomeryInt>& in2,
                                       const Params* params);
 
   // Batch subtraction of one vector with a scalar.
-  static rlwe::StatusOr<std::vector<MontgomeryInt>> BatchSub(
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> BatchSub(
       const std::vector<MontgomeryInt>& in1, const MontgomeryInt& in2,
       const Params* params);
-  static absl::Status BatchSubInPlace(std::vector<MontgomeryInt>* in1,
+  static SHELL_ENCRYPTION_EXPORT absl::Status BatchSubInPlace(std::vector<MontgomeryInt>* in1,
                                       const MontgomeryInt& in2,
                                       const Params* params);
 
   // Batch multiplication of two vectors.
-  static rlwe::StatusOr<std::vector<MontgomeryInt>> BatchMul(
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> BatchMul(
       const std::vector<MontgomeryInt>& in1,
       const std::vector<MontgomeryInt>& in2, const Params* params);
-  static absl::Status BatchMulInPlace(std::vector<MontgomeryInt>* in1,
+  static SHELL_ENCRYPTION_EXPORT absl::Status BatchMulInPlace(std::vector<MontgomeryInt>* in1,
                                       const std::vector<MontgomeryInt>& in2,
                                       const Params* params);
 
   // Batch multiplication of two vectors, where the second vector is a constant.
-  static rlwe::StatusOr<std::vector<MontgomeryInt>> BatchMulConstant(
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> BatchMulConstant(
       const std::vector<MontgomeryInt>& in1,
       const std::vector<Int>& in2_constant,
       const std::vector<Int>& in2_constant_barrett, const Params* params);
-  static absl::Status BatchMulConstantInPlace(
+  static SHELL_ENCRYPTION_EXPORT absl::Status BatchMulConstantInPlace(
       std::vector<MontgomeryInt>* in1, const std::vector<Int>& in2_constant,
       const std::vector<Int>& in2_constant_barrett, const Params* params);
 
   // Batch multiplication of a vector with a scalar.
-  static rlwe::StatusOr<std::vector<MontgomeryInt>> BatchMul(
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> BatchMul(
       const std::vector<MontgomeryInt>& in1, const MontgomeryInt& in2,
       const Params* params);
-  static absl::Status BatchMulInPlace(std::vector<MontgomeryInt>* in1,
+  static SHELL_ENCRYPTION_EXPORT absl::Status BatchMulInPlace(std::vector<MontgomeryInt>* in1,
                                       const MontgomeryInt& in2,
                                       const Params* params);
 
   // Batch multiplication of a vector with a constant scalar.
-  static rlwe::StatusOr<std::vector<MontgomeryInt>> BatchMulConstant(
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> BatchMulConstant(
       const std::vector<MontgomeryInt>& in1, const Int& constant,
       const Int& constant_barrett, const Params* params);
-  static absl::Status BatchMulConstantInPlace(std::vector<MontgomeryInt>* in1,
+  static SHELL_ENCRYPTION_EXPORT absl::Status BatchMulConstantInPlace(std::vector<MontgomeryInt>* in1,
                                               const Int& constant,
                                               const Int& constant_barrett,
                                               const Params* params);
@@ -423,14 +425,14 @@
   bool operator!=(const MontgomeryInt& that) const { return !(*this == that); }
 
   // Modular exponentiation.
-  MontgomeryInt ModExp(Int exponent, const Params* params) const;
+  SHELL_ENCRYPTION_EXPORT MontgomeryInt ModExp(Int exponent, const Params* params) const;
 
   // Inverse.
-  MontgomeryInt MultiplicativeInverse(const Params* params) const;
+  SHELL_ENCRYPTION_EXPORT  MontgomeryInt MultiplicativeInverse(const Params* params) const;
 
  private:
   template <typename Prng = rlwe::SecurePrng>
-  static rlwe::StatusOr<Int> GenerateRandomInt(int log_modulus, Prng* prng) {
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<Int> GenerateRandomInt(int log_modulus, Prng* prng) {
     // Generate a random Int. As the modulus is always smaller than max(Int),
     // there will be no issues with overflow.
     int max_bits_per_step = std::min((int)Params::bitsize_int, (int)64);
@@ -467,6 +469,17 @@
   Int n_;
 };
 
+// Instantiations of MontgomeryInt and MontgomeryIntParams with specific
+// integral types.
+extern template struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams<Uint16>;
+extern template struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams<Uint32>;
+extern template struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams<Uint64>;
+extern template struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams<absl::uint128>;
+extern template class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryInt<Uint16>;
+extern template class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryInt<Uint32>;
+extern template class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryInt<Uint64>;
+extern template class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryInt<absl::uint128>;
+
 }  // namespace rlwe
 
 #endif  // RLWE_MONTGOMERY_H_
diff --git a/third_party/shell-encryption/src/montgomery_test.cc b/third_party/shell-encryption/src/montgomery_test.cc
index 7caf459a..2d952ddf 100644
--- a/third_party/shell-encryption/src/montgomery_test.cc
+++ b/third_party/shell-encryption/src/montgomery_test.cc
@@ -30,6 +30,8 @@
 #include "testing/status_matchers.h"
 #include "testing/status_testing.h"
 #include "testing/testing_prng.h"
+#include "third_party/shell-encryption/base/shell_encryption_export.h"
+#include "third_party/shell-encryption/base/shell_encryption_export_template.h"
 
 namespace rlwe {
 namespace {
@@ -65,7 +67,7 @@
 }
 
 template <typename T>
-class MontgomeryTest : public ::testing::Test {};
+class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryTest : public ::testing::Test {};
 TYPED_TEST_SUITE(MontgomeryTest, testing::ModularIntTypes);
 
 TYPED_TEST(MontgomeryTest, ModulusTooLarge) {
diff --git a/third_party/shell-encryption/src/ntt_parameters.h b/third_party/shell-encryption/src/ntt_parameters.h
index 55671ec..eba9579 100644
--- a/third_party/shell-encryption/src/ntt_parameters.h
+++ b/third_party/shell-encryption/src/ntt_parameters.h
@@ -24,6 +24,7 @@
 #include "constants.h"
 #include "status_macros.h"
 #include "statusor.h"
+#include "third_party/shell-encryption/base/shell_encryption_export.h"
 
 namespace rlwe {
 namespace internal {
@@ -94,7 +95,7 @@
 // Creates a vector containing the indices necessary to perform the NTT bit
 // reversal operation. Index i of the returned vector contains an integer with
 // the rightmost log_n bits of i reversed.
-std::vector<unsigned int> BitrevArray(unsigned int log_n);
+SHELL_ENCRYPTION_EXPORT std::vector<unsigned int> BitrevArray(unsigned int log_n);
 
 // Helper function: Perform the bit-reversal operation in-place on coeffs_.
 template <typename ModularInt>
diff --git a/third_party/shell-encryption/src/prng/chacha_prng.h b/third_party/shell-encryption/src/prng/chacha_prng.h
index 27940fd..7e4f719 100644
--- a/third_party/shell-encryption/src/prng/chacha_prng.h
+++ b/third_party/shell-encryption/src/prng/chacha_prng.h
@@ -26,6 +26,7 @@
 #include "prng/chacha_prng_util.h"
 #include "prng/prng.h"
 #include "statusor.h"
+#include "third_party/shell-encryption/base/shell_encryption_export.h"
 
 namespace rlwe {
 
@@ -56,7 +57,7 @@
   // errors.
   //
   // Thread safe.
-  static rlwe::StatusOr<std::unique_ptr<ChaChaPrng>> Create(
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::unique_ptr<ChaChaPrng>> Create(
       absl::string_view in_key);
 
   // Returns 8 bits of randomness.
@@ -72,12 +73,12 @@
   // Generate a valid seed for the Prng.
   //
   // Fails on internal cryptographic errors.
-  static rlwe::StatusOr<std::string> GenerateSeed() {
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::string> GenerateSeed() {
     return internal::ChaChaPrngGenerateKey();
   }
 
   // Output the size of the expected generated seed.
-  static int SeedLength() { return internal::kChaChaKeyBytesSize; }
+  static SHELL_ENCRYPTION_EXPORT int SeedLength() { return internal::kChaChaKeyBytesSize; }
 
  private:
   explicit ChaChaPrng(absl::string_view in_key, int position_in_buffer,
diff --git a/third_party/shell-encryption/src/prng/chacha_prng_util.h b/third_party/shell-encryption/src/prng/chacha_prng_util.h
index 8eb8118..80f0cedf 100644
--- a/third_party/shell-encryption/src/prng/chacha_prng_util.h
+++ b/third_party/shell-encryption/src/prng/chacha_prng_util.h
@@ -27,6 +27,7 @@
 #include "absl/strings/string_view.h"
 #include "integral_types.h"
 #include "statusor.h"
+#include "third_party/shell-encryption/base/shell_encryption_export.h"
 
 namespace rlwe {
 namespace internal {
@@ -37,28 +38,33 @@
 
 // Once pseudorandom output is exhausted, the salt is updated to construct
 // new pseudorandom output.
-absl::Status ChaChaPrngResalt(absl::string_view key, int buffer_size,
-                              int* salt_counter, int* position_in_buffer,
-                              std::vector<Uint8>* buffer);
+SHELL_ENCRYPTION_EXPORT absl::Status ChaChaPrngResalt(
+    absl::string_view key,
+    int buffer_size,
+    int* salt_counter,
+    int* position_in_buffer,
+    std::vector<Uint8>* buffer);
 
 // Generates a secure key for instantiating an CHACHA.
-rlwe::StatusOr<std::string> ChaChaPrngGenerateKey();
+SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::string> ChaChaPrngGenerateKey();
 
 // Returns 8 bits of randomness.
 //
 // Fails on internal cryptographic errors.
-rlwe::StatusOr<Uint8> ChaChaPrngRand8(absl::string_view key,
-                                      int* position_in_buffer,
-                                      int* salt_counter,
-                                      std::vector<Uint8>* buffer);
+SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<Uint8> ChaChaPrngRand8(
+    absl::string_view key,
+    int* position_in_buffer,
+    int* salt_counter,
+    std::vector<Uint8>* buffer);
 
 // Returns 64 bits of randomness.
 //
 // Fails on internal cryptographic errors.
-rlwe::StatusOr<Uint64> ChaChaPrngRand64(absl::string_view key,
-                                        int* position_in_buffer,
-                                        int* salt_counter,
-                                        std::vector<Uint8>* buffer);
+SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<Uint64> ChaChaPrngRand64(
+    absl::string_view key,
+    int* position_in_buffer,
+    int* salt_counter,
+    std::vector<Uint8>* buffer);
 
 }  // namespace internal
 }  // namespace rlwe
diff --git a/third_party/shell-encryption/src/prng/single_thread_chacha_prng.h b/third_party/shell-encryption/src/prng/single_thread_chacha_prng.h
index fcaff827..2fddf6f3 100644
--- a/third_party/shell-encryption/src/prng/single_thread_chacha_prng.h
+++ b/third_party/shell-encryption/src/prng/single_thread_chacha_prng.h
@@ -24,6 +24,7 @@
 #include "prng/chacha_prng_util.h"
 #include "prng/prng.h"
 #include "statusor.h"
+#include "third_party/shell-encryption/base/shell_encryption_export.h"
 
 namespace rlwe {
 
@@ -52,7 +53,7 @@
   //
   // Fails if the key is not the expected size or on internal cryptographic
   // errors.
-  static rlwe::StatusOr<std::unique_ptr<SingleThreadChaChaPrng>> Create(
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::unique_ptr<SingleThreadChaChaPrng>> Create(
       absl::string_view in_key);
 
   // Returns 8 bits of randomness.
diff --git a/third_party/shell-encryption/src/relinearization_key.cc b/third_party/shell-encryption/src/relinearization_key.cc
index 7982a2f..0b22a50 100644
--- a/third_party/shell-encryption/src/relinearization_key.cc
+++ b/third_party/shell-encryption/src/relinearization_key.cc
@@ -22,6 +22,8 @@
 #include "status_macros.h"
 #include "statusor.h"
 #include "symmetric_encryption_with_prng.h"
+#include "third_party/shell-encryption/base/shell_encryption_export.h"
+#include "third_party/shell-encryption/base/shell_encryption_export_template.h"
 
 namespace rlwe {
 namespace {
@@ -432,9 +434,9 @@
 // Instantiations of RelinearizationKey with specific MontgomeryInt classes.
 // If any new types are added, montgomery.h should be updated accordingly (such
 // as ensuring BigInt is correctly specialized, etc.).
-template class RelinearizationKey<MontgomeryInt<Uint16>>;
-template class RelinearizationKey<MontgomeryInt<Uint32>>;
-template class RelinearizationKey<MontgomeryInt<Uint64>>;
-template class RelinearizationKey<MontgomeryInt<absl::uint128>>;
+template class EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey<MontgomeryInt<Uint16>>;
+template class EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey<MontgomeryInt<Uint32>>;
+template class EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey<MontgomeryInt<Uint64>>;
+template class EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey<MontgomeryInt<absl::uint128>>;
 
 }  //  namespace rlwe
diff --git a/third_party/shell-encryption/src/relinearization_key.h b/third_party/shell-encryption/src/relinearization_key.h
index 265da33..1aff179 100644
--- a/third_party/shell-encryption/src/relinearization_key.h
+++ b/third_party/shell-encryption/src/relinearization_key.h
@@ -22,6 +22,8 @@
 #include "sample_error.h"
 #include "statusor.h"
 #include "symmetric_encryption.h"
+#include "third_party/shell-encryption/base/shell_encryption_export.h"
+#include "third_party/shell-encryption/base/shell_encryption_export_template.h"
 
 namespace rlwe {
 // Represents a RelinearizationKey constructed from a symmetric-key. Applying a
@@ -57,7 +59,7 @@
 // length (k - 1), where k is the number of parts of the ciphertext it applies
 // to.
 template <typename ModularInt>
-class RelinearizationKey {
+class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey {
   using ModularIntParams = typename ModularInt::Params;
 
  public:
@@ -73,7 +75,7 @@
   // with (1, s^k). In that case, we would use a relinearization key with
   // substition_power = k to return the ciphertext to be encrypted with (1,s).
   // See GaloisKey for an explicit wrapper around RelinearizationKey.
-  static rlwe::StatusOr<RelinearizationKey> Create(
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<RelinearizationKey> Create(
       const SymmetricRlweKey<ModularInt>& key, absl::string_view prng_seed,
       ssize_t num_parts, Uint64 log_decomposition_modulus,
       Uint64 substitution_power = 1);
@@ -192,6 +194,13 @@
   std::string prng_seed_;
 };
 
+// Instantiations of RelinearizationKey with specific MontgomeryInt classes.
+// If any new types are added, montgomery.h should be updated accordingly (such
+// as ensuring BigInt is correctly specialized, etc.).
+extern template class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey<MontgomeryInt<Uint16>>;
+extern template class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey<MontgomeryInt<Uint32>>;
+extern template class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey<MontgomeryInt<Uint64>>;
+extern template class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey<MontgomeryInt<absl::uint128>>;
 }  // namespace rlwe
 
 #endif  // RLWE_RELINEARIZATION_KEY_H_
diff --git a/third_party/shell-encryption/src/statusor.h b/third_party/shell-encryption/src/statusor.h
index 200f62d9..b7ada09 100644
--- a/third_party/shell-encryption/src/statusor.h
+++ b/third_party/shell-encryption/src/statusor.h
@@ -22,11 +22,12 @@
 #include "absl/base/attributes.h"
 #include "absl/status/status.h"
 #include "absl/types/optional.h"
+#include "third_party/shell-encryption/base/shell_encryption_export.h"
 
 namespace rlwe {
 
 template <typename T>
-class StatusOr {
+class SHELL_ENCRYPTION_EXPORT StatusOr {
  public:
   // Construct a new StatusOr with Status::UNKNOWN status
   StatusOr();
@@ -112,12 +113,12 @@
 
 namespace internal {
 
-class StatusOrHelper {
+class SHELL_ENCRYPTION_EXPORT StatusOrHelper {
  public:
   // Move type-agnostic error handling to the .cc.
-  static absl::Status HandleInvalidStatusCtorArg();
-  static absl::Status HandleNullObjectCtorArg();
-  static void Crash(const absl::Status& status);
+  static SHELL_ENCRYPTION_EXPORT absl::Status HandleInvalidStatusCtorArg();
+  static SHELL_ENCRYPTION_EXPORT absl::Status HandleNullObjectCtorArg();
+  static SHELL_ENCRYPTION_EXPORT void Crash(const absl::Status& status);
 
   // Customized behavior for StatusOr<T> vs. StatusOr<T*>
   template <typename T>
@@ -125,13 +126,13 @@
 };
 
 template <typename T>
-struct StatusOrHelper::Specialize {
+struct SHELL_ENCRYPTION_EXPORT StatusOrHelper::Specialize {
   // For non-pointer T, a reference can never be NULL.
   static inline bool IsValueNull(const T& t) { return false; }
 };
 
 template <typename T>
-struct StatusOrHelper::Specialize<T*> {
+struct SHELL_ENCRYPTION_EXPORT StatusOrHelper::Specialize<T*> {
   static inline bool IsValueNull(const T* t) { return t == nullptr; }
 };
 
diff --git a/third_party/subresource-filter-ruleset/README.chromium b/third_party/subresource-filter-ruleset/README.chromium
index 83c8dac2..f52c818 100644
--- a/third_party/subresource-filter-ruleset/README.chromium
+++ b/third_party/subresource-filter-ruleset/README.chromium
@@ -1,6 +1,6 @@
 Name: EasyList
 URL: https://easylist.to/easylist/easylist.txt
-Version: 202110121535
+Version: 202111091709
 License: Creative Commons Attribution-ShareAlike 3.0 Unported
 License Android Compatible: yes
 License File: LICENSE
diff --git a/third_party/subresource-filter-ruleset/data/UnindexedRules.sha1 b/third_party/subresource-filter-ruleset/data/UnindexedRules.sha1
index df774bc..b1640f8 100644
--- a/third_party/subresource-filter-ruleset/data/UnindexedRules.sha1
+++ b/third_party/subresource-filter-ruleset/data/UnindexedRules.sha1
@@ -1 +1 @@
-50461c265b3ff063690dfd7b5fdf742ba06de36d
\ No newline at end of file
+a703507a694340bac1a78ce6e54dfbd641062da5
\ No newline at end of file
diff --git a/third_party/subresource-filter-ruleset/manifest.json b/third_party/subresource-filter-ruleset/manifest.json
index db9c58b7..8bc6c2d6 100644
--- a/third_party/subresource-filter-ruleset/manifest.json
+++ b/third_party/subresource-filter-ruleset/manifest.json
@@ -2,5 +2,5 @@
   "manifest_version": 2,
   "name": "Subresource Filtering Rules",
   "ruleset_format": 1,
-  "version": "9.32.0"
+  "version": "9.33.0"
 }
diff --git a/tools/binary_size/libsupersize/archive.py b/tools/binary_size/libsupersize/archive.py
index e6bbb0a..cdb26d9 100644
--- a/tools/binary_size/libsupersize/archive.py
+++ b/tools/binary_size/libsupersize/archive.py
@@ -124,9 +124,6 @@
 # Parameters and states for archiving a container.
 @dataclasses.dataclass
 class ContainerArchiveOptions:
-  # TODO(agrieve): Delete pak_compression_ratio. We haven't compressed .pak
-  #    files since moving to bundles.
-  pak_compression_ratio: float = 0.5
   # Whether to count number of relative relocations instead of binary size.
   relocations_mode: bool = False
   # Whether to break down .so files.
@@ -287,44 +284,42 @@
   return True, path
 
 
-def _ExtractSourcePathsAndNormalizeObjectPaths(raw_symbols,
-                                               object_source_mapper,
-                                               address_source_mapper):
+def _AddSourcePathsUsingObjectPaths(ninja_source_mapper, raw_symbols):
+  logging.info('Looking up source paths from ninja files')
+  for symbol in raw_symbols:
+    if symbol.IsDex() or symbol.IsOther():
+      continue
+    # Native symbols and pak symbols use object paths.
+    object_path = symbol.object_path
+    if not object_path:
+      continue
+
+    # We don't have source info for prebuilt .a files.
+    if not os.path.isabs(object_path) and not object_path.startswith('..'):
+      symbol.source_path = ninja_source_mapper.FindSourceForPath(object_path)
+  assert ninja_source_mapper.unmatched_paths_count == 0, (
+      'One or more source file paths could not be found. Likely caused by '
+      '.ninja files being generated at a different time than the .map file.')
+
+
+def _AddSourcePathsUsingAddress(dwarf_source_mapper, raw_symbols):
+  logging.info('Looking up source paths from dwarfdump')
+  for symbol in raw_symbols:
+    if symbol.section_name != models.SECTION_TEXT:
+      continue
+    source_path = dwarf_source_mapper.FindSourceForTextAddress(symbol.address)
+    if source_path and not os.path.isabs(source_path):
+      symbol.source_path = source_path
+  # Majority of unmatched queries are for assembly source files (ex libav1d)
+  # and v8 builtins.
+  assert dwarf_source_mapper.unmatched_queries_ratio < 0.1, (
+      'Percentage of failing |dwarf_source_mapper| queries ' +
+      '({}%) >= 10% '.format(dwarf_source_mapper.unmatched_queries_ratio * 100)
+      + 'FindSourceForTextAddress() likely has a bug.')
+
+
+def _NormalizeObjectPaths(raw_symbols):
   """Fills in the |source_path| attribute and normalizes |object_path|."""
-  assert not object_source_mapper or not address_source_mapper
-  if object_source_mapper:
-    logging.info('Looking up source paths from ninja files')
-    for symbol in raw_symbols:
-      if symbol.IsDex() or symbol.IsOther():
-        continue
-      # Native symbols and pak symbols use object paths.
-      object_path = symbol.object_path
-      if not object_path:
-        continue
-
-      # We don't have source info for prebuilt .a files.
-      if not os.path.isabs(object_path) and not object_path.startswith('..'):
-        symbol.source_path = object_source_mapper.FindSourceForPath(object_path)
-    assert object_source_mapper.unmatched_paths_count == 0, (
-        'One or more source file paths could not be found. Likely caused by '
-        '.ninja files being generated at a different time than the .map file.')
-  elif address_source_mapper:
-    logging.info('Looking up source paths from dwarfdump')
-    for symbol in raw_symbols:
-      if symbol.section_name != models.SECTION_TEXT:
-        continue
-      source_path = address_source_mapper.FindSourceForTextAddress(
-          symbol.address)
-      if source_path and not os.path.isabs(source_path):
-        symbol.source_path = source_path
-    # Majority of unmatched queries are for assembly source files (ex libav1d)
-    # and v8 builtins.
-    assert address_source_mapper.unmatched_queries_ratio < 0.1, (
-        'Percentage of failing |address_source_mapper| queries ' +
-        '({}%) >= 10% '.format(
-            address_source_mapper.unmatched_queries_ratio * 100) +
-        'FindSourceForTextAddress() likely has a bug.')
-
   logging.info('Normalizing source and object paths')
   for symbol in raw_symbols:
     if symbol.object_path:
@@ -981,12 +976,9 @@
           raw_symbols, object_paths_by_name)
 
 
-def _ComputePakFileSymbols(
-    file_name, contents, res_info, symbols_by_id, compression_ratio=1):
-  id_map = {
-      id(v): k
-      for k, v in sorted(list(contents.resources.items()), reverse=True)
-  }
+def _ComputePakFileSymbols(file_name, contents, res_info, symbols_by_id):
+  # Reversed so that aliases are clobbered by the entries they are aliases of.
+  id_map = {id(v): k for k, v in reversed(contents.resources.items())}
   alias_map = {
       k: id_map[id(v)]
       for k, v in contents.resources.items() if id_map[id(v)] != k
@@ -999,15 +991,16 @@
   else:
     # E.g.: resources.pak, chrome_100_percent.pak.
     section_name = models.SECTION_PAK_NONTRANSLATED
-  overhead = (12 + 6) * compression_ratio  # Header size plus extra offset
+  overhead = 12 + 6  # Header size plus extra offset
   # Key just needs to be unique from other IDs and pak overhead symbols.
   symbols_by_id[-len(symbols_by_id) - 1] = models.Symbol(
       section_name, overhead, full_name='Overhead: {}'.format(file_name))
   for resource_id in sorted(contents.resources):
-    if resource_id in alias_map:
+    aliased_resource_id = alias_map.get(resource_id)
+    if aliased_resource_id is not None:
       # 4 extra bytes of metadata (2 16-bit ints)
       size = 4
-      resource_id = alias_map[resource_id]
+      resource_id = aliased_resource_id
     else:
       resource_data = contents.resources[resource_id]
       # 6 extra bytes of metadata (1 32-bit int, 1 16-bit int)
@@ -1022,7 +1015,6 @@
           new_symbol.flags |= models.FLAG_UNCOMPRESSED
         symbols_by_id[resource_id] = new_symbol
 
-    size *= compression_ratio
     symbols_by_id[resource_id].size += size
   return section_name
 
@@ -1102,27 +1094,10 @@
           full_name=symbol.full_name, object_path=path, aliases=aliases)
       aliases.append(new_sym)
       raw_symbols.append(new_sym)
-  raw_total = 0.0
-  int_total = 0
-  for symbol in raw_symbols:
-    raw_total += symbol.size
-    # We truncate rather than round to ensure that we do not over attribute. It
-    # is easier to add another symbol to make up the difference.
-    symbol.size = int(symbol.size)
-    int_total += symbol.size
-  # Attribute excess to translations since only those are compressed.
-  overhead_size = round(raw_total - int_total)
-  if overhead_size:
-    raw_symbols.append(
-        models.Symbol(models.SECTION_PAK_TRANSLATIONS,
-                      overhead_size,
-                      address=raw_symbols[-1].end_address,
-                      full_name='Overhead: Pak compression artifacts'))
 
   # Pre-sort to make final sort faster.
   # Note: _SECTION_SORT_ORDER[] for pak symbols matches section_name ordering.
-  raw_symbols.sort(
-      key=lambda s: (s.section_name, s.IsOverhead(), s.address, s.object_path))
+  raw_symbols.sort(key=lambda s: (s.section_name, s.address, s.object_path))
   return raw_symbols
 
 
@@ -1242,33 +1217,22 @@
   return object_paths_by_pak_id
 
 
-def _FindPakSymbolsFromApk(opts, section_ranges, apk_path, size_info_prefix):
+def _FindPakSymbolsFromApk(section_ranges, apk_path, size_info_prefix):
   with zipfile.ZipFile(apk_path) as z:
     pak_zip_infos = (f for f in z.infolist() if f.filename.endswith('.pak'))
     pak_info_path = size_info_prefix + '.pak.info'
     res_info = _ParsePakInfoFile(pak_info_path)
     symbols_by_id = {}
-    total_compressed_size = 0
-    total_uncompressed_size = 0
     for zip_info in pak_zip_infos:
       contents = data_pack.ReadDataPackFromString(z.read(zip_info))
-      compression_ratio = 1.0
-      if zip_info.compress_size < zip_info.file_size:
-        total_compressed_size += zip_info.compress_size
-        total_uncompressed_size += zip_info.file_size
-        compression_ratio = opts.pak_compression_ratio
-      section_name = _ComputePakFileSymbols(
-          zip_info.filename, contents,
-          res_info, symbols_by_id, compression_ratio=compression_ratio)
+      if zip_info.compress_type != zipfile.ZIP_STORED:
+        logging.warning(
+            'Expected .pak files to be STORED, but this one is compressed: %s',
+            zip_info.filename)
+      section_name = _ComputePakFileSymbols(zip_info.filename, contents,
+                                            res_info, symbols_by_id)
       _ExtendSectionRange(section_ranges, section_name, zip_info.compress_size)
 
-    if total_uncompressed_size > 0:
-      actual_ratio = (
-          float(total_compressed_size) / total_uncompressed_size)
-      logging.info(
-          'Pak Compression Ratio: %f Actual: %f Diff: %.0f',
-          opts.pak_compression_ratio, actual_ratio,
-          (opts.pak_compression_ratio - actual_ratio) * total_uncompressed_size)
   return symbols_by_id
 
 
@@ -1460,8 +1424,8 @@
         _ElfInfoFromApk,
         (apk_spec.apk_path, native_spec.apk_so_path, native_spec.tool_prefix))
 
-  object_source_mapper = None
-  address_source_mapper = None
+  ninja_source_mapper = None
+  dwarf_source_mapper = None
   section_ranges = {}
   raw_symbols = []
   object_paths_by_name = None
@@ -1469,15 +1433,15 @@
     ninja_elf_object_paths = None
     if output_directory and native_spec.map_path:
       # Finds all objects passed to the linker and creates a map of .o -> .cc.
-      object_source_mapper, ninja_elf_object_paths = _ParseNinjaFiles(
+      ninja_source_mapper, ninja_elf_object_paths = _ParseNinjaFiles(
           output_directory, native_spec.elf_path)
     elif native_spec.elf_path:
       logging.info('Parsing source path info via dwarfdump')
-      address_source_mapper = dwarfdump.CreateAddressSourceMapper(
+      dwarf_source_mapper = dwarfdump.CreateAddressSourceMapper(
           native_spec.elf_path, native_spec.tool_prefix)
       logging.info('Found %d source paths across %s ranges',
-                   address_source_mapper.NumberOfPaths(),
-                   address_source_mapper.num_ranges)
+                   dwarf_source_mapper.NumberOfPaths(),
+                   dwarf_source_mapper.num_ranges)
 
     # Start by finding elf_object_paths so that nm can run on them while the
     # linker .map is being parsed.
@@ -1491,9 +1455,9 @@
       known_inputs = None
       # When we don't know which elf file is used, just search all paths.
       # TODO(agrieve): Seems to be used only for tests. Remove?
-      if object_source_mapper:
+      if ninja_source_mapper:
         thin_archives = set(
-            p for p in object_source_mapper.IterAllPaths() if p.endswith('.a')
+            p for p in ninja_source_mapper.IterAllPaths() if p.endswith('.a')
             and ar.IsThinArchive(os.path.join(output_directory, p)))
       else:
         thin_archives = None
@@ -1535,7 +1499,7 @@
   other_symbols = []
   if apk_spec and apk_spec.size_info_prefix and not opts.relocations_mode:
     # Can modify |section_ranges|.
-    pak_symbols_by_id = _FindPakSymbolsFromApk(opts, section_ranges,
+    pak_symbols_by_id = _FindPakSymbolsFromApk(section_ranges,
                                                apk_spec.apk_path,
                                                apk_spec.size_info_prefix)
 
@@ -1609,8 +1573,12 @@
       '**'), s.address, s.full_name))
   raw_symbols.extend(other_symbols)
 
-  _ExtractSourcePathsAndNormalizeObjectPaths(raw_symbols, object_source_mapper,
-                                             address_source_mapper)
+  if ninja_source_mapper:
+    _AddSourcePathsUsingObjectPaths(ninja_source_mapper, raw_symbols)
+  elif dwarf_source_mapper:
+    _AddSourcePathsUsingAddress(dwarf_source_mapper, raw_symbols)
+  _NormalizeObjectPaths(raw_symbols)
+
   dir_metadata.PopulateComponents(raw_symbols, source_directory)
   logging.info('Converting excessive aliases into shared-path symbols')
   _CompactLargeAliasesIntoSharedSymbols(raw_symbols, knobs)
@@ -2042,10 +2010,6 @@
       sub_args.aux_elf_file = None
 
   opts = ContainerArchiveOptions()
-  # An estimate of pak translation compression ratio to make comparisons
-  # between .size files reasonable. Otherwise this can differ every pak
-  # change.
-  opts.pak_compression_ratio = 0.38 if sub_args.minimal_apks_file else 0.33
   opts.relocations_mode = top_args.relocations
   opts.analyze_native = not (sub_args.java_only or sub_args.no_native
                              or top_args.java_only or top_args.no_native)
diff --git a/tools/clang/scripts/build.py b/tools/clang/scripts/build.py
index 9737e7d..5cf7099 100755
--- a/tools/clang/scripts/build.py
+++ b/tools/clang/scripts/build.py
@@ -995,14 +995,12 @@
       cflags = [
           '--target=' + target_triple,
           '--sysroot=%s/sysroot' % toolchain_dir,
-          '--gcc-toolchain=' + toolchain_dir,
           # android_ndk/toolchains/llvm/prebuilt/linux-x86_64/aarch64-linux-android/bin/ld
           # depends on a newer version of libxml2.so than what's available on
           # the bots. To make things work, use our just-built lld as linker.
           '-fuse-ld=lld',
-          # The compiler we're building with (just-built clang) doesn't have the
-          # compiler-rt builtins; use libgcc to get past the CMake checks.
-          '--rtlib=libgcc',
+          # We don't have an unwinder ready, and don't need it either.
+          '--unwindlib=none',
       ]
 
       android_args = base_cmake_args + [
@@ -1017,17 +1015,45 @@
         '-DCOMPILER_RT_BUILD_LIBFUZZER=OFF',
         '-DCOMPILER_RT_BUILD_MEMPROF=OFF',
         '-DCOMPILER_RT_BUILD_ORC=OFF',
-        '-DCOMPILER_RT_BUILD_PROFILE=ON',
-        '-DCOMPILER_RT_BUILD_SANITIZERS=ON',
+        '-DCOMPILER_RT_BUILD_PROFILE=OFF',
+        '-DCOMPILER_RT_BUILD_SANITIZERS=OFF',
         '-DCOMPILER_RT_BUILD_XRAY=OFF',
         '-DSANITIZER_CXX_ABI=libcxxabi',
         '-DCMAKE_SHARED_LINKER_FLAGS=-Wl,-u__cxa_demangle',
-        '-DANDROID=1']
+        '-DANDROID=1'
+
+        # These are necessary because otherwise CMake tries to build an
+        # executable to test to see if the compiler is working, but in doing so,
+        # it links against the builtins.a that we're about to build.
+        '-DCMAKE_CXX_COMPILER_WORKS=ON',
+        '-DCMAKE_C_COMPILER_WORKS=ON',
+        '-DCMAKE_ASM_COMPILER_WORKS=ON',
+      ]
+
+      # First build the builtins and copy to the main build tree.
+      RunCommand(['cmake'] +
+                 android_args +
+                 [os.path.join(COMPILER_RT_DIR, 'lib', 'builtins')])
+      builtins_a = 'lib/linux/libclang_rt.builtins-%s-android.a' % target_arch
+      RunCommand(['ninja', builtins_a])
+      shutil.copy(builtins_a, rt_lib_dst_dir)
+
+      # With the builtins in place, we can build the other runtimes.
+      build_dir_2 = build_dir + '-phase2'
+      if not os.path.exists(build_dir_2):
+        os.mkdir(os.path.join(build_dir_2))
+      os.chdir(build_dir_2)
+
+      android_args.extend([
+        '-DCOMPILER_RT_BUILD_BUILTINS=OFF',
+        '-DCOMPILER_RT_USE_BUILTINS_LIBRARY=ON',
+        '-DCOMPILER_RT_BUILD_PROFILE=ON',
+        '-DCOMPILER_RT_BUILD_SANITIZERS=ON',
+      ])
       RunCommand(['cmake'] + android_args + [COMPILER_RT_DIR])
 
       libs_want = [
           'lib/linux/libclang_rt.asan-{0}-android.so',
-          'lib/linux/libclang_rt.builtins-{0}-android.a',
           'lib/linux/libclang_rt.ubsan_standalone-{0}-android.so',
           'lib/linux/libclang_rt.profile-{0}-android.a',
       ]
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index 9a8bae5..81e0425 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -35,7 +35,7 @@
 # Reverting problematic clang rolls is safe, though.
 # This is the output of `git describe` and is usable as a commit-ish.
 CLANG_REVISION = 'llvmorg-14-init-8564-g34b903d8'
-CLANG_SUB_REVISION = 2
+CLANG_SUB_REVISION = 7
 
 PACKAGE_VERSION = '%s-%s' % (CLANG_REVISION, CLANG_SUB_REVISION)
 RELEASE_VERSION = '14.0.0'
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index d85dfc56..44d4f904 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -30082,6 +30082,7 @@
   <int value="1599" label="WEB_AUTHENTICATION_PROXY_DETACH"/>
   <int value="1600" label="FILEMANAGERPRIVATE_CANCELIOTASK"/>
   <int value="1601" label="AUTOTESTPRIVATE_GETDISPLAYSMOOTHNESS"/>
+  <int value="1602" label="AUTOTESTPRIVATE_RESETHOLDINGSPACE"/>
 </enum>
 
 <enum name="ExtensionIconState">
@@ -50192,6 +50193,7 @@
   <int value="-915035507" label="ArcPrintSpoolerExperiment:enabled"/>
   <int value="-914210146" label="enable-web-based-signin"/>
   <int value="-914097698" label="EnableDbusAndX11StatusIcons:disabled"/>
+  <int value="-913925109" label="FilesExtractArchive:disabled"/>
   <int value="-913294939" label="DriveFS:enabled"/>
   <int value="-912585719" label="AppManagementIntentSettings:disabled"/>
   <int value="-912456561" label="MidiManagerWinrt:enabled"/>
@@ -51206,6 +51208,7 @@
   <int value="-127666141" label="TabGroups:disabled"/>
   <int value="-127231994" label="VrBrowsingNativeAndroidUi:disabled"/>
   <int value="-122492389" label="enable-browser-task-scheduler"/>
+  <int value="-122458532" label="FilesExtractArchive:enabled"/>
   <int value="-121563330" label="SecurePaymentConfirmationBrowser:disabled"/>
   <int value="-120521482" label="DirectManipulationStylus:enabled"/>
   <int value="-120091289" label="CrostiniAppSearch:enabled"/>
@@ -52133,7 +52136,6 @@
   <int value="570469494" label="LoginDetection:disabled"/>
   <int value="571349694" label="AllowAllSitesToInitiateMirroring:enabled"/>
   <int value="571562912" label="InstallableAmbientBadgeMessage:disabled"/>
-  <int value="572915501" label="FilesZipUnpack:disabled"/>
   <int value="573385109" label="SharedClipboardUI:enabled"/>
   <int value="575380532" label="ExperimentalAccessibilityLabels:disabled"/>
   <int value="575394365" label="AndroidPaymentApps:disabled"/>
@@ -52202,7 +52204,6 @@
   <int value="624783596" label="SamePartyCookiesConsideredFirstParty:enabled"/>
   <int value="625273056" label="disable-boot-animation"/>
   <int value="625725485" label="FilesTrash:enabled"/>
-  <int value="626605468" label="FilesZipUnpack:enabled"/>
   <int value="628302973" label="NTPSnippets:enabled"/>
   <int value="628570445" label="AndroidAutofillAccessibility:enabled"/>
   <int value="629549626" label="ContextualSearchMlTapSuppression:enabled"/>
@@ -64251,6 +64252,7 @@
   <int value="1112" label="Privacy: Remove Fingerprint V2"/>
   <int value="1113"
       label="Privacy: Enable/Disable peripheral data access protection"/>
+  <int value="1114" label="Privacy: Snooping Protection"/>
   <int value="1200" label="Add Language"/>
   <int value="1201" label="Show Input Options In Shelf"/>
   <int value="1202" label="Show Personal Information Suggestions"/>
@@ -64306,6 +64308,7 @@
   <int value="1706" label="View Terms of Service"/>
   <int value="1707" label="Open Diagnostics App"/>
   <int value="1708" label="Change Device Name"/>
+  <int value="1709" label="Open Firmware Updates App"/>
   <int value="1800" label="Add Kerberos Ticket V2"/>
   <int value="1801" label="Remove Kerberos Ticket V2"/>
   <int value="1802" label="Set Active Kerberos Ticket V2"/>
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml
index 2f3b4fb..149abea 100644
--- a/tools/metrics/histograms/metadata/android/histograms.xml
+++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -2865,6 +2865,22 @@
   </summary>
 </histogram>
 
+<histogram
+    name="Android.StartupTabPreloader.LoadDecisionToFirstNavigationStart.{LoadDecision}"
+    units="ms" expires_after="2022-09-01">
+  <owner>blundell@chromium.org</owner>
+  <owner>yfriedman@chromium.org</owner>
+  <summary>
+    The time in a cold start between the triggerpoint for a startup tab being
+    preloaded and first navigation start, recorded in the case where a startup
+    tab preload is determined {LoadDecision} viable.
+  </summary>
+  <token key="LoadDecision">
+    <variant name="Load" summary="to be"/>
+    <variant name="NoLoad" summary="not to be"/>
+  </token>
+</histogram>
+
 <histogram name="Android.StrictMode.OverrideUrlLoadingTime" units="ms"
     expires_after="2022-04-24">
   <owner>yfriedman@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/media/histograms.xml b/tools/metrics/histograms/metadata/media/histograms.xml
index afb0f17..1b18f15c 100644
--- a/tools/metrics/histograms/metadata/media/histograms.xml
+++ b/tools/metrics/histograms/metadata/media/histograms.xml
@@ -1279,7 +1279,7 @@
 </histogram>
 
 <histogram name="Media.AudioRendererAudioGlitches" enum="AudioGlitchResult"
-    expires_after="2022-04-10">
+    expires_after="2022-02-10">
   <owner>henrika@chromium.org</owner>
   <owner>olka@chromium.org</owner>
   <summary>
@@ -1288,6 +1288,24 @@
   </summary>
 </histogram>
 
+<histogram name="Media.AudioRendererAudioGlitches2.{LatencyTag}"
+    enum="AudioGlitchResult" expires_after="2022-10-04">
+  <owner>dalecurtis@chromium.org</owner>
+  <owner>olka@chromium.org</owner>
+  <owner>tguilbert@chromium.org</owner>
+  <summary>
+    Captures if render-side audio glitches are detected or not. Sampled when a
+    low-latency output audio stream is destructed.
+  </summary>
+  <token key="LatencyTag">
+    <variant name="LatencyExactMs"/>
+    <variant name="LatencyInteractive"/>
+    <variant name="LatencyPlayback"/>
+    <variant name="LatencyRtc"/>
+    <variant name="LatencyUnknown"/>
+  </token>
+</histogram>
+
 <histogram name="Media.AudioRendererImpl.SinkStatus" enum="OutputDeviceStatus"
     expires_after="2022-12-12">
   <owner>armax@chromium.org</owner>
@@ -1304,7 +1322,7 @@
 </histogram>
 
 <histogram name="Media.AudioRendererMissedDeadline" units="%"
-    expires_after="2022-04-17">
+    expires_after="2022-02-10">
   <owner>dalecurtis@chromium.org</owner>
   <owner>olka@chromium.org</owner>
   <summary>
@@ -1313,6 +1331,29 @@
   </summary>
 </histogram>
 
+<histogram name="Media.AudioRendererMissedDeadline2.{Duration}.{LatencyTag}"
+    units="permille" expires_after="2022-10-04">
+  <owner>dalecurtis@chromium.org</owner>
+  <owner>olka@chromium.org</owner>
+  <owner>tguilbert@chromium.org</owner>
+  <summary>
+    Proportion of AudioSyncReader::Read() calls where the renderer missed its
+    realtime deadline. Rounded up, clipped to 1/10 of the stream callbacks.
+    Sampled when a low-latency output audio stream is destructed.
+  </summary>
+  <token key="Duration">
+    <variant name="Long" summary="Streams equal or longer than 3000 callbacks"/>
+    <variant name="Short" summary="Streams shorter than 3000 callbacks"/>
+  </token>
+  <token key="LatencyTag">
+    <variant name="LatencyExactMs"/>
+    <variant name="LatencyInteractive"/>
+    <variant name="LatencyPlayback"/>
+    <variant name="LatencyRtc"/>
+    <variant name="LatencyUnknown"/>
+  </token>
+</histogram>
+
 <histogram name="Media.AudioService.AudioManagerStartupTime" units="ms"
     expires_after="2022-04-05">
   <owner>guidou@chromium.org</owner>
diff --git a/tools/perf/cli_tools/update_wpr/update_wpr.py b/tools/perf/cli_tools/update_wpr/update_wpr.py
index efb821a..dcc7aedc 100644
--- a/tools/perf/cli_tools/update_wpr/update_wpr.py
+++ b/tools/perf/cli_tools/update_wpr/update_wpr.py
@@ -45,8 +45,11 @@
 
 
 def _GetBranchName():
-  return subprocess.check_output(
+  branch_name = subprocess.check_output(
       ['git', 'rev-parse', '--abbrev-ref', 'HEAD']).strip()
+  if isinstance(branch_name, bytes):
+    return branch_name.decode("utf-8")
+  return branch_name
 
 
 def _OpenBrowser(url):
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index fd3e249..2992f05 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 @@
         },
         "win": {
             "hash": "2abe2291e0e9b51e8750059797f1284422cb27d8",
-            "remote_path": "perfetto_binaries/trace_processor_shell/win/f32b6ac45880108dee6eaa9e585aa8c6217607cb/trace_processor_shell.exe"
+            "remote_path": "perfetto_binaries/trace_processor_shell/win/19fb767c32d05d75314d376933b98be246e0b723/trace_processor_shell.exe"
         },
         "mac": {
             "hash": "0dda5c90a9ea600e8c3dd1276671b55a07260c4d",
@@ -18,7 +18,7 @@
         },
         "linux": {
             "hash": "4cdd2006b2e87bbf78fe37b58a3e09d6392977e1",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/f32b6ac45880108dee6eaa9e585aa8c6217607cb/trace_processor_shell"
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/19fb767c32d05d75314d376933b98be246e0b723/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/file_manager/file_manager/common/js/util.js b/ui/file_manager/file_manager/common/js/util.js
index 6d5c7b1..f30544f 100644
--- a/ui/file_manager/file_manager/common/js/util.js
+++ b/ui/file_manager/file_manager/common/js/util.js
@@ -1492,15 +1492,6 @@
 };
 
 /**
- * Returns true when FilesZipUnpack feature is enabled.
- * TODO(crbug.com/912236) Remove once transition to new ZIP system is finished.
- * @return {boolean}
- */
-util.isZipUnpackEnabled = () => {
-  return loadTimeData.getBoolean('ZIP_UNPACK');
-};
-
-/**
  * Returns true if FilesSinglePartitionFormat flag is enabled.
  * @return {boolean}
  */
@@ -1525,6 +1516,14 @@
 };
 
 /**
+ * Returns true if FilesExtractArchive flag is enabled.
+ * @return {boolean}
+ */
+util.isExtractArchiveEnabled = () => {
+  return loadTimeData.getBoolean('EXTRACT_ARCHIVE');
+};
+
+/**
  * Retrieves all entries inside the given |rootEntry|.
  * @param {!DirectoryEntry} rootEntry
  * @param {function(!Array<!Entry>)} entriesCallback Called when some chunk of
diff --git a/ui/native_theme/BUILD.gn b/ui/native_theme/BUILD.gn
index e203452..833dc42 100644
--- a/ui/native_theme/BUILD.gn
+++ b/ui/native_theme/BUILD.gn
@@ -44,6 +44,8 @@
       "caption_style_mac.mm",
       "native_theme_mac.h",
       "native_theme_mac.mm",
+      "scrollbar_animator_mac.cc",
+      "scrollbar_animator_mac.h",
     ]
   }
 
@@ -75,6 +77,7 @@
     "//ui/color:mixers",
     "//ui/display",
     "//ui/gfx",
+    "//ui/gfx/animation/keyframe",
     "//ui/gfx/geometry",
     "//ui/resources",
   ]
@@ -149,7 +152,10 @@
   ]
 
   if (is_mac) {
-    sources += [ "native_theme_mac_unittest.mm" ]
+    sources += [
+      "native_theme_mac_unittest.mm",
+      "scrollbar_animator_mac_unittest.cc",
+    ]
     deps += [
       "//ui/color",
       "//ui/gfx:test_support",
diff --git a/ui/native_theme/scrollbar_animator_mac.cc b/ui/native_theme/scrollbar_animator_mac.cc
new file mode 100644
index 0000000..c71d4b3
--- /dev/null
+++ b/ui/native_theme/scrollbar_animator_mac.cc
@@ -0,0 +1,257 @@
+// 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/native_theme/scrollbar_animator_mac.h"
+
+#include "base/cxx17_backports.h"
+
+namespace ui {
+
+///////////////////////////////////////////////////////////////////////////////
+// ScrollbarAnimationTimerMac
+
+ScrollbarAnimationTimerMac::ScrollbarAnimationTimerMac(
+    base::RepeatingCallback<void(double)> callback,
+    double duration,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+    : start_time_(0.0), duration_(duration), callback_(std::move(callback)) {
+  timing_function_ = gfx::CubicBezierTimingFunction::CreatePreset(
+      gfx::CubicBezierTimingFunction::EaseType::EASE_IN_OUT);
+}
+
+ScrollbarAnimationTimerMac::~ScrollbarAnimationTimerMac() {}
+
+void ScrollbarAnimationTimerMac::Start() {
+  start_time_ = base::Time::Now().ToDoubleT();
+  // Set the framerate of the animation. NSAnimation uses a default
+  // framerate of 60 Hz, so use that here.
+  timer_.Start(FROM_HERE, base::Seconds(1.0 / 60.0), this,
+               &ScrollbarAnimationTimerMac::TimerFired);
+}
+
+void ScrollbarAnimationTimerMac::Stop() {
+  timer_.Stop();
+}
+
+void ScrollbarAnimationTimerMac::SetDuration(double duration) {
+  duration_ = duration;
+}
+
+void ScrollbarAnimationTimerMac::TimerFired() {
+  double current_time = base::Time::Now().ToDoubleT();
+  double delta = current_time - start_time_;
+
+  if (delta >= duration_)
+    timer_.Stop();
+
+  double fraction = delta / duration_;
+  fraction = base::clamp(fraction, 0.0, 1.0);
+  double progress = timing_function_->GetValue(fraction);
+  // Note that `this` may be destroyed from within `callback_`, so it is not
+  // safe to call any other code after it.
+  callback_.Run(progress);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// OverlayScrollbarAnimatorMac
+
+OverlayScrollbarAnimatorMac::OverlayScrollbarAnimatorMac(
+    Client* client,
+    int thumb_width_expanded,
+    int thumb_width_unexpanded,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+    : client_(client),
+      thumb_width_expanded_(thumb_width_expanded),
+      thumb_width_unexpanded_(thumb_width_unexpanded),
+      thumb_width_(thumb_width_unexpanded),
+      task_runner_(task_runner),
+      weak_factory_(this) {}
+
+OverlayScrollbarAnimatorMac::~OverlayScrollbarAnimatorMac() = default;
+
+void OverlayScrollbarAnimatorMac::MouseDidEnter() {
+  // If the scrollbar is completely hidden, ignore this. We will initialize
+  // the `mouse_in_track_` state if there's a scroll.
+  if (thumb_alpha_ == 0.f)
+    return;
+
+  if (mouse_in_track_)
+    return;
+  mouse_in_track_ = true;
+
+  // Cancel any in-progress fade-out, and ensure that the fade-out timer be
+  // disabled.
+  if (fade_out_animation_)
+    FadeOutAnimationCancel();
+  FadeOutTimerUpdate();
+
+  // Start the fade-in animation (unless it is in progress or has already
+  // completed).
+  if (!fade_in_track_animation_ && track_alpha_ != 1.f)
+    FadeInTrackAnimationStart();
+
+  // Start the expand-thumb animation (unless it is in progress or has already
+  // completed).
+  if (!expand_thumb_animation_ && thumb_width_ != thumb_width_expanded_)
+    ExpandThumbAnimationStart();
+}
+
+void OverlayScrollbarAnimatorMac::MouseDidExit() {
+  // Ensure that the fade-out timer be re-enabled.
+  mouse_in_track_ = false;
+  FadeOutTimerUpdate();
+}
+
+void OverlayScrollbarAnimatorMac::DidScroll() {
+  // If we were fading out, then immediately return to being fully opaque for
+  // both the thumb and track.
+  if (fade_out_animation_) {
+    FadeOutAnimationCancel();
+    FadeOutTimerUpdate();
+    return;
+  }
+
+  // If the scrollbar was already fully visible, then just update the fade-out
+  // timer.
+  if (thumb_alpha_ == 1.f) {
+    FadeOutTimerUpdate();
+    return;
+  }
+
+  // This is an initial scroll causing the thumb to appear.
+  DCHECK_EQ(thumb_width_, thumb_width_unexpanded_);
+  DCHECK_EQ(thumb_alpha_, 0.f);
+  DCHECK(!fade_in_track_animation_);
+  thumb_width_ = thumb_width_unexpanded_;
+  thumb_alpha_ = 1;
+  client_->SetThumbNeedsDisplay();
+  client_->SetHidden(false);
+
+  // If the initial scroll is done inside the scrollbar's area, then also
+  // cause the track to appear and the thumb to enlarge.
+  if (client_->IsMouseInScrollbarFrameRect()) {
+    mouse_in_track_ = true;
+    thumb_width_ = thumb_width_expanded_;
+    track_alpha_ = 1;
+    client_->SetTrackNeedsDisplay();
+  }
+
+  // Update the fade-out timer (now that we know whether or not the mouse
+  // is in the track).
+  FadeOutTimerUpdate();
+}
+
+void OverlayScrollbarAnimatorMac::ExpandThumbAnimationStart() {
+  DCHECK(!expand_thumb_animation_);
+  DCHECK_NE(thumb_width_, thumb_width_expanded_);
+  expand_thumb_animation_ = std::make_unique<ScrollbarAnimationTimerMac>(
+      base::BindRepeating(
+          &OverlayScrollbarAnimatorMac::ExpandThumbAnimationTicked,
+          weak_factory_.GetWeakPtr()),
+      kAnimationDurationSeconds, task_runner_);
+  expand_thumb_animation_->Start();
+}
+
+void OverlayScrollbarAnimatorMac::ExpandThumbAnimationTicked(double progress) {
+  thumb_width_ = (1 - progress) * thumb_width_unexpanded_ +
+                 progress * thumb_width_expanded_;
+  client_->SetThumbNeedsDisplay();
+  if (progress == 1)
+    expand_thumb_animation_.reset();
+}
+
+void OverlayScrollbarAnimatorMac::FadeInTrackAnimationStart() {
+  DCHECK(!fade_in_track_animation_);
+  DCHECK(!fade_out_animation_);
+  fade_in_track_animation_ = std::make_unique<ScrollbarAnimationTimerMac>(
+      base::BindRepeating(
+          &OverlayScrollbarAnimatorMac::FadeInTrackAnimationTicked,
+          weak_factory_.GetWeakPtr()),
+      kAnimationDurationSeconds, task_runner_);
+  fade_in_track_animation_->Start();
+}
+
+void OverlayScrollbarAnimatorMac::FadeInTrackAnimationTicked(double progress) {
+  // Fade-in and fade-out are mutually exclusive.
+  DCHECK(!fade_out_animation_);
+
+  track_alpha_ = progress;
+  client_->SetTrackNeedsDisplay();
+  if (progress == 1) {
+    fade_in_track_animation_.reset();
+  }
+}
+
+void OverlayScrollbarAnimatorMac::FadeOutTimerUpdate() {
+  if (mouse_in_track_) {
+    start_scrollbar_fade_out_timer_.reset();
+    return;
+  }
+  if (!start_scrollbar_fade_out_timer_) {
+    start_scrollbar_fade_out_timer_ =
+        std::make_unique<base::RetainingOneShotTimer>(
+            FROM_HERE, kFadeOutDelay,
+            base::BindRepeating(
+                &OverlayScrollbarAnimatorMac::FadeOutAnimationStart,
+                weak_factory_.GetWeakPtr()));
+    start_scrollbar_fade_out_timer_->SetTaskRunner(task_runner_);
+  }
+  start_scrollbar_fade_out_timer_->Reset();
+}
+
+void OverlayScrollbarAnimatorMac::FadeOutAnimationStart() {
+  start_scrollbar_fade_out_timer_.reset();
+  fade_in_track_animation_.reset();
+  fade_out_animation_.reset();
+
+  fade_out_animation_ = std::make_unique<ScrollbarAnimationTimerMac>(
+      base::BindRepeating(&OverlayScrollbarAnimatorMac::FadeOutAnimationTicked,
+                          weak_factory_.GetWeakPtr()),
+      kAnimationDurationSeconds, task_runner_);
+  fade_out_animation_->Start();
+}
+
+void OverlayScrollbarAnimatorMac::FadeOutAnimationTicked(double progress) {
+  DCHECK(!fade_in_track_animation_);
+
+  // Fade out the thumb.
+  thumb_alpha_ = 1 - progress;
+  client_->SetThumbNeedsDisplay();
+
+  // If the track is not already invisible, fade it out.
+  if (track_alpha_ != 0) {
+    track_alpha_ = 1 - progress;
+    client_->SetTrackNeedsDisplay();
+  }
+
+  // Once completely faded out, reset all state.
+  if (progress == 1) {
+    expand_thumb_animation_.reset();
+    fade_out_animation_.reset();
+
+    thumb_width_ = thumb_width_unexpanded_;
+    DCHECK_EQ(thumb_alpha_, 0.f);
+    DCHECK_EQ(track_alpha_, 0.f);
+
+    // Mark that the scrollbars were hidden.
+    client_->SetHidden(true);
+  }
+}
+
+void OverlayScrollbarAnimatorMac::FadeOutAnimationCancel() {
+  DCHECK(fade_out_animation_);
+  fade_out_animation_.reset();
+  thumb_alpha_ = 1;
+  client_->SetThumbNeedsDisplay();
+  if (track_alpha_ > 0) {
+    track_alpha_ = 1;
+    client_->SetTrackNeedsDisplay();
+  }
+}
+
+const float OverlayScrollbarAnimatorMac::kAnimationDurationSeconds = 0.25f;
+const base::TimeDelta OverlayScrollbarAnimatorMac::kFadeOutDelay =
+    base::Milliseconds(500);
+
+}  // namespace ui
diff --git a/ui/native_theme/scrollbar_animator_mac.h b/ui/native_theme/scrollbar_animator_mac.h
new file mode 100644
index 0000000..66a4158
--- /dev/null
+++ b/ui/native_theme/scrollbar_animator_mac.h
@@ -0,0 +1,129 @@
+// 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_NATIVE_THEME_SCROLLBAR_ANIMATOR_MAC_H_
+#define UI_NATIVE_THEME_SCROLLBAR_ANIMATOR_MAC_H_
+
+#include "base/callback.h"
+#include "base/task/single_thread_task_runner.h"
+#include "base/timer/timer.h"
+#include "ui/gfx/animation/keyframe/timing_function.h"
+#include "ui/native_theme/native_theme_export.h"
+
+namespace ui {
+
+// Timer used for animating scrollbar effects.
+// TODO(https://crbug.com/961835): Change this to be driven by the client
+// (Blink or Views) animation system.
+class NATIVE_THEME_EXPORT ScrollbarAnimationTimerMac {
+ public:
+  ScrollbarAnimationTimerMac(
+      base::RepeatingCallback<void(double)> callback,
+      double duration,
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+
+  ~ScrollbarAnimationTimerMac();
+
+  void Start();
+  void Stop();
+  void SetDuration(double duration);
+
+ private:
+  void TimerFired();
+
+  base::RepeatingTimer timer_;
+  double start_time_;  // In seconds.
+  double duration_;    // In seconds.
+  base::RepeatingCallback<void(double)> callback_;
+  std::unique_ptr<gfx::CubicBezierTimingFunction> timing_function_;
+};
+
+class NATIVE_THEME_EXPORT OverlayScrollbarAnimatorMac {
+ public:
+  class Client {
+   public:
+    virtual ~Client() {}
+    // Return true if the mouse is currently in the tracks' frame. This is used
+    // during an initial scroll, because MouseDidEnter and MouseDidExit are not
+    // while the scrollbar is hidden.
+    virtual bool IsMouseInScrollbarFrameRect() const = 0;
+
+    // Set whether all of the scrollbar is hidden.
+    virtual void SetHidden(bool) = 0;
+
+    // Called whenever the value of `GetThumbAlpha` or `GetThumbWidth` may
+    // have changed.
+    virtual void SetThumbNeedsDisplay() = 0;
+
+    // Called whenever the value of `GetTrackAlpha` may have changed.
+    virtual void SetTrackNeedsDisplay() = 0;
+  };
+
+  OverlayScrollbarAnimatorMac(
+      Client* client,
+      int thumb_width_expanded,
+      int thumb_width_unexpanded,
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+  ~OverlayScrollbarAnimatorMac();
+
+  // Called when the mouse moves to be over the scrollbar's area.
+  void MouseDidEnter();
+
+  // Called when the mouse leave the scrollbar's area.
+  void MouseDidExit();
+
+  // Called in response to a scroll in the direction of this scrollbar.
+  void DidScroll();
+
+  // Retrieve the rendering properties of the scrollbar.
+  float GetThumbAlpha() const { return thumb_alpha_; }
+  float GetTrackAlpha() const { return track_alpha_; }
+  int GetThumbWidth() const { return thumb_width_; }
+
+ protected:
+  void ExpandThumbAnimationStart();
+  void ExpandThumbAnimationTicked(double progress);
+
+  void FadeInTrackAnimationStart();
+  void FadeInTrackAnimationTicked(double progress);
+
+  void FadeOutTimerUpdate();
+  void FadeOutAnimationStart();
+  void FadeOutAnimationTicked(double progress);
+  void FadeOutAnimationCancel();
+
+  Client* const client_;  // Weak, owns `this`.
+  const int thumb_width_expanded_;
+  const int thumb_width_unexpanded_;
+
+  int thumb_width_ = 0;
+  float thumb_alpha_ = 0;
+  float track_alpha_ = 0;
+  bool mouse_in_track_ = false;
+
+  static const float kAnimationDurationSeconds;
+  static const base::TimeDelta kFadeOutDelay;
+
+  // Animator for expanding `thumb_width_` when the mouse enters the
+  // scrollbar area.
+  std::unique_ptr<ScrollbarAnimationTimerMac> expand_thumb_animation_;
+
+  // Animator for fading in `track_alpha_` when the mouse enters the scrollbar
+  // area. Note that `thumb_alpha_` never fades in (it instantaneously appears).
+  std::unique_ptr<ScrollbarAnimationTimerMac> fade_in_track_animation_;
+
+  // Animator for fading out `track_alpha_` and `thumb_alpha_` together after
+  // inactivity.
+  std::unique_ptr<ScrollbarAnimationTimerMac> fade_out_animation_;
+
+  // Timer to trigger the `fade_out_animation_`.
+  std::unique_ptr<base::RetainingOneShotTimer> start_scrollbar_fade_out_timer_;
+
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+  base::WeakPtrFactory<OverlayScrollbarAnimatorMac> weak_factory_;
+};
+
+}  // namespace ui
+
+#endif  // UI_NATIVE_THEME_OVERLAY_SCROLLBAR_ANIMATOR_MAC_H_
diff --git a/ui/native_theme/scrollbar_animator_mac_unittest.cc b/ui/native_theme/scrollbar_animator_mac_unittest.cc
new file mode 100644
index 0000000..a40964d
--- /dev/null
+++ b/ui/native_theme/scrollbar_animator_mac_unittest.cc
@@ -0,0 +1,271 @@
+// 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/native_theme/scrollbar_animator_mac.h"
+
+#include "base/logging.h"
+#include "base/test/task_environment.h"
+#include "base/time/time.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::Return;
+
+namespace ui {
+
+namespace {
+
+constexpr int kExpandedWidth = 32;
+constexpr int kUnexpandedWidth = 16;
+constexpr auto kTimeToFadeOut = base::Milliseconds(500);
+constexpr auto kAnimationTime = base::Milliseconds(250);
+
+class MockClient : public OverlayScrollbarAnimatorMac::Client {
+ public:
+  MOCK_CONST_METHOD0(IsMouseInScrollbarFrameRect, bool());
+  MOCK_METHOD1(SetHidden, void(bool));
+  MOCK_METHOD0(SetThumbNeedsDisplay, void());
+  MOCK_METHOD0(SetTrackNeedsDisplay, void());
+};
+
+class MacScrollbarAnimatorTest : public ::testing::Test {
+ public:
+  MacScrollbarAnimatorTest()
+      : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
+    scrollbar_ = std::make_unique<OverlayScrollbarAnimatorMac>(
+        &client_, kExpandedWidth, kUnexpandedWidth,
+        task_environment_.GetMainThreadTaskRunner());
+  }
+
+  ~MacScrollbarAnimatorTest() override = default;
+
+  base::test::TaskEnvironment task_environment_;
+  MockClient client_;
+  std::unique_ptr<OverlayScrollbarAnimatorMac> scrollbar_;
+};
+
+TEST_F(MacScrollbarAnimatorTest, DidScrollThenFadeOut) {
+  // The scrollbar_ starts as invisible.
+  EXPECT_EQ(scrollbar_->GetThumbAlpha(), 0.f);
+  EXPECT_EQ(scrollbar_->GetTrackAlpha(), 0.f);
+
+  // Scroll with the mouse not in the track.
+  EXPECT_CALL(client_, IsMouseInScrollbarFrameRect()).WillOnce(Return(false));
+  EXPECT_CALL(client_, SetHidden(false));
+  EXPECT_CALL(client_, SetThumbNeedsDisplay());
+  scrollbar_->DidScroll();
+  EXPECT_EQ(scrollbar_->GetThumbAlpha(), 1.f);
+  EXPECT_EQ(scrollbar_->GetTrackAlpha(), 0.f);
+  EXPECT_EQ(scrollbar_->GetThumbWidth(), kUnexpandedWidth);
+
+  // Fast-forward until just before the timeout threshold is hit.
+  EXPECT_CALL(client_, SetThumbNeedsDisplay()).Times(0);
+  task_environment_.FastForwardBy(kTimeToFadeOut);
+  EXPECT_EQ(scrollbar_->GetThumbAlpha(), 1.f);
+  EXPECT_EQ(scrollbar_->GetTrackAlpha(), 0.f);
+
+  // Fast-forward through half of the fade-out animation.
+  EXPECT_CALL(client_, SetThumbNeedsDisplay()).WillRepeatedly(Return());
+  task_environment_.FastForwardBy(kAnimationTime / 2);
+  EXPECT_GT(scrollbar_->GetThumbAlpha(), 0.f);
+  EXPECT_LT(scrollbar_->GetThumbAlpha(), 1.f);
+  EXPECT_EQ(scrollbar_->GetThumbWidth(), kUnexpandedWidth);
+
+  // Fast-forward through past the end of the fade-out animation. The scrollbar_
+  // should be hidden.
+  EXPECT_CALL(client_, SetHidden(true));
+  task_environment_.FastForwardBy(kAnimationTime);
+  EXPECT_EQ(scrollbar_->GetThumbAlpha(), 0.f);
+  EXPECT_EQ(scrollbar_->GetTrackAlpha(), 0.f);
+}
+
+TEST_F(MacScrollbarAnimatorTest, DidScrollStartsInTrack) {
+  // Scroll with the mouse in the track.
+  EXPECT_CALL(client_, IsMouseInScrollbarFrameRect()).WillOnce(Return(true));
+  EXPECT_CALL(client_, SetHidden(false));
+  EXPECT_CALL(client_, SetThumbNeedsDisplay());
+  EXPECT_CALL(client_, SetTrackNeedsDisplay());
+  scrollbar_->DidScroll();
+  EXPECT_EQ(scrollbar_->GetThumbAlpha(), 1.f);
+  EXPECT_EQ(scrollbar_->GetTrackAlpha(), 1.f);
+  EXPECT_EQ(scrollbar_->GetThumbWidth(), kExpandedWidth);
+
+  // Fast-forward long past the fade-out animation would happen. Nothing should
+  // happen.
+  EXPECT_CALL(client_, SetThumbNeedsDisplay()).Times(0);
+  EXPECT_CALL(client_, SetTrackNeedsDisplay()).Times(0);
+  task_environment_.FastForwardBy(10 * kTimeToFadeOut);
+  EXPECT_EQ(scrollbar_->GetThumbAlpha(), 1.f);
+  EXPECT_EQ(scrollbar_->GetTrackAlpha(), 1.f);
+  EXPECT_EQ(scrollbar_->GetThumbWidth(), kExpandedWidth);
+
+  // Scroll again. Again, nothing should happen.
+  scrollbar_->DidScroll();
+  EXPECT_EQ(scrollbar_->GetThumbAlpha(), 1.f);
+  EXPECT_EQ(scrollbar_->GetTrackAlpha(), 1.f);
+  EXPECT_EQ(scrollbar_->GetThumbWidth(), kExpandedWidth);
+
+  // Have the mouse leave the scrollbar_. Fast-forward not enough time for the
+  // scrollbar_ to fade out, then have the mouse re-enter. Exit again. Still
+  // nothing should happen.
+  scrollbar_->MouseDidExit();
+  task_environment_.FastForwardBy(kTimeToFadeOut / 2);
+  scrollbar_->MouseDidEnter();
+  EXPECT_EQ(scrollbar_->GetThumbAlpha(), 1.f);
+  EXPECT_EQ(scrollbar_->GetTrackAlpha(), 1.f);
+  EXPECT_EQ(scrollbar_->GetThumbWidth(), kExpandedWidth);
+  scrollbar_->MouseDidExit();
+  EXPECT_EQ(scrollbar_->GetThumbAlpha(), 1.f);
+  EXPECT_EQ(scrollbar_->GetTrackAlpha(), 1.f);
+  EXPECT_EQ(scrollbar_->GetThumbWidth(), kExpandedWidth);
+
+  // Wait most of the fade-out timer's time. Nothing should happen in this
+  // interval.
+  task_environment_.FastForwardBy(3 * kTimeToFadeOut / 4);
+  EXPECT_EQ(scrollbar_->GetThumbAlpha(), 1.f);
+  EXPECT_EQ(scrollbar_->GetTrackAlpha(), 1.f);
+  EXPECT_EQ(scrollbar_->GetThumbWidth(), kExpandedWidth);
+
+  // Allow the fade-out animation to progress about halfway through.
+  EXPECT_CALL(client_, SetThumbNeedsDisplay()).WillRepeatedly(Return());
+  EXPECT_CALL(client_, SetTrackNeedsDisplay()).WillRepeatedly(Return());
+  task_environment_.FastForwardBy(kTimeToFadeOut / 4 + kAnimationTime / 2);
+  EXPECT_GT(scrollbar_->GetThumbAlpha(), 0.f);
+  EXPECT_LT(scrollbar_->GetThumbAlpha(), 1.f);
+  EXPECT_GT(scrollbar_->GetTrackAlpha(), 0.f);
+  EXPECT_LT(scrollbar_->GetTrackAlpha(), 1.f);
+  EXPECT_EQ(scrollbar_->GetThumbWidth(), kExpandedWidth);
+
+  // Scroll, interrupting the fade-out animation.
+  EXPECT_CALL(client_, SetThumbNeedsDisplay()).Times(1);
+  EXPECT_CALL(client_, SetTrackNeedsDisplay()).Times(1);
+  scrollbar_->DidScroll();
+  EXPECT_EQ(scrollbar_->GetThumbAlpha(), 1.f);
+  EXPECT_EQ(scrollbar_->GetTrackAlpha(), 1.f);
+  EXPECT_EQ(scrollbar_->GetThumbWidth(), kExpandedWidth);
+
+  // Wait most of the fade-out timer's time. Nothing should happen in this time.
+  EXPECT_CALL(client_, SetThumbNeedsDisplay()).Times(0);
+  EXPECT_CALL(client_, SetTrackNeedsDisplay()).Times(0);
+  task_environment_.FastForwardBy(3 * kTimeToFadeOut / 4);
+
+  // Allow the fade-out animation to progress halfway again.
+  EXPECT_CALL(client_, SetThumbNeedsDisplay()).WillRepeatedly(Return());
+  EXPECT_CALL(client_, SetTrackNeedsDisplay()).WillRepeatedly(Return());
+  task_environment_.FastForwardBy(kTimeToFadeOut / 4 + kAnimationTime / 2);
+  EXPECT_GT(scrollbar_->GetThumbAlpha(), 0.f);
+  EXPECT_LT(scrollbar_->GetThumbAlpha(), 1.f);
+  EXPECT_GT(scrollbar_->GetTrackAlpha(), 0.f);
+  EXPECT_LT(scrollbar_->GetTrackAlpha(), 1.f);
+  EXPECT_EQ(scrollbar_->GetThumbWidth(), kExpandedWidth);
+
+  // And let it progress to completion. The scrollbar_ should hide itself.
+  EXPECT_CALL(client_, SetHidden(true));
+  task_environment_.FastForwardBy(kAnimationTime);
+  EXPECT_EQ(scrollbar_->GetThumbAlpha(), 0.f);
+  EXPECT_EQ(scrollbar_->GetTrackAlpha(), 0.f);
+  EXPECT_EQ(scrollbar_->GetThumbWidth(), kUnexpandedWidth);
+}
+
+TEST_F(MacScrollbarAnimatorTest, EnterTrack) {
+  // First try to enter the track before showing the scrollbar_. Nothing should
+  // happen.
+  EXPECT_EQ(scrollbar_->GetThumbAlpha(), 0.f);
+  EXPECT_EQ(scrollbar_->GetTrackAlpha(), 0.f);
+  scrollbar_->MouseDidEnter();
+  EXPECT_EQ(scrollbar_->GetThumbAlpha(), 0.f);
+  EXPECT_EQ(scrollbar_->GetTrackAlpha(), 0.f);
+
+  // Scroll with the mouse in the track.
+  EXPECT_CALL(client_, IsMouseInScrollbarFrameRect()).WillOnce(Return(false));
+  EXPECT_CALL(client_, SetHidden(false));
+  EXPECT_CALL(client_, SetThumbNeedsDisplay());
+  scrollbar_->DidScroll();
+  EXPECT_EQ(scrollbar_->GetThumbAlpha(), 1.f);
+  EXPECT_EQ(scrollbar_->GetTrackAlpha(), 0.f);
+  EXPECT_EQ(scrollbar_->GetThumbWidth(), kUnexpandedWidth);
+
+  // Have the mouse enter the scrollbar_ area.
+  scrollbar_->MouseDidEnter();
+  EXPECT_EQ(scrollbar_->GetThumbAlpha(), 1.f);
+  EXPECT_EQ(scrollbar_->GetTrackAlpha(), 0.f);
+  EXPECT_EQ(scrollbar_->GetThumbWidth(), kUnexpandedWidth);
+
+  // Fast-forward part of the animation.
+  EXPECT_CALL(client_, SetThumbNeedsDisplay()).WillRepeatedly(Return());
+  EXPECT_CALL(client_, SetTrackNeedsDisplay()).WillRepeatedly(Return());
+  task_environment_.FastForwardBy(kAnimationTime / 2);
+  EXPECT_EQ(scrollbar_->GetThumbAlpha(), 1.f);
+  EXPECT_GT(scrollbar_->GetTrackAlpha(), 0.f);
+  EXPECT_LT(scrollbar_->GetTrackAlpha(), 1.f);
+  EXPECT_GT(scrollbar_->GetThumbWidth(), kUnexpandedWidth);
+  EXPECT_LT(scrollbar_->GetThumbWidth(), kExpandedWidth);
+
+  // Fast-forward through the rest of the animation.
+  task_environment_.FastForwardBy(kAnimationTime);
+  EXPECT_EQ(scrollbar_->GetThumbAlpha(), 1.f);
+  EXPECT_EQ(scrollbar_->GetTrackAlpha(), 1.f);
+  EXPECT_EQ(scrollbar_->GetThumbWidth(), kExpandedWidth);
+
+  // Fast-forward for a long time. Nothing should happen (no fade-out).
+  EXPECT_CALL(client_, SetThumbNeedsDisplay()).Times(0);
+  EXPECT_CALL(client_, SetTrackNeedsDisplay()).Times(0);
+  task_environment_.FastForwardBy(10 * kTimeToFadeOut);
+  EXPECT_EQ(scrollbar_->GetThumbAlpha(), 1.f);
+  EXPECT_EQ(scrollbar_->GetTrackAlpha(), 1.f);
+  EXPECT_EQ(scrollbar_->GetThumbWidth(), kExpandedWidth);
+
+  // Move the mouse out and wait for the the fade-out should start.
+  scrollbar_->MouseDidExit();
+  task_environment_.FastForwardBy(kTimeToFadeOut);
+  EXPECT_CALL(client_, SetThumbNeedsDisplay()).WillRepeatedly(Return());
+  EXPECT_CALL(client_, SetTrackNeedsDisplay()).WillRepeatedly(Return());
+  task_environment_.FastForwardBy(kAnimationTime / 2);
+  EXPECT_GT(scrollbar_->GetThumbAlpha(), 0.f);
+  EXPECT_LT(scrollbar_->GetThumbAlpha(), 1.f);
+  EXPECT_GT(scrollbar_->GetTrackAlpha(), 0.f);
+  EXPECT_LT(scrollbar_->GetTrackAlpha(), 1.f);
+  EXPECT_EQ(scrollbar_->GetThumbWidth(), kExpandedWidth);
+
+  // Re-enter. This will immediately return us to full opacity.
+  EXPECT_CALL(client_, SetThumbNeedsDisplay()).Times(1);
+  EXPECT_CALL(client_, SetTrackNeedsDisplay()).Times(1);
+  scrollbar_->MouseDidEnter();
+  EXPECT_EQ(scrollbar_->GetThumbAlpha(), 1.f);
+  EXPECT_EQ(scrollbar_->GetTrackAlpha(), 1.f);
+  EXPECT_EQ(scrollbar_->GetThumbWidth(), kExpandedWidth);
+
+  // Wait for a long time. Nothing should happen.
+  EXPECT_CALL(client_, SetThumbNeedsDisplay()).Times(0);
+  EXPECT_CALL(client_, SetTrackNeedsDisplay()).Times(0);
+  task_environment_.FastForwardBy(10 * kTimeToFadeOut);
+  EXPECT_EQ(scrollbar_->GetThumbAlpha(), 1.f);
+  EXPECT_EQ(scrollbar_->GetTrackAlpha(), 1.f);
+  EXPECT_EQ(scrollbar_->GetThumbWidth(), kExpandedWidth);
+
+  // Re-leave the track. Again nothing should happen.
+  scrollbar_->MouseDidExit();
+
+  // Fast-forward through the start of the fade-out.
+  task_environment_.FastForwardBy(kTimeToFadeOut);
+  EXPECT_CALL(client_, SetThumbNeedsDisplay()).WillRepeatedly(Return());
+  EXPECT_CALL(client_, SetTrackNeedsDisplay()).WillRepeatedly(Return());
+  task_environment_.FastForwardBy(kAnimationTime / 2);
+  EXPECT_GT(scrollbar_->GetThumbAlpha(), 0.f);
+  EXPECT_LT(scrollbar_->GetThumbAlpha(), 1.f);
+  EXPECT_GT(scrollbar_->GetTrackAlpha(), 0.f);
+  EXPECT_LT(scrollbar_->GetTrackAlpha(), 1.f);
+  EXPECT_EQ(scrollbar_->GetThumbWidth(), kExpandedWidth);
+
+  // Fast-forward until the fade-out completes.
+  EXPECT_CALL(client_, SetHidden(true));
+  task_environment_.FastForwardBy(kAnimationTime);
+  EXPECT_EQ(scrollbar_->GetThumbAlpha(), 0.f);
+  EXPECT_EQ(scrollbar_->GetTrackAlpha(), 0.f);
+  EXPECT_EQ(scrollbar_->GetThumbWidth(), kUnexpandedWidth);
+}
+
+}  // namespace
+
+}  // namespace ui
diff --git a/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc b/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc
index 33dc6db5..93909d0 100644
--- a/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc
+++ b/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc
@@ -9,6 +9,7 @@
 #include "base/bind.h"
 #include "base/process/process.h"
 #include "base/task/current_thread.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "ui/gfx/geometry/rrect_f.h"
 #include "ui/gfx/linux/drm_util_linux.h"
 #include "ui/gfx/overlay_priority_hint.h"
@@ -69,6 +70,30 @@
   // open the handle early and store it.
   OpenAndStoreDrmRenderNodeFd();
 #endif
+
+  // The WaylandBufferManagerGpu takes the task runner where it was created.
+  // However, it might be null in tests and be available later after
+  // initialization is done. Though, when this code runs outside tests, a race
+  // between setting a task runner via ::Initialize and ::RegisterSurface may
+  // happen, and a surface will never be registered. Thus, the following two
+  // cases are possible:
+  // 1) The WaylandBufferManagerGpu runs normally outside tests.
+  // ThreadTaskRunnerHandle is set and it is passed during construction and
+  // never changes.
+  // 2) The WaylandBufferManagerGpu runs in unit tests and when it's created,
+  // the task runner is not available and must be set later when ::Initialize is
+  // called. In this case, there is no race between ::Initialize and
+  // ::RegisterSurface and it's fine to defer setting the task runner.
+  //
+  // TODO(msisov): think about making unit tests initialize Ozone after task
+  // runner is set that would allow to always set the task runner.
+  if (base::ThreadTaskRunnerHandle::IsSet()) {
+    gpu_thread_runner_ = base::ThreadTaskRunnerHandle::Get();
+  } else {
+    // In tests, the further calls might happen on a different sequence.
+    // Otherwise, ThreadTaskRunnerHandle should have already been set.
+    DETACH_FROM_SEQUENCE(gpu_sequence_checker_);
+  }
 }
 
 WaylandBufferManagerGpu::~WaylandBufferManagerGpu() = default;
@@ -81,6 +106,12 @@
     bool supports_viewporter,
     bool supports_acquire_fence,
     bool supports_non_backed_solid_color_buffers) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
+
+  // See the comment in the constructor.
+  if (!gpu_thread_runner_)
+    gpu_thread_runner_ = base::ThreadTaskRunnerHandle::Get();
+
   supported_buffer_formats_with_modifiers_ = buffer_formats_with_modifiers;
   supports_viewporter_ = supports_viewporter;
   supports_acquire_fence_ = supports_acquire_fence;
@@ -89,16 +120,15 @@
   supports_dmabuf_ = supports_dma_buf;
 
   BindHostInterface(std::move(remote_host));
-
-  io_thread_runner_ = base::ThreadTaskRunnerHandle::Get();
 }
 
 void WaylandBufferManagerGpu::OnSubmission(gfx::AcceleratedWidget widget,
                                            uint32_t buffer_id,
                                            gfx::SwapResult swap_result,
                                            gfx::GpuFenceHandle release_fence) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
+
   base::AutoLock scoped_lock(lock_);
-  DCHECK(io_thread_runner_->BelongsToCurrentThread());
   DCHECK_LE(commit_thread_runners_.count(widget), 1u);
   // Return back to the same thread where the commit request came from.
   auto it = commit_thread_runners_.find(widget);
@@ -115,8 +145,9 @@
     gfx::AcceleratedWidget widget,
     uint32_t buffer_id,
     const gfx::PresentationFeedback& feedback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
+
   base::AutoLock scoped_lock(lock_);
-  DCHECK(io_thread_runner_->BelongsToCurrentThread());
   DCHECK_LE(commit_thread_runners_.count(widget), 1u);
   // Return back to the same thread where the commit request came from.
   auto it = commit_thread_runners_.find(widget);
@@ -130,13 +161,8 @@
 
 void WaylandBufferManagerGpu::RegisterSurface(gfx::AcceleratedWidget widget,
                                               WaylandSurfaceGpu* surface) {
-  if (!io_thread_runner_) {
-    LOG(ERROR) << "WaylandBufferManagerGpu is not initialized. Can't register "
-                  "a surface.";
-    return;
-  }
-
-  io_thread_runner_->PostTask(
+  DCHECK(gpu_thread_runner_);
+  gpu_thread_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(
           &WaylandBufferManagerGpu::SaveTaskRunnerForWidgetOnIOThread,
@@ -147,13 +173,8 @@
 }
 
 void WaylandBufferManagerGpu::UnregisterSurface(gfx::AcceleratedWidget widget) {
-  if (!io_thread_runner_) {
-    LOG(ERROR) << "WaylandBufferManagerGpu is not initialized. Can't register "
-                  "a surface.";
-    return;
-  }
-
-  io_thread_runner_->PostTask(
+  DCHECK(gpu_thread_runner_);
+  gpu_thread_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(
           &WaylandBufferManagerGpu::ForgetTaskRunnerForWidgetOnIOThread,
@@ -182,14 +203,9 @@
     uint32_t current_format,
     uint32_t planes_count,
     uint32_t buffer_id) {
-  if (!remote_host_) {
-    LOG(ERROR) << "Interface is not bound. Can't request "
-                  "WaylandBufferManagerHost to create/commit/destroy buffers.";
-    return;
-  }
-
-  // Do the mojo call on the IO child thread.
-  io_thread_runner_->PostTask(
+  DCHECK(gpu_thread_runner_);
+  // Do the mojo call on the GpuMainThread.
+  gpu_thread_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(&WaylandBufferManagerGpu::CreateDmabufBasedBufferInternal,
                      base::Unretained(this), std::move(dmabuf_fd),
@@ -202,14 +218,9 @@
                                                    size_t length,
                                                    gfx::Size size,
                                                    uint32_t buffer_id) {
-  if (!remote_host_) {
-    LOG(ERROR) << "Interface is not bound. Can't request "
-                  "WaylandBufferManagerHost to create/commit/destroy buffers.";
-    return;
-  }
-
-  // Do the mojo call on the IO child thread.
-  io_thread_runner_->PostTask(
+  DCHECK(gpu_thread_runner_);
+  // Do the mojo call on the GpuMainThread.
+  gpu_thread_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(&WaylandBufferManagerGpu::CreateShmBasedBufferInternal,
                      base::Unretained(this), std::move(shm_fd), length,
@@ -219,14 +230,9 @@
 void WaylandBufferManagerGpu::CreateSolidColorBuffer(SkColor color,
                                                      const gfx::Size& size,
                                                      uint32_t buf_id) {
-  if (!remote_host_) {
-    LOG(ERROR) << "Interface is not bound. Can't request "
-                  "WaylandBufferManagerHost to create/commit/destroy buffers.";
-    return;
-  }
-
-  // Do the mojo call on the IO child thread.
-  io_thread_runner_->PostTask(
+  DCHECK(gpu_thread_runner_);
+  // Do the mojo call on the GpuMainThread.
+  gpu_thread_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(&WaylandBufferManagerGpu::CreateSolidColorBufferInternal,
                      base::Unretained(this), color, size, buf_id));
@@ -252,14 +258,9 @@
 void WaylandBufferManagerGpu::CommitOverlays(
     gfx::AcceleratedWidget widget,
     std::vector<ozone::mojom::WaylandOverlayConfigPtr> overlays) {
-  if (!remote_host_) {
-    LOG(ERROR) << "Interface is not bound. Can't request "
-                  "WaylandBufferManagerHost to create/commit/destroy buffers.";
-    return;
-  }
-
-  // Do the mojo call on the IO child thread.
-  io_thread_runner_->PostTask(
+  DCHECK(gpu_thread_runner_);
+  // Do the mojo call on the GpuMainThread.
+  gpu_thread_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(&WaylandBufferManagerGpu::CommitOverlaysInternal,
                      base::Unretained(this), widget, std::move(overlays)));
@@ -267,14 +268,9 @@
 
 void WaylandBufferManagerGpu::DestroyBuffer(gfx::AcceleratedWidget widget,
                                             uint32_t buffer_id) {
-  if (!remote_host_) {
-    LOG(ERROR) << "Interface is not bound. Can't request "
-                  "WaylandBufferManagerHost to create/commit/destroy buffers.";
-    return;
-  }
-
-  // Do the mojo call on the IO child thread.
-  io_thread_runner_->PostTask(
+  DCHECK(gpu_thread_runner_);
+  // Do the mojo call on the GpuMainThread.
+  gpu_thread_runner_->PostTask(
       FROM_HERE, base::BindOnce(&WaylandBufferManagerGpu::DestroyBufferInternal,
                                 base::Unretained(this), widget, buffer_id));
 }
@@ -331,7 +327,9 @@
     uint32_t current_format,
     uint32_t planes_count,
     uint32_t buffer_id) {
-  DCHECK(io_thread_runner_->BelongsToCurrentThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
+
+  DCHECK(remote_host_);
   remote_host_->CreateDmabufBasedBuffer(
       mojo::PlatformHandle(std::move(dmabuf_fd)), size, strides, offsets,
       modifiers, current_format, planes_count, buffer_id);
@@ -342,7 +340,9 @@
     size_t length,
     gfx::Size size,
     uint32_t buffer_id) {
-  DCHECK(io_thread_runner_->BelongsToCurrentThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
+
+  DCHECK(remote_host_);
   remote_host_->CreateShmBasedBuffer(mojo::PlatformHandle(std::move(shm_fd)),
                                      length, size, buffer_id);
 }
@@ -351,33 +351,40 @@
     SkColor color,
     const gfx::Size& size,
     uint32_t buf_id) {
-  DCHECK(io_thread_runner_->BelongsToCurrentThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
+
+  DCHECK(remote_host_);
   remote_host_->CreateSolidColorBuffer(size, color, buf_id);
 }
 
 void WaylandBufferManagerGpu::CommitOverlaysInternal(
     gfx::AcceleratedWidget widget,
     std::vector<ozone::mojom::WaylandOverlayConfigPtr> overlays) {
-  DCHECK(io_thread_runner_->BelongsToCurrentThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
+
+  DCHECK(remote_host_);
   remote_host_->CommitOverlays(widget, std::move(overlays));
 }
 
 void WaylandBufferManagerGpu::DestroyBufferInternal(
     gfx::AcceleratedWidget widget,
     uint32_t buffer_id) {
-  DCHECK(io_thread_runner_->BelongsToCurrentThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
+
+  DCHECK(remote_host_);
   remote_host_->DestroyBuffer(widget, buffer_id);
 }
 
 void WaylandBufferManagerGpu::BindHostInterface(
     mojo::PendingRemote<ozone::mojom::WaylandBufferManagerHost> remote_host) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
+
+  DCHECK(!remote_host_.is_bound() && !associated_receiver_.is_bound());
+  remote_host_.Bind(std::move(remote_host));
   // WaylandBufferManagerHost may bind host again after an error. See
   // WaylandBufferManagerHost::BindInterface for more details.
-  if (remote_host_.is_bound()) {
-    remote_host_.reset();
-    associated_receiver_.reset();
-  }
-  remote_host_.Bind(std::move(remote_host));
+  remote_host_.set_disconnect_handler(base::BindOnce(
+      &WaylandBufferManagerGpu::OnHostDisconnected, base::Unretained(this)));
 
   // Setup associated interface.
   mojo::PendingAssociatedRemote<ozone::mojom::WaylandBufferManagerGpu>
@@ -390,15 +397,17 @@
 void WaylandBufferManagerGpu::SaveTaskRunnerForWidgetOnIOThread(
     gfx::AcceleratedWidget widget,
     scoped_refptr<base::SingleThreadTaskRunner> origin_runner) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
+
   DCHECK_NE(widget, gfx::kNullAcceleratedWidget);
-  DCHECK(io_thread_runner_->BelongsToCurrentThread());
   commit_thread_runners_.emplace(widget, origin_runner);
 }
 
 void WaylandBufferManagerGpu::ForgetTaskRunnerForWidgetOnIOThread(
     gfx::AcceleratedWidget widget) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
+
   DCHECK_NE(widget, gfx::kNullAcceleratedWidget);
-  DCHECK(io_thread_runner_->BelongsToCurrentThread());
   commit_thread_runners_.erase(widget);
 }
 
@@ -445,4 +454,15 @@
 }
 #endif
 
+void WaylandBufferManagerGpu::OnHostDisconnected() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
+
+  // WaylandBufferManagerHost may bind host again after an error. See
+  // WaylandBufferManagerHost::BindInterface for more details.
+  remote_host_.reset();
+  // When the remote host is disconnected, it also disconnects the associated
+  // receiver. Thus, reset that as well.
+  associated_receiver_.reset();
+}
+
 }  // namespace ui
diff --git a/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h b/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h
index 8db3ff79..a65842ff 100644
--- a/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h
+++ b/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h
@@ -40,12 +40,15 @@
 class WaylandBufferManagerGpu : public ozone::mojom::WaylandBufferManagerGpu {
  public:
   WaylandBufferManagerGpu();
-
   WaylandBufferManagerGpu(const WaylandBufferManagerGpu&) = delete;
   WaylandBufferManagerGpu& operator=(const WaylandBufferManagerGpu&) = delete;
 
   ~WaylandBufferManagerGpu() override;
 
+  scoped_refptr<base::SingleThreadTaskRunner> gpu_thread_runner() const {
+    return gpu_thread_runner_;
+  }
+
   // WaylandBufferManagerGpu overrides:
   void Initialize(
       mojo::PendingRemote<ozone::mojom::WaylandBufferManagerHost> remote_host,
@@ -198,6 +201,8 @@
       uint32_t buffer_id,
       const gfx::PresentationFeedback& feedback);
 
+  void OnHostDisconnected();
+
 #if defined(WAYLAND_GBM)
   // Finds drm render node, opens it and stores the handle into
   // |drm_render_node_fd|.
@@ -246,26 +251,30 @@
 
   // These task runners can be used to pass messages back to the same thread,
   // where the commit buffer request came from. For example, swap requests can
-  // come from the GpuMainThread, but are rerouted to the IOChildThread and then
+  // come from the Viz thread, but are rerouted to the GpuMainThread and then
   // mojo calls happen. However, when the manager receives mojo calls, it has to
   // reroute calls back to the same thread where the calls came from to ensure
-  // correct sequence. Note that not all calls come from the GpuMainThread, e.g.
-  // WaylandCanvasSurface calls from the VizCompositorThread.
-  // This map must only be accessed from the IO thread.
+  // correct sequence. Note that not all calls come from the Viz thread, e.g.
+  // GbmPixmapWayland may call from either the GpuMainThread or IOChildThread.
+  // This map must only be accessed from the GpuMainThread.
   base::small_map<std::map<gfx::AcceleratedWidget,
                            scoped_refptr<base::SingleThreadTaskRunner>>>
       commit_thread_runners_;
 
   // A task runner, which is initialized in a multi-process mode. It is used to
-  // ensure all the methods of this class are run on IOChildThread. This is
+  // ensure all the methods of this class are run on GpuMainThread. This is
   // needed to ensure mojo calls happen on a right sequence.
-  scoped_refptr<base::SingleThreadTaskRunner> io_thread_runner_;
+  scoped_refptr<base::SingleThreadTaskRunner> gpu_thread_runner_;
 
   // Protects access to |widget_to_surface_map_| and |commit_thread_runners_|.
   base::Lock lock_;
 
   // Keeps track of the next unique buffer ID.
   uint32_t next_buffer_id_ = 0;
+
+  // All calls must happen on the correct sequence. See comments in the
+  // constructor for more details.
+  SEQUENCE_CHECKER(gpu_sequence_checker_);
 };
 
 }  // namespace ui
diff --git a/ui/ozone/platform/wayland/ozone_platform_wayland.cc b/ui/ozone/platform/wayland/ozone_platform_wayland.cc
index f50b2348..b4e5e74 100644
--- a/ui/ozone/platform/wayland/ozone_platform_wayland.cc
+++ b/ui/ozone/platform/wayland/ozone_platform_wayland.cc
@@ -14,7 +14,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/message_loop/message_pump_type.h"
 #include "base/no_destructor.h"
-#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "ui/base/buildflags.h"
 #include "ui/base/cursor/cursor_factory.h"
 #include "ui/base/dragdrop/os_exchange_data_provider_factory_ozone.h"
@@ -327,11 +327,22 @@
   }
 
   void AddInterfaces(mojo::BinderMap* binders) override {
+    // It's preferred to reuse the same task runner where the
+    // WaylandBufferManagerGpu has been created. However, when tests are
+    // executed, the task runner might not have been set at that time. Thus, use
+    // the current one. See the comment in WaylandBufferManagerGpu why it takes
+    // a task runner.
+    //
+    // Please note this call happens on the gpu.
+    auto gpu_task_runner = buffer_manager_->gpu_thread_runner();
+    if (!gpu_task_runner)
+      gpu_task_runner = base::ThreadTaskRunnerHandle::Get();
+
     binders->Add<ozone::mojom::WaylandBufferManagerGpu>(
         base::BindRepeating(
             &OzonePlatformWayland::CreateWaylandBufferManagerGpuBinding,
             base::Unretained(this)),
-        base::SequencedTaskRunnerHandle::Get());
+        gpu_task_runner);
   }
 
   void CreateWaylandBufferManagerGpuBinding(