diff --git a/DEPS b/DEPS
index e40334b..4258baa 100644
--- a/DEPS
+++ b/DEPS
@@ -308,7 +308,7 @@
   # 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': '34bb500bea6c2d21d9c0584815b1ea6045fc4c12',
+  'skia_revision': '9f52bfca10c08bdfef4570537421812f2b342dbd',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -387,7 +387,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': 'a2ff6753e59bce86a836ad7b44f463339f7a26ce',
+  'devtools_frontend_revision': 'e558072b02badb1c9e24191fdbd39787a1f5cd87',
   # 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.
@@ -423,7 +423,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': 'fdbc31f76b30377c449c918abaac3fb74f114204',
+  'dawn_revision': '3aad6d186e035609dda8f2b268d5536fe5e9eb4b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -441,10 +441,6 @@
   # and whatever else without interference from each other.
   'wuffs_revision': 'a8205c2fe7564b12fea81ee028ba670112cc7719',
   # Three lines of non-changing comments so that
-  # the commit queue can handle CLs rolling libgifcodec
-  # and whatever else without interference from each other.
-  'libgifcodec_revision': 'd06d2a6d42baf6c0c91cacc28df2542a911d05fe',
-  # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libavif
   # and whatever else without interference from each other.
   'libavif_revision': 'cd0bb358f83d01867f0fa53079470043618c9af5',
@@ -997,7 +993,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'Zo1OX5xTBzLf0QSH3d1Rb_85KqT0IL6lo_GDkvcrwAQC',
+          'version': 'Qrr7XbiFU6roecwamtJrTYTvM4ZrQGtEoV4s3hWRA-wC',
       },
     ],
     'condition': 'checkout_android',
@@ -1212,7 +1208,7 @@
   # Tools used when building Chrome for Chrome OS. This affects both the Simple
   # Chrome workflow, as well as the chromeos-chrome ebuild.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '44ed5becfd7ae1a63c88070a23e6528961bbada5',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '245b02947f1e2b8d5976c036cd9ebd100dc96db8',
       'condition': 'checkout_chromeos',
   },
 
@@ -1240,13 +1236,13 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'e3ed6a8e015bada7f2708d990b5e8c4d01bcf047',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '1b8211ff13f933697b9666f8a77c11ddaf43595a',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
 
   'src/third_party/devtools-frontend-internal': {
-      'url': 'https://chrome-internal.googlesource.com/devtools/devtools-internal.git' + '@' + '64c9e7da48eaa3f2962da515392533c11e1701de',
+      'url': 'https://chrome-internal.googlesource.com/devtools/devtools-internal.git' + '@' + '290a84a037fbefb1d888397d0714aebb94d728fc',
     'condition': 'checkout_src_internal',
   },
 
@@ -1658,7 +1654,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '1d22e34af6597ada72fc7409890109de30841c8d',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '43b695bca3d9f4e6fca62093f4835a2f00c3dedd',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1843,10 +1839,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'e84b11b1d90efe59e94bb24d1f1276071f50481e',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '4d41913d14378aff5a72ee1a8cbc23d43579836a',
-
-  'src/third_party/libgifcodec':
-     Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
+    Var('webrtc_git') + '/src.git' + '@' + '48912451d470b243c8602e9af5ca548173a64353',
 
   # Wuffs' canonical repository is at github.com/google/wuffs, but we use
   # Skia's mirror of Wuffs, the same as in upstream Skia's DEPS file.
@@ -1916,7 +1909,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@7c6f7910a955f6993791a2c27334678e7599326b',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@beef645437edb0931003a2655bea62bd32d7607f',
     'condition': 'checkout_src_internal',
   },
 
@@ -1946,7 +1939,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': 'W1uYgk2OV6uUYDY61zydFOyDmMJnom75jGmAz91V0hQC',
+        'version': '2ZGDlIax9X418GLNndKZ1adesI5hlx4HBH30XydUdrgC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -1957,7 +1950,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': '-175YH83BENX1QhaDhvMX2HeTXah_MzCAhXTffTqV4MC',
+        'version': 'pN0SqD7BQEI8s5tiYdhzaWTo6-baJ4cRwnic5JluU4UC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/ash/capture_mode/DIR_METADATA b/ash/capture_mode/DIR_METADATA
index ad46d34..34a74c0 100644
--- a/ash/capture_mode/DIR_METADATA
+++ b/ash/capture_mode/DIR_METADATA
@@ -1,3 +1,10 @@
+team_email: "chromeos-wm-corexp@google.com"
+buganizer: {
+  component_id:1253115
+}
+buganizer_public: {
+  component_id:1253131
+}
 monorail {
   component: "UI>Shell>ScreenCapture"
 }
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 1806b92..dc32bee 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -680,7 +680,7 @@
 // Enables sending start signaling to establish Eche's WebRTC connection.
 BASE_FEATURE(kEcheSWASendStartSignaling,
              "EcheSWASendStartSignaling",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+             base::FEATURE_DISABLED_BY_DEFAULT);
 
 // Allows disabling the stun servers when establishing a WebRTC connection to
 // Eche.
diff --git a/ash/public/cpp/capture_mode/DIR_METADATA b/ash/public/cpp/capture_mode/DIR_METADATA
index ad46d34..34a74c0 100644
--- a/ash/public/cpp/capture_mode/DIR_METADATA
+++ b/ash/public/cpp/capture_mode/DIR_METADATA
@@ -1,3 +1,10 @@
+team_email: "chromeos-wm-corexp@google.com"
+buganizer: {
+  component_id:1253115
+}
+buganizer_public: {
+  component_id:1253131
+}
 monorail {
   component: "UI>Shell>ScreenCapture"
 }
diff --git a/ash/root_window_controller.cc b/ash/root_window_controller.cc
index c9de52b..eeebd64e 100644
--- a/ash/root_window_controller.cc
+++ b/ash/root_window_controller.cc
@@ -959,10 +959,14 @@
       root_window->SetEventTargeter(std::make_unique<RootWindowTargeter>());
   DCHECK(!old_targeter);
 
+  std::unique_ptr<RootWindowLayoutManager> root_window_layout_manager =
+      std::make_unique<RootWindowLayoutManager>(root_window);
+  root_window_layout_manager_ = root_window_layout_manager.get();
+
   CreateContainers();
   CreateSystemWallpaper(root_window_type);
 
-  InitLayoutManagers();
+  InitLayoutManagers(std::move(root_window_layout_manager));
   InitTouchHuds();
 
   // `shelf_` was created in the constructor.
@@ -1005,14 +1009,15 @@
   }
 }
 
-void RootWindowController::InitLayoutManagers() {
+void RootWindowController::InitLayoutManagers(
+    std::unique_ptr<RootWindowLayoutManager> root_window_layout_manager) {
   // Create the shelf and status area widgets. Creates the ShelfLayoutManager
   // as a side-effect.
   DCHECK(!shelf_->shelf_widget());
   aura::Window* root = GetRootWindow();
   shelf_->CreateShelfWidget(root);
 
-  root->SetLayoutManager(root_window_layout_manager_);
+  root->SetLayoutManager(std::move(root_window_layout_manager));
 
   for (auto* container : desks_util::GetDesksContainers(root)) {
     // Installs WorkspaceLayoutManager on the container.
@@ -1063,8 +1068,10 @@
 }
 
 void RootWindowController::CreateContainers() {
+  // CreateContainer() depends on root_window_layout_manager_.
+  DCHECK(root_window_layout_manager_);
+
   aura::Window* root = GetRootWindow();
-  root_window_layout_manager_ = new RootWindowLayoutManager(root);
 
   // Add a NOT_DRAWN layer in between the root_window's layer and its current
   // children so that we only need to initiate two LayerAnimationSequences for
diff --git a/ash/root_window_controller.h b/ash/root_window_controller.h
index 6ed0e29..38f265e 100644
--- a/ash/root_window_controller.h
+++ b/ash/root_window_controller.h
@@ -262,7 +262,8 @@
   // Initializes the RootWindowController based on |root_window_type|.
   void Init(RootWindowType root_window_type);
 
-  void InitLayoutManagers();
+  void InitLayoutManagers(
+      std::unique_ptr<RootWindowLayoutManager> root_window_layout_manager);
 
   AccessibilityPanelLayoutManager* GetAccessibilityPanelLayoutManager() const;
 
diff --git a/ash/services/recording/DIR_METADATA b/ash/services/recording/DIR_METADATA
index ad46d34..34a74c0 100644
--- a/ash/services/recording/DIR_METADATA
+++ b/ash/services/recording/DIR_METADATA
@@ -1,3 +1,10 @@
+team_email: "chromeos-wm-corexp@google.com"
+buganizer: {
+  component_id:1253115
+}
+buganizer_public: {
+  component_id:1253131
+}
 monorail {
   component: "UI>Shell>ScreenCapture"
 }
diff --git a/ash/shelf/shelf_widget.cc b/ash/shelf/shelf_widget.cc
index acb8f5e..ccd0948 100644
--- a/ash/shelf/shelf_widget.cc
+++ b/ash/shelf/shelf_widget.cc
@@ -754,7 +754,9 @@
 ShelfWidget::ShelfWidget(Shelf* shelf)
     : shelf_(shelf),
       background_animator_(shelf_, Shell::Get()->wallpaper_controller()),
-      shelf_layout_manager_(new ShelfLayoutManager(this, shelf)),
+      shelf_layout_manager_owned_(
+          std::make_unique<ShelfLayoutManager>(this, shelf)),
+      shelf_layout_manager_(shelf_layout_manager_owned_.get()),
       delegate_view_(new DelegateView(this, shelf_)),
       scoped_session_observer_(this) {
   DCHECK(shelf_);
@@ -793,7 +795,7 @@
   SetContentsView(delegate_view_);
 
   shelf_layout_manager_->AddObserver(this);
-  shelf_container->SetLayoutManager(shelf_layout_manager_);
+  shelf_container->SetLayoutManager(std::move(shelf_layout_manager_owned_));
   shelf_layout_manager_->InitObservers();
   background_animator_.Init(ShelfBackgroundType::kDefaultBg);
   background_animator_.PaintBackground(
diff --git a/ash/shelf/shelf_widget.h b/ash/shelf/shelf_widget.h
index 1474fbf..0295923 100644
--- a/ash/shelf/shelf_widget.h
+++ b/ash/shelf/shelf_widget.h
@@ -205,6 +205,9 @@
   gfx::Rect target_bounds_;
   ShelfBackgroundAnimator background_animator_;
 
+  // Set only during initialization.
+  std::unique_ptr<ShelfLayoutManager> shelf_layout_manager_owned_;
+
   // Owned by the shelf container's window.
   ShelfLayoutManager* shelf_layout_manager_;
 
diff --git a/ash/style/DIR_METADATA b/ash/style/DIR_METADATA
new file mode 100644
index 0000000..b45ffbf
--- /dev/null
+++ b/ash/style/DIR_METADATA
@@ -0,0 +1,10 @@
+team_email: "chromeos-wm-corexp@google.com"
+buganizer: {
+  component_id:1253063
+}
+buganizer_public: {
+  component_id:1253110
+}
+monorail {
+  component: "UI>Shell>SysUIComponents"
+}
diff --git a/ash/system/night_light/DIR_METADATA b/ash/system/night_light/DIR_METADATA
index ade2a773..0abca3d9 100644
--- a/ash/system/night_light/DIR_METADATA
+++ b/ash/system/night_light/DIR_METADATA
@@ -1,3 +1,10 @@
+team_email: "chromeos-wm-corexp@google.com"
+buganizer: {
+  component_id:1252585
+}
+buganizer_public: {
+  component_id:1253076
+}
 monorail {
   component: "UI>NightLight"
 }
diff --git a/ash/webui/common/resources/BUILD.gn b/ash/webui/common/resources/BUILD.gn
index 1f4280a6..0aca57f 100644
--- a/ash/webui/common/resources/BUILD.gn
+++ b/ash/webui/common/resources/BUILD.gn
@@ -109,6 +109,7 @@
   "focus_row_behavior.js",
   "focus_without_ink_js.js",
   "keyboard_shortcut_list_js.js",
+  "list_property_update_behavior.js",
   "multidevice_setup/fake_mojo_service.js",
   "multidevice_setup/mojo_api.js",
   "multidevice_setup/multidevice_setup_browser_proxy.js",
@@ -303,6 +304,7 @@
   "focus_row_behavior.d.ts",
   "keyboard_diagram.d.ts",
   "keyboard_key.d.ts",
+  "list_property_update_behavior.d.ts",
 
   "page_toolbar.d.ts",
   "navigation_view_panel.d.ts",
@@ -398,6 +400,7 @@
     ":keyboard_key",
     ":keyboard_layouts",
     ":keyboard_shortcut_list_js",
+    ":list_property_update_behavior",
     ":mojo_utils",
     ":navigation_selector",
     ":navigation_view_panel",
@@ -460,6 +463,12 @@
 js_library("keyboard_shortcut_list_js") {
 }
 
+js_library("list_property_update_behavior") {
+  deps = [
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+  ]
+}
+
 js_library("mojo_utils") {
   deps = [ "//ash/webui/common/mojom:mojom_js_library_for_compile" ]
 }
diff --git a/ui/webui/resources/js/list_property_update_behavior.d.ts b/ash/webui/common/resources/list_property_update_behavior.d.ts
similarity index 100%
rename from ui/webui/resources/js/list_property_update_behavior.d.ts
rename to ash/webui/common/resources/list_property_update_behavior.d.ts
diff --git a/ui/webui/resources/js/list_property_update_behavior.js b/ash/webui/common/resources/list_property_update_behavior.js
similarity index 100%
rename from ui/webui/resources/js/list_property_update_behavior.js
rename to ash/webui/common/resources/list_property_update_behavior.js
diff --git a/ash/webui/common/resources/network/BUILD.gn b/ash/webui/common/resources/network/BUILD.gn
index 4111ac4d..ad43cd31 100644
--- a/ash/webui/common/resources/network/BUILD.gn
+++ b/ash/webui/common/resources/network/BUILD.gn
@@ -179,10 +179,10 @@
     ":network_list_item",
     ":network_list_types",
     ":onc_mojo",
+    "//ash/webui/common/resources:list_property_update_behavior",
     "//third_party/polymer/v3_0/components-chromium/iron-list:iron-list",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
     "//ui/webui/resources/cr_elements:cr_scrollable_behavior",
-    "//ui/webui/resources/js:list_property_update_behavior",
   ]
 }
 
diff --git a/ash/webui/common/resources/network/network_list.js b/ash/webui/common/resources/network/network_list.js
index ae67dfd2d..bd5d6e6 100644
--- a/ash/webui/common/resources/network/network_list.js
+++ b/ash/webui/common/resources/network/network_list.js
@@ -12,7 +12,7 @@
 import '//resources/polymer/v3_0/iron-list/iron-list.js';
 
 import {CrScrollableBehavior} from '//resources/cr_elements/cr_scrollable_behavior.js';
-import {ListPropertyUpdateBehavior} from '//resources/js/list_property_update_behavior.js';
+import {ListPropertyUpdateBehavior} from '//resources/ash/common/list_property_update_behavior.js';
 import {Polymer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {GlobalPolicy} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-webui.js';
 
diff --git a/ash/webui/eche_app_ui/system_info_provider_unittest.cc b/ash/webui/eche_app_ui/system_info_provider_unittest.cc
index ed9de01..e6dd099 100644
--- a/ash/webui/eche_app_ui/system_info_provider_unittest.cc
+++ b/ash/webui/eche_app_ui/system_info_provider_unittest.cc
@@ -29,7 +29,7 @@
 const char kFakeGaiaId[] = "123";
 const char kFakeDeviceType[] = "Chromebook";
 const bool kFakeMeasureLatency = false;
-const bool kFakeSendStartSignaling = true;
+const bool kFakeSendStartSignaling = false;
 const bool kFakeDisableStunServer = false;
 
 void ParseJson(const std::string& json,
diff --git a/ash/webui/os_feedback_ui/resources/share_data_page.js b/ash/webui/os_feedback_ui/resources/share_data_page.js
index 5d062624..daf8ed3 100644
--- a/ash/webui/os_feedback_ui/resources/share_data_page.js
+++ b/ash/webui/os_feedback_ui/resources/share_data_page.js
@@ -42,7 +42,13 @@
 
   static get properties() {
     return {
-      feedbackContext: {type: FeedbackContext, readOnly: false, notify: true},
+      feedbackContext: {
+        type: FeedbackContext,
+        readOnly: false,
+        notify: true,
+        observer: 'onFeedbackContextChanged_',
+      },
+
       screenshotUrl: {type: String, readOnly: false, notify: true},
       shouldShowBluetoothCheckbox:
           {type: Boolean, readOnly: false, notify: true},
@@ -105,7 +111,7 @@
     super.ready();
     this.setPrivacyNote_();
     this.setSysInfoCheckboxLabelAndAttributes_();
-    this.setPerformanceTraceCheckboxLabelAndAttributes_();
+    this.setPerformanceTraceCheckboxLabel_();
     this.setAssistantLogsCheckboxLabelAndAttributes_();
     this.setBluetoothLogsCheckboxLabelAndAttributes_();
     // Set the aria description works the best for screen reader.
@@ -406,11 +412,9 @@
   }
 
   /** @private */
-  setPerformanceTraceCheckboxLabelAndAttributes_() {
+  setPerformanceTraceCheckboxLabel_() {
     this.performanceTraceCheckboxLabel_ = this.i18nAdvanced(
         'includePerformanceTraceCheckboxLabel', {attrs: ['id']});
-    // TODO(swifton): Make the hyperlink download the trace like in the original
-    // app.
   }
 
   /** @private */
@@ -437,6 +441,17 @@
     bluetoothLogsLink.addEventListener(
         'click', (e) => void this.handleOpenBluetoothLogsInfoDialog_(e));
   }
+
+  /** @private */
+  onFeedbackContextChanged_() {
+    // We can only set up the hyperlink for the performance trace checkbox once
+    // we receive the trace id.
+    if (this.feedbackContext !== null && this.feedbackContext.traceId !== 0) {
+      this.openLinkInNewWindow_(
+          '#performanceTraceLink',
+          `chrome://slow_trace/tracing.zip#${this.feedbackContext.traceId}`);
+    }
+  }
 }
 
 customElements.define(ShareDataPageElement.is, ShareDataPageElement);
diff --git a/ash/wm/DIR_METADATA b/ash/wm/DIR_METADATA
index 72020ac..a92c6cde 100644
--- a/ash/wm/DIR_METADATA
+++ b/ash/wm/DIR_METADATA
@@ -1,3 +1,10 @@
+team_email: "chromeos-wm-corexp@google.com"
+buganizer: {
+  component_id:1238037
+}
+buganizer_public: {
+  component_id:1238204
+}
 monorail {
   component: "UI>Shell>WindowManager"
 }
diff --git a/ash/wm/float/DIR_METADATA b/ash/wm/float/DIR_METADATA
index cefd782..c1b5a4b 100644
--- a/ash/wm/float/DIR_METADATA
+++ b/ash/wm/float/DIR_METADATA
@@ -1,3 +1,10 @@
+team_email: "chromeos-wm-corexp@google.com"
+buganizer: {
+  component_id:1252568
+}
+buganizer_public: {
+  component_id:1253054
+}
 monorail {
   component: "UI>Shell>WindowManager>FloatingWindow"
 }
diff --git a/ash/wm/gestures/DIR_METADATA b/ash/wm/gestures/DIR_METADATA
index 2eada40..8bff435 100644
--- a/ash/wm/gestures/DIR_METADATA
+++ b/ash/wm/gestures/DIR_METADATA
@@ -1,3 +1,10 @@
+team_email: "chromeos-wm-corexp@google.com"
+buganizer: {
+  component_id:1253088
+}
+buganizer_public: {
+  component_id:1253613
+}
 monorail {
   component: "UI>Shell>GestureNav"
 }
diff --git a/ash/wm/overview/DIR_METADATA b/ash/wm/overview/DIR_METADATA
index 86718f7..6df9ac2 100644
--- a/ash/wm/overview/DIR_METADATA
+++ b/ash/wm/overview/DIR_METADATA
@@ -1,3 +1,10 @@
+team_email: "chromeos-wm-corexp@google.com"
+buganizer: {
+  component_id:1252584
+}
+buganizer_public: {
+  component_id:1253075
+}
 monorail {
   component: "UI>Shell>WindowManager>OverviewMode"
 }
diff --git a/ash/wm/pip/DIR_METADATA b/ash/wm/pip/DIR_METADATA
index 92214fb5..c25b61ae 100644
--- a/ash/wm/pip/DIR_METADATA
+++ b/ash/wm/pip/DIR_METADATA
@@ -1,3 +1,10 @@
+team_email: "chromeos-wm-corexp@google.com"
+buganizer: {
+  component_id:1252583
+}
+buganizer_public: {
+  component_id:1253051
+}
 monorail {
   component: "UI>Shell>WindowManager>PictureInPicture"
 }
diff --git a/ash/wm/splitview/DIR_METADATA b/ash/wm/splitview/DIR_METADATA
index 719daf79..6b4efb7 100644
--- a/ash/wm/splitview/DIR_METADATA
+++ b/ash/wm/splitview/DIR_METADATA
@@ -1,3 +1,10 @@
+team_email: "chromeos-wm-corexp@google.com"
+buganizer: {
+  component_id:1252451
+}
+buganizer_public: {
+  component_id:1253108
+}
 monorail {
   component: "UI>Shell>WindowManager>Splitscreen"
 }
diff --git a/ash/wm/tablet_mode/DIR_METADATA b/ash/wm/tablet_mode/DIR_METADATA
index 8e8a6a7..65248f40 100644
--- a/ash/wm/tablet_mode/DIR_METADATA
+++ b/ash/wm/tablet_mode/DIR_METADATA
@@ -1,3 +1,10 @@
+team_email: "chromeos-wm-corexp@google.com"
+buganizer: {
+  component_id:1253116
+}
+buganizer_public: {
+  component_id:1253614
+}
 monorail {
   component: "UI>Shell>TabletMode"
 }
diff --git a/ash/wm_mode/DIR_METADATA b/ash/wm_mode/DIR_METADATA
index 72020ac..a92c6cde 100644
--- a/ash/wm_mode/DIR_METADATA
+++ b/ash/wm_mode/DIR_METADATA
@@ -1,3 +1,10 @@
+team_email: "chromeos-wm-corexp@google.com"
+buganizer: {
+  component_id:1238037
+}
+buganizer_public: {
+  component_id:1238204
+}
 monorail {
   component: "UI>Shell>WindowManager"
 }
diff --git a/base/allocator/partition_allocator/hardening_unittest.cc b/base/allocator/partition_allocator/hardening_unittest.cc
index 6d02d0a..760d0ca 100644
--- a/base/allocator/partition_allocator/hardening_unittest.cc
+++ b/base/allocator/partition_allocator/hardening_unittest.cc
@@ -112,6 +112,9 @@
 #endif  // !BUILDFLAG(IS_ANDROID) && defined(GTEST_HAS_DEATH_TEST) &&
         // defined(PA_HAS_FREELIST_SHADOW_ENTRY)
 
+// Below test also misbehaves on Android, crbug.com/1370048
+#if !BUILDFLAG(IS_ANDROID)
+
 TEST(HardeningTest, SuccessfulCorruption) {
   PartitionRoot<ThreadSafe> root({
       PartitionOptions::AlignedAlloc::kAllowed,
@@ -156,6 +159,7 @@
   EXPECT_EQ(new_data2, to_corrupt);
 #endif  // BUILDFLAG(USE_FREESLOT_BITMAP)
 }
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 }  // namespace
 }  // namespace partition_alloc::internal
diff --git a/base/i18n/rtl.cc b/base/i18n/rtl.cc
index f239910a..f9008c1 100644
--- a/base/i18n/rtl.cc
+++ b/base/i18n/rtl.cc
@@ -37,10 +37,16 @@
   const char* language = locale.getLanguage();
   const char* country = locale.getCountry();
   const char* variant = locale.getVariant();
+  const char* script = locale.getScript();
 
   std::string result =
       (language != nullptr && *language != '\0') ? language : "und";
 
+  if (script != nullptr && *script != '\0') {
+    result += '-';
+    result += script;
+  }
+
   if (country != nullptr && *country != '\0') {
     result += '-';
     result += country;
diff --git a/base/types/optional_ref.h b/base/types/optional_ref.h
index bff386a..561e2bd 100644
--- a/base/types/optional_ref.h
+++ b/base/types/optional_ref.h
@@ -121,6 +121,10 @@
   // Note: when constructing from a const reference, `optional_ref`'s template
   // argument must be const-qualified as well.
   // Note 2: avoiding direct use of `T` prevents implicit conversions.
+  template <typename U, typename = std::enable_if_t<IsCompatibleV<const U>>>
+  // NOLINTNEXTLINE(google-explicit-constructor)
+  constexpr optional_ref(const U& r ABSL_ATTRIBUTE_LIFETIME_BOUND)
+      : ptr_(std::addressof(r)) {}
   template <typename U, typename = std::enable_if_t<IsCompatibleV<U>>>
   // NOLINTNEXTLINE(google-explicit-constructor)
   constexpr optional_ref(U& r ABSL_ATTRIBUTE_LIFETIME_BOUND)
@@ -182,6 +186,19 @@
   T* const ptr_ = nullptr;
 };
 
+template <typename T>
+optional_ref(const T&) -> optional_ref<const T>;
+template <typename T>
+optional_ref(T&) -> optional_ref<T>;
+
+template <typename T>
+optional_ref(const absl::optional<T>&) -> optional_ref<const T>;
+template <typename T>
+optional_ref(absl::optional<T>&) -> optional_ref<T>;
+
+template <typename T>
+optional_ref(T*) -> optional_ref<T>;
+
 }  // namespace base
 
 #endif  // BASE_TYPES_OPTIONAL_REF_H_
diff --git a/base/types/optional_ref_unittest.cc b/base/types/optional_ref_unittest.cc
index 17f16956..2cfd7c11 100644
--- a/base/types/optional_ref_unittest.cc
+++ b/base/types/optional_ref_unittest.cc
@@ -25,6 +25,7 @@
     !std::is_constructible_v<optional_ref<int>, const absl::optional<int>&>);
 static_assert(!std::is_constructible_v<optional_ref<int>, const int*>);
 static_assert(!std::is_constructible_v<optional_ref<int>, const int&>);
+static_assert(!std::is_constructible_v<optional_ref<int>, int&&>);
 static_assert(!std::is_constructible_v<optional_ref<int>, const int>);
 static_assert(
     !std::is_constructible_v<optional_ref<int>, optional_ref<const int>>);
@@ -182,6 +183,11 @@
     EXPECT_EQ(6, r.value());
   }(value);
 
+  [](optional_ref<const int> r) {
+    EXPECT_TRUE(r.has_value());
+    EXPECT_EQ(6, r.value());
+  }(6);
+
   // Mutable case covered by static_assert test above.
 }
 
@@ -405,6 +411,47 @@
   }(absl::nullopt);
 }
 
+TEST(OptionalRefTest, ClassTemplateArgumentDeduction) {
+  static_assert(
+      std::is_same_v<decltype(optional_ref{int()}), optional_ref<const int>>);
+
+  {
+    const int i = 0;
+    static_assert(
+        std::is_same_v<decltype(optional_ref(i)), optional_ref<const int>>);
+  }
+
+  {
+    int i = 0;
+    static_assert(std::is_same_v<decltype(optional_ref(i)), optional_ref<int>>);
+  }
+
+  static_assert(std::is_same_v<decltype(optional_ref(absl::optional<int>())),
+                               optional_ref<const int>>);
+
+  {
+    const absl::optional<int> o;
+    static_assert(
+        std::is_same_v<decltype(optional_ref(o)), optional_ref<const int>>);
+  }
+
+  {
+    absl::optional<int> o;
+    static_assert(std::is_same_v<decltype(optional_ref(o)), optional_ref<int>>);
+  }
+
+  {
+    const int* p = nullptr;
+    static_assert(
+        std::is_same_v<decltype(optional_ref(p)), optional_ref<const int>>);
+  }
+
+  {
+    int* p = nullptr;
+    static_assert(std::is_same_v<decltype(optional_ref(p)), optional_ref<int>>);
+  }
+}
+
 }  // namespace
 
 }  // namespace base
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1
index d93a77b..c934e8c0 100644
--- a/build/fuchsia/linux_internal.sdk.sha1
+++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@
-9.20220930.0.1
+9.20220930.2.1
diff --git a/build/linux/sysroot_scripts/sysroot-creator.sh b/build/linux/sysroot_scripts/sysroot-creator.sh
index 15a10d5..4220cd3 100644
--- a/build/linux/sysroot_scripts/sysroot-creator.sh
+++ b/build/linux/sysroot_scripts/sysroot-creator.sh
@@ -484,9 +484,9 @@
     dpkg-deb -e ${package} ${INSTALL_ROOT}/debian/${base_package}/DEBIAN
   done
 
-  # Prune /usr/share, leaving only pkgconfig and wayland-protocols.
+  # Prune /usr/share, leaving only pkgconfig, wayland, and wayland-protocols.
   ls -d ${INSTALL_ROOT}/usr/share/* | \
-    grep -v "/\(pkgconfig\|wayland-protocols\)$" | xargs rm -r
+    grep -v "/\(pkgconfig\|wayland\|wayland-protocols\)$" | xargs rm -r
 }
 
 
diff --git a/build/linux/sysroot_scripts/sysroots.json b/build/linux/sysroot_scripts/sysroots.json
index fec4188..0549eaa 100644
--- a/build/linux/sysroot_scripts/sysroots.json
+++ b/build/linux/sysroot_scripts/sysroots.json
@@ -1,36 +1,36 @@
 {
     "bullseye_amd64": {
-        "Sha1Sum": "538c28a35982ebc680ca8111c4c06dfa7adf4948",
+        "Sha1Sum": "393193b824afacfcd2fb53cb47fc2023740f4995",
         "SysrootDir": "debian_bullseye_amd64-sysroot",
         "Tarball": "debian_bullseye_amd64_sysroot.tar.xz"
     },
     "bullseye_arm": {
-        "Sha1Sum": "377dede7e003c11c729e0b3f9f88ed2c8e91e403",
+        "Sha1Sum": "1b097e2e837d7a0b81ae42c8ff6e473dbd24256d",
         "SysrootDir": "debian_bullseye_arm-sysroot",
         "Tarball": "debian_bullseye_arm_sysroot.tar.xz"
     },
     "bullseye_arm64": {
-        "Sha1Sum": "2e3f631551d94198958b6fe2981c7fab136f3d01",
+        "Sha1Sum": "3a98f836c7a3386dd757268cd07c427f9e6c7c86",
         "SysrootDir": "debian_bullseye_arm64-sysroot",
         "Tarball": "debian_bullseye_arm64_sysroot.tar.xz"
     },
     "bullseye_armel": {
-        "Sha1Sum": "79e323d256849defe51521812efa0c60e841e07c",
+        "Sha1Sum": "1ff91eb5b00cfc857d4ea99d45f32fd5df303292",
         "SysrootDir": "debian_bullseye_armel-sysroot",
         "Tarball": "debian_bullseye_armel_sysroot.tar.xz"
     },
     "bullseye_i386": {
-        "Sha1Sum": "7a8849c90ad9c6b602c98165f78ffbe4eeb00366",
+        "Sha1Sum": "af39443b6101f4c417bb83113a306fc028d2b665",
         "SysrootDir": "debian_bullseye_i386-sysroot",
         "Tarball": "debian_bullseye_i386_sysroot.tar.xz"
     },
     "bullseye_mips": {
-        "Sha1Sum": "57e6628ecf6650dfd880c60c3fba8c006788435b",
+        "Sha1Sum": "9ca3ace5b108e3078dee4b2f5d395f685a909acc",
         "SysrootDir": "debian_bullseye_mips-sysroot",
         "Tarball": "debian_bullseye_mips_sysroot.tar.xz"
     },
     "bullseye_mips64el": {
-        "Sha1Sum": "d23f639437eb51e788cf18738b69dafdca96defe",
+        "Sha1Sum": "9a5e7d0868f363f526675487568ab35a7254bdd5",
         "SysrootDir": "debian_bullseye_mips64el-sysroot",
         "Tarball": "debian_bullseye_mips64el_sysroot.tar.xz"
     }
diff --git a/buildtools/checkdeps/cpp_checker.py b/buildtools/checkdeps/cpp_checker.py
index 1ee2749..f6d85b86 100644
--- a/buildtools/checkdeps/cpp_checker.py
+++ b/buildtools/checkdeps/cpp_checker.py
@@ -34,7 +34,7 @@
   # This regular expression will be used to extract filenames from include
   # statements.
   _EXTRACT_INCLUDE_PATH = re.compile(
-      r'[ \t]*#[ \t]*(?:include|import)[ \t]+"(.*)"')
+      r'[ \t]*#[ \t]*(?:include|import)[ \t]*"(.*)"')
 
   def __init__(self, verbose, resolve_dotdot=False, root_dir=''):
     self._verbose = verbose
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index fc66534a..58879be 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -1687,7 +1687,10 @@
     }
 
     if (is_android) {
-      deps += [ "//components/crash/android:crash_android" ]
+      deps += [
+        "//chrome/browser/flags:flags_android",
+        "//components/crash/android:crash_android",
+      ]
     }
 
     if (is_chromeos_ash) {
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 60bf54e..01ef8d63 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -1350,6 +1350,7 @@
       "javatests/src/org/chromium/chrome/browser/IntentFilterUnitTest.java",
       "javatests/src/org/chromium/chrome/browser/IntentHandlerUnitTest.java",
       "javatests/src/org/chromium/chrome/browser/autofill/AutofillUnitTest.java",
+      "javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkActionBarTest.java",
       "javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkItemRowTest.java",
       "javatests/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkShoppingItemRowTest.java",
       "javatests/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkUtilsTest.java",
@@ -1411,6 +1412,7 @@
       "//chrome/browser/feature_engagement:java",
       "//chrome/browser/first_run/android:java",
       "//chrome/browser/flags:java",
+      "//chrome/browser/incognito:java",
       "//chrome/browser/lens:java",
       "//chrome/browser/preferences:java",
       "//chrome/browser/profiles/android:java",
@@ -1462,6 +1464,7 @@
       "//third_party/android_deps:espresso_java",
       "//third_party/android_deps:guava_android_java",
       "//third_party/android_sdk:android_test_base_java",
+      "//third_party/android_support_test_runner:rules_java",
       "//third_party/android_support_test_runner:runner_java",
       "//third_party/androidx:androidx_annotation_annotation_java",
       "//third_party/androidx:androidx_browser_browser_java",
diff --git a/chrome/android/expectations/lint-baseline.xml b/chrome/android/expectations/lint-baseline.xml
index 9783cbf..f4ef3020 100644
--- a/chrome/android/expectations/lint-baseline.xml
+++ b/chrome/android/expectations/lint-baseline.xml
@@ -9642,4 +9642,25 @@
             column="17"/>
     </issue>
 
+    <issue
+        id="GestureBackNavigation"
+        message="If intercepting back events, this should be handled through the registration of callbacks on the window level; Please see https://developer.android.com/about/versions/13/features/predictive-back-gesture"
+        errorLine1="            if (keyCode == KeyEvent.KEYCODE_BACK) {"
+        errorLine2="                           ~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="$SRC/chrome/android/java/src/org/chromium/chrome/browser/findinpage/FindToolbar.java"
+            line="137"
+            column="28"/>
+    </issue>
+
+    <issue
+        id="GestureBackNavigation"
+        message="If intercepting back events, this should be handled through the registration of callbacks on the window level; Please see https://developer.android.com/about/versions/13/features/predictive-back-gesture"
+        errorLine1="        } else if (keyCode == KeyEvent.KEYCODE_BACK) {"
+        errorLine2="                              ~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="$SRC/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java"
+            line="1143"
+            column="31"/>
+    </issue>
 </issues>
diff --git a/chrome/android/expectations/monochrome_public_bundle__base.AndroidManifest.expected b/chrome/android/expectations/monochrome_public_bundle__base.AndroidManifest.expected
index 7f51549..ee24ca2 100644
--- a/chrome/android/expectations/monochrome_public_bundle__base.AndroidManifest.expected
+++ b/chrome/android/expectations/monochrome_public_bundle__base.AndroidManifest.expected
@@ -72,6 +72,7 @@
       android:allowAudioPlaybackCapture="false"
       android:allowBackup="false"
       android:appComponentFactory="org.chromium.chrome.browser.base.SplitCompatAppComponentFactory"
+      android:enableOnBackInvokedCallback="true"
       android:extractNativeLibs="false"
       android:icon="@drawable/ic_launcher"
       android:label="@string/app_name"
diff --git a/chrome/android/expectations/trichrome_chrome_bundle__base.AndroidManifest.expected b/chrome/android/expectations/trichrome_chrome_bundle__base.AndroidManifest.expected
index a3c242c..c43cfff 100644
--- a/chrome/android/expectations/trichrome_chrome_bundle__base.AndroidManifest.expected
+++ b/chrome/android/expectations/trichrome_chrome_bundle__base.AndroidManifest.expected
@@ -72,6 +72,7 @@
       android:allowAudioPlaybackCapture="false"
       android:allowBackup="false"
       android:appComponentFactory="org.chromium.chrome.browser.base.SplitCompatAppComponentFactory"
+      android:enableOnBackInvokedCallback="true"
       android:extractNativeLibs="false"
       android:icon="@drawable/ic_launcher"
       android:label="@string/app_name"
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java
index 5009a14..4d4ffbb 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java
@@ -46,7 +46,6 @@
 import org.chromium.chrome.browser.price_tracking.PriceTrackingUtilities;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabCreationState;
-import org.chromium.chrome.browser.tab.TabHidingType;
 import org.chromium.chrome.browser.tab.TabSelectionType;
 import org.chromium.chrome.browser.tab.state.ShoppingPersistedTabData;
 import org.chromium.chrome.browser.tabmodel.TabList;
@@ -291,13 +290,6 @@
                 }
                 if (!mContainerViewModel.get(IS_VISIBLE)) return;
                 mResetHandler.resetWithTabList(currentTabModelFilter, false, mShowTabsInMruOrder);
-
-                // Hide the currently selected tab when moving to an empty incognito model.
-                // TODO(crbug.com/1082612): If the need arises, generalize this solution in the
-                // IncognitoTabModel.
-                if (newModel.isIncognito() && newModel.getCount() == 0 && oldModel.getCount() > 0) {
-                    oldModel.getTabAt(oldModel.index()).hide(TabHidingType.CHANGED_TABS);
-                }
                 setInitialScrollIndexOffset();
             }
         };
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediatorUnitTest.java
index 25fdcabe..ebac851 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediatorUnitTest.java
@@ -54,7 +54,6 @@
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.multiwindow.MultiWindowModeStateDispatcher;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.tab.TabHidingType;
 import org.chromium.chrome.browser.tab.TabSelectionType;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelFilter;
@@ -450,38 +449,6 @@
     }
 
     @Test
-    public void hidesPreviouslySelectedTabAfterNewTabModelSelected_regularToEmptyIncognito() {
-        initAndAssertAllProperties();
-        mModel.set(TabListContainerProperties.IS_VISIBLE, true);
-
-        TabModel incognitoTabModel = mock(TabModel.class);
-        doReturn(0).when(incognitoTabModel).getCount();
-        doReturn(true).when(incognitoTabModel).isIncognito();
-
-        mTabModelSelectorObserverCaptor.getValue().onTabModelSelected(incognitoTabModel, mTabModel);
-        verify(/* mTab3 is the current tab. */ mTab3).hide(TabHidingType.CHANGED_TABS);
-    }
-
-    @Test
-    public void noHidesPreviouslySelectedTabAfterNewTabModelSelected_incognitoToEmptyRegular() {
-        // The tab shouldn't be hidden when moving from incognito to the regular tab switcher.
-        initAndAssertAllProperties();
-        mModel.set(TabListContainerProperties.IS_VISIBLE, true);
-
-        Tab tab = mock(Tab.class);
-        TabModel incognitoTabModel = mock(TabModel.class);
-        doReturn(0).when(mTabModel).getCount();
-        doReturn(0).when(mTabModel).index();
-        doReturn(1).when(incognitoTabModel).getCount();
-        doReturn(0).when(mTabModel).index();
-        doReturn(true).when(incognitoTabModel).isIncognito();
-        doReturn(tab).when(incognitoTabModel).getTabAt(0);
-
-        mTabModelSelectorObserverCaptor.getValue().onTabModelSelected(incognitoTabModel, mTabModel);
-        verify(/* mTab3 is the current tab. */ mTab3, times(0)).hide(TabHidingType.CHANGED_TABS);
-    }
-
-    @Test
     public void scrollAfterNewTabModelSelected() {
         initAndAssertAllProperties();
         mModel.set(TabListContainerProperties.IS_VISIBLE, true);
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
index 3bd176d..22d88b52 100644
--- a/chrome/android/java/AndroidManifest.xml
+++ b/chrome/android/java/AndroidManifest.xml
@@ -191,6 +191,7 @@
         android:networkSecurityConfig="@xml/network_security_config"
         android:allowAudioPlaybackCapture="false"
         android:appComponentFactory="org.chromium.chrome.browser.base.SplitCompatAppComponentFactory"
+        android:enableOnBackInvokedCallback="true"
         {% block extra_application_attributes %}{% endblock %}>
 
       {% macro application_definitions() %}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java b/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
index 6250cfa..628a63e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
@@ -130,6 +130,7 @@
                 add(ChromeFeatureList.sTabToGTSAnimation);
                 add(ChromeFeatureList.sToolbarUseHardwareBitmapDraw);
                 add(ChromeFeatureList.sUseChimeAndroidSdk);
+                add(ChromeFeatureList.sUseLibunwindstackNativeUnwinderAndroid);
                 add(ChromeFeatureList.sWebApkTrampolineOnInitialIntent);
             }
         };
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java
index 8e9df09..f37ab524 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java
@@ -966,7 +966,7 @@
 
     @Override
     public boolean shouldHideAndroidBrowserControls() {
-        return isPanelOpened();
+        return isPanelOpened() && mCanHideAndroidBrowserControls;
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelBase.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelBase.java
index fedd7ff..0a0e7e19 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelBase.java
@@ -136,6 +136,12 @@
     /** The padding on each side of the close and open-tab icons. */
     protected final int mButtonPaddingDps;
 
+    /**
+     *  Indicates whether the Toolbar is allowed to hide vs cannot ever be hidden.
+     *  @see OverlayPanel#shouldHideAndroidBrowserControls
+     */
+    protected boolean mCanHideAndroidBrowserControls = true;
+
     // ============================================================================================
     // Constructor
     // ============================================================================================
@@ -276,7 +282,7 @@
      * @return The current Y-position of the Overlay Panel.
      */
     protected float calculateOverlayPanelY() {
-        return getTabHeight() - mHeight;
+        return getTabHeight() + heightForNeverHideBrowserControls() - mHeight;
     }
 
     /**
@@ -319,7 +325,7 @@
      * @return The height of the tab the panel is displayed on top of.
      */
     public float getTabHeight() {
-        return mLayoutHeight;
+        return mLayoutHeight - heightForNeverHideBrowserControls();
     }
 
     /**
@@ -355,9 +361,32 @@
     }
 
     // ============================================================================================
-    // UI States
+    // Controls for a never hidden Toolbar.
     // ============================================================================================
 
+    /**
+     * Tells this Panel whether it can ever hide the Browser Controls (Toolbar).
+     * This is set to false by a Partial-height Chrome Custom Tab, and defaults to true.
+     * @param canHideAndroidBrowserControls whether hiding is ever allowed.
+     */
+    public void setCanHideAndroidBrowserControls(boolean canHideAndroidBrowserControls) {
+        mCanHideAndroidBrowserControls = canHideAndroidBrowserControls;
+    }
+
+    /**
+     * @return The Tab height adjustment needed for Android Browser controls that can never hide,
+     * or 0 if the Toolbar is allowed to hide. When the Toolbar cannot hide, it obscures part of
+     * the Base Page so the Overlay cannot use that part of the page height. Value in pixels.
+     */
+    private float heightForNeverHideBrowserControls() {
+        return mCanHideAndroidBrowserControls ? 0.f : mToolbarHeightDp * mPxToDp;
+    }
+
+    @VisibleForTesting
+    protected boolean getCanHideAndroidBrowserControls() {
+        return mCanHideAndroidBrowserControls;
+    }
+
     // --------------------------------------------------------------------------------------------
     // Overlay Panel states
     // --------------------------------------------------------------------------------------------
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java
index 512e84a..ab88a91 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java
@@ -1299,4 +1299,10 @@
                 break;
         }
     }
+
+    @Override
+    @VisibleForTesting
+    public boolean getCanHideAndroidBrowserControls() {
+        return super.getCanHideAndroidBrowserControls();
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelCoordinator.java
index c9d4ede..9ce2e49 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelCoordinator.java
@@ -300,6 +300,9 @@
     }
 
     @Override
+    public void setCanHideAndroidBrowserControls(boolean canHideAndroidBrowserControls) {}
+
+    @Override
     public boolean isPanelOpened() {
         return mBottomSheetController.isSheetOpen();
     }
@@ -329,6 +332,12 @@
         return PanelState.UNDEFINED;
     }
 
+    @Override
+    @VisibleForTesting
+    public boolean getCanHideAndroidBrowserControls() {
+        return false;
+    }
+
     // ---------------------------------------------------------------------------------------------
     // endregion
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelInterface.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelInterface.java
index 12de0dd..ea613db 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelInterface.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelInterface.java
@@ -73,6 +73,7 @@
     boolean isPeeking();
     WebContents getWebContents();
     ViewGroup getContainerView();
+    void setCanHideAndroidBrowserControls(boolean canHideAndroidBrowserControls);
 
     /** {@link OverlayPanelBase} methods */
     boolean isPanelOpened();
@@ -84,4 +85,6 @@
     void showPanel(@StateChangeReason int reason);
     @PanelState
     int getPanelState();
+    @VisibleForTesting
+    boolean getCanHideAndroidBrowserControls();
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
index fe5cd87..3c263bd1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
@@ -1821,6 +1821,9 @@
         float delta = (numMarginsToSlide * mTabMarginWidth);
         float startValue = mScrollOffset - startMarginDelta;
         float endValue = startValue - delta;
+
+        if (startValue < mMinScrollOffset) return;
+
         if (animationList != null) {
             CompositorAnimator scrollAnimator =
                     CompositorAnimator.ofFloatProperty(mUpdateHost.getAnimationHandler(), this,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
index 1a10434..f6b3c380 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
@@ -973,6 +973,20 @@
         getSearchPanelWebContents().stop();
     }
 
+    /**
+     * Tells the Panel whether it can ever hide the Browser Controls (Toolbar).
+     * This is set to false by a Partial-height Chrome Custom Tab, and defaults to true.
+     * @param canHideAndroidBrowserControls whether hiding is ever allowed.
+     */
+    public void setCanHideAndroidBrowserControls(boolean canHideAndroidBrowserControls) {
+        mSearchPanel.setCanHideAndroidBrowserControls(canHideAndroidBrowserControls);
+    }
+
+    @VisibleForTesting
+    public boolean getCanHideAndroidBrowserControls() {
+        return mSearchPanel.getCanHideAndroidBrowserControls();
+    }
+
     // ============================================================================================
     // Observers
     // ============================================================================================
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinator.java
index 4c4ca5cd..7435a8e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinator.java
@@ -48,7 +48,9 @@
 import org.chromium.chrome.browser.reengagement.ReengagementNotificationController;
 import org.chromium.chrome.browser.settings.SettingsLauncherImpl;
 import org.chromium.chrome.browser.share.ShareDelegate;
+import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.RequestDesktopUtils;
+import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.ui.RootUiCoordinator;
@@ -201,6 +203,16 @@
                     ((PartialCustomTabHeightStrategy) mCustomTabHeightStrategy)::onShowSoftInput;
             mTabController.get().registerTabObserver(
                     new PartialCustomTabTabObserver(softInputCallback));
+            mTabController.get().registerTabObserver(new EmptyTabObserver() {
+                @Override
+                public void didFirstVisuallyNonEmptyPaint(Tab tab) {
+                    BaseCustomTabActivity baseActivity = (BaseCustomTabActivity) mActivity;
+                    assert baseActivity != null;
+                    baseActivity.getContextualSearchManagerSupplier()
+                            .get()
+                            .setCanHideAndroidBrowserControls(false);
+                }
+            });
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/findinpage/FindToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/findinpage/FindToolbar.java
index e16fb46..b22ef17 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/findinpage/FindToolbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/findinpage/FindToolbar.java
@@ -35,6 +35,7 @@
 import androidx.core.view.accessibility.AccessibilityEventCompat;
 import androidx.core.view.inputmethod.EditorInfoCompat;
 
+import org.chromium.base.BuildInfo;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.ObservableSupplierImpl;
@@ -123,7 +124,7 @@
 
         public FindQuery(Context context, AttributeSet attrs) {
             super(context, attrs);
-            if (!BackPressManager.isEnabled()) {
+            if (!BackPressManager.isEnabled() && !BuildInfo.isAtLeastT()) {
                 setOnKeyListener(this);
             }
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
index 7f32af8..d255ac9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
@@ -19,6 +19,7 @@
 import androidx.annotation.VisibleForTesting;
 import androidx.core.app.ActivityOptionsCompat;
 
+import org.chromium.base.BuildInfo;
 import org.chromium.base.Callback;
 import org.chromium.base.IntentUtils;
 import org.chromium.base.Log;
@@ -192,7 +193,7 @@
         };
 
         BackPressManager backPressManager = null;
-        if (BackPressManager.isEnabled()) {
+        if (BackPressManager.isEnabled() || BuildInfo.isAtLeastT()) {
             backPressManager = new BackPressManager();
             getOnBackPressedDispatcher().addCallback(this, backPressManager.getCallback());
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index 7b33419..a28428a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -118,6 +118,7 @@
 import org.chromium.chrome.browser.theme.ThemeColorProvider.ThemeColorObserver;
 import org.chromium.chrome.browser.theme.ThemeColorProvider.TintObserver;
 import org.chromium.chrome.browser.theme.TopUiThemeColorProvider;
+import org.chromium.chrome.browser.toolbar.adaptive.AdaptiveToolbarFeatures.AdaptiveToolbarButtonVariant;
 import org.chromium.chrome.browser.toolbar.bottom.BottomControlsCoordinator;
 import org.chromium.chrome.browser.toolbar.bottom.ScrollingBottomViewResourceFrameLayout;
 import org.chromium.chrome.browser.toolbar.load_progress.LoadProgressCoordinator;
@@ -682,7 +683,12 @@
                     TabWindowManagerSingleton::getInstance,
                     (url) -> mBookmarkBridgeSupplier.hasValue()
                             && mBookmarkBridgeSupplier.get().isBookmarked(url),
-                    VoiceToolbarButtonController::isToolbarMicEnabled, jankTracker,
+                    () ->
+                    {
+                        return mToolbar.getCurrentOptionalButtonVariant() ==
+                            AdaptiveToolbarButtonVariant.VOICE;
+                    },
+                    jankTracker,
                     merchantTrustSignalsCoordinatorSupplier,
                     omniboxPedalDelegate, mControlsVisibilityDelegate,
                     ChromePureJavaExceptionReporter::postReportJavaException, backPressManager);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkActionBarTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkActionBarTest.java
new file mode 100644
index 0000000..b0ea811
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkActionBarTest.java
@@ -0,0 +1,454 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.bookmarks;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Activity;
+import android.app.Instrumentation.ActivityMonitor;
+import android.graphics.Color;
+import android.support.test.InstrumentationRegistry;
+import android.view.MenuItem;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+
+import androidx.test.espresso.core.deps.guava.primitives.Ints;
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import org.chromium.base.test.UiThreadTest;
+import org.chromium.base.test.util.Batch;
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.app.bookmarks.BookmarkAddEditFolderActivity;
+import org.chromium.chrome.browser.app.bookmarks.BookmarkEditActivity;
+import org.chromium.chrome.browser.app.bookmarks.BookmarkFolderSelectActivity;
+import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.browser.incognito.IncognitoUtils;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.components.bookmarks.BookmarkId;
+import org.chromium.components.bookmarks.BookmarkItem;
+import org.chromium.components.bookmarks.BookmarkType;
+import org.chromium.components.browser_ui.widget.selectable_list.SelectableListToolbar.SearchDelegate;
+import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.ui.base.WindowAndroid;
+import org.chromium.ui.test.util.BlankUiTestActivityTestCase;
+import org.chromium.url.GURL;
+import org.chromium.url.JUnitTestGURLs;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/** On device unit test for {@link BookmarkActionBar}. */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@Batch(Batch.PER_CLASS)
+@CommandLineFlags.Add(ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE)
+public class BookmarkActionBarTest extends BlankUiTestActivityTestCase {
+    private static final List<Integer> SELECTION_MENU_IDS =
+            Arrays.asList(R.id.selection_mode_edit_menu_id, R.id.selection_mode_move_menu_id,
+                    R.id.selection_mode_delete_menu_id, R.id.selection_open_in_new_tab_id,
+                    R.id.selection_open_in_incognito_tab_id);
+    private static final BookmarkId BOOKMARK_ID_ROOT = new BookmarkId(0, BookmarkType.NORMAL);
+    private static final BookmarkId BOOKMARK_ID_FOLDER = new BookmarkId(1, BookmarkType.NORMAL);
+    private static final BookmarkId BOOKMARK_ID_ONE = new BookmarkId(2, BookmarkType.NORMAL);
+    private static final BookmarkId BOOKMARK_ID_TWO = new BookmarkId(3, BookmarkType.NORMAL);
+    private static final BookmarkId BOOKMARK_ID_PARTNER = new BookmarkId(4, BookmarkType.PARTNER);
+    private static final BookmarkId BOOKMARK_ID_READING_LIST =
+            new BookmarkId(5, BookmarkType.READING_LIST);
+
+    @Rule
+    public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    @Mock
+    BookmarkDelegate mBookmarkDelegate;
+    @Mock
+    SelectionDelegate<BookmarkId> mSelectionDelegate;
+    @Mock
+    SearchDelegate mSearchDelegate;
+    @Mock
+    BookmarkModel mBookmarkModel;
+
+    private Activity mActivity;
+    private WindowAndroid mWindowAndroid;
+    private ViewGroup mContentView;
+    private BookmarkActionBar mBookmarkActionBar;
+    private final List<ActivityMonitor> mActivityMonitorList = new ArrayList<>();
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mBookmarkDelegate.getModel()).thenReturn(mBookmarkModel);
+        when(mBookmarkDelegate.getSelectionDelegate()).thenReturn(mSelectionDelegate);
+
+        IncognitoUtils.setEnabledForTesting(true);
+
+        mActivity = getActivity();
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            mWindowAndroid = new WindowAndroid(mActivity);
+            mContentView = new LinearLayout(mActivity);
+            mContentView.setBackgroundColor(Color.WHITE);
+            FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
+                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+            mActivity.setContentView(mContentView, params);
+
+            mBookmarkActionBar = mActivity.getLayoutInflater()
+                                         .inflate(R.layout.bookmark_action_bar, mContentView, true)
+                                         .findViewById(R.id.bookmark_action_bar);
+
+            when(mBookmarkModel.getRootFolderId()).thenReturn(BOOKMARK_ID_ROOT);
+            when(mBookmarkModel.getTopLevelFolderParentIDs())
+                    .thenReturn(Collections.singletonList(BOOKMARK_ID_ROOT));
+
+            mockBookmarkItem(BOOKMARK_ID_FOLDER, "folder", null, true, BOOKMARK_ID_ROOT);
+            mockBookmarkItem(
+                    BOOKMARK_ID_ONE, "one", JUnitTestGURLs.URL_1, false, BOOKMARK_ID_FOLDER);
+            mockBookmarkItem(
+                    BOOKMARK_ID_TWO, "two", JUnitTestGURLs.URL_2, false, BOOKMARK_ID_FOLDER);
+            mockBookmarkItem(BOOKMARK_ID_PARTNER, "partner", JUnitTestGURLs.RED_1, false,
+                    BOOKMARK_ID_FOLDER);
+            mockBookmarkItem(BOOKMARK_ID_READING_LIST, "reading list", JUnitTestGURLs.BLUE_1, false,
+                    BOOKMARK_ID_FOLDER);
+        });
+    }
+
+    @After
+    public void tearDown() {
+        IncognitoUtils.setEnabledForTesting(null);
+
+        // Since these monitors block the creation of activities, it is crucial that they're removed
+        // so that when batching tests the subsequent cases actually see their activities.
+        for (ActivityMonitor activityMonitor : mActivityMonitorList) {
+            InstrumentationRegistry.getInstrumentation().removeMonitor(activityMonitor);
+        }
+        mActivityMonitorList.clear();
+
+        TestThreadUtils.runOnUiThreadBlocking(() -> { mWindowAndroid.destroy(); });
+    }
+
+    private void initializeNormal() {
+        mBookmarkActionBar.initialize(mSelectionDelegate, 0, R.id.normal_menu_group,
+                R.id.selection_mode_menu_group, false);
+        mBookmarkActionBar.onBookmarkDelegateInitialized(mBookmarkDelegate);
+        mBookmarkActionBar.initializeSearchView(
+                mSearchDelegate, R.string.bookmark_action_bar_search, R.id.search_menu_id);
+    }
+
+    private void initializeAsDialog() {
+        when(mBookmarkDelegate.isDialogUi()).thenReturn(true);
+        initializeNormal();
+    }
+
+    private void mockBookmarkItem(
+            BookmarkId bookmarkId, String title, String url, boolean isFolder, BookmarkId parent) {
+        BookmarkItem bookmarkItem = new BookmarkItem(
+                bookmarkId, title, new GURL(url), isFolder, parent, false, false, 0, false);
+        when(mBookmarkModel.getBookmarkById(bookmarkId)).thenReturn(bookmarkItem);
+    }
+
+    /**
+     * {@link SelectionDelegate} has two accessors to get the currently selected {@link BookmarkId}
+     * objects. Instead of each test case trying to set mocks for the specific calls that are
+     * invoked, this helper sets up both every time.
+     */
+    private void setCurrentSelection(BookmarkId... bookmarkIdArray) {
+        List<BookmarkId> bookmarkIdList = Arrays.asList(bookmarkIdArray);
+        when(mSelectionDelegate.getSelectedItemsAsList()).thenReturn(bookmarkIdList);
+        when(mSelectionDelegate.getSelectedItems()).thenReturn(new HashSet<>(bookmarkIdList));
+    }
+
+    private ActivityMonitor addBlockingActivityMonitor(Class clazz) {
+        ActivityMonitor activityMonitor = new ActivityMonitor(clazz.getName(), null, true);
+        InstrumentationRegistry.getInstrumentation().addMonitor(activityMonitor);
+        mActivityMonitorList.add(activityMonitor);
+        return activityMonitor;
+    }
+
+    private void verifySelectionMenuVisibility(int... hiddenMenuIds) {
+        verifyMenuVisibility(SELECTION_MENU_IDS, hiddenMenuIds);
+    }
+
+    private void verifyMenuVisibility(List<Integer> applicableMenuIds, int... hiddenMenuIds) {
+        Set<Integer> hiddenIdSet = new HashSet<>(Ints.asList(hiddenMenuIds));
+        for (int menuId : applicableMenuIds) {
+            boolean isVisible = !hiddenIdSet.contains(menuId);
+            MenuItem menuItem = mBookmarkActionBar.getMenu().findItem(menuId);
+            Assert.assertNotNull(menuId);
+            Assert.assertEquals("Mismatched visibility for menu item " + menuItem, isVisible,
+                    menuItem.isVisible());
+        }
+    }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void onNavigationBack() {
+        initializeNormal();
+        mBookmarkActionBar.onFolderStateSet(BOOKMARK_ID_FOLDER);
+        mBookmarkActionBar.onNavigationBack();
+        Mockito.verify(mBookmarkDelegate, Mockito.times(1)).openFolder(BOOKMARK_ID_ROOT);
+    }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void onNavigationBack_searching() {
+        initializeNormal();
+        mBookmarkActionBar.showSearchView(false);
+        mBookmarkActionBar.onNavigationBack();
+        Assert.assertFalse(mBookmarkActionBar.isSearching());
+    }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void testOnMenuItemClick_editMenu() {
+        ActivityMonitor activityMonitor =
+                addBlockingActivityMonitor(BookmarkAddEditFolderActivity.class);
+        initializeNormal();
+
+        mBookmarkActionBar.onFolderStateSet(BOOKMARK_ID_FOLDER);
+        Assert.assertEquals(0, activityMonitor.getHits());
+
+        Assert.assertTrue(mBookmarkActionBar.onMenuItemClick(
+                mBookmarkActionBar.getMenu().findItem(R.id.edit_menu_id)));
+        Assert.assertEquals(1, activityMonitor.getHits());
+    }
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void testOnMenuItemClick_closeMenu() {
+        initializeAsDialog();
+
+        MenuItem menuItem = mBookmarkActionBar.getMenu().findItem(R.id.close_menu_id);
+        Assert.assertNotNull(menuItem);
+        Assert.assertTrue(mBookmarkActionBar.onMenuItemClick(menuItem));
+    }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void testOnMenuItemClick_searchMenu() {
+        initializeNormal();
+        Assert.assertTrue(mBookmarkActionBar.onMenuItemClick(
+                mBookmarkActionBar.getMenu().findItem(R.id.search_menu_id)));
+        Mockito.verify(mBookmarkDelegate, Mockito.times(1)).openSearchUI();
+    }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void testOnMenuItemClick_selectionModeEditMenu() {
+        ActivityMonitor activityMonitor = addBlockingActivityMonitor(BookmarkEditActivity.class);
+        initializeNormal();
+        setCurrentSelection(BOOKMARK_ID_ONE);
+
+        Assert.assertTrue(mBookmarkActionBar.onMenuItemClick(
+                mBookmarkActionBar.getMenu().findItem(R.id.selection_mode_edit_menu_id)));
+        Assert.assertEquals(1, activityMonitor.getHits());
+    }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void testOnMenuItemClick_selectionModeEditMenuFolder() {
+        ActivityMonitor activityMonitor =
+                addBlockingActivityMonitor(BookmarkAddEditFolderActivity.class);
+        initializeNormal();
+        setCurrentSelection(BOOKMARK_ID_FOLDER);
+
+        Assert.assertTrue(mBookmarkActionBar.onMenuItemClick(
+                mBookmarkActionBar.getMenu().findItem(R.id.selection_mode_edit_menu_id)));
+        Assert.assertEquals(1, activityMonitor.getHits());
+    }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void testOnMenuItemClick_selectionModeMoveMenu() {
+        ActivityMonitor activityMonitor =
+                addBlockingActivityMonitor(BookmarkFolderSelectActivity.class);
+        initializeNormal();
+        setCurrentSelection(BOOKMARK_ID_ONE, BOOKMARK_ID_TWO);
+
+        Assert.assertTrue(mBookmarkActionBar.onMenuItemClick(
+                mBookmarkActionBar.getMenu().findItem(R.id.selection_mode_move_menu_id)));
+        Assert.assertEquals(1, activityMonitor.getHits());
+    }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void testOnMenuItemClick_selectionModeDeleteMenu() {
+        initializeNormal();
+        setCurrentSelection(BOOKMARK_ID_ONE, BOOKMARK_ID_TWO);
+
+        Assert.assertTrue(mBookmarkActionBar.onMenuItemClick(
+                mBookmarkActionBar.getMenu().findItem(R.id.selection_mode_delete_menu_id)));
+        verify(mBookmarkModel, Mockito.times(1)).deleteBookmarks(Mockito.any());
+    }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void testOnBookmarkDelegateInitialized() {
+        mBookmarkActionBar.onBookmarkDelegateInitialized(mBookmarkDelegate);
+        Assert.assertNull(mBookmarkActionBar.getMenu().findItem(R.id.close_menu_id));
+    }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void testOnBookmarkDelegateInitialized_isDialog() {
+        when(mBookmarkDelegate.isDialogUi()).thenReturn(true);
+        mBookmarkActionBar.onBookmarkDelegateInitialized(mBookmarkDelegate);
+        Assert.assertNotNull(mBookmarkActionBar.getMenu().findItem(R.id.close_menu_id));
+    }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void testOnDestroy() {
+        mBookmarkActionBar.onBookmarkDelegateInitialized(mBookmarkDelegate);
+        mBookmarkActionBar.onDestroy();
+        Mockito.verify(mBookmarkDelegate, Mockito.times(1)).removeUIObserver(mBookmarkActionBar);
+    }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void testOnDestroy_nullDelegate() {
+        mBookmarkActionBar.onDestroy();
+        Mockito.verify(mBookmarkDelegate, Mockito.never()).removeUIObserver(mBookmarkActionBar);
+    }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void testOnSearchStateSet() {
+        mBookmarkActionBar.onSearchStateSet();
+    }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void testOnSelectionStateChange_nullBookmarkDelegate() {
+        mBookmarkActionBar.initialize(mSelectionDelegate, 0, R.id.normal_menu_group,
+                R.id.selection_mode_menu_group, false);
+        mBookmarkActionBar.initializeSearchView(
+                mSearchDelegate, R.string.bookmark_action_bar_search, R.id.search_menu_id);
+        mBookmarkActionBar.onSelectionStateChange(Collections.singletonList(BOOKMARK_ID_ONE));
+
+        verifySelectionMenuVisibility(R.id.selection_mode_edit_menu_id,
+                R.id.selection_mode_move_menu_id, R.id.selection_mode_delete_menu_id,
+                R.id.selection_open_in_new_tab_id, R.id.selection_open_in_incognito_tab_id);
+    }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void testOnSelectionStateChange_selectionNotEnabled() {
+        initializeNormal();
+        mBookmarkActionBar.onSelectionStateChange(Collections.emptyList());
+
+        verifySelectionMenuVisibility(R.id.selection_mode_edit_menu_id,
+                R.id.selection_mode_move_menu_id, R.id.selection_mode_delete_menu_id,
+                R.id.selection_open_in_new_tab_id, R.id.selection_open_in_incognito_tab_id);
+        Mockito.verify(mBookmarkDelegate, Mockito.times(1)).notifyStateChange(mBookmarkActionBar);
+    }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void testOnSelectionStateChange_multiple() {
+        initializeNormal();
+        when(mSelectionDelegate.isSelectionEnabled()).thenReturn(true);
+
+        mBookmarkActionBar.onSelectionStateChange(Arrays.asList(BOOKMARK_ID_ONE, BOOKMARK_ID_TWO));
+        verifySelectionMenuVisibility(R.id.selection_mode_edit_menu_id);
+    }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void testOnSelectionStateChange_incognitoDisabled() {
+        IncognitoUtils.setEnabledForTesting(false);
+        initializeNormal();
+        when(mSelectionDelegate.isSelectionEnabled()).thenReturn(true);
+
+        mBookmarkActionBar.onSelectionStateChange(Collections.singletonList(BOOKMARK_ID_ONE));
+        verifySelectionMenuVisibility(R.id.selection_open_in_incognito_tab_id);
+    }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void testOnSelectionStateChange_folder() {
+        initializeNormal();
+        when(mSelectionDelegate.isSelectionEnabled()).thenReturn(true);
+
+        mBookmarkActionBar.onSelectionStateChange(Collections.singletonList(BOOKMARK_ID_FOLDER));
+        verifySelectionMenuVisibility(
+                R.id.selection_open_in_new_tab_id, R.id.selection_open_in_incognito_tab_id);
+    }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void testOnSelectionStateChange_partner() {
+        initializeNormal();
+        when(mSelectionDelegate.isSelectionEnabled()).thenReturn(true);
+
+        mBookmarkActionBar.onSelectionStateChange(Collections.singletonList(BOOKMARK_ID_PARTNER));
+        verifySelectionMenuVisibility(R.id.selection_mode_move_menu_id);
+    }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void testOnSelectionStateChange_readingList() {
+        initializeNormal();
+        when(mSelectionDelegate.isSelectionEnabled()).thenReturn(true);
+
+        mBookmarkActionBar.onSelectionStateChange(
+                Collections.singletonList(BOOKMARK_ID_READING_LIST));
+        verifySelectionMenuVisibility(
+                R.id.selection_mode_edit_menu_id, R.id.selection_mode_move_menu_id);
+    }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void testOnDragStateChange() {
+        initializeNormal();
+
+        mBookmarkActionBar.onDragStateChange(true);
+        Assert.assertFalse(mBookmarkActionBar.getMenu()
+                                   .findItem(R.id.selection_mode_edit_menu_id)
+                                   .isEnabled());
+
+        mBookmarkActionBar.onDragStateChange(false);
+        Assert.assertTrue(mBookmarkActionBar.getMenu()
+                                  .findItem(R.id.selection_mode_edit_menu_id)
+                                  .isEnabled());
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
index 4fe2649..cb47fbfe 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
@@ -1807,6 +1807,15 @@
     @Features.DisableFeatures({ChromeFeatureList.CCT_RESIZABLE_WINDOW_ABOVE_NAVBAR})
     public void testLaunchPartialCustomTabActivity_fixedWindow() throws Exception {
         testLaunchPartialCustomTabActivity();
+        assertOverlayPanelCanHideAndroidBrowserControls(false);
+    }
+
+    @Test
+    @SmallTest
+    public void testCanHideBrowserControls_notPartial() throws Exception {
+        CustomTabsSessionToken session = warmUpAndLaunchUrlWithSession();
+        assertEquals(getActivity().getIntentDataProvider().getSession(), session);
+        assertOverlayPanelCanHideAndroidBrowserControls(true);
     }
 
     @Test
@@ -1851,6 +1860,23 @@
         eventHelper.waitForCallback(0);
     }
 
+    /** Asserts that the Overlay Panel is set to allow or not allow ever hiding the Toolbar. */
+    private void assertOverlayPanelCanHideAndroidBrowserControls(boolean canEverHide) {
+        // Wait for CS to get initialized.
+        CriteriaHelper.pollUiThread(
+                ()
+                        -> getActivity().getContextualSearchManagerSupplier() != null
+                        && getActivity().getContextualSearchManagerSupplier().get() != null);
+
+        // The toolbar cannot go away for Partial Height Custom Tabs, but can for full height ones.
+        CriteriaHelper.pollUiThread(()
+                                            -> getActivity()
+                                                       .getContextualSearchManagerSupplier()
+                                                       .get()
+                                                       .getCanHideAndroidBrowserControls()
+                        == canEverHide);
+    }
+
     private void verifyHistoryAfterHiddenTab(boolean speculationWasAHit) throws Exception {
         String speculationUrl = mTestPage;
         String navigationUrl = speculationWasAHit ? mTestPage : mTestPage2;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModelTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModelTest.java
index 38536d1..8d9f5a2 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModelTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModelTest.java
@@ -4,6 +4,9 @@
 
 package org.chromium.chrome.browser.tabmodel;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.SmallTest;
 
@@ -39,18 +42,22 @@
     @Rule
     public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
 
-    private TabModel mTabModel;
+    private TabModel mRegularTabModel;
+    private TabModel mIncognitoTabModel;
 
     @Before
     public void setUp() throws InterruptedException {
         mActivityTestRule.startMainActivityOnBlankPage();
-        mTabModel = mActivityTestRule.getActivity().getTabModelSelector().getModel(true);
+        mRegularTabModel =
+                mActivityTestRule.getActivity().getTabModelSelectorSupplier().get().getModel(false);
+        mIncognitoTabModel =
+                mActivityTestRule.getActivity().getTabModelSelectorSupplier().get().getModel(true);
     }
 
     private class CloseAllDuringAddTabTabModelObserver implements TabModelObserver {
         @Override
         public void willAddTab(Tab tab, @TabLaunchType int type) {
-            mTabModel.closeAllTabs();
+            mIncognitoTabModel.closeAllTabs();
         }
     }
 
@@ -72,12 +79,12 @@
     @Feature({"OffTheRecord"})
     public void testCloseAllDuringAddTabDoesNotCrash() {
         createTabOnUiThread();
-        Assert.assertEquals(1, mTabModel.getCount());
+        Assert.assertEquals(1, mIncognitoTabModel.getCount());
         TestThreadUtils.runOnUiThreadBlocking(
-                () -> mTabModel.addObserver(new CloseAllDuringAddTabTabModelObserver()));
+                () -> mIncognitoTabModel.addObserver(new CloseAllDuringAddTabTabModelObserver()));
 
         createTabOnUiThread();
-        Assert.assertEquals(1, mTabModel.getCount());
+        Assert.assertEquals(1, mIncognitoTabModel.getCount());
     }
 
     @Test
@@ -106,7 +113,7 @@
         CallbackHelper tabRemovedCallbackHelper = new CallbackHelper();
 
         TestThreadUtils.runOnUiThreadBlocking(() -> {
-            mTabModel.addObserver(new TabModelObserver() {
+            mIncognitoTabModel.addObserver(new TabModelObserver() {
                 @Override
                 public void tabRemoved(Tab tab) {
                     tabRemovedCallbackHelper.notifyCalled();
@@ -130,12 +137,28 @@
                 "TabModelObserver#didAddTab should have been called", 0);
 
         Tab tab = mActivityTestRule.getActivity().getTabModelSelector().getCurrentTab();
-        Assert.assertTrue(tab.isIncognito());
+        assertTrue(tab.isIncognito());
         TestThreadUtils.runOnUiThreadBlocking(() -> {
-            mTabModel.removeTab(tab);
+            mIncognitoTabModel.removeTab(tab);
             tab.destroy();
         });
         tabRemovedCallbackHelper.waitForCallback(
                 "TabModelObserver#tabRemoved should have been called", 0);
     }
+
+    @Test
+    @SmallTest
+    public void testHideLastRegularTab_OnModelChange() {
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            assert mRegularTabModel == mActivityTestRule.getActivity().getCurrentTabModel();
+            // In setup we create a blank tab.
+            assert mRegularTabModel.getCount() == 1;
+            assert mIncognitoTabModel.getCount() == 0;
+
+            Tab mTab = mRegularTabModel.getTabAt(0);
+            assertFalse(mTab.isHidden());
+            mActivityTestRule.getActivity().getTabModelSelectorSupplier().get().selectModel(true);
+            assertTrue(mTab.isHidden());
+        });
+    }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java
index 451ade7..090b1b6 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java
@@ -1111,14 +1111,14 @@
     @Test
     @Feature("Tab Groups on Tab Strip")
     public void testTabGroupMargins_ScrollOnReorder() {
-        // Mock 1 tab to the right of 2 tab groups with 2 tabs each.
-        initializeTest(false, false, true, 0, 5);
+        // Mock 6 tabs to the right of 2 tab groups with 2 tabs each.
+        initializeTest(false, false, true, 0, 10);
         groupTabs(0, 2);
         groupTabs(2, 4);
         mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP);
         mStripLayoutHelper.testSetScrollOffset(0);
 
-        // Start reorder on rightmost tab. 2 margins to left of tab, so should scroll.
+        // Start reorder on tab to the right of groups. 2 margins to left of tab, so should scroll.
         // Verify the scroll offset is 2 * (-marginWidth) + startMargin = 2 * -95 + -95 = -285
         float expectedOffset = -285f;
         mStripLayoutHelper.startReorderModeAtIndexForTesting(4);
@@ -1134,14 +1134,14 @@
     @Test
     @Feature("Tab Groups on Tab Strip")
     public void testTabGroupMargins_ScrollOnReorder_Animated() {
-        // Mock 1 tab to the right of 2 tab groups with 2 tabs each.
-        initializeTest(false, false, false, 0, 5);
+        // Mock 6 tabs to the right of 2 tab groups with 2 tabs each.
+        initializeTest(false, false, false, 0, 10);
         groupTabs(0, 2);
         groupTabs(2, 4);
         mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP);
         mStripLayoutHelper.testSetScrollOffset(0);
 
-        // Start reorder on rightmost tab. 2 margins to left of tab, so should scroll.
+        // Start reorder on tab to the right of groups. 2 margins to left of tab, so should scroll.
         // Verify the scroll offset has not yet changed.
         mStripLayoutHelper.startReorderModeAtIndexForTesting(4);
         assertEquals("The scroller has not finished yet, so the offset shouldn't change.", 0f,
diff --git a/chrome/app/BUILD.gn b/chrome/app/BUILD.gn
index e3324b9..c5a087d 100644
--- a/chrome/app/BUILD.gn
+++ b/chrome/app/BUILD.gn
@@ -203,7 +203,10 @@
   }
 
   if (is_android) {
-    deps += [ "//components/crash/android:crash_android" ]
+    deps += [
+      "//chrome/browser/flags:flags_android",
+      "//components/crash/android:crash_android",
+    ]
   }
 
   if (is_mac) {
diff --git a/chrome/app/chrome_main_delegate.cc b/chrome/app/chrome_main_delegate.cc
index a55f837..2651f09 100644
--- a/chrome/app/chrome_main_delegate.cc
+++ b/chrome/app/chrome_main_delegate.cc
@@ -161,6 +161,7 @@
 #include "base/android/java_exception_reporter.h"
 #include "base/android/library_loader/library_loader_hooks.h"
 #include "chrome/browser/android/metrics/uma_session_stats.h"
+#include "chrome/browser/flags/android/cached_feature_flags.h"
 #include "chrome/browser/flags/android/chrome_feature_list.h"
 #include "chrome/common/chrome_descriptors.h"
 #include "components/crash/android/pure_java_exception_handler.h"
@@ -968,9 +969,24 @@
   // Setup tracing sampler profiler as early as possible at startup if needed.
   // We pass in CreateCoreUnwindersFactory here since it lives in the chrome/
   // layer while TracingSamplerProfiler is outside of chrome/.
+  //
+  // When we're the browser on android use libunwindstack completely for tracing
+  // sampler profiler because it can support java frames which is essential for
+  // the main thread.
+  base::RepeatingCallback tracing_factory =
+      base::BindRepeating(&CreateCoreUnwindersFactory);
+#if BUILDFLAG(IS_ANDROID)
+  // If we are the browser process (missing process type), then use the
+  // experimental libunwindstack unwinder.
+  if (!command_line.HasSwitch(switches::kProcessType) &&
+      chrome::android::IsJavaDrivenFeatureEnabled(
+          chrome::android::kUseLibunwindstackNativeUnwinderAndroid)) {
+    tracing_factory = base::BindRepeating(&CreateLibunwindstackUnwinderFactory);
+  }
+#endif
   tracing_sampler_profiler_ =
       tracing::TracingSamplerProfiler::CreateOnMainThread(
-          base::BindRepeating(&CreateCoreUnwindersFactory));
+          std::move(tracing_factory));
 
 #if BUILDFLAG(IS_WIN)
   v8_crashpad_support::SetUp();
diff --git a/chrome/app/supervised_user_error_page_strings.grdp b/chrome/app/supervised_user_error_page_strings.grdp
index 08a7b26..f337445 100644
--- a/chrome/app/supervised_user_error_page_strings.grdp
+++ b/chrome/app/supervised_user_error_page_strings.grdp
@@ -20,7 +20,7 @@
         You need permission to visit this site
       </message>
       <message name="IDS_CHILD_BLOCK_INTERSTITIAL_MESSAGE_V2" desc="A message for the child user when they attempt to visit a site that is not permitted by their parent.">
-        A parent or guardian has to say that it's OK for you to visit this website.
+        A parent or guardian has to say that it's OK for you to visit this website
       </message>
       <message name="IDS_BLOCK_INTERSTITIAL_MESSAGE_SUPERVISED_USERS_DEPRECATED" desc="Message to be shown to a supervised user directing them to the supervisor.">
         Questions? Contact the person who supervises your profile.
diff --git a/chrome/app/supervised_user_error_page_strings_grdp/IDS_CHILD_BLOCK_INTERSTITIAL_MESSAGE_V2.png.sha1 b/chrome/app/supervised_user_error_page_strings_grdp/IDS_CHILD_BLOCK_INTERSTITIAL_MESSAGE_V2.png.sha1
index 606cd24..9902ee6 100644
--- a/chrome/app/supervised_user_error_page_strings_grdp/IDS_CHILD_BLOCK_INTERSTITIAL_MESSAGE_V2.png.sha1
+++ b/chrome/app/supervised_user_error_page_strings_grdp/IDS_CHILD_BLOCK_INTERSTITIAL_MESSAGE_V2.png.sha1
@@ -1 +1 @@
-d05ee8add541ae283f8416a46ae28b9956cbfd13
\ No newline at end of file
+151cfabf502661830b50a9408304b9d895997d95
\ No newline at end of file
diff --git a/chrome/app_shim/OWNERS b/chrome/app_shim/OWNERS
index 9138d28..c4ce1b58 100644
--- a/chrome/app_shim/OWNERS
+++ b/chrome/app_shim/OWNERS
@@ -1,3 +1,7 @@
+# Primary
+mek@chromium.org
+
+# Secondary
 ccameron@chromium.org
 cmp@chromium.org
 dmurph@chromium.org
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 5cc7889f6..ae9a4e2 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1111,6 +1111,8 @@
     "performance_manager/policies/working_set_trimmer_policy.h",
     "performance_manager/public/chrome_browser_main_extra_parts_performance_manager.h",
     "performance_manager/public/chrome_content_browser_client_performance_manager_part.h",
+    "performance_manager/public/user_tuning/user_tuning_utils.h",
+    "performance_manager/user_tuning/user_tuning_utils.cc",
     "performance_monitor/chrome_browser_main_extra_parts_performance_monitor.cc",
     "performance_monitor/chrome_browser_main_extra_parts_performance_monitor.h",
     "performance_monitor/system_monitor.cc",
@@ -6615,7 +6617,7 @@
     ]
   }
 
-  if (is_win || is_mac || is_linux || is_chromeos_lacros || is_fuchsia) {
+  if (is_win || is_mac || is_linux) {
     sources += [
       "browser_switcher/alternative_browser_driver.h",
       "browser_switcher/browser_switcher_features.cc",
@@ -6636,7 +6638,7 @@
     if (is_win) {
       sources += [ "browser_switcher/alternative_browser_driver_win.cc" ]
     }
-    if (is_mac || is_linux || is_chromeos_lacros) {
+    if (is_mac || is_linux) {
       sources += [ "browser_switcher/alternative_browser_driver_posix.cc" ]
     }
   }
diff --git a/chrome/browser/apps/intent_helper/intent_picker_helpers.cc b/chrome/browser/apps/intent_helper/intent_picker_helpers.cc
index e2ec459..17ab1ed 100644
--- a/chrome/browser/apps/intent_helper/intent_picker_helpers.cc
+++ b/chrome/browser/apps/intent_helper/intent_picker_helpers.cc
@@ -7,10 +7,8 @@
 #include <string>
 #include <utility>
 
-#include "base/functional/bind.h"
 #include "base/memory/weak_ptr.h"
 #include "base/notreached.h"
-#include "base/task/task_traits.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
@@ -34,7 +32,6 @@
 #include "chrome/browser/apps/intent_helper/chromeos_intent_picker_helpers.h"
 #include "chrome/browser/apps/intent_helper/metrics/intent_handling_metrics.h"
 #elif BUILDFLAG(IS_MAC)
-#include "base/task/thread_pool.h"
 #include "chrome/browser/apps/intent_helper/mac_intent_picker_helpers.h"
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
@@ -42,11 +39,16 @@
 
 namespace {
 
-void AppendAppsForUrlSync(
+std::vector<IntentPickerAppInfo> FindAppsForUrl(
     content::WebContents* web_contents,
     const GURL& url,
-    base::OnceCallback<void(std::vector<IntentPickerAppInfo>)> callback,
     std::vector<IntentPickerAppInfo> apps) {
+#if BUILDFLAG(IS_MAC)
+  // On the Mac, if there is a Universal Link, it goes first.
+  if (absl::optional<IntentPickerAppInfo> mac_app = FindMacAppForUrl(url))
+    apps.push_back(std::move(mac_app.value()));
+#endif  // BUILDFLAG(IS_MAC)
+
   Profile* profile =
       Profile::FromBrowserContext(web_contents->GetBrowserContext());
 
@@ -62,53 +64,7 @@
                        ui::ImageModel(), update.AppId(), update.Name());
         });
   }
-
-  std::move(callback).Run(std::move(apps));
-}
-
-void FindAppsForUrl(
-    content::WebContents* web_contents,
-    const GURL& url,
-    base::OnceCallback<void(std::vector<IntentPickerAppInfo>)> callback) {
-#if BUILDFLAG(IS_MAC)
-  // On the Mac, if there is a Universal Link, it goes first. Jump to a worker
-  // thread to do this.
-
-  auto get_mac_app = [](const GURL& url) {
-    std::vector<IntentPickerAppInfo> apps;
-    if (absl::optional<IntentPickerAppInfo> mac_app = FindMacAppForUrl(url))
-      apps.push_back(std::move(mac_app.value()));
-    return apps;
-  };
-
-  IntentPickerTabHelper* helper =
-      IntentPickerTabHelper::FromWebContents(web_contents);
-  int commit_count = helper->commit_count();
-
-  auto then_append_apps =
-      [](base::WeakPtr<content::WebContents> web_contents,
-         IntentPickerTabHelper* helper, int commit_count, const GURL& url,
-         base::OnceCallback<void(std::vector<IntentPickerAppInfo>)> callback,
-         std::vector<IntentPickerAppInfo> apps) {
-        if (!web_contents)
-          return;
-        if (helper->commit_count() != commit_count)
-          return;
-
-        AppendAppsForUrlSync(web_contents.get(), url, std::move(callback),
-                             std::move(apps));
-      };
-
-  base::ThreadPool::PostTaskAndReplyWithResult(
-      FROM_HERE, {base::TaskPriority::USER_BLOCKING, base::MayBlock()},
-      base::BindOnce(get_mac_app, url),
-      base::BindOnce(then_append_apps, web_contents->GetWeakPtr(), helper,
-                     commit_count, url, std::move(callback)));
-
-#else
-  // On other platforms, just proceed synchronously.
-  AppendAppsForUrlSync(web_contents, url, std::move(callback), {});
-#endif  // BUILDFLAG(IS_MAC)
+  return apps;
 }
 
 void LaunchAppFromIntentPicker(content::WebContents* web_contents,
@@ -182,28 +138,43 @@
       base::BindOnce(&OnIntentPickerClosed, web_contents->GetWeakPtr(), url));
 }
 
-void GetAppsForIntentPicker(
-    content::WebContents* web_contents,
-    base::OnceCallback<void(std::vector<IntentPickerAppInfo>)> callback) {
-  if (!ShouldCheckAppsForUrl(web_contents)) {
-    std::move(callback).Run({});
-    return;
-  }
+std::vector<IntentPickerAppInfo> GetAppsForIntentPicker(
+    content::WebContents* web_contents) {
+  std::vector<IntentPickerAppInfo> apps = {};
+  if (!ShouldCheckAppsForUrl(web_contents))
+    return apps;
 
   Profile* profile =
       Profile::FromBrowserContext(web_contents->GetBrowserContext());
-  if (!AppServiceProxyFactory::IsAppServiceAvailableForProfile(profile)) {
-    std::move(callback).Run({});
-    return;
-  }
+  if (!AppServiceProxyFactory::IsAppServiceAvailableForProfile(profile))
+    return apps;
 
-  FindAppsForUrl(web_contents, web_contents->GetLastCommittedURL(),
-                 std::move(callback));
+  const GURL& url = web_contents->GetLastCommittedURL();
+  apps = FindAppsForUrl(web_contents, url, std::move(apps));
+  return apps;
 }
 
-void ShowIntentPickerOrLaunchAppImpl(content::WebContents* web_contents,
-                                     const GURL& url,
-                                     std::vector<IntentPickerAppInfo> apps) {
+}  // namespace
+
+// for chromeos, this should apply when navigation is not deferred for pwa only
+// case also when navigation deferred and then resumed
+void MaybeShowIntentPicker(content::NavigationHandle* navigation_handle) {
+  content::WebContents* web_contents = navigation_handle->GetWebContents();
+  auto apps = GetAppsForIntentPicker(web_contents);
+  IntentPickerTabHelper::FromWebContents(web_contents)->ShowIconForApps(apps);
+#if BUILDFLAG(IS_CHROMEOS)
+  MaybeShowIntentPickerBubble(navigation_handle, std::move(apps));
+#endif  // BUILDFLAG(IS_CHROMEOS)
+}
+
+void MaybeShowIntentPicker(content::WebContents* web_contents) {
+  IntentPickerTabHelper::FromWebContents(web_contents)
+      ->ShowIconForApps(GetAppsForIntentPicker(web_contents));
+}
+
+void ShowIntentPickerOrLaunchApp(content::WebContents* web_contents,
+                                 const GURL& url) {
+  std::vector<IntentPickerAppInfo> apps = FindAppsForUrl(web_contents, url, {});
   if (apps.empty())
     return;
 
@@ -239,65 +210,6 @@
       base::BindOnce(&OnAppIconsLoaded, web_contents, url));
 }
 
-}  // namespace
-
-void MaybeShowIntentPicker(content::NavigationHandle* navigation_handle) {
-  content::WebContents* web_contents = navigation_handle->GetWebContents();
-  IntentPickerTabHelper* helper =
-      IntentPickerTabHelper::FromWebContents(web_contents);
-  int commit_count = helper->commit_count();
-
-  auto task = [](base::WeakPtr<content::WebContents> web_contents,
-                 content::NavigationHandle* navigation_handle,
-                 IntentPickerTabHelper* helper, int commit_count,
-                 std::vector<IntentPickerAppInfo> apps) {
-    if (!web_contents)
-      return;
-    if (helper->commit_count() != commit_count)
-      return;
-
-    helper->ShowIconForApps(apps);
-#if BUILDFLAG(IS_CHROMEOS)
-    // Note that `navigation_handle` is valid until the `DidFinishNavigation`
-    // call is made, so as long as the `commit_count` value matches, this is
-    // safe.
-    MaybeShowIntentPickerBubble(navigation_handle, std::move(apps));
-#endif  // BUILDFLAG(IS_CHROMEOS)
-  };
-
-  GetAppsForIntentPicker(
-      web_contents, base::BindOnce(task, web_contents->GetWeakPtr(),
-                                   navigation_handle, helper, commit_count));
-}
-
-void MaybeShowIntentPicker(content::WebContents* web_contents) {
-  IntentPickerTabHelper* helper =
-      IntentPickerTabHelper::FromWebContents(web_contents);
-  int commit_count = helper->commit_count();
-
-  auto task = [](base::WeakPtr<content::WebContents> web_contents,
-                 IntentPickerTabHelper* helper, int commit_count,
-                 std::vector<IntentPickerAppInfo> apps) {
-    if (!web_contents)
-      return;
-    if (helper->commit_count() != commit_count)
-      return;
-
-    helper->ShowIconForApps(apps);
-  };
-
-  GetAppsForIntentPicker(
-      web_contents,
-      base::BindOnce(task, web_contents->GetWeakPtr(), helper, commit_count));
-}
-
-void ShowIntentPickerOrLaunchApp(content::WebContents* web_contents,
-                                 const GURL& url) {
-  FindAppsForUrl(
-      web_contents, url,
-      base::BindOnce(&ShowIntentPickerOrLaunchAppImpl, web_contents, url));
-}
-
 bool IntentPickerPwaPersistenceEnabled() {
 #if BUILDFLAG(IS_CHROMEOS)
   return true;
diff --git a/chrome/browser/apps/intent_helper/mac_intent_picker_helpers.h b/chrome/browser/apps/intent_helper/mac_intent_picker_helpers.h
index 5ce550b..6170804 100644
--- a/chrome/browser/apps/intent_helper/mac_intent_picker_helpers.h
+++ b/chrome/browser/apps/intent_helper/mac_intent_picker_helpers.h
@@ -20,15 +20,6 @@
 // by `FindMacAppForUrl` above, for the given `url`.
 void LaunchMacApp(const GURL& url, const std::string& launch_name);
 
-// Force `FindMacAppForUrl` to return fixed values for testing.
-// - If `fake` is `true` and `app_path` is set to a path, then
-//   `FindMacAppForUrl` will return an `IntentPickerAppInfo` for the app at that
-//   path.
-// - If `fake` is `true` and `app_path` is empty, then `FindMacAppForUrl` will
-//   return a `nullopt`.
-// - If `fake` is `false`, then `FindMacAppForUrl` will behave normally.
-void OverrideMacAppForUrlForTesting(bool fake, const std::string& app_path);
-
 }  // namespace apps
 
 #endif  // CHROME_BROWSER_APPS_INTENT_HELPER_MAC_INTENT_PICKER_HELPERS_H_
diff --git a/chrome/browser/apps/intent_helper/mac_intent_picker_helpers.mm b/chrome/browser/apps/intent_helper/mac_intent_picker_helpers.mm
index e6f7091d..2f3e286 100644
--- a/chrome/browser/apps/intent_helper/mac_intent_picker_helpers.mm
+++ b/chrome/browser/apps/intent_helper/mac_intent_picker_helpers.mm
@@ -8,7 +8,6 @@
 #import <SafariServices/SafariServices.h>
 
 #include "base/feature_list.h"
-#include "base/no_destructor.h"
 #include "base/strings/sys_string_conversions.h"
 #include "chrome/browser/browser_features.h"
 #include "net/base/mac/url_conversions.h"
@@ -18,16 +17,6 @@
 
 namespace {
 
-bool& UseFakeAppForTesting() {
-  static bool value = false;
-  return value;
-}
-
-std::string& FakeAppForTesting() {
-  static base::NoDestructor<std::string> value;
-  return *value;
-}
-
 IntentPickerAppInfo AppInfoForAppUrl(NSURL* app_url) {
   NSString* app_name = nil;
   if (![app_url getResourceValue:&app_name
@@ -54,15 +43,6 @@
 }  // namespace
 
 absl::optional<IntentPickerAppInfo> FindMacAppForUrl(const GURL& url) {
-  if (UseFakeAppForTesting()) {
-    std::string fake_app = FakeAppForTesting();
-    if (fake_app.empty())
-      return absl::nullopt;
-
-    return AppInfoForAppUrl(
-        [NSURL fileURLWithPath:base::SysUTF8ToNSString(fake_app)]);
-  }
-
   static bool universal_links_enabled =
       base::FeatureList::IsEnabled(features::kEnableUniveralLinks);
   if (!universal_links_enabled)
@@ -102,9 +82,4 @@
   }
 }
 
-void OverrideMacAppForUrlForTesting(bool fake, const std::string& app_path) {
-  UseFakeAppForTesting() = fake;
-  FakeAppForTesting() = app_path;
-}
-
 }  // namespace apps
diff --git a/chrome/browser/ash/crosapi/login_ash.cc b/chrome/browser/ash/crosapi/login_ash.cc
index e31fae9..273439c 100644
--- a/chrome/browser/ash/crosapi/login_ash.cc
+++ b/chrome/browser/ash/crosapi/login_ash.cc
@@ -17,6 +17,7 @@
 #include "chromeos/ash/components/login/auth/public/cryptohome_key_constants.h"
 #include "chromeos/ash/components/login/auth/public/key.h"
 #include "chromeos/ash/components/login/auth/public/user_context.h"
+#include "components/account_id/account_id.h"
 #include "components/session_manager/core/session_manager.h"
 #include "components/user_manager/known_user.h"
 #include "components/user_manager/user.h"
@@ -155,8 +156,7 @@
   }
 
   chromeos::UserContext context(user_manager::USER_TYPE_REGULAR,
-                                user_manager::known_user::GetAccountId(
-                                    email, gaia_id, AccountType::GOOGLE));
+                                AccountId::FromUserEmailGaiaId(email, gaia_id));
   chromeos::Key key(password);
   key.SetLabel(ash::kCryptohomeGaiaKeyLabel);
   context.SetKey(key);
diff --git a/chrome/browser/browser_features.cc b/chrome/browser/browser_features.cc
index 6986935..f7d39b25 100644
--- a/chrome/browser/browser_features.cc
+++ b/chrome/browser/browser_features.cc
@@ -76,7 +76,7 @@
 // Enables integration with the macOS feature Universal Links.
 BASE_FEATURE(kEnableUniveralLinks,
              "EnableUniveralLinks",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+             base::FEATURE_DISABLED_BY_DEFAULT);
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc
index 273ad63..a7a2bfe6 100644
--- a/chrome/browser/browser_process_impl.cc
+++ b/chrome/browser/browser_process_impl.cc
@@ -1200,6 +1200,10 @@
   bool result = base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
   DCHECK(result);
   if (breadcrumbs::IsEnabled()) {
+    // Start crash reporter listening for breadcrumb events. Collected
+    // breadcrumbs will be attached to crash reports.
+    breadcrumbs::CrashReporterBreadcrumbObserver::GetInstance();
+
     application_breadcrumbs_logger_ =
         std::make_unique<breadcrumbs::ApplicationBreadcrumbsLogger>(
             user_data_dir, base::BindRepeating([] {
diff --git a/chrome/browser/browser_switcher/browser_switcher_service_factory.cc b/chrome/browser/browser_switcher/browser_switcher_service_factory.cc
index 4be6b011..c0dcc65 100644
--- a/chrome/browser/browser_switcher/browser_switcher_service_factory.cc
+++ b/chrome/browser/browser_switcher/browser_switcher_service_factory.cc
@@ -15,6 +15,14 @@
 #include "chrome/browser/browser_switcher/browser_switcher_service_win.h"
 #endif
 
+#if BUILDFLAG(IS_CHROMEOS)
+#error BrowserSwitcher is not supported on ChromeOS. Neither Ash nor LaCrOS.
+#endif
+
+#if BUILDFLAG(IS_FUCHSIA)
+#error BrowserSwitcher is not support on Fuchsia.
+#endif
+
 namespace browser_switcher {
 
 namespace {
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 218c3501d..b468918a 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -4687,10 +4687,7 @@
             handle));
   }
 
-// TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
-// of lacros-chrome is complete.
-#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || \
-    (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS))
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
   MaybeAddThrottle(browser_switcher::BrowserSwitcherNavigationThrottle::
                        MaybeCreateThrottleFor(handle),
                    &throttles);
diff --git a/chrome/browser/dips/dips_helper_browsertest.cc b/chrome/browser/dips/dips_helper_browsertest.cc
index 87e9ee4..b6d4a5a 100644
--- a/chrome/browser/dips/dips_helper_browsertest.cc
+++ b/chrome/browser/dips/dips_helper_browsertest.cc
@@ -310,3 +310,18 @@
   histograms.ExpectTotalCount(kTimeToStorage, 1);
   histograms.ExpectUniqueTimeSample(kTimeToStorage, base::Seconds(10), 1);
 }
+
+IN_PROC_BROWSER_TEST_F(DIPSTabHelperBrowserTest, PRE_PrepopulateTest) {
+  // Simulate the user typing the URL to visit the page, which will record site
+  // engagement.
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(
+      browser(), embedded_test_server()->GetURL("a.test", "/title1.html")));
+}
+
+IN_PROC_BROWSER_TEST_F(DIPSTabHelperBrowserTest, PrepopulateTest) {
+  // Since there was previous site engagement, the DIPS DB should be
+  // prepopulated with a user interaction timestamp.
+  auto state = GetDIPSState(GURL("http://a.test"));
+  ASSERT_TRUE(state.has_value());
+  EXPECT_TRUE(state->user_interaction_time.has_value());
+}
diff --git a/chrome/browser/dips/dips_service.cc b/chrome/browser/dips/dips_service.cc
index 4bc06a7..6033264 100644
--- a/chrome/browser/dips/dips_service.cc
+++ b/chrome/browser/dips/dips_service.cc
@@ -4,11 +4,42 @@
 
 #include "chrome/browser/dips/dips_service.h"
 
+#include <set>
+
+#include "base/memory/scoped_refptr.h"
 #include "base/task/thread_pool.h"
 #include "chrome/browser/content_settings/cookie_settings_factory.h"
+#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/dips/dips_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/content_settings/core/browser/cookie_settings.h"
+#include "components/site_engagement/content/site_engagement_service.h"
+#include "components/site_engagement/core/mojom/site_engagement_details.mojom.h"
+
+namespace {
+
+std::vector<std::string> GetEngagedSitesInBackground(
+    base::Time now,
+    scoped_refptr<HostContentSettingsMap> map) {
+  std::set<std::string> unique_sites;
+  auto details =
+      site_engagement::SiteEngagementService::GetAllDetailsInBackground(now,
+                                                                        map);
+  for (const site_engagement::mojom::SiteEngagementDetails& detail : details) {
+    if (!detail.origin.SchemeIsHTTPOrHTTPS()) {
+      continue;
+    }
+    if (!site_engagement::SiteEngagementService::IsEngagementAtLeast(
+            detail.total_score, blink::mojom::EngagementLevel::MINIMAL)) {
+      continue;
+    }
+    unique_sites.insert(GetSiteForDIPS(detail.origin));
+  }
+
+  return std::vector(unique_sites.begin(), unique_sites.end());
+}
+
+}  // namespace
 
 DIPSService::DIPSService(content::BrowserContext* context)
     : browser_context_(context),
@@ -17,6 +48,8 @@
       storage_(base::SequenceBound<DIPSStorage>(CreateTaskRunner())) {
   // TODO(crbug.com/1342228): Persist DB to disk for non-OTR profiles.
   storage_.AsyncCall(&DIPSStorage::Init).WithArgs(absl::nullopt);
+  // TODO: Prevent use of the DB until prepopulation starts.
+  InitializeStorageWithEngagedSites();
 }
 
 DIPSService::~DIPSService() = default;
@@ -39,3 +72,22 @@
 bool DIPSService::ShouldBlockThirdPartyCookies() const {
   return cookie_settings_->ShouldBlockThirdPartyCookies();
 }
+
+void DIPSService::InitializeStorageWithEngagedSites() {
+  base::Time now = base::Time::Now();
+  base::ThreadPool::PostTaskAndReplyWithResult(
+      FROM_HERE,
+      {base::TaskPriority::USER_BLOCKING,
+       base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
+      base::BindOnce(
+          &GetEngagedSitesInBackground, now,
+          base::WrapRefCounted(
+              HostContentSettingsMapFactory::GetForProfile(browser_context_))),
+      base::BindOnce(&DIPSService::InitializeStorage,
+                     weak_factory_.GetWeakPtr(), now));
+}
+
+void DIPSService::InitializeStorage(base::Time time,
+                                    std::vector<std::string> sites) {
+  storage_.AsyncCall(&DIPSStorage::Prepopulate).WithArgs(time, sites);
+}
diff --git a/chrome/browser/dips/dips_service.h b/chrome/browser/dips/dips_service.h
index bd1a0e1..3086a1b 100644
--- a/chrome/browser/dips/dips_service.h
+++ b/chrome/browser/dips/dips_service.h
@@ -7,6 +7,7 @@
 
 #include "base/memory/raw_ptr.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
 #include "base/threading/sequence_bound.h"
 #include "chrome/browser/dips/dips_storage.h"
 #include "components/keyed_service/core/keyed_service.h"
@@ -36,10 +37,14 @@
   void Shutdown() override;
 
   scoped_refptr<base::SequencedTaskRunner> CreateTaskRunner();
+  void InitializeStorageWithEngagedSites();
+  void InitializeStorage(base::Time time, std::vector<std::string> sites);
 
   raw_ptr<content::BrowserContext> browser_context_;
   scoped_refptr<content_settings::CookieSettings> cookie_settings_;
   base::SequenceBound<DIPSStorage> storage_;
+
+  base::WeakPtrFactory<DIPSService> weak_factory_{this};
 };
 
 #endif  // CHROME_BROWSER_DIPS_DIPS_SERVICE_H_
diff --git a/chrome/browser/dips/dips_service_factory.cc b/chrome/browser/dips/dips_service_factory.cc
index 8a49b220..c73e73a 100644
--- a/chrome/browser/dips/dips_service_factory.cc
+++ b/chrome/browser/dips/dips_service_factory.cc
@@ -4,9 +4,9 @@
 
 #include "chrome/browser/dips/dips_service_factory.h"
 
-#include "base/logging.h"
 #include "base/memory/singleton.h"
 #include "chrome/browser/dips/dips_service.h"
+#include "chrome/browser/engagement/site_engagement_service_factory.h"
 
 // static
 DIPSService* DIPSServiceFactory::GetForBrowserContext(
@@ -22,7 +22,9 @@
 DIPSServiceFactory::DIPSServiceFactory()
     : ProfileKeyedServiceFactory(
           "DIPSService",
-          ProfileSelections::BuildForRegularAndIncognito()) {}
+          ProfileSelections::BuildForRegularAndIncognito()) {
+  DependsOn(site_engagement::SiteEngagementServiceFactory::GetInstance());
+}
 
 DIPSServiceFactory::~DIPSServiceFactory() = default;
 
diff --git a/chrome/browser/dips/dips_storage.cc b/chrome/browser/dips/dips_storage.cc
index 0ab1962..3959252 100644
--- a/chrome/browser/dips/dips_storage.cc
+++ b/chrome/browser/dips/dips_storage.cc
@@ -6,9 +6,11 @@
 
 #include <memory>
 
+#include "base/bind.h"
 #include "base/files/file_path.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/strings/strcat.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "base/threading/thread_restrictions.h"
 #include "chrome/browser/dips/dips_utils.h"
 #include "sql/init_status.h"
@@ -36,8 +38,21 @@
                                 /*max=*/base::Days(7), 100);
 }
 
+// The number of sites to process in each call to DIPSStorage::Prepopulate().
+// Intended to be constant; settable only for testing.
+size_t g_prepopulate_chunk_size = 100;
+
 }  // namespace
 
+DIPSStorage::PrepopulateArgs::PrepopulateArgs(base::Time time,
+                                              size_t offset,
+                                              std::vector<std::string> sites)
+    : time(time), offset(offset), sites(std::move(sites)) {}
+
+DIPSStorage::PrepopulateArgs::PrepopulateArgs(PrepopulateArgs&&) = default;
+
+DIPSStorage::PrepopulateArgs::~PrepopulateArgs() = default;
+
 DIPSStorage::DIPSStorage() {
   base::AssertLongCPUWorkAllowed();
 }
@@ -65,8 +80,11 @@
 // DIPSDatabase interaction functions ------------------------------------------
 
 DIPSState DIPSStorage::Read(const GURL& url) {
+  return ReadSite(GetSiteForDIPS(url));
+}
+
+DIPSState DIPSStorage::ReadSite(std::string site) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  std::string site = GetSiteForDIPS(url);
   absl::optional<StateValue> state = db_->Read(site);
 
   if (state.has_value()) {
@@ -127,3 +145,39 @@
   // a long time ago, it may no longer be relevant.)
   state.set_user_interaction_time(time);
 }
+
+/* static */
+size_t DIPSStorage::SetPrepopulateChunkSizeForTesting(size_t size) {
+  return std::exchange(g_prepopulate_chunk_size, size);
+}
+
+void DIPSStorage::PrepopulateChunk(PrepopulateArgs args) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CHECK_LE(args.offset, args.sites.size());
+
+  size_t chunk_size =
+      std::min(args.sites.size() - args.offset, g_prepopulate_chunk_size);
+  for (size_t i = 0; i < chunk_size; i++) {
+    DIPSState state = ReadSite(args.sites[args.offset + i]);
+    if (state.user_interaction_time()) {
+      continue;
+    }
+
+    state.set_user_interaction_time(args.time);
+
+    if (!state.site_storage_time()) {
+      // If we set a fake interaction time but no storage time, then when
+      // storage does happen we'll report an incorrect
+      // TimeFromInteractionToStorage metric. So set the storage time too.
+      state.set_site_storage_time(args.time);
+    }
+  }
+
+  // Increment chunk offset in args and resubmit task if incomplete.
+  args.offset += chunk_size;
+  if (args.offset < args.sites.size()) {
+    base::SequencedTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(&DIPSStorage::PrepopulateChunk,
+                                  weak_factory_.GetWeakPtr(), std::move(args)));
+  }
+}
diff --git a/chrome/browser/dips/dips_storage.h b/chrome/browser/dips/dips_storage.h
index 6ec7fcc..de60ccc9 100644
--- a/chrome/browser/dips/dips_storage.h
+++ b/chrome/browser/dips/dips_storage.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "base/files/file_path.h"
+#include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
 #include "base/time/time.h"
 #include "chrome/browser/dips/dips_database.h"
@@ -36,12 +37,42 @@
   // Empty method intended for testing use only.
   void DoNothing() {}
 
+  static size_t SetPrepopulateChunkSizeForTesting(size_t size);
+
+  // For each site in |sites|, set the interaction and storage timestamps to
+  // |time|. Note this may run asynchronously -- the DB is not guaranteed to be
+  // fully prepopulated when this method returns.
+  void Prepopulate(base::Time time, std::vector<std::string> sites) {
+    PrepopulateChunk(PrepopulateArgs{time, 0, std::move(sites)});
+  }
+
+  // Because we keep posting tasks with Prepopulate() with mostly the same
+  // arguments (only |offset| changes), group them into a struct that can easily
+  // be posted again.
+  struct PrepopulateArgs {
+    PrepopulateArgs(base::Time time,
+                    size_t offset,
+                    std::vector<std::string> sites);
+    PrepopulateArgs(PrepopulateArgs&&);
+    ~PrepopulateArgs();
+
+    base::Time time;
+    size_t offset;
+    std::vector<std::string> sites;
+  };
+
  private:
   friend class DIPSState;
+  DIPSState ReadSite(std::string site);
   void Write(const DIPSState& state);
+  // Prepopulate the DB with one chunk of |args.sites|, and schedule another
+  // task to continue if more sites remain.
+  void PrepopulateChunk(PrepopulateArgs args);
 
   std::unique_ptr<DIPSDatabase> db_ GUARDED_BY_CONTEXT(sequence_checker_);
+
   SEQUENCE_CHECKER(sequence_checker_);
+  base::WeakPtrFactory<DIPSStorage> weak_factory_{this};
 };
 
 #endif  // CHROME_BROWSER_DIPS_DIPS_STORAGE_H_
diff --git a/chrome/browser/dips/dips_storage_unittest.cc b/chrome/browser/dips/dips_storage_unittest.cc
index 6f9cc04..cca3f903 100644
--- a/chrome/browser/dips/dips_storage_unittest.cc
+++ b/chrome/browser/dips/dips_storage_unittest.cc
@@ -4,8 +4,13 @@
 
 #include "chrome/browser/dips/dips_storage.h"
 
+#include "base/functional/bind.h"
+#include "base/task/thread_pool.h"
+#include "base/test/task_environment.h"
+#include "base/threading/sequence_bound.h"
 #include "chrome/browser/dips/dips_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
 class DIPSStorageTest : public testing::Test {
@@ -114,3 +119,155 @@
   EXPECT_EQ(storage_.Read(url1).site_storage_time(), time1);
   EXPECT_EQ(storage_.Read(url2).site_storage_time(), time2);
 }
+
+scoped_refptr<base::SequencedTaskRunner> CreateTaskRunner() {
+  return base::ThreadPool::CreateSequencedTaskRunner(
+      {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+       base::ThreadPolicy::PREFER_BACKGROUND});
+}
+
+void StoreState(absl::optional<StateValue>* state_value,
+                const DIPSState& state) {
+  *state_value = state.was_loaded() ? absl::make_optional(state.ToStateValue())
+                                    : absl::nullopt;
+}
+
+TEST(DIPSStoragePrepopulateTest, NoExistingTime) {
+  base::test::TaskEnvironment task_environment;
+  base::SequenceBound<DIPSStorage> storage(CreateTaskRunner());
+  base::Time time = base::Time::FromDoubleT(1);
+
+  storage.AsyncCall(&DIPSStorage::Init).WithArgs(absl::nullopt);
+  storage.AsyncCall(&DIPSStorage::Prepopulate)
+      .WithArgs(time, std::vector<std::string>{"site"});
+  absl::optional<StateValue> state;
+  storage.AsyncCall(&DIPSStorage::Read)
+      .WithArgs(GURL("http://site"))
+      .Then(base::BindOnce(StoreState, &state));
+  storage.FlushPostedTasksForTesting();
+
+  ASSERT_TRUE(state.has_value());
+  EXPECT_EQ(state->user_interaction_time, time);  // written
+  EXPECT_EQ(state->site_storage_time, time);      // written
+}
+
+TEST(DIPSStoragePrepopulateTest, ExistingStorageAndInteractionTimes) {
+  base::test::TaskEnvironment task_environment;
+  base::SequenceBound<DIPSStorage> storage(CreateTaskRunner());
+  base::Time interaction_time = base::Time::FromDoubleT(1);
+  base::Time storage_time = base::Time::FromDoubleT(2);
+  base::Time prepopulate_time = base::Time::FromDoubleT(3);
+
+  storage.AsyncCall(&DIPSStorage::Init).WithArgs(absl::nullopt);
+  // First record interaction and storage for the site, then call Prepopulate().
+  storage.AsyncCall(&DIPSStorage::RecordInteraction)
+      .WithArgs(GURL("http://site"), interaction_time,
+                DIPSCookieMode::kStandard);
+  storage.AsyncCall(&DIPSStorage::RecordStorage)
+      .WithArgs(GURL("http://site"), storage_time, DIPSCookieMode::kStandard);
+  storage.AsyncCall(&DIPSStorage::Prepopulate)
+      .WithArgs(prepopulate_time, std::vector<std::string>{"site"});
+  absl::optional<StateValue> state;
+  storage.AsyncCall(&DIPSStorage::Read)
+      .WithArgs(GURL("http://site"))
+      .Then(base::BindOnce(StoreState, &state));
+  storage.FlushPostedTasksForTesting();
+
+  // Prepopulate() didn't overwrite the previous timestamps.
+  ASSERT_TRUE(state.has_value());
+  EXPECT_EQ(state->user_interaction_time, interaction_time);  // no change
+  EXPECT_EQ(state->site_storage_time, storage_time);          // no change
+}
+
+TEST(DIPSStoragePrepopulateTest, ExistingStorageTime) {
+  base::test::TaskEnvironment task_environment;
+  base::SequenceBound<DIPSStorage> storage(CreateTaskRunner());
+  base::Time storage_time = base::Time::FromDoubleT(1);
+  base::Time prepopulate_time = base::Time::FromDoubleT(2);
+
+  storage.AsyncCall(&DIPSStorage::Init).WithArgs(absl::nullopt);
+  // Record only storage for the site, then call Prepopulate().
+  storage.AsyncCall(&DIPSStorage::RecordStorage)
+      .WithArgs(GURL("http://site"), storage_time, DIPSCookieMode::kStandard);
+  storage.AsyncCall(&DIPSStorage::Prepopulate)
+      .WithArgs(prepopulate_time, std::vector<std::string>{"site"});
+  absl::optional<StateValue> state;
+  storage.AsyncCall(&DIPSStorage::Read)
+      .WithArgs(GURL("http://site"))
+      .Then(base::BindOnce(StoreState, &state));
+  storage.FlushPostedTasksForTesting();
+
+  ASSERT_TRUE(state.has_value());
+  EXPECT_EQ(state->site_storage_time, storage_time);          // no change
+  EXPECT_EQ(state->user_interaction_time, prepopulate_time);  // written
+}
+
+TEST(DIPSStoragePrepopulateTest, ExistingInteractionTime) {
+  base::test::TaskEnvironment task_environment;
+  base::SequenceBound<DIPSStorage> storage(CreateTaskRunner());
+  base::Time interaction_time = base::Time::FromDoubleT(1);
+  base::Time prepopulate_time = base::Time::FromDoubleT(2);
+
+  storage.AsyncCall(&DIPSStorage::Init).WithArgs(absl::nullopt);
+  // Record only storage for the site, then call Prepopulate().
+  storage.AsyncCall(&DIPSStorage::RecordInteraction)
+      .WithArgs(GURL("http://site"), interaction_time,
+                DIPSCookieMode::kStandard);
+  storage.AsyncCall(&DIPSStorage::Prepopulate)
+      .WithArgs(prepopulate_time, std::vector<std::string>{"site"});
+  absl::optional<StateValue> state;
+  storage.AsyncCall(&DIPSStorage::Read)
+      .WithArgs(GURL("http://site"))
+      .Then(base::BindOnce(StoreState, &state));
+  storage.FlushPostedTasksForTesting();
+
+  ASSERT_TRUE(state.has_value());
+  EXPECT_EQ(state->user_interaction_time, interaction_time);  // no change
+  EXPECT_EQ(state->site_storage_time, absl::nullopt);         // no change
+}
+
+TEST(DIPSStoragePrepopulateTest, WorksOnChunks) {
+  base::test::TaskEnvironment task_environment(
+      base::test::TaskEnvironment::ThreadPoolExecutionMode::QUEUED);
+  base::SequenceBound<DIPSStorage> storage(CreateTaskRunner());
+  base::Time time = base::Time::FromDoubleT(1);
+  std::vector<std::string> sites = {"site1", "site2", "site3"};
+  DIPSStorage::SetPrepopulateChunkSizeForTesting(2);
+
+  absl::optional<StateValue> state1, state2, state3;
+  auto queue_state_reads = [&]() {
+    storage.AsyncCall(&DIPSStorage::Read)
+        .WithArgs(GURL("http://site1"))
+        .Then(base::BindOnce(StoreState, &state1));
+    storage.AsyncCall(&DIPSStorage::Read)
+        .WithArgs(GURL("http://site2"))
+        .Then(base::BindOnce(StoreState, &state2));
+    storage.AsyncCall(&DIPSStorage::Read)
+        .WithArgs(GURL("http://site3"))
+        .Then(base::BindOnce(StoreState, &state3));
+  };
+
+  storage.AsyncCall(&DIPSStorage::Init).WithArgs(absl::nullopt);
+  storage.AsyncCall(&DIPSStorage::Prepopulate).WithArgs(time, std::move(sites));
+  queue_state_reads();
+  task_environment.RunUntilIdle();
+
+  // At this point, the entire |sites| vector has been processed. But we made
+  // async calls to read the state for each site before Prepopulate()
+  // actually ran, so the reads were performed after only the first chunk of
+  // |sites| was processed.
+
+  // The first two sites were prepopulated.
+  EXPECT_TRUE(state1.has_value());
+  EXPECT_TRUE(state2.has_value());
+  // The last wasn't.
+  ASSERT_FALSE(state3.has_value());
+
+  queue_state_reads();
+  task_environment.RunUntilIdle();
+
+  // Now we've read the final state for all sites.
+  EXPECT_TRUE(state1.has_value());
+  EXPECT_TRUE(state2.has_value());
+  EXPECT_TRUE(state3.has_value());
+}
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 84d69fd7..36df4c3 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -326,6 +326,7 @@
     &kUmaBackgroundSessions,
     &kUpdateHistoryEntryPointsInIncognito,
     &kUpdateNotificationScheduleServiceImmediateShowOption,
+    &kUseLibunwindstackNativeUnwinderAndroid,
     &kVoiceSearchAudioCapturePolicy,
     &kVoiceButtonInTopToolbar,
     &kVrBrowsingFeedback,
@@ -1097,6 +1098,15 @@
              "UpdateNotificationScheduleServiceImmediateShowOption",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// Use the LibunwindstackNativeUnwinderAndroid for only browser main thread, and
+// only on Android.
+//
+// Enable by default to collect stack java samples for scroll jank effort as
+// soon as possible.
+BASE_FEATURE(kUseLibunwindstackNativeUnwinderAndroid,
+             "UseLibunwindstackNativeUnwinderAndroid",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
 BASE_FEATURE(kUserMediaScreenCapturing,
              "UserMediaScreenCapturing",
              base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h
index fd876cf..e9aad57e 100644
--- a/chrome/browser/flags/android/chrome_feature_list.h
+++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -184,6 +184,7 @@
 BASE_DECLARE_FEATURE(kUmaBackgroundSessions);
 BASE_DECLARE_FEATURE(kUpdateHistoryEntryPointsInIncognito);
 BASE_DECLARE_FEATURE(kUpdateNotificationScheduleServiceImmediateShowOption);
+BASE_DECLARE_FEATURE(kUseLibunwindstackNativeUnwinderAndroid);
 BASE_DECLARE_FEATURE(kUserMediaScreenCapturing);
 BASE_DECLARE_FEATURE(kVoiceSearchAudioCapturePolicy);
 BASE_DECLARE_FEATURE(kVoiceButtonInTopToolbar);
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
index 7326ad1..1a460f41 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
@@ -109,6 +109,7 @@
                     .put(ChromeFeatureList.TEST_DEFAULT_ENABLED, true)
                     .put(ChromeFeatureList.TOOLBAR_USE_HARDWARE_BITMAP_DRAW, false)
                     .put(ChromeFeatureList.USE_CHIME_ANDROID_SDK, false)
+                    .put(ChromeFeatureList.USE_LIBUNWINDSTACK_NATIVE_UNWINDER_ANDROID, true)
                     .put(ChromeFeatureList.WEB_APK_TRAMPOLINE_ON_INITIAL_INTENT, true)
                     .build();
     /**
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
index b01b824..613f984 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -609,6 +609,8 @@
             "UpdateHistoryEntryPointsInIncognito";
     public static final String USE_CHIME_ANDROID_SDK = "UseChimeAndroidSdk";
     public static final String USE_CLIENT_CONFIG_IPH = "UseClientConfigIPH";
+    public static final String USE_LIBUNWINDSTACK_NATIVE_UNWINDER_ANDROID =
+            "UseLibunwindstackNativeUnwinderAndroid";
     public static final String VOICE_SEARCH_AUDIO_CAPTURE_POLICY = "VoiceSearchAudioCapturePolicy";
     public static final String VOICE_BUTTON_IN_TOP_TOOLBAR = "VoiceButtonInTopToolbar";
     public static final String VR_BROWSING_FEEDBACK = "VrBrowsingFeedback";
@@ -737,6 +739,8 @@
             new CachedFlag(TOOLBAR_USE_HARDWARE_BITMAP_DRAW, false);
     public static final CachedFlag sUseChimeAndroidSdk =
             new CachedFlag(USE_CHIME_ANDROID_SDK, false);
+    public static final CachedFlag sUseLibunwindstackNativeUnwinderAndroid =
+            new CachedFlag(USE_LIBUNWINDSTACK_NATIVE_UNWINDER_ANDROID, true);
     public static final CachedFlag sWebApkTrampolineOnInitialIntent =
             new CachedFlag(WEB_APK_TRAMPOLINE_ON_INITIAL_INTENT, true);
 
diff --git a/chrome/browser/nearby_sharing/nearby_connections_manager_impl.cc b/chrome/browser/nearby_sharing/nearby_connections_manager_impl.cc
index 9082d4d..c334a8a 100644
--- a/chrome/browser/nearby_sharing/nearby_connections_manager_impl.cc
+++ b/chrome/browser/nearby_sharing/nearby_connections_manager_impl.cc
@@ -588,8 +588,8 @@
     incoming_connection_listener_->OnIncomingConnection(
         endpoint_id, it->second->endpoint_info, result.first->second.get());
   } else {
-    auto it = pending_outgoing_connections_.find(endpoint_id);
-    if (it == pending_outgoing_connections_.end()) {
+    auto pending_it = pending_outgoing_connections_.find(endpoint_id);
+    if (pending_it == pending_outgoing_connections_.end()) {
       Disconnect(endpoint_id);
       return;
     }
@@ -597,8 +597,8 @@
     auto result = connections_.emplace(
         endpoint_id, std::make_unique<NearbyConnectionImpl>(this, endpoint_id));
     DCHECK(result.second);
-    std::move(it->second).Run(result.first->second.get());
-    pending_outgoing_connections_.erase(it);
+    std::move(pending_it->second).Run(result.first->second.get());
+    pending_outgoing_connections_.erase(pending_it);
     connect_timeout_timers_.erase(endpoint_id);
   }
 }
diff --git a/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer.cc
index d6153aa..0a520ae 100644
--- a/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer.cc
@@ -18,6 +18,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/content_settings/cookie_settings_factory.h"
 #include "chrome/browser/history_clusters/history_clusters_tab_helper.h"
+#include "chrome/browser/performance_manager/public/user_tuning/user_tuning_utils.h"
 #include "chrome/browser/preloading/prefetch/no_state_prefetch/no_state_prefetch_manager_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
@@ -246,6 +247,8 @@
       navigation_handle->GetWebContents()->GetBrowserContext(),
       navigation_handle->GetURL());
   was_discarded_ = navigation_handle->ExistingDocumentWasDiscarded();
+  refresh_rate_throttled_ =
+      performance_manager::user_tuning::IsRefreshRateThrottled();
 
   return CONTINUE_OBSERVING;
 }
@@ -872,6 +875,9 @@
   if (GetDelegate().DidCommit() && was_discarded_) {
     builder.SetWasDiscarded(true);
   }
+  if (GetDelegate().DidCommit() && refresh_rate_throttled_) {
+    builder.SetRefreshRateThrottled(true);
+  }
   if (GetDelegate().DidCommit() && navigation_is_cross_process_) {
     builder.SetIsCrossProcessNavigation(navigation_is_cross_process_);
   }
diff --git a/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer.h
index 6c602ad0..80cccb0 100644
--- a/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer.h
@@ -327,6 +327,12 @@
   // navigation and a scoped search would have been possible.
   bool was_scoped_search_like_navigation_ = false;
 
+  // True if the refresh rate is capped at 30Hz because of power saver mode when
+  // navigation starts. It is possible but very unlikely for this to change mid
+  // navigation, for instance due to a change by the user in settings or the
+  // battery being recharged above 20%.
+  bool refresh_rate_throttled_ = false;
+
   base::WeakPtrFactory<UkmPageLoadMetricsObserver> weak_factory_{this};
 };
 
diff --git a/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer_unittest.cc
index d5b8792..6a8dfd7 100644
--- a/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer_unittest.cc
@@ -21,6 +21,7 @@
 #include "chrome/browser/history_clusters/history_clusters_service_factory.h"
 #include "chrome/browser/history_clusters/history_clusters_tab_helper.h"
 #include "chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h"
+#include "chrome/browser/performance_manager/test_support/test_user_performance_tuning_manager_environment.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/search_test_utils.h"
@@ -41,7 +42,10 @@
 #include "components/page_load_metrics/browser/page_load_tracker.h"
 #include "components/page_load_metrics/common/page_visit_final_status.h"
 #include "components/page_load_metrics/common/test/page_load_metrics_test_util.h"
+#include "components/performance_manager/public/features.h"
+#include "components/performance_manager/public/user_tuning/prefs.h"
 #include "components/prefs/pref_service.h"
+#include "components/prefs/testing_pref_service.h"
 #include "components/search_engines/template_url_service.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/navigation_simulator.h"
@@ -360,6 +364,8 @@
         kv.second.get(), PageLoad::kPageTiming_ForegroundDurationName));
     EXPECT_FALSE(tester()->test_ukm_recorder().EntryHasMetric(
         kv.second.get(), PageLoad::kWasDiscardedName));
+    EXPECT_FALSE(tester()->test_ukm_recorder().EntryHasMetric(
+        kv.second.get(), PageLoad::kRefreshRateThrottledName));
   }
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> pagevisit_entries =
       tester()->test_ukm_recorder().GetMergedEntriesByName(
@@ -3064,3 +3070,38 @@
   tester()->test_ukm_recorder().ExpectEntryMetric(
       entry, PageLoad::kWasDiscardedName, 1);
 }
+
+#if !BUILDFLAG(IS_ANDROID)
+// Power saver mode only exists on desktop.
+TEST_F(UkmPageLoadMetricsObserverTest, TestRefreshRateThrottled) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(
+      performance_manager::features::kBatterySaverModeAvailable);
+
+  TestingPrefServiceSimple local_state;
+  performance_manager::user_tuning::prefs::RegisterLocalStatePrefs(
+      local_state.registry());
+  local_state.SetInteger(
+      performance_manager::user_tuning::prefs::kBatterySaverModeState,
+      static_cast<int>(performance_manager::user_tuning::prefs::
+                           BatterySaverModeState::kEnabled));
+  performance_manager::user_tuning::TestUserPerformanceTuningManagerEnvironment
+      uptm_environment;
+  uptm_environment.SetUp(&local_state);
+
+  NavigateAndCommit(GURL(kTestUrl1));
+
+  // Simulate closing the tab.
+  DeleteContents();
+
+  const auto& ukm_recorder = tester()->test_ukm_recorder();
+  std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
+      ukm_recorder.GetMergedEntriesByName(PageLoad::kEntryName);
+  EXPECT_EQ(1ul, merged_entries.size());
+  const ukm::mojom::UkmEntry* entry = merged_entries.begin()->second.get();
+  tester()->test_ukm_recorder().ExpectEntryMetric(
+      entry, PageLoad::kRefreshRateThrottledName, 1);
+
+  uptm_environment.TearDown();
+}
+#endif
diff --git a/chrome/browser/performance_manager/public/user_tuning/user_tuning_utils.h b/chrome/browser/performance_manager/public/user_tuning/user_tuning_utils.h
new file mode 100644
index 0000000..76238a5
--- /dev/null
+++ b/chrome/browser/performance_manager/public/user_tuning/user_tuning_utils.h
@@ -0,0 +1,17 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PERFORMANCE_MANAGER_PUBLIC_USER_TUNING_USER_TUNING_UTILS_H_
+#define CHROME_BROWSER_PERFORMANCE_MANAGER_PUBLIC_USER_TUNING_USER_TUNING_UTILS_H_
+
+namespace performance_manager::user_tuning {
+
+// Convenience shortcut for metrics code.
+// Returns true if battery saver mode is available (via Finch) and battery saver
+// mode is currently active.
+bool IsRefreshRateThrottled();
+
+}  // namespace performance_manager::user_tuning
+
+#endif  // CHROME_BROWSER_PERFORMANCE_MANAGER_PUBLIC_USER_TUNING_USER_TUNING_UTILS_H_
diff --git a/chrome/browser/performance_manager/user_tuning/user_tuning_utils.cc b/chrome/browser/performance_manager/user_tuning/user_tuning_utils.cc
new file mode 100644
index 0000000..960a131
--- /dev/null
+++ b/chrome/browser/performance_manager/user_tuning/user_tuning_utils.cc
@@ -0,0 +1,25 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/performance_manager/public/user_tuning/user_tuning_utils.h"
+
+#include "base/feature_list.h"
+#include "build/build_config.h"
+#include "build/buildflag.h"
+#include "chrome/browser/performance_manager/public/user_tuning/user_performance_tuning_manager.h"
+#include "components/performance_manager/public/features.h"
+
+namespace performance_manager::user_tuning {
+
+bool IsRefreshRateThrottled() {
+#if BUILDFLAG(IS_ANDROID)
+  return false;
+#else
+  return base::FeatureList::IsEnabled(
+             performance_manager::features::kBatterySaverModeAvailable) &&
+         UserPerformanceTuningManager::GetInstance()->IsBatterySaverActive();
+#endif
+}
+
+}  //  namespace performance_manager::user_tuning
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 19b5492d..644b4be 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -1542,15 +1542,24 @@
   { key::kCommandLineFlagSecurityWarningsEnabled,
     prefs::kCommandLineFlagSecurityWarningsEnabled,
     base::Value::Type::BOOLEAN },
+  { key::kBrowserGuestModeEnforced,
+    prefs::kBrowserGuestModeEnforced,
+    base::Value::Type::BOOLEAN },
+  { key::kChromeVariations,
+    variations::prefs::kVariationsRestrictionsByPolicy,
+    base::Value::Type::INTEGER },
+  { key::kSigninInterceptionEnabled,
+    prefs::kSigninInterceptionEnabled,
+    base::Value::Type::BOOLEAN },
+#endif // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
+
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
   { key::kAlternativeBrowserPath,
     browser_switcher::prefs::kAlternativeBrowserPath,
     base::Value::Type::STRING },
   { key::kAlternativeBrowserParameters,
     browser_switcher::prefs::kAlternativeBrowserParameters,
     base::Value::Type::LIST },
-  { key::kBrowserGuestModeEnforced,
-    prefs::kBrowserGuestModeEnforced,
-    base::Value::Type::BOOLEAN },
   { key::kBrowserSwitcherParsingMode,
     browser_switcher::prefs::kParsingMode,
     base::Value::Type::INTEGER },
@@ -1575,13 +1584,7 @@
   { key::kBrowserSwitcherDelay,
     browser_switcher::prefs::kDelay,
     base::Value::Type::INTEGER },
-  { key::kChromeVariations,
-    variations::prefs::kVariationsRestrictionsByPolicy,
-    base::Value::Type::INTEGER },
-  { key::kSigninInterceptionEnabled,
-    prefs::kSigninInterceptionEnabled,
-    base::Value::Type::BOOLEAN },
-#endif // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
+#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
 
 #if BUILDFLAG(IS_CHROMEOS)
   { key::kDeskAPIThirdPartyAccessEnabled,
diff --git a/chrome/browser/predictors/preconnect_manager.cc b/chrome/browser/predictors/preconnect_manager.cc
index 6162959..eb9081cd 100644
--- a/chrome/browser/predictors/preconnect_manager.cc
+++ b/chrome/browser/predictors/preconnect_manager.cc
@@ -11,6 +11,8 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/trace_event/trace_event.h"
 #include "chrome/browser/predictors/resource_prefetch_predictor.h"
+#include "chrome/browser/prefetch/prefetch_prefs.h"
+#include "chrome/browser/profiles/profile.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -79,9 +81,19 @@
 
 PreconnectManager::~PreconnectManager() = default;
 
+bool PreconnectManager::IsEnabled() {
+  Profile* profile = Profile::FromBrowserContext(browser_context_);
+  if (!profile) {
+    return false;
+  }
+  return prefetch::IsSomePreloadingEnabled(*profile->GetPrefs());
+}
+
 void PreconnectManager::Start(const GURL& url,
                               std::vector<PreconnectRequest> requests) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  if (!IsEnabled())
+    return;
   PreresolveInfo* info;
   if (preresolve_info_.find(url) == preresolve_info_.end()) {
     auto iterator_and_whether_inserted = preresolve_info_.emplace(
@@ -105,6 +117,8 @@
     const GURL& url,
     const net::NetworkIsolationKey& network_isolation_key) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  if (!IsEnabled())
+    return;
   if (!url.SchemeIsHTTPOrHTTPS())
     return;
   PreresolveJobId job_id = preresolve_jobs_.Add(std::make_unique<PreresolveJob>(
@@ -119,6 +133,8 @@
     const std::vector<std::string>& hostnames,
     const net::NetworkIsolationKey& network_isolation_key) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  if (!IsEnabled())
+    return;
   // Push jobs in front of the queue due to higher priority.
   for (const std::string& hostname : base::Reversed(hostnames)) {
     PreresolveJobId job_id = preresolve_jobs_.Add(
@@ -136,6 +152,8 @@
     bool allow_credentials,
     net::NetworkIsolationKey network_isolation_key) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  if (!IsEnabled())
+    return;
   if (!url.SchemeIsHTTPOrHTTPS())
     return;
   PreresolveJobId job_id = preresolve_jobs_.Add(std::make_unique<PreresolveJob>(
diff --git a/chrome/browser/predictors/preconnect_manager.h b/chrome/browser/predictors/preconnect_manager.h
index eab9e3d7..a71d680 100644
--- a/chrome/browser/predictors/preconnect_manager.h
+++ b/chrome/browser/predictors/preconnect_manager.h
@@ -222,6 +222,9 @@
       const net::NetworkIsolationKey& network_isolation_key,
       ProxyLookupCallback callback) const;
 
+  // Whether the PreconnectManager should be performing preloading operations
+  // or if preloading is disabled.
+  bool IsEnabled();
   void TryToLaunchPreresolveJobs();
   void OnPreresolveFinished(PreresolveJobId job_id, bool success);
   void OnProxyLookupFinished(PreresolveJobId job_id, bool success);
diff --git a/chrome/browser/predictors/preconnect_manager_unittest.cc b/chrome/browser/predictors/preconnect_manager_unittest.cc
index b45f918..ae67e112 100644
--- a/chrome/browser/predictors/preconnect_manager_unittest.cc
+++ b/chrome/browser/predictors/preconnect_manager_unittest.cc
@@ -18,6 +18,7 @@
 #include "chrome/browser/predictors/loading_test_util.h"
 #include "chrome/browser/predictors/proxy_lookup_client_impl.h"
 #include "chrome/browser/predictors/resolve_host_client_impl.h"
+#include "chrome/browser/prefetch/prefetch_prefs.h"
 #include "chrome/test/base/testing_profile.h"
 #include "content/public/test/browser_task_environment.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
@@ -828,6 +829,19 @@
   preconnect_manager_->StartPreresolveHost(non_http_url, network_isolation_key);
 }
 
+TEST_F(PreconnectManagerTest, TestStartPreresolveHostDisabledViaUI) {
+  prefetch::SetPreloadPagesState(profile_->GetPrefs(),
+                                 prefetch::PreloadPagesState::kNoPreloading);
+  GURL url("http://cdn.google.com/script.js");
+  GURL origin("http://cdn.google.com");
+  net::NetworkIsolationKey network_isolation_key =
+      CreateNetworkIsolationKey(origin);
+
+  // mock_network_context_.ResolveHostProxy shouldn't be called. The StrictMock
+  // will raise an error if it happens.
+  preconnect_manager_->StartPreresolveHost(url, network_isolation_key);
+}
+
 TEST_F(PreconnectManagerTest, TestStartPreresolveHosts) {
   GURL cdn("http://cdn.google.com");
   GURL fonts("http://fonts.google.com");
@@ -844,6 +858,20 @@
                                             net::OK);
 }
 
+TEST_F(PreconnectManagerTest, TestStartPreresolveHostsDisabledViaUI) {
+  prefetch::SetPreloadPagesState(profile_->GetPrefs(),
+                                 prefetch::PreloadPagesState::kNoPreloading);
+  GURL cdn("http://cdn.google.com");
+  GURL fonts("http://fonts.google.com");
+  net::NetworkIsolationKey network_isolation_key =
+      CreateNetworkIsolationKey(cdn);
+
+  // mock_network_context_.ResolveHostProxy shouldn't be called. The StrictMock
+  // will raise an error if it happens.
+  preconnect_manager_->StartPreresolveHosts({cdn.host(), fonts.host()},
+                                            network_isolation_key);
+}
+
 TEST_F(PreconnectManagerTest, TestStartPreconnectUrl) {
   GURL url("http://cdn.google.com/script.js");
   net::NetworkIsolationKey network_isolation_key =
@@ -867,6 +895,21 @@
                                           network_isolation_key);
 }
 
+TEST_F(PreconnectManagerTest, TestStartPreconnectUrlDisabledViaUI) {
+  prefetch::SetPreloadPagesState(profile_->GetPrefs(),
+                                 prefetch::PreloadPagesState::kNoPreloading);
+  GURL url("http://cdn.google.com/script.js");
+  net::NetworkIsolationKey network_isolation_key =
+      CreateNetworkIsolationKey(url);
+  GURL origin("http://cdn.google.com");
+  bool allow_credentials = false;
+
+  // mock_network_context_.ResolveHostProxy shouldn't be called. The StrictMock
+  // will raise an error if it happens.
+  preconnect_manager_->StartPreconnectUrl(url, allow_credentials,
+                                          network_isolation_key);
+}
+
 TEST_F(PreconnectManagerTest, TestStartPreconnectUrlWithNetworkIsolationKey) {
   GURL url("http://cdn.google.com/script.js");
   GURL origin("http://cdn.google.com");
@@ -960,6 +1003,23 @@
                                              GetIndirectProxyInfo());
 }
 
+TEST_F(PreconnectManagerTest, TestStartDisabledViaUI) {
+  prefetch::SetPreloadPagesState(profile_->GetPrefs(),
+                                 prefetch::PreloadPagesState::kNoPreloading);
+  mock_network_context_->EnableProxyTesting();
+  GURL main_frame_url("http://google.com");
+  net::NetworkIsolationKey network_isolation_key =
+      CreateNetworkIsolationKey(main_frame_url);
+  url::Origin origin_to_preconnect =
+      url::Origin::Create(GURL("http://cdn.google.com"));
+
+  // mock_delegate_.PreconnectInitiated shouldn't be called. The StrictMock
+  // will raise an error if it happens.
+  preconnect_manager_->Start(
+      main_frame_url,
+      {PreconnectRequest(origin_to_preconnect, 1, network_isolation_key)});
+}
+
 TEST_F(PreconnectManagerTest, TestSuccessfulHostLookupAfterProxyLookupFailure) {
   mock_network_context_->EnableProxyTesting();
   GURL main_frame_url("http://google.com");
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 4a3a155e..6e10ebc4 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -1540,10 +1540,7 @@
   device_signals::RegisterProfilePrefs(registry);
 #endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 
-// TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
-// of lacros-chrome is complete.
-#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || \
-    (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS))
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
   browser_switcher::BrowserSwitcherPrefs::RegisterProfilePrefs(registry);
 #endif
 
diff --git a/chrome/browser/privacy_sandbox/OWNERS b/chrome/browser/privacy_sandbox/OWNERS
index c911888..230e9a1d 100644
--- a/chrome/browser/privacy_sandbox/OWNERS
+++ b/chrome/browser/privacy_sandbox/OWNERS
@@ -1,5 +1 @@
-andzaytsev@google.com
-harrisonsean@chromium.org
-msramek@chromium.org
-sauski@google.com
-olesiamarukhno@google.com
+file://components/privacy_sandbox/OWNERS
diff --git a/chrome/browser/privacy_sandbox/mock_privacy_sandbox_service.h b/chrome/browser/privacy_sandbox/mock_privacy_sandbox_service.h
index f416fc04..9300f28 100644
--- a/chrome/browser/privacy_sandbox/mock_privacy_sandbox_service.h
+++ b/chrome/browser/privacy_sandbox/mock_privacy_sandbox_service.h
@@ -34,6 +34,10 @@
               GetFirstPartySets,
               (),
               (override, const));
+  MOCK_METHOD(absl::optional<net::SchemefulSite>,
+              GetFirstPartySetOwner,
+              (const GURL& site_url),
+              (override, const));
   MOCK_METHOD(absl::optional<std::u16string>,
               GetFirstPartySetOwnerForDisplay,
               (const GURL& site_url),
diff --git a/chrome/browser/privacy_sandbox/privacy_sandbox_service.cc b/chrome/browser/privacy_sandbox/privacy_sandbox_service.cc
index d9a8a43..54cb638 100644
--- a/chrome/browser/privacy_sandbox/privacy_sandbox_service.cc
+++ b/chrome/browser/privacy_sandbox/privacy_sandbox_service.cc
@@ -648,8 +648,7 @@
   return {};
 }
 
-absl::optional<std::u16string>
-PrivacySandboxService::GetFirstPartySetOwnerForDisplay(
+absl::optional<net::SchemefulSite> PrivacySandboxService::GetFirstPartySetOwner(
     const GURL& site_url) const {
   auto sets = GetFirstPartySets();
   auto schemeful_site = net::SchemefulSite(site_url);
@@ -657,9 +656,20 @@
   if (!sets.count(schemeful_site))
     return absl::nullopt;
 
+  return sets[schemeful_site];
+}
+
+absl::optional<std::u16string>
+PrivacySandboxService::GetFirstPartySetOwnerForDisplay(
+    const GURL& site_url) const {
+  auto fpsOwner = GetFirstPartySetOwner(site_url);
+  if (!fpsOwner.has_value()) {
+    return absl::nullopt;
+  }
+
   // TODO(crbug.com/1332513): Apply formatting that correctly displays unicode
   // domains.
-  return base::UTF8ToUTF16(sets[schemeful_site].GetURL().host());
+  return base::UTF8ToUTF16(fpsOwner->GetURL().host());
 }
 
 bool PrivacySandboxService::IsPartOfManagedFirstPartySet(
diff --git a/chrome/browser/privacy_sandbox/privacy_sandbox_service.h b/chrome/browser/privacy_sandbox/privacy_sandbox_service.h
index 677bada..3001341 100644
--- a/chrome/browser/privacy_sandbox/privacy_sandbox_service.h
+++ b/chrome/browser/privacy_sandbox/privacy_sandbox_service.h
@@ -215,6 +215,8 @@
   virtual void SetTopicAllowed(privacy_sandbox::CanonicalTopic topic,
                                bool allowed);
 
+  // DEPRECATED - Do not use in new code. It will be replaced with the in-memory
+  // FPS map.
   // Returns the first party sets recognised by the current profile. If FPS is
   // disabled, or if sets have not been loaded yet, an empty map is returned.
   // Encapsulates logic about whether FPS information should be shown, if it
@@ -230,6 +232,10 @@
   // Encapsulates logic about whether FPS information should be shown, if it
   // should not, absl::nullopt is always returned.
   // Virtual for mocking in tests.
+  virtual absl::optional<net::SchemefulSite> GetFirstPartySetOwner(
+      const GURL& site_url) const;
+
+  // Same as GetFirstPartySetOwner but returns a formatted string.
   virtual absl::optional<std::u16string> GetFirstPartySetOwnerForDisplay(
       const GURL& site_url) const;
 
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/common/command_store.js b/chrome/browser/resources/chromeos/accessibility/chromevox/common/command_store.js
index af21c2f..fa0acbf2 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/common/command_store.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/common/command_store.js
@@ -305,9 +305,7 @@
  *                  announce: boolean,
  *                  category: (undefined|!CommandCategory),
  *                  findNext: (undefined|string),
- *                  doDefault: (undefined|boolean),
  *                  msgId: (undefined|string),
- *                  nodeList: (undefined|string),
  *                  allowEvents: (undefined|boolean),
  *                  denySignedOut: (undefined|boolean)}>}
  *  announce: Whether to call finishNavCommand and announce the current
@@ -315,11 +313,7 @@
  *  findNext: The id from the map above if this command is used for
  *            finding next/previous of something.
  *  category: The command's category.
- *  doDefault: Whether to do the default action. This means that keys will be
- *             passed through to the usual DOM capture/bubble phases.
  *  msgId: The message resource describing the command.
- *  nodeList: The id from the map above if this command is used for
- *            showing a list of nodes.
  *  denySignedOut: Explicitly denies this command when on chrome://oobe/* or
  *             other signed-out contexts. Defaults to false.
  *  allowEvents: Allows EventWatcher to continue processing events which can
@@ -339,7 +333,6 @@
 
   [Command.STOP_SPEECH]: {
     announce: false,
-    doDefault: true,
     msgId: 'stop_speech_key',
     category: CommandCategory.CONTROLLING_SPEECH,
   },
@@ -685,31 +678,26 @@
 
   [Command.SHOW_FORMS_LIST]: {
     announce: false,
-    nodeList: 'formField',
     msgId: 'show_forms_list',
     category: CommandCategory.OVERVIEW,
   },
   [Command.SHOW_HEADINGS_LIST]: {
     announce: false,
-    nodeList: 'heading',
     msgId: 'show_headings_list',
     category: CommandCategory.OVERVIEW,
   },
   [Command.SHOW_LANDMARKS_LIST]: {
     announce: false,
-    nodeList: 'landmark',
     msgId: 'show_landmarks_list',
     category: CommandCategory.OVERVIEW,
   },
   [Command.SHOW_LINKS_LIST]: {
     announce: false,
-    nodeList: 'link',
     msgId: 'show_links_list',
     category: CommandCategory.OVERVIEW,
   },
   [Command.SHOW_TABLES_LIST]: {
     announce: false,
-    nodeList: 'table',
     msgId: 'show_tables_list',
     category: CommandCategory.OVERVIEW,
   },
diff --git a/chrome/browser/resources/new_tab_page/app.html b/chrome/browser/resources/new_tab_page/app.html
index 4e80351..d066a0a 100644
--- a/chrome/browser/resources/new_tab_page/app.html
+++ b/chrome/browser/resources/new_tab_page/app.html
@@ -303,8 +303,7 @@
     <template is="dom-if" if="[[oneGoogleBarEnabled_]]">
       <ntp-iframe id="oneGoogleBar" src="[[oneGoogleBarIframePath_]]"
           hidden$="[[!oneGoogleBarLoaded_]]"
-          allow="camera [[oneGoogleBarIframeOrigin_]];
-                display-capture [[oneGoogleBarIframeOrigin_]]">
+          allow="camera [[oneGoogleBarIframeOrigin_]]; display-capture [[oneGoogleBarIframeOrigin_]]"> <!-- presubmit: ignore-long-line -->
       </ntp-iframe>
     </template>
   </template>
diff --git a/chrome/browser/resources/privacy_sandbox/OWNERS b/chrome/browser/resources/privacy_sandbox/OWNERS
index e46b4fee..230e9a1d 100644
--- a/chrome/browser/resources/privacy_sandbox/OWNERS
+++ b/chrome/browser/resources/privacy_sandbox/OWNERS
@@ -1,4 +1 @@
-# Chrome Privacy Team
-rainhard@chromium.org
-sauski@google.com
-olesiamarukhno@google.com
+file://components/privacy_sandbox/OWNERS
diff --git a/chrome/browser/resources/settings/chromeos/bluetooth_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/bluetooth_page/BUILD.gn
index cf98268..8a78bc96 100644
--- a/chrome/browser/resources/settings/chromeos/bluetooth_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/bluetooth_page/BUILD.gn
@@ -56,6 +56,7 @@
     "..:os_route",
     "..:route_observer_behavior",
     "../..:router",
+    "//ash/webui/common/resources:list_property_update_behavior",
     "//ash/webui/common/resources/bluetooth:bluetooth_dialog",
     "//ash/webui/common/resources/bluetooth:bluetooth_metrics_utils",
     "//services/device/public/mojom:mojom_js_library_for_compile",
@@ -63,7 +64,6 @@
     "//ui/webui/resources/cr_elements:cr_scrollable_behavior",
     "//ui/webui/resources/cr_elements:i18n_behavior",
     "//ui/webui/resources/js:assert",
-    "//ui/webui/resources/js:list_property_update_behavior",
   ]
   externs_list = [
     "$externs_path/bluetooth.js",
diff --git a/chrome/browser/resources/settings/chromeos/bluetooth_page/bluetooth_subpage.js b/chrome/browser/resources/settings/chromeos/bluetooth_page/bluetooth_subpage.js
index c963f96..f26b01ca 100644
--- a/chrome/browser/resources/settings/chromeos/bluetooth_page/bluetooth_subpage.js
+++ b/chrome/browser/resources/settings/chromeos/bluetooth_page/bluetooth_subpage.js
@@ -21,7 +21,7 @@
 import {BluetoothUiSurface, recordBluetoothUiSurfaceMetrics, recordUserInitiatedReconnectionAttemptDuration} from 'chrome://resources/ash/common/bluetooth/bluetooth_metrics_utils.js';
 import {CrScrollableBehavior, CrScrollableBehaviorInterface} from 'chrome://resources/cr_elements/cr_scrollable_behavior.js';
 import {I18nBehavior, I18nBehaviorInterface} from 'chrome://resources/cr_elements/i18n_behavior.js';
-import {ListPropertyUpdateBehavior, ListPropertyUpdateBehaviorInterface} from 'chrome://resources/js/list_property_update_behavior.js';
+import {ListPropertyUpdateBehavior, ListPropertyUpdateBehaviorInterface} from 'chrome://resources/ash/common/list_property_update_behavior.js';
 import {flush, html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {Setting} from '../../mojom-webui/setting.mojom-webui.js';
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_printing_page/BUILD.gn
index ad5db2d..c399e3d 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/BUILD.gn
@@ -145,8 +145,8 @@
   deps = [
     ":cups_printer_types",
     ":cups_printers_browser_proxy",
+    "//ash/webui/common/resources:list_property_update_behavior",
     "//ui/webui/resources/cr_elements:web_ui_listener_behavior",
-    "//ui/webui/resources/js:list_property_update_behavior",
   ]
 
   externs_list = [
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_enterprise_printers.js b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_enterprise_printers.js
index 0d8aba0..2165fd31 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_enterprise_printers.js
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_enterprise_printers.js
@@ -14,7 +14,7 @@
 import './cups_printers_entry.js';
 import '../../settings_shared.css.js';
 
-import {ListPropertyUpdateBehavior, ListPropertyUpdateBehaviorInterface} from 'chrome://resources/js/list_property_update_behavior.js';
+import {ListPropertyUpdateBehavior, ListPropertyUpdateBehaviorInterface} from 'chrome://resources/ash/common/list_property_update_behavior.js';
 import {WebUIListenerBehavior, WebUIListenerBehaviorInterface} from 'chrome://resources/cr_elements/web_ui_listener_behavior.js';
 import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_nearby_printers.js b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_nearby_printers.js
index 07c7890..69bf363 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_nearby_printers.js
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_nearby_printers.js
@@ -10,7 +10,7 @@
 import './cups_printers_entry.js';
 import '../../settings_shared.css.js';
 
-import {ListPropertyUpdateBehavior, ListPropertyUpdateBehaviorInterface} from 'chrome://resources/js/list_property_update_behavior.js';
+import {ListPropertyUpdateBehavior, ListPropertyUpdateBehaviorInterface} from 'chrome://resources/ash/common/list_property_update_behavior.js';
 import {WebUIListenerBehavior, WebUIListenerBehaviorInterface} from 'chrome://resources/cr_elements/web_ui_listener_behavior.js';
 import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_saved_printers.js b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_saved_printers.js
index fe7d9d12..7c80805 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_saved_printers.js
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_saved_printers.js
@@ -14,7 +14,7 @@
 import './cups_printers_entry.js';
 import '../../settings_shared.css.js';
 
-import {ListPropertyUpdateBehavior, ListPropertyUpdateBehaviorInterface} from 'chrome://resources/js/list_property_update_behavior.js';
+import {ListPropertyUpdateBehavior, ListPropertyUpdateBehaviorInterface} from 'chrome://resources/ash/common/list_property_update_behavior.js';
 import {WebUIListenerBehavior, WebUIListenerBehaviorInterface} from 'chrome://resources/cr_elements/web_ui_listener_behavior.js';
 import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
diff --git a/chrome/browser/resources/settings/privacy_sandbox/OWNERS b/chrome/browser/resources/settings/privacy_sandbox/OWNERS
index 0bab1928..230e9a1d 100644
--- a/chrome/browser/resources/settings/privacy_sandbox/OWNERS
+++ b/chrome/browser/resources/settings/privacy_sandbox/OWNERS
@@ -1,3 +1 @@
-# Chrome Privacy Team
-rainhard@chromium.org
-sauski@google.com
+file://components/privacy_sandbox/OWNERS
diff --git a/chrome/browser/resources/settings/site_settings/all_sites.html b/chrome/browser/resources/settings/site_settings/all_sites.html
index 4588fee..b4807744 100644
--- a/chrome/browser/resources/settings/site_settings/all_sites.html
+++ b/chrome/browser/resources/settings/site_settings/all_sites.html
@@ -73,7 +73,8 @@
     <div class="list-frame" hidden$="[[!siteGroupMapEmpty_(siteGroupMap)]]">
       <div class="list-item secondary">$i18n{emptyAllSitesPage}</div>
     </div>
-    <div class="list-frame" hidden$="[[!noSearchResultFound_(filteredList_)]]">
+    <div id="noSitesFoundText" class="list-frame"
+     hidden$="[[!noSearchResultFound_(filteredList_)]]">
       <div class="list-item secondary">$i18n{noSitesFound}</div>
     </div>
     <div class="list-frame without-heading" id="listContainer">
diff --git a/chrome/browser/resources/settings/site_settings/all_sites.ts b/chrome/browser/resources/settings/site_settings/all_sites.ts
index cccd79ec..0b5fe9b 100644
--- a/chrome/browser/resources/settings/site_settings/all_sites.ts
+++ b/chrome/browser/resources/settings/site_settings/all_sites.ts
@@ -1094,7 +1094,6 @@
    */
   private onClearAllData_(e: Event) {
     this.browserProxy.recordAction(AllSitesAction2.CLEAR_ALL_DATA);
-
     const scopes = [AllSitesDialog.CLEAR_DATA, 'All'];
     const anyAppsInstalled = this.filteredList_.some(g => g.hasInstalledPWA);
     const installed = anyAppsInstalled ? 'Installed' : '';
@@ -1103,7 +1102,9 @@
     for (let index = this.filteredList_.length - 1; index >= 0; index--) {
       this.clearDataForSiteGroupIndex_(index);
     }
-    this.$.allSitesList.fire('iron-resize');
+    // Needed to update the filteredList_ for the "No sites found" text to
+    // appear.
+    this.forceListUpdate_();
     this.totalUsage_ = '0 B';
     this.onCloseDialog_(e);
   }
diff --git a/chrome/browser/resources/side_panel/bookmarks/bookmark_folder.html b/chrome/browser/resources/side_panel/bookmarks/bookmark_folder.html
index d6229f7..4a81ced 100644
--- a/chrome/browser/resources/side_panel/bookmarks/bookmark_folder.html
+++ b/chrome/browser/resources/side_panel/bookmarks/bookmark_folder.html
@@ -155,11 +155,11 @@
     content: '';
     display: block;
     height: 2px;
-    margin-inline-start: calc(
+    left: calc(
         var(--row-padding-inline-start) + var(--row-icon-width));
     position: absolute;
+    right: 0;
     top: -1px;
-    width: 100%;
   }
 
   [drop-position='below']::after {
diff --git a/chrome/browser/resources/side_panel/bookmarks/bookmarks.html b/chrome/browser/resources/side_panel/bookmarks/bookmarks.html
index 6e2d1e8..a3c1bdb 100644
--- a/chrome/browser/resources/side_panel/bookmarks/bookmarks.html
+++ b/chrome/browser/resources/side_panel/bookmarks/bookmarks.html
@@ -17,7 +17,8 @@
 
     body {
       background: white;
-      overflow: auto;
+      overflow-x: hidden;
+      overflow-y: auto;
     }
 
     @media (prefers-color-scheme: dark) {
@@ -31,4 +32,4 @@
   <bookmarks-list></bookmarks-list>
   <script type="module" src="bookmarks/bookmarks_list.js"></script>
 </body>
-</html>
\ No newline at end of file
+</html>
diff --git a/chrome/browser/resources/webui_gallery/BUILD.gn b/chrome/browser/resources/webui_gallery/BUILD.gn
index 0a94bc6..1262463 100644
--- a/chrome/browser/resources/webui_gallery/BUILD.gn
+++ b/chrome/browser/resources/webui_gallery/BUILD.gn
@@ -80,6 +80,7 @@
     "demos/md_select/md_select_demo.html",
     "demos/progress_indicator_nonpolymer_demo.html",
     "demos/progress_indicator_polymer_demo.html",
+    "demos/scroll_view/scroll_view_demo.html",
     "webui_gallery.html",
   ]
 
diff --git a/chrome/browser/resources/webui_gallery/app.ts b/chrome/browser/resources/webui_gallery/app.ts
index dd40a75..ca17e36 100644
--- a/chrome/browser/resources/webui_gallery/app.ts
+++ b/chrome/browser/resources/webui_gallery/app.ts
@@ -90,6 +90,11 @@
               src: 'cr_radio_demo.html',
             },
             {
+              name: 'Scroll view',
+              path: 'scroll-view',
+              src: 'scroll_view/scroll_view_demo.html',
+            },
+            {
               name: 'Select menu',
               path: 'select-menu',
               src: 'md_select/md_select_demo.html',
diff --git a/chrome/browser/resources/webui_gallery/demos/scroll_view/scroll_view_demo.html b/chrome/browser/resources/webui_gallery/demos/scroll_view/scroll_view_demo.html
new file mode 100644
index 0000000..e08dab0
--- /dev/null
+++ b/chrome/browser/resources/webui_gallery/demos/scroll_view/scroll_view_demo.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Scroll view demo</title>
+    <link rel="stylesheet" href="../demo.css">
+  </head>
+  <body>
+    <scroll-view-demo></scroll-view-demo>
+    <script src="scroll_view_demo_component.js" type="module"></script>
+  </body>
+</html>
diff --git a/chrome/browser/resources/webui_gallery/demos/scroll_view/scroll_view_demo_component.html b/chrome/browser/resources/webui_gallery/demos/scroll_view/scroll_view_demo_component.html
new file mode 100644
index 0000000..e7332b5
--- /dev/null
+++ b/chrome/browser/resources/webui_gallery/demos/scroll_view/scroll_view_demo_component.html
@@ -0,0 +1,99 @@
+<link rel="stylesheet" href="../demo.css">
+<style include="cr-shared-style">
+  #layout {
+    display: flex;
+    flex-direction: column;
+    width: 100%;
+  }
+
+  /* Parent of #container for CrContainerShadowMixin needs to be flex column. */
+  #layout {
+    display: flex;
+    flex-direction: column;
+  }
+
+  #container {
+    height: 500px;
+    overflow: auto;
+    position: relative;
+    width: 100%;
+  }
+
+  #mockContent {
+    background: linear-gradient(180deg, white, black);
+    height: 200%;
+    width: 100%;
+  }
+
+  #ironListScrollView {
+    max-height: 300px;
+    width: 100%;
+  }
+
+  .item {
+    padding: 16px;
+    text-align: center;
+    width: 100%;
+  }
+
+  #canScrollLog,
+  #isScrolledLog,
+  #scrolledToBottomLog {
+    display: none;
+  }
+
+  .can-scroll ~ #canScrollLog {
+    display: block;
+  }
+
+  .is-scrolled ~ #isScrolledLog {
+    display: block;
+  }
+
+  .scrolled-to-bottom ~ #scrolledToBottomLog {
+    display: block;
+  }
+
+  #sliderContainer {
+    align-items: center;
+    display: flex;
+    gap: 12px;
+  }
+
+  #itemsLengthSlider {
+    width: 200px;
+  }
+</style>
+
+<h1>Scroll view with shadows indicating scroll</h1>
+<div class="demos">
+  <div id="layout">
+    <div id="container" show-bottom-shadow>
+      <div id="mockContent"></div>
+    </div>
+  </div>
+</div>
+
+<h1>Scroll view with &lt;iron-list&gt; and dynamic height</h1>
+<div class="demos">
+  <div id="sliderContainer">
+    <label id="sliderLabel">Number of items in iron-list</label>
+    <cr-slider id="itemsLengthSlider" min="0" max="30" value="[[items_.length]]"
+        aria-labelledby="sliderLabel"
+        on-cr-slider-value-changed="onItemsLengthChanged_">
+    </cr-slider>
+    [[items_.length]]
+  </div>
+
+  <div id="ironListScrollView" scrollable>
+    <iron-list items="[[items_]]" scroll-target="ironListScrollView">
+      <template>
+        <div class="item" tabindex="0">Focusable item</div>
+      </template>
+    </iron-list>
+  </div>
+
+  <div id="canScrollLog">can scroll</div>
+  <div id="isScrolledLog">is scrolled</div>
+  <div id="scrolledToBottomLog">scrolled to bottom</div>
+</div>
diff --git a/chrome/browser/resources/webui_gallery/demos/scroll_view/scroll_view_demo_component.ts b/chrome/browser/resources/webui_gallery/demos/scroll_view/scroll_view_demo_component.ts
new file mode 100644
index 0000000..4471eb5
--- /dev/null
+++ b/chrome/browser/resources/webui_gallery/demos/scroll_view/scroll_view_demo_component.ts
@@ -0,0 +1,62 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://resources/cr_elements/cr_shared_style.css.js';
+import 'chrome://resources/cr_elements/cr_shared_vars.css.js';
+import 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
+import 'chrome://resources/cr_elements/cr_slider/cr_slider.js';
+
+import {CrContainerShadowMixin} from 'chrome://resources/cr_elements/cr_container_shadow_mixin.js';
+import {CrScrollableMixin} from 'chrome://resources/cr_elements/cr_scrollable_mixin.js';
+import {CrSliderElement} from 'chrome://resources/cr_elements/cr_slider/cr_slider.js';
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {getTemplate} from './scroll_view_demo_component.html.js';
+
+interface ScrollViewDemoComponent {
+  $: {
+    itemsLengthSlider: CrSliderElement,
+  };
+}
+
+const ScrollViewDemoComponentBase =
+    CrContainerShadowMixin(CrScrollableMixin(PolymerElement));
+
+class ScrollViewDemoComponent extends ScrollViewDemoComponentBase {
+  static get is() {
+    return 'scroll-view-demo';
+  }
+
+  static get template() {
+    return getTemplate();
+  }
+
+  static get properties() {
+    return {
+      items_: {
+        type: Array,
+        value: () => [0, 1, 2, 3],
+      },
+    };
+  }
+
+  private items_: number[];
+
+  override ready() {
+    super.ready();
+    this.updateScrollableContents();
+  }
+
+  private onItemsLengthChanged_() {
+    const length = this.$.itemsLengthSlider.value;
+    const items: number[] = [];
+    for (let i = 0; i < length; i++) {
+      items.push(i);
+    }
+    this.items_ = items;
+    this.updateScrollableContents();
+  }
+}
+
+customElements.define(ScrollViewDemoComponent.is, ScrollViewDemoComponent);
diff --git a/chrome/browser/resources/webui_gallery/webui_gallery.gni b/chrome/browser/resources/webui_gallery/webui_gallery.gni
index ff372ef..d757faf2 100644
--- a/chrome/browser/resources/webui_gallery/webui_gallery.gni
+++ b/chrome/browser/resources/webui_gallery/webui_gallery.gni
@@ -16,6 +16,7 @@
   "demos/cr_toast/cr_toast_demo_component.ts",
   "demos/cr_toolbar/cr_toolbar_demo_component.ts",
   "demos/md_select/md_select_demo_component.ts",
+  "demos/scroll_view/scroll_view_demo_component.ts",
 ]
 
 web_component_files_native = [
diff --git a/chrome/browser/sync/test/integration/migration_test.cc b/chrome/browser/sync/test/integration/migration_test.cc
index 95aa211..fcc9483 100644
--- a/chrome/browser/sync/test/integration/migration_test.cc
+++ b/chrome/browser/sync/test/integration/migration_test.cc
@@ -369,19 +369,4 @@
   RunTwoClientMigrationTest(migration_list, MODIFY_BOOKMARK);
 }
 
-class MigrationReconfigureTest : public MigrationTwoClientTest {
- public:
-  MigrationReconfigureTest() = default;
-
-  void SetUpCommandLine(base::CommandLine* cl) override {
-    AddTestSwitches(cl);
-    // Do not add optional datatypes.
-  }
-
-  MigrationReconfigureTest(const MigrationReconfigureTest&) = delete;
-  MigrationReconfigureTest& operator=(const MigrationReconfigureTest&) = delete;
-
-  ~MigrationReconfigureTest() override = default;
-};
-
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/sync_test.cc b/chrome/browser/sync/test/integration/sync_test.cc
index 467a2caa..45af12a 100644
--- a/chrome/browser/sync/test/integration/sync_test.cc
+++ b/chrome/browser/sync/test/integration/sync_test.cc
@@ -335,17 +335,6 @@
 }
 
 void SyncTest::SetUpCommandLine(base::CommandLine* cl) {
-  AddTestSwitches(cl);
-
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-  cl->AppendSwitch(ash::switches::kIgnoreUserProfileMappingForTests);
-  cl->AppendSwitch(ash::switches::kDisableArcOptInVerification);
-  cl->AppendSwitch(ash::switches::kDisableLacrosKeepAliveForTesting);
-  arc::SetArcAvailableCommandLineForTesting(cl);
-#endif
-}
-
-void SyncTest::AddTestSwitches(base::CommandLine* cl) {
   // Disable non-essential access of external network resources.
   if (!cl->HasSwitch(switches::kDisableBackgroundNetworking)) {
     cl->AppendSwitch(switches::kDisableBackgroundNetworking);
@@ -372,6 +361,13 @@
     // Effectively disables kSyncTrustedVaultPassphraseRecovery for E2E tests.
     cl->AppendSwitchASCII(syncer::kTrustedVaultServiceURL, "broken_url");
   }
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  cl->AppendSwitch(ash::switches::kIgnoreUserProfileMappingForTests);
+  cl->AppendSwitch(ash::switches::kDisableArcOptInVerification);
+  cl->AppendSwitch(ash::switches::kDisableLacrosKeepAliveForTesting);
+  arc::SetArcAvailableCommandLineForTesting(cl);
+#endif
 }
 
 void SyncTest::BeforeSetupClient(int index,
diff --git a/chrome/browser/sync/test/integration/sync_test.h b/chrome/browser/sync/test/integration/sync_test.h
index 1a802bc..400fdce 100644
--- a/chrome/browser/sync/test/integration/sync_test.h
+++ b/chrome/browser/sync/test/integration/sync_test.h
@@ -272,9 +272,6 @@
   std::string GetCacheGuid(size_t profile_index) const;
 
  protected:
-  // Add custom switches needed for running the test.
-  void AddTestSwitches(base::CommandLine* cl);
-
   // BrowserTestBase implementation:
   void SetUpOnMainThread() override;
   void TearDownOnMainThread() override;
diff --git a/chrome/browser/sync/test/integration/two_client_web_apps_bmo_sync_test.cc b/chrome/browser/sync/test/integration/two_client_web_apps_bmo_sync_test.cc
index e00eef8..ada238c 100644
--- a/chrome/browser/sync/test/integration/two_client_web_apps_bmo_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_web_apps_bmo_sync_test.cc
@@ -15,7 +15,6 @@
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
-#include "chrome/browser/web_applications/commands/fetch_manifest_and_install_command.h"
 #include "chrome/browser/web_applications/commands/install_from_info_command.h"
 #include "chrome/browser/web_applications/os_integration/os_integration_manager.h"
 #include "chrome/browser/web_applications/os_integration/web_app_shortcut_manager.h"
@@ -26,6 +25,7 @@
 #include "chrome/browser/web_applications/user_display_mode.h"
 #include "chrome/browser/web_applications/web_app.h"
 #include "chrome/browser/web_applications/web_app_command_manager.h"
+#include "chrome/browser/web_applications/web_app_command_scheduler.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
 #include "chrome/browser/web_applications/web_app_install_params.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
@@ -109,19 +109,18 @@
     AppId app_id;
     base::RunLoop run_loop;
     auto* provider = WebAppProvider::GetForTest(profile);
-    provider->command_manager().ScheduleCommand(
-        std::make_unique<FetchManifestAndInstallCommand>(
-            &provider->install_finalizer(), &provider->registrar(), source,
-            browser->tab_strip_model()->GetActiveWebContents()->GetWeakPtr(),
-            /*bypass_service_worker_check=*/false,
-            base::BindOnce(test::TestAcceptDialogCallback),
-            base::BindLambdaForTesting([&](const AppId& new_app_id,
-                                           webapps::InstallResultCode code) {
+    provider->scheduler().FetchManifestAndInstall(
+        source,
+        browser->tab_strip_model()->GetActiveWebContents()->GetWeakPtr(),
+        /*bypass_service_worker_check=*/false,
+        base::BindOnce(test::TestAcceptDialogCallback),
+        base::BindLambdaForTesting(
+            [&](const AppId& new_app_id, webapps::InstallResultCode code) {
               EXPECT_EQ(code, webapps::InstallResultCode::kSuccessNewInstall);
               app_id = new_app_id;
               run_loop.Quit();
             }),
-            /*use_fallback=*/true, WebAppInstallFlow::kInstallSite));
+        /*use_fallback=*/true);
     run_loop.Run();
     return app_id;
   }
diff --git a/chrome/browser/ui/android/omnibox/java/res/values/dimens.xml b/chrome/browser/ui/android/omnibox/java/res/values/dimens.xml
index 350a647..ea48b2a1 100644
--- a/chrome/browser/ui/android/omnibox/java/res/values/dimens.xml
+++ b/chrome/browser/ui/android/omnibox/java/res/values/dimens.xml
@@ -55,7 +55,6 @@
     <dimen name="omnibox_suggestion_compact_height">48dp</dimen>
     <dimen name="omnibox_suggestion_semicompact_padding">8dp</dimen>
     <dimen name="omnibox_suggestion_compact_padding">6dp</dimen>
-    <dimen name="omnibox_suggestion_list_padding_side">16dp</dimen>
     <dimen name="omnibox_suggestion_list_padding_bottom">8dp</dimen>
     <dimen name="omnibox_suggestion_start_offset_without_icon">18dp</dimen>
     <dimen name="omnibox_carousel_suggestion_padding">12dp</dimen>
@@ -82,6 +81,7 @@
     <dimen name="omnibox_suggestion_bg_round_corner_radius">16dp</dimen>
     <dimen name="omnibox_suggestion_bg_rectangle_corner_radius">0dp</dimen>
     <dimen name="omnibox_suggestion_vertical_spacing">2dp</dimen>
+    <dimen name="omnibox_suggestion_side_spacing">16dp</dimen>
 
     <dimen name="omnibox_suggestion_dropdown_bg_elevation">@dimen/default_elevation_1</dimen>
 
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdown.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdown.java
index c8a3b95..2ee3fb2a 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdown.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdown.java
@@ -197,12 +197,9 @@
         boolean shouldShowModernizeVisualUpdate =
                 OmniboxFeatures.shouldShowModernizeVisualUpdate(context);
         final Resources resources = context.getResources();
-        int paddingSide = shouldShowModernizeVisualUpdate
-                ? resources.getDimensionPixelOffset(R.dimen.omnibox_suggestion_list_padding_side)
-                : 0;
         int paddingBottom =
                 resources.getDimensionPixelOffset(R.dimen.omnibox_suggestion_list_padding_bottom);
-        ViewCompat.setPaddingRelative(this, paddingSide, 0, paddingSide, paddingBottom);
+        ViewCompat.setPaddingRelative(this, 0, 0, 0, paddingBottom);
 
         mStandardBgColor = shouldShowModernizeVisualUpdate
                 ? ChromeColors.getSurfaceColor(
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java
index 79e5206..e2af590 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java
@@ -292,7 +292,10 @@
         if (layoutParams instanceof MarginLayoutParams) {
             int verticalSpacing = view.getContext().getResources().getDimensionPixelSize(
                     R.dimen.omnibox_suggestion_vertical_spacing);
-            ((MarginLayoutParams) layoutParams).setMargins(0, verticalSpacing, 0, 0);
+            int sideSpacing = view.getContext().getResources().getDimensionPixelOffset(
+                    R.dimen.omnibox_suggestion_side_spacing);
+            ((MarginLayoutParams) layoutParams)
+                    .setMargins(sideSpacing, verticalSpacing, sideSpacing, 0);
         }
         view.setLayoutParams(layoutParams);
     }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinderUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinderUnitTest.java
index 3d95fc27..a5dce25 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinderUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinderUnitTest.java
@@ -293,11 +293,15 @@
         Assert.assertNotNull(mBaseView.getBackground());
 
         verify(mBaseView).setLayoutParams(any());
+        int verticalSpacing = mBaseView.getContext().getResources().getDimensionPixelSize(
+                R.dimen.omnibox_suggestion_vertical_spacing);
+        int sideSpacing = mBaseView.getContext().getResources().getDimensionPixelOffset(
+                R.dimen.omnibox_suggestion_side_spacing);
         MarginLayoutParams layoutParams = (MarginLayoutParams) mBaseView.getLayoutParams();
         Assert.assertNotNull(layoutParams);
-        Assert.assertEquals(0, layoutParams.leftMargin);
-        Assert.assertNotEquals(0, layoutParams.topMargin);
-        Assert.assertEquals(0, layoutParams.rightMargin);
+        Assert.assertEquals(sideSpacing, layoutParams.leftMargin);
+        Assert.assertEquals(verticalSpacing, layoutParams.topMargin);
+        Assert.assertEquals(sideSpacing, layoutParams.rightMargin);
         Assert.assertEquals(0, layoutParams.bottomMargin);
     }
 
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/carousel/BaseCarouselSuggestionView.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/carousel/BaseCarouselSuggestionView.java
index 63e6fbd0..3baa643 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/carousel/BaseCarouselSuggestionView.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/carousel/BaseCarouselSuggestionView.java
@@ -62,7 +62,7 @@
                 new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false));
         mRecyclerView.setClipToPadding(false);
         mRecyclerView.setPaddingRelative(
-                getResources().getDimensionPixelSize(R.dimen.omnibox_suggestion_list_padding_side),
+                getResources().getDimensionPixelSize(R.dimen.omnibox_suggestion_side_spacing),
                 mRecyclerView.getPaddingTop(), mRecyclerView.getPaddingEnd(),
                 mRecyclerView.getPaddingBottom());
 
diff --git a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/fre/SigninFirstRunMediator.java b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/fre/SigninFirstRunMediator.java
index 5c32e63..537121c 100644
--- a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/fre/SigninFirstRunMediator.java
+++ b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/fre/SigninFirstRunMediator.java
@@ -186,8 +186,6 @@
         boolean isSigninDisabledByPolicy = false;
         boolean isMetricsReportingDisabledByPolicy = false;
         if (hasPolicies) {
-            assert mDelegate.getNativeInitializationPromise().isFulfilled()
-                : "Must wait for native initialization if enterprise policies were found!";
             isSigninDisabledByPolicy =
                     IdentityServicesProvider.get()
                             .getSigninManager(Profile.getLastUsedRegularProfile())
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/OptionalBrowsingModeButtonController.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/OptionalBrowsingModeButtonController.java
index 37a109c..b26dd3bd 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/OptionalBrowsingModeButtonController.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/OptionalBrowsingModeButtonController.java
@@ -10,6 +10,7 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.toolbar.ButtonData;
 import org.chromium.chrome.browser.toolbar.ButtonDataProvider;
+import org.chromium.chrome.browser.toolbar.adaptive.AdaptiveToolbarFeatures.AdaptiveToolbarButtonVariant;
 import org.chromium.chrome.browser.user_education.UserEducationHelper;
 
 import java.util.HashMap;
@@ -60,6 +61,25 @@
         mObserverMap.clear();
     }
 
+    /**
+     * Gets the {@link AdaptiveToolbarButtonVariant} of the currently shown button. {@code
+     * AdaptiveToolbarButtonVariant.NONE} is returned if there's no visible button.
+     * @return A value from {@link AdaptiveToolbarButtonVariant}.
+     */
+    public @AdaptiveToolbarButtonVariant int getCurrentButtonVariant() {
+        if (mCurrentProvider == null || mTabSupplier == null) {
+            return AdaptiveToolbarButtonVariant.NONE;
+        }
+
+        ButtonData currentButton = mCurrentProvider.get(mTabSupplier.get());
+
+        if (currentButton == null || !currentButton.canShow()) {
+            return AdaptiveToolbarButtonVariant.NONE;
+        }
+
+        return currentButton.getButtonSpec().getButtonVariant();
+    }
+
     void updateButtonVisibility() {
         showHighestPrecedenceOptionalButton();
     }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
index 8044788..4b145de 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
@@ -38,6 +38,7 @@
 import org.chromium.chrome.browser.toolbar.ToolbarDataProvider;
 import org.chromium.chrome.browser.toolbar.ToolbarProgressBar;
 import org.chromium.chrome.browser.toolbar.ToolbarTabController;
+import org.chromium.chrome.browser.toolbar.adaptive.AdaptiveToolbarFeatures.AdaptiveToolbarButtonVariant;
 import org.chromium.chrome.browser.toolbar.menu_button.MenuButton;
 import org.chromium.chrome.browser.toolbar.menu_button.MenuButtonCoordinator;
 import org.chromium.chrome.browser.toolbar.top.NavigationPopup.HistoryDelegate;
@@ -396,6 +397,15 @@
     }
 
     /**
+     * Gets the {@link AdaptiveToolbarButtonVariant} of the currently shown optional button. {@code
+     * AdaptiveToolbarButtonVariant.NONE} is returned if there's no visible optional button.
+     * @return A value from {@link AdaptiveToolbarButtonVariant}.
+     */
+    public @AdaptiveToolbarButtonVariant int getCurrentOptionalButtonVariant() {
+        return mOptionalButtonController.getCurrentButtonVariant();
+    }
+
+    /**
      * Gives inheriting classes the chance to update the visibility of the
      * back button.
      * @param canGoBack Whether or not the current tab has any history to go back to.
diff --git a/chrome/browser/ui/android/webid/internal/java/res/layout/account_selection_account_item.xml b/chrome/browser/ui/android/webid/internal/java/res/layout/account_selection_account_item.xml
index 12b5d09..1b2172b 100644
--- a/chrome/browser/ui/android/webid/internal/java/res/layout/account_selection_account_item.xml
+++ b/chrome/browser/ui/android/webid/internal/java/res/layout/account_selection_account_item.xml
@@ -15,7 +15,9 @@
     android:paddingBottom="@dimen/account_selection_sheet_item_padding"
     android:paddingTop="@dimen/account_selection_sheet_item_padding"
     android:gravity="center_vertical"
-    android:orientation="horizontal">
+    android:orientation="horizontal"
+    android:layoutDirection="locale"
+    android:textDirection="locale">
 
     <org.chromium.ui.widget.ChromeImageView
         android:id="@+id/start_icon"
diff --git a/chrome/browser/ui/app_list/arc/arc_app_launcher.cc b/chrome/browser/ui/app_list/arc/arc_app_launcher.cc
index cca0bf3a..0cf15c3 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_launcher.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_launcher.cc
@@ -106,15 +106,12 @@
       return false;
     }
 
-    auto readiness = apps::Readiness::kUnknown;
     proxy->AppRegistryCache().ForOneApp(
         app_id, [&readiness](const apps::AppUpdate& update) {
           readiness = update.Readiness();
         });
-
-    if (readiness != apps::Readiness::kReady)
-      return false;
-  } else if (readiness != apps::Readiness::kReady) {
+  }
+  if (readiness != apps::Readiness::kReady) {
     return false;
   }
 
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/app_launch_event_logger.cc b/chrome/browser/ui/app_list/search/search_result_ranker/app_launch_event_logger.cc
index b7c3bd6..c2f26927 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/app_launch_event_logger.cc
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/app_launch_event_logger.cc
@@ -445,22 +445,23 @@
 }
 
 void AppLaunchEventLogger::Log(AppLaunchEvent app_launch_event) {
-  auto app = app_features_map_.find(app_launch_event.app_id());
-  if (app == app_features_map_.end()) {
+  auto app_iter = app_features_map_.find(app_launch_event.app_id());
+  if (app_iter == app_features_map_.end()) {
     RecordAppTypeClicked(AppLaunchEvent_AppType_OTHER);
     return;
   }
-  RecordAppTypeClicked(app->second.app_type());
+  const AppLaunchFeatures& features = app_iter->second;
+  RecordAppTypeClicked(features.app_type());
   ukm::SourceId launch_source_id =
-      GetSourceId(app->second.app_type(), app_launch_event.app_id(),
-                  app->second.arc_package_name(), app->second.pwa_url());
+      GetSourceId(features.app_type(), app_launch_event.app_id(),
+                  features.arc_package_name(), features.pwa_url());
 
   base::Time now(base::Time::Now());
   const base::TimeDelta duration = now - start_time_;
   all_clicks_last_hour_->Log(duration);
   all_clicks_last_24_hours_->Log(duration);
 
-  if (app->second.is_policy_compliant() &&
+  if (features.is_policy_compliant() &&
       launch_source_id != ukm::kInvalidSourceId) {
     ukm::builders::AppListAppLaunch app_launch(launch_source_id);
     if (app_launch_event.launched_from() ==
@@ -469,7 +470,7 @@
             AppLaunchEvent_LaunchedFrom_SEARCH_BOX) {
       app_launch.SetPositionIndex(app_launch_event.index());
     }
-    app_launch.SetAppType(app->second.app_type())
+    app_launch.SetAppType(features.app_type())
         .SetLaunchedFrom(app_launch_event.launched_from())
         .SetDayOfWeek(DayOfWeek(now))
         .SetHourOfDay(HourOfDay(now))
diff --git a/chrome/browser/ui/ash/arc_open_url_delegate_impl.cc b/chrome/browser/ui/ash/arc_open_url_delegate_impl.cc
index 4157c40..7a41dd54 100644
--- a/chrome/browser/ui/ash/arc_open_url_delegate_impl.cc
+++ b/chrome/browser/ui/ash/arc_open_url_delegate_impl.cc
@@ -373,8 +373,8 @@
   if (!arc_tracker)
     return;
 
-  for (const auto& app_id : prefs->GetAppsForPackage(package_name.value()))
-    arc_tracker->CloseWindows(app_id);
+  for (const auto& id : prefs->GetAppsForPackage(package_name.value()))
+    arc_tracker->CloseWindows(id);
 }
 
 void ArcOpenUrlDelegateImpl::OpenArcCustomTab(
diff --git a/chrome/browser/ui/ash/assistant/search_and_assistant_enabled_checker.cc b/chrome/browser/ui/ash/assistant/search_and_assistant_enabled_checker.cc
index 87e5a27..82431f5 100644
--- a/chrome/browser/ui/ash/assistant/search_and_assistant_enabled_checker.cc
+++ b/chrome/browser/ui/ash/assistant/search_and_assistant_enabled_checker.cc
@@ -28,6 +28,39 @@
 
 constexpr int kMaxBodySize = 1024;
 
+constexpr net::NetworkTrafficAnnotationTag
+    kSearchAndAssistantEnabledCheckerNetworkTag =
+        net::DefineNetworkTrafficAnnotation(
+            "search_and_assistant_enabled_checker",
+            R"(
+        semantics {
+          sender: "Search and Assistant Enabled Checker"
+          description:
+            "HTTP request used to check if Search and Assistant (SAA) "
+            "service has been disabled by dasher admin. When service is "
+            "disabled, SAA will provide offline experiences for user."
+          trigger:
+            "User logs in, switches active profile, enables SAA or "
+            "updates account information so that assistant is allowed. "
+            "Also triggered when dasher admin enables SAA service for "
+            "user. "
+          data: "User GAIA Credentials"
+          destination: GOOGLE_OWNED_SERVICE
+        }
+        policy {
+         cookies_allowed: YES
+         cookies_store: "user"
+         chrome_policy {
+           AssistantVoiceMatchEnabledDuringOobe {
+             AssistantVoiceMatchEnabledDuringOobe : false
+           }
+         }
+         setting:
+           "User can turn on/off SAA in ChromeOS Settings under the "
+           "Search and Assistant menu. For managed devices, dasher "
+           "admin can disable SAA from the Additional Services menu."
+        })");
+
 bool HasJsonSafetyPrefix(std::string& json_body) {
   return base::StartsWith(json_body, kJsonSafetyPrefix,
                           base::CompareCase::SENSITIVE);
@@ -49,8 +82,8 @@
           GURL(chromeos::assistant::kServiceIdEndpoint),
           chromeos::assistant::kPayloadParamName,
           chromeos::assistant::kServiceIdRequestPayload);
-  url_loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
-                                                 NO_TRAFFIC_ANNOTATION_YET);
+  url_loader_ = network::SimpleURLLoader::Create(
+      std::move(resource_request), kSearchAndAssistantEnabledCheckerNetworkTag);
   url_loader_->DownloadToString(
       url_loader_factory_,
       base::BindOnce(
diff --git a/chrome/browser/ui/ash/capture_mode/DIR_METADATA b/chrome/browser/ui/ash/capture_mode/DIR_METADATA
index ad46d34..34a74c0 100644
--- a/chrome/browser/ui/ash/capture_mode/DIR_METADATA
+++ b/chrome/browser/ui/ash/capture_mode/DIR_METADATA
@@ -1,3 +1,10 @@
+team_email: "chromeos-wm-corexp@google.com"
+buganizer: {
+  component_id:1253115
+}
+buganizer_public: {
+  component_id:1253131
+}
 monorail {
   component: "UI>Shell>ScreenCapture"
 }
diff --git a/chrome/browser/ui/ash/network/network_state_notifier_unittest.cc b/chrome/browser/ui/ash/network/network_state_notifier_unittest.cc
index 02e21d0f..a569519 100644
--- a/chrome/browser/ui/ash/network/network_state_notifier_unittest.cc
+++ b/chrome/browser/ui/ash/network/network_state_notifier_unittest.cc
@@ -116,7 +116,6 @@
 
  protected:
   void SetupESimNetwork() {
-    const char kCellularEsimServicePath[] = "/service/cellular_esim1";
     const char kTestEuiccPath[] = "euicc_path";
     const char kTestEidName[] = "eid";
     const char kTestIccid[] = "iccid";
diff --git a/chrome/browser/ui/ash/shelf/app_service/app_service_instance_registry_helper.cc b/chrome/browser/ui/ash/shelf/app_service/app_service_instance_registry_helper.cc
index 37bb0a235..eb35bed 100644
--- a/chrome/browser/ui/ash/shelf/app_service/app_service_instance_registry_helper.cc
+++ b/chrome/browser/ui/ash/shelf/app_service/app_service_instance_registry_helper.cc
@@ -271,8 +271,8 @@
     return;
   }
 
-  apps::InstanceState state = CalculateVisibilityState(window, visible);
-  OnInstances(app_constants::kChromeAppId, window, std::string(), state);
+  OnInstances(app_constants::kChromeAppId, window, std::string(),
+              CalculateVisibilityState(window, visible));
 
   if (!base::Contains(browser_window_to_tab_windows_, window))
     return;
@@ -283,8 +283,8 @@
     const std::string app_id = GetAppId(it);
     if (app_id.empty())
       continue;
-    apps::InstanceState state = CalculateVisibilityState(it, visible);
-    OnInstances(app_id, it, std::string(), state);
+    OnInstances(app_id, it, std::string(),
+                CalculateVisibilityState(it, visible));
   }
 }
 
@@ -317,8 +317,8 @@
     return;
   }
 
-  apps::InstanceState state = CalculateActivatedState(window, active);
-  OnInstances(app_constants::kChromeAppId, window, std::string(), state);
+  OnInstances(app_constants::kChromeAppId, window, std::string(),
+              CalculateActivatedState(window, active));
 
   if (!base::Contains(browser_window_to_tab_windows_, window))
     return;
@@ -335,7 +335,7 @@
     if (!contents)
       return;
 
-    apps::InstanceState state = static_cast<apps::InstanceState>(
+    constexpr auto kState = static_cast<apps::InstanceState>(
         apps::InstanceState::kStarted | apps::InstanceState::kRunning |
         apps::InstanceState::kActive | apps::InstanceState::kVisible);
     auto* contents_window = GetWindow(contents);
@@ -351,7 +351,7 @@
     // browser window.
     UpdateTabWindow(app_id, contents_window);
 
-    OnInstances(app_id, contents_window, std::string(), state);
+    OnInstances(app_id, contents_window, std::string(), kState);
     return;
   }
 
@@ -361,8 +361,7 @@
     const std::string app_id = GetAppId(it);
     if (app_id.empty())
       continue;
-    apps::InstanceState state = CalculateActivatedState(it, active);
-    OnInstances(app_id, it, std::string(), state);
+    OnInstances(app_id, it, std::string(), CalculateActivatedState(it, active));
   }
 }
 
diff --git a/chrome/browser/ui/intent_picker_tab_helper.cc b/chrome/browser/ui/intent_picker_tab_helper.cc
index cb4fd8d..d2c22d7 100644
--- a/chrome/browser/ui/intent_picker_tab_helper.cc
+++ b/chrome/browser/ui/intent_picker_tab_helper.cc
@@ -308,11 +308,9 @@
   // or bubble if there are some apps available. We only want to check this if
   // the navigation happens in the primary main frame, and the navigation is not
   // the same document with same URL.
-  if (!web_contents())
+  if (!web_contents()) {
     return;
-
-  ++commit_count_;
-
+  }
   if (IsNavigatingToNewSite(navigation_handle)) {
     bool is_valid_page = navigation_handle->GetURL().SchemeIsHTTPOrHTTPS() &&
                          !navigation_handle->IsErrorPage();
diff --git a/chrome/browser/ui/intent_picker_tab_helper.h b/chrome/browser/ui/intent_picker_tab_helper.h
index 771fe85..b294de1 100644
--- a/chrome/browser/ui/intent_picker_tab_helper.h
+++ b/chrome/browser/ui/intent_picker_tab_helper.h
@@ -62,8 +62,6 @@
                            std::vector<apps::IntentPickerAppInfo> apps,
                            IntentPickerIconLoaderCallback callback);
 
-  int commit_count() { return commit_count_; }
-
   // Sets a OnceClosure callback which will be called next time the icon is
   // updated. If include_latest_navigation is true, and the latest navigation
   // was finished, the callback is called immediately.
@@ -113,10 +111,6 @@
   // on this origin.
   bool show_expanded_chip_from_usage_ = false;
 
-  // Tracks the number of commits on this page, to allow for checking to make
-  // sure that asynchronous invocations do not cause a stale intent picker.
-  int commit_count_ = 0;
-
   // Contains the app ID of an app which can be opened through the intent
   // picker. This is only set when ShowIconForApps() is called with a single
   // app. Will be set to the empty string in all other cases (e.g. when there
diff --git a/chrome/browser/ui/privacy_sandbox/OWNERS b/chrome/browser/ui/privacy_sandbox/OWNERS
index 282c715..230e9a1d 100644
--- a/chrome/browser/ui/privacy_sandbox/OWNERS
+++ b/chrome/browser/ui/privacy_sandbox/OWNERS
@@ -1 +1 @@
-file://chrome/browser/privacy_sandbox/OWNERS
+file://components/privacy_sandbox/OWNERS
diff --git a/chrome/browser/ui/translate/partial_translate_bubble_model.h b/chrome/browser/ui/translate/partial_translate_bubble_model.h
index 25a3351..eaa48254 100644
--- a/chrome/browser/ui/translate/partial_translate_bubble_model.h
+++ b/chrome/browser/ui/translate/partial_translate_bubble_model.h
@@ -106,9 +106,6 @@
   // Closes the Partial Translate bubble, then immediately opens the Full Page
   // Translate bubble and starts a translation.
   virtual void TranslateFullPage(content::WebContents* web_contents) = 0;
-
-  // Set whether the selected text is truncated.
-  virtual void SetSourceTextTruncated(bool is_truncated) = 0;
 };
 
 #endif  // CHROME_BROWSER_UI_TRANSLATE_PARTIAL_TRANSLATE_BUBBLE_MODEL_H_
diff --git a/chrome/browser/ui/translate/partial_translate_bubble_model_impl.cc b/chrome/browser/ui/translate/partial_translate_bubble_model_impl.cc
index 87a0a40..dd364429 100644
--- a/chrome/browser/ui/translate/partial_translate_bubble_model_impl.cc
+++ b/chrome/browser/ui/translate/partial_translate_bubble_model_impl.cc
@@ -4,8 +4,6 @@
 
 #include "chrome/browser/ui/translate/partial_translate_bubble_model_impl.h"
 
-#include <limits>
-#include <string>
 #include <utility>
 
 #include "base/metrics/histogram_functions.h"
@@ -19,9 +17,6 @@
 #include "components/translate/core/browser/translate_ui_delegate.h"
 #include "components/translate/core/common/translate_constants.h"
 #include "components/translate/core/common/translate_errors.h"
-#include "ui/gfx/font_list.h"
-#include "ui/gfx/text_constants.h"
-#include "ui/gfx/text_elider.h"
 
 namespace {
 
@@ -97,14 +92,7 @@
 
 void PartialTranslateBubbleModelImpl::SetTargetText(
     const std::u16string& text) {
-  // Note: Some languages have syntactic differences in use of ellipses.
-  // Luxembourgish uses a leading space and is the only one of these languages
-  // supported by Translate in Chrome. Given this, specific localization is not
-  // handled, but could be in the future if more languages are included.
-  if (source_text_truncated_)
-    target_text_ = text + u"…";
-  else
-    target_text_ = text;
+  target_text_ = text;
 }
 
 std::u16string PartialTranslateBubbleModelImpl::GetTargetText() const {
@@ -172,14 +160,7 @@
 void PartialTranslateBubbleModelImpl::Translate(
     content::WebContents* web_contents) {
   PartialTranslateRequest request;
-  // If the selected text was truncated, strip the trailing ellipses before
-  // sending for translation.
-  std::u16string source_text = GetSourceText();
-  if (source_text_truncated_)
-    request.selection_text = source_text.substr(0, source_text.size() - 1);
-  else
-    request.selection_text = source_text;
-
+  request.selection_text = GetSourceText();
   request.selection_encoding = web_contents->GetEncoding();
   std::string source_language_code = GetSourceLanguageCode();
   if (source_language_code != translate::kUnknownLanguageCode) {
@@ -213,11 +194,6 @@
   translate_manager->ShowTranslateUI(GetTargetLanguageCode(), true);
 }
 
-void PartialTranslateBubbleModelImpl::SetSourceTextTruncated(
-    bool is_truncated) {
-  source_text_truncated_ = is_truncated;
-}
-
 void PartialTranslateBubbleModelImpl::OnPartialTranslateResponse(
     const PartialTranslateRequest& request,
     const PartialTranslateResponse& response) {
diff --git a/chrome/browser/ui/translate/partial_translate_bubble_model_impl.h b/chrome/browser/ui/translate/partial_translate_bubble_model_impl.h
index f52ffa6..b72aa63 100644
--- a/chrome/browser/ui/translate/partial_translate_bubble_model_impl.h
+++ b/chrome/browser/ui/translate/partial_translate_bubble_model_impl.h
@@ -60,7 +60,6 @@
   std::string GetTargetLanguageCode() const override;
   void Translate(content::WebContents* web_contents) override;
   void TranslateFullPage(content::WebContents* web_contents) override;
-  void SetSourceTextTruncated(bool is_truncated) override;
 
  private:
   // Updates the partial translate model based on the given response.
@@ -82,9 +81,6 @@
   // The selected text, which may be truncated.
   std::u16string source_text_;
 
-  // Whether or not the current selected text is truncated.
-  bool source_text_truncated_;
-
   // The translated text, or empty if the translation has not yet been
   // performed.
   std::u16string target_text_;
diff --git a/chrome/browser/ui/views/bubble_anchor_util_views.cc b/chrome/browser/ui/views/bubble_anchor_util_views.cc
index 1cd6b61..6025075e4 100644
--- a/chrome/browser/ui/views/bubble_anchor_util_views.cc
+++ b/chrome/browser/ui/views/bubble_anchor_util_views.cc
@@ -5,14 +5,13 @@
 #include "chrome/browser/ui/views/bubble_anchor_util_views.h"
 
 #include "build/build_config.h"
-#include "chrome/browser/ui/bubble_anchor_util.h"
 #include "chrome/browser/ui/views/frame/app_menu_button.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.h"
 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
+#include "chrome/browser/ui/views/location_bar/location_icon_view.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
 #include "components/permissions/features.h"
-#include "ui/views/bubble/bubble_border.h"
 
 // This file contains the bubble_anchor_util implementation for a Views
 // browser window (BrowserView).
@@ -22,12 +21,6 @@
 AnchorConfiguration GetPageInfoAnchorConfiguration(Browser* browser,
                                                    Anchor anchor) {
   BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
-  if (anchor == kLocationBar &&
-      browser_view->GetLocationBarView()->ShouldHideContentSettingImage()) {
-    return {browser_view->GetLocationBarView()->chip_controller()->chip(),
-            browser_view->GetLocationBarView()->chip_controller()->chip(),
-            views::BubbleBorder::TOP_LEFT};
-  }
 
   if (anchor == kLocationBar && browser_view->GetLocationBarView()->IsDrawn())
     return {browser_view->GetLocationBarView(),
diff --git a/chrome/browser/ui/views/intent_picker_bubble_view_browsertest_mac.cc b/chrome/browser/ui/views/intent_picker_bubble_view_browsertest_mac.cc
deleted file mode 100644
index ffa49dc6..0000000
--- a/chrome/browser/ui/views/intent_picker_bubble_view_browsertest_mac.cc
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <string>
-
-#include "chrome/browser/apps/intent_helper/mac_intent_picker_helpers.h"
-#include "chrome/browser/ui/intent_picker_tab_helper.h"
-#include "chrome/browser/ui/views/frame/browser_view.h"
-#include "chrome/browser/ui/views/frame/toolbar_button_provider.h"
-#include "chrome/browser/ui/views/intent_picker_bubble_view.h"
-#include "chrome/browser/ui/views/page_action/page_action_icon_view.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "chrome/test/base/ui_test_utils.h"
-#include "content/public/test/browser_test.h"
-#include "content/public/test/browser_test_utils.h"
-#include "net/dns/mock_host_resolver.h"
-#include "net/test/embedded_test_server/embedded_test_server.h"
-#include "ui/views/widget/any_widget_observer.h"
-
-class IntentPickerBubbleViewBrowserTestMac : public InProcessBrowserTest {
- public:
-  PageActionIconView* GetIntentPickerIcon() {
-    return BrowserView::GetBrowserViewForBrowser(browser())
-        ->toolbar_button_provider()
-        ->GetPageActionIconView(PageActionIconType::kIntentPicker);
-  }
-
-  IntentPickerBubbleView* intent_picker_bubble() {
-    return IntentPickerBubbleView::intent_picker_bubble();
-  }
-
-  size_t GetItemContainerSize(IntentPickerBubbleView* bubble) {
-    return bubble->GetViewByID(IntentPickerBubbleView::ViewId::kItemContainer)
-        ->children()
-        .size();
-  }
-
- private:
-  void SetUpOnMainThread() override {
-    host_resolver()->AddRule("*", "127.0.0.1");
-    ASSERT_TRUE(embedded_test_server()->Start());
-  }
-};
-
-// Test that if there is a Universal Link for a site, it shows the picker icon
-// and lists the app as an option in the bubble.
-IN_PROC_BROWSER_TEST_F(IntentPickerBubbleViewBrowserTestMac,
-                       ShowUniversalLinkInIntentPicker) {
-  content::WebContents* contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  IntentPickerTabHelper* tab_helper =
-      IntentPickerTabHelper::FromWebContents(contents);
-
-  const GURL url1(embedded_test_server()->GetURL("/title1.html"));
-  const GURL url2(embedded_test_server()->GetURL("/title2.html"));
-
-  const char* kFinderAppName = "Finder";
-  const char* kFinderAppPath = "/System/Library/CoreServices/Finder.app";
-
-  // Start with a page with no corresponding native app.
-
-  base::RunLoop run_loop;
-  tab_helper->SetIconUpdateCallbackForTesting(
-      run_loop.QuitClosure(),
-      /*include_latest_navigation=*/false);
-  apps::OverrideMacAppForUrlForTesting(true, "");
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url1));
-  run_loop.Run();
-
-  // Verify that no icon was shown.
-
-  ASSERT_FALSE(GetIntentPickerIcon()->GetVisible());
-
-  // Load a different page while simulating it having a native app.
-
-  base::RunLoop run_loop2;
-  tab_helper->SetIconUpdateCallbackForTesting(
-      run_loop2.QuitClosure(),
-      /*include_latest_navigation=*/false);
-  apps::OverrideMacAppForUrlForTesting(true, kFinderAppPath);
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url2));
-  run_loop2.Run();
-
-  // Verify that an icon was shown, but no bubble.
-
-  ASSERT_TRUE(GetIntentPickerIcon()->GetVisible());
-  EXPECT_FALSE(intent_picker_bubble());
-
-  // Open the bubble.
-
-  views::NamedWidgetShownWaiter waiter(views::test::AnyWidgetTestPasskey{},
-                                       "IntentPickerBubbleView");
-  GetIntentPickerIcon()->ExecuteForTesting();
-  waiter.WaitIfNeededAndGet();
-
-  // Verify the bubble contents.
-
-  ASSERT_TRUE(intent_picker_bubble());
-  EXPECT_TRUE(intent_picker_bubble()->GetVisible());
-
-  EXPECT_EQ(1U, GetItemContainerSize(intent_picker_bubble()));
-  auto& app_info = intent_picker_bubble()->app_info_for_testing();
-  ASSERT_EQ(1U, app_info.size());
-  EXPECT_EQ(kFinderAppPath, app_info[0].launch_name);
-  EXPECT_EQ(kFinderAppName, app_info[0].display_name);
-
-  // Navigate to the first page while simulating no native app.
-
-  base::RunLoop run_loop3;
-  tab_helper->SetIconUpdateCallbackForTesting(
-      run_loop3.QuitClosure(),
-      /*include_latest_navigation=*/false);
-  apps::OverrideMacAppForUrlForTesting(true, "");
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url1));
-  run_loop3.Run();
-
-  // Verify that the icon was hidden.
-
-  ASSERT_FALSE(GetIntentPickerIcon()->GetVisible());
-}
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc
index e69a09b..20dbdfb5 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -87,7 +87,6 @@
 #include "components/omnibox/browser/vector_icons.h"
 #include "components/omnibox/common/omnibox_features.h"
 #include "components/performance_manager/public/features.h"
-#include "components/permissions/features.h"
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing/core/common/features.h"
 #include "components/search_engines/template_url.h"
@@ -611,15 +610,15 @@
   // label/chip.
   const double kLeadingDecorationMaxFraction = 0.5;
 
-  if (chip_controller_ && chip_controller_->chip()->GetVisible() &&
+  if (chip_controller_ && chip_controller_->IsPermissionPromptChipVisible() &&
       !ShouldShowKeywordBubble()) {
     leading_decorations.AddDecoration(vertical_padding, location_height, false,
                                       0, edge_padding,
                                       chip_controller_->chip());
   }
 
-  location_icon_view_->SetVisible(false);
   if (ShouldShowKeywordBubble()) {
+    location_icon_view_->SetVisible(false);
     leading_decorations.AddDecoration(vertical_padding, location_height, false,
                                       kLeadingDecorationMaxFraction,
                                       edge_padding, selected_keyword_view_);
@@ -646,14 +645,11 @@
       }
       selected_keyword_view_->SetCustomImage(image);
     }
-  } else if (location_icon_view_->GetShowText() &&
-             !ShouldChipOverrideLocationIcon()) {
-    location_icon_view_->SetVisible(true);
+  } else if (location_icon_view_->GetShowText()) {
     leading_decorations.AddDecoration(vertical_padding, location_height, false,
                                       kLeadingDecorationMaxFraction,
                                       edge_padding, location_icon_view_);
-  } else if (!ShouldChipOverrideLocationIcon()) {
-    location_icon_view_->SetVisible(true);
+  } else {
     leading_decorations.AddDecoration(vertical_padding, location_height, false,
                                       0, edge_padding, location_icon_view_);
   }
@@ -1355,14 +1351,6 @@
   PreferredSizeChanged();
 }
 
-bool LocationBarView::ShouldChipOverrideLocationIcon() {
-  bool has_visible_chip =
-      chip_controller_ && chip_controller_->chip()->GetVisible();
-  return has_visible_chip &&
-         base::FeatureList::IsEnabled(
-             permissions::features::kChipLocationBarIconOverride);
-}
-
 bool LocationBarView::IsEditingOrEmpty() const {
   return omnibox_view_ && omnibox_view_->IsEditingOrEmpty();
 }
@@ -1437,14 +1425,14 @@
 }
 
 void LocationBarView::UpdateChipVisibility() {
-  if (!chip_controller_ || !chip_controller_->chip()->GetVisible()) {
+  if (!chip_controller_ || !chip_controller_->IsPermissionPromptChipVisible()) {
     return;
   }
 
   if (IsEditingOrEmpty()) {
     // If a user starts typing, a permission request should be ignored and the
     // chip finalized.
-    chip_controller_->ResetChip();
+    chip_controller_->FinalizeChip();
   }
 }
 
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.h b/chrome/browser/ui/views/location_bar/location_bar_view.h
index 25eec4b0..08632a06 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.h
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.h
@@ -369,10 +369,6 @@
 
   void OnTouchUiChanged();
 
-  // Determines whether the location icon should be overridden while a chip is
-  // being displayed
-  bool ShouldChipOverrideLocationIcon();
-
   // Called with an async fetched for the keyword view.
   void OnKeywordFaviconFetched(const gfx::Image& icon);
 
diff --git a/chrome/browser/ui/views/location_bar/omnibox_chip_button.cc b/chrome/browser/ui/views/location_bar/omnibox_chip_button.cc
index 6ccb59f..43195778 100644
--- a/chrome/browser/ui/views/location_bar/omnibox_chip_button.cc
+++ b/chrome/browser/ui/views/location_bar/omnibox_chip_button.cc
@@ -5,7 +5,6 @@
 #include "chrome/browser/ui/views/location_bar/omnibox_chip_button.h"
 #include <cstddef>
 
-#include "base/time/time.h"
 #include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/ui/color/chrome_color_id.h"
 #include "chrome/browser/ui/layout_constants.h"
@@ -60,12 +59,14 @@
   }
 }
 
-void OmniboxChipButton::AnimateCollapse(base::TimeDelta kAnimationDuration) {
+void OmniboxChipButton::AnimateCollapse() {
+  constexpr auto kAnimationDuration = base::Milliseconds(250);
   animation_->SetSlideDuration(kAnimationDuration);
   animation_->Hide();
 }
 
-void OmniboxChipButton::AnimateExpand(base::TimeDelta kAnimationDuration) {
+void OmniboxChipButton::AnimateExpand() {
+  constexpr auto kAnimationDuration = base::Milliseconds(350);
   animation_->SetSlideDuration(kAnimationDuration);
   animation_->Show();
 }
@@ -80,11 +81,6 @@
   expand_animation_ended_callback_ = callback;
 }
 
-void OmniboxChipButton::SetCollapseEndedCallback(
-    base::RepeatingCallback<void()> callback) {
-  collapse_animation_ended_callback_ = callback;
-}
-
 gfx::Size OmniboxChipButton::CalculatePreferredSize() const {
   const int fixed_width = GetIconSize() + GetInsets().width();
   const int collapsable_width = label()->GetPreferredSize().width() +
@@ -116,15 +112,8 @@
     return;
 
   fully_collapsed_ = animation->GetCurrentValue() != 1.0;
-
-  if (animation->GetCurrentValue() == 1.0 && expand_animation_ended_callback_) {
+  if (animation->GetCurrentValue() == 1.0 && expand_animation_ended_callback_)
     expand_animation_ended_callback_.Run();
-  }
-
-  if (animation->GetCurrentValue() == 0.0 &&
-      collapse_animation_ended_callback_) {
-    collapse_animation_ended_callback_.Run();
-  }
 }
 
 void OmniboxChipButton::AnimationProgressed(const gfx::Animation* animation) {
diff --git a/chrome/browser/ui/views/location_bar/omnibox_chip_button.h b/chrome/browser/ui/views/location_bar/omnibox_chip_button.h
index f42e8cf..d541168 100644
--- a/chrome/browser/ui/views/location_bar/omnibox_chip_button.h
+++ b/chrome/browser/ui/views/location_bar/omnibox_chip_button.h
@@ -30,12 +30,11 @@
 
   void VisibilityChanged(views::View* starting_from, bool is_visible) override;
 
-  void AnimateCollapse(base::TimeDelta duration);
-  void AnimateExpand(base::TimeDelta duration);
+  void AnimateCollapse();
+  void AnimateExpand();
   void ResetAnimation(double value = 0);
   void SetExpandAnimationEndedCallback(
       base::RepeatingCallback<void()> callback);
-  void SetCollapseEndedCallback(base::RepeatingCallback<void()> callback);
   bool is_fully_collapsed() const { return fully_collapsed_; }
   bool is_animating() const { return animation_->is_animating(); }
   gfx::SlideAnimation* animation_for_testing() { return animation_.get(); }
@@ -89,8 +88,6 @@
 
   base::RepeatingCallback<void()> expand_animation_ended_callback_;
 
-  base::RepeatingCallback<void()> collapse_animation_ended_callback_;
-
   base::RepeatingCallback<void()> visibility_changed_callback_;
 
   bool force_expanded_for_testing_ = false;
diff --git a/chrome/browser/ui/views/performance_controls/high_efficiency_chip_view.cc b/chrome/browser/ui/views/performance_controls/high_efficiency_chip_view.cc
index 261d092..e782aec 100644
--- a/chrome/browser/ui/views/performance_controls/high_efficiency_chip_view.cc
+++ b/chrome/browser/ui/views/performance_controls/high_efficiency_chip_view.cc
@@ -6,6 +6,7 @@
 
 #include "base/time/time.h"
 #include "chrome/app/vector_icons/vector_icons.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/performance_manager/public/user_tuning/user_performance_tuning_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser_element_identifiers.h"
@@ -21,6 +22,7 @@
 #include "components/feature_engagement/public/event_constants.h"
 #include "components/feature_engagement/public/feature_constants.h"
 #include "components/performance_manager/public/features.h"
+#include "components/performance_manager/public/user_tuning/prefs.h"
 #include "components/prefs/pref_service.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
@@ -54,6 +56,13 @@
       browser_(browser) {
   DCHECK(browser_);
 
+  registrar_.Init(g_browser_process->local_state());
+  registrar_.Add(
+      performance_manager::user_tuning::prefs::kHighEfficiencyModeEnabled,
+      base::BindRepeating(&HighEfficiencyChipView::OnPrefChanged,
+                          base::Unretained(this)));
+  OnPrefChanged();
+
   SetUpForInOutAnimation(kChipAnimationDuration);
   SetPaintLabelOverSolidBackground(true);
   SetProperty(views::kElementIdentifierKey, kHighEfficiencyChipElementId);
@@ -77,7 +86,7 @@
   }
   TabDiscardTabHelper* const tab_helper =
       TabDiscardTabHelper::FromWebContents(web_contents);
-  if (tab_helper->IsChipVisible()) {
+  if (tab_helper->IsChipVisible() && is_high_efficiency_mode_enabled_) {
     SetVisible(true);
     if (tab_helper->ShouldIconAnimate()) {
       // Only animate the chip to the expanded view the first 3 times it is
@@ -148,5 +157,10 @@
   }
 }
 
+void HighEfficiencyChipView::OnPrefChanged() {
+  is_high_efficiency_mode_enabled_ = registrar_.prefs()->GetBoolean(
+      performance_manager::user_tuning::prefs::kHighEfficiencyModeEnabled);
+}
+
 BEGIN_METADATA(HighEfficiencyChipView, PageActionIconView)
 END_METADATA
diff --git a/chrome/browser/ui/views/performance_controls/high_efficiency_chip_view.h b/chrome/browser/ui/views/performance_controls/high_efficiency_chip_view.h
index a76a193..aebcc7c 100644
--- a/chrome/browser/ui/views/performance_controls/high_efficiency_chip_view.h
+++ b/chrome/browser/ui/views/performance_controls/high_efficiency_chip_view.h
@@ -8,6 +8,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/performance_controls/high_efficiency_bubble_observer.h"
 #include "chrome/browser/ui/views/page_action/page_action_icon_view.h"
+#include "components/prefs/pref_change_registrar.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/views/bubble/bubble_dialog_model_host.h"
 
@@ -42,9 +43,16 @@
   const raw_ptr<Browser> browser_;
   base::OneShotTimer timer_;
   raw_ptr<views::BubbleDialogModelHost> bubble_ = nullptr;
-  base::WeakPtrFactory<HighEfficiencyChipView> weak_ptr_factory_{this};
+  PrefChangeRegistrar registrar_;
+  bool is_high_efficiency_mode_enabled_ = false;
 
   void MaybeShowIPH();
+
+  // Callback for the registrar. Checks whether high efficiency mode is
+  // currently enabled.
+  void OnPrefChanged();
+
+  base::WeakPtrFactory<HighEfficiencyChipView> weak_ptr_factory_{this};
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_PERFORMANCE_CONTROLS_HIGH_EFFICIENCY_CHIP_VIEW_H_
diff --git a/chrome/browser/ui/views/performance_controls/high_efficiency_chip_view_unittest.cc b/chrome/browser/ui/views/performance_controls/high_efficiency_chip_view_unittest.cc
index 19b189e..f6cc1cc2 100644
--- a/chrome/browser/ui/views/performance_controls/high_efficiency_chip_view_unittest.cc
+++ b/chrome/browser/ui/views/performance_controls/high_efficiency_chip_view_unittest.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/views/performance_controls/high_efficiency_chip_view.h"
 
 #include "base/test/metrics/histogram_tester.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/performance_manager/test_support/test_user_performance_tuning_manager_environment.h"
 #include "chrome/browser/ui/browser_element_identifiers.h"
 #include "chrome/browser/ui/performance_controls/performance_controls_metrics.h"
@@ -17,6 +18,7 @@
 #include "chrome/browser/ui/views/performance_controls/high_efficiency_bubble_view.h"
 #include "components/performance_manager/public/features.h"
 #include "components/performance_manager/public/user_tuning/prefs.h"
+#include "components/prefs/pref_service.h"
 #include "components/prefs/testing_pref_service.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/mock_navigation_handle.h"
@@ -80,6 +82,12 @@
         ->UpdateAll();
   }
 
+  void SetHighEfficiencyModeEnabled(bool enabled) {
+    g_browser_process->local_state()->SetBoolean(
+        performance_manager::user_tuning::prefs::kHighEfficiencyModeEnabled,
+        enabled);
+  }
+
   PageActionIconView* GetPageActionIconView() {
     return browser_view()
         ->GetLocationBarView()
@@ -108,6 +116,7 @@
 // When the previous page has a tab discard state of true, when the icon is
 // updated it should be visible.
 TEST_F(HighEfficiencyChipViewTest, ShouldShowForDiscardedPage) {
+  SetHighEfficiencyModeEnabled(true);
   SetTabDiscardState(true);
 
   PageActionIconView* view = GetPageActionIconView();
@@ -115,9 +124,21 @@
   EXPECT_TRUE(view->GetVisible());
 }
 
+// If a discard is triggered when the user doesn't have high efficiency mode
+// enabled, we don't show the chip.
+TEST_F(HighEfficiencyChipViewTest, ShouldNotShowWhenPrefIsFalse) {
+  SetHighEfficiencyModeEnabled(false);
+  SetTabDiscardState(true);
+
+  PageActionIconView* view = GetPageActionIconView();
+
+  EXPECT_FALSE(view->GetVisible());
+}
+
 // When the previous page was not previously discarded, the icon should not be
 // visible.
 TEST_F(HighEfficiencyChipViewTest, ShouldNotShowForRegularPage) {
+  SetHighEfficiencyModeEnabled(true);
   SetTabDiscardState(false);
 
   PageActionIconView* view = GetPageActionIconView();
@@ -126,6 +147,7 @@
 
 // When the page action chip is clicked, the dialog should open.
 TEST_F(HighEfficiencyChipViewTest, ShouldOpenDialogOnClick) {
+  SetHighEfficiencyModeEnabled(true);
   SetTabDiscardState(true);
 
   PageActionIconView* view = GetPageActionIconView();
@@ -197,6 +219,7 @@
 // When the previous page was not previously discarded, the icon should not be
 // visible.
 TEST_F(HighEfficiencyChipViewTest, ShouldHideLabelAfterThreeTimes) {
+  SetHighEfficiencyModeEnabled(true);
   // Open the tab 3 times with the label being visible.
   for (int i = 0; i < 3; i++) {
     SetTabDiscardState(true);
diff --git a/chrome/browser/ui/views/permissions/chip_controller.cc b/chrome/browser/ui/views/permissions/chip_controller.cc
index 1956269..a52a54d 100644
--- a/chrome/browser/ui/views/permissions/chip_controller.cc
+++ b/chrome/browser/ui/views/permissions/chip_controller.cc
@@ -10,38 +10,19 @@
 #include "base/bind.h"
 #include "base/callback_forward.h"
 #include "base/check.h"
-#include "base/feature_list.h"
-#include "base/logging.h"
 #include "base/memory/raw_ptr.h"
-#include "base/time/time.h"
-#include "chrome/browser/ui/page_info/page_info_dialog.h"
 #include "chrome/browser/ui/views/content_setting_bubble_contents.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
-#include "chrome/browser/ui/views/omnibox/omnibox_view_views.h"
-#include "chrome/browser/ui/views/page_info/page_info_bubble_view.h"
 #include "chrome/browser/ui/views/permissions/permission_prompt_chip_model.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/permissions/features.h"
+
 #include "components/permissions/permission_prompt.h"
-#include "components/permissions/permission_request_manager.h"
-#include "components/permissions/permission_util.h"
-#include "content/public/browser/navigation_entry.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/button/button_controller.h"
-#include "ui/views/widget/widget.h"
-
-constexpr auto kExpandDuration = base::Milliseconds(350);
-constexpr auto kPromptCollapseDuration = base::Milliseconds(250);
-constexpr auto kConfirmationCollapseDuration = base::Milliseconds(75);
-constexpr auto kConfirmationDisplayDuration = base::Seconds(4);
-constexpr auto kDelayBeforeCollapsingChip = base::Seconds(12);
-
-// Abusive origins do not support expand animation, hence the dismiss timer
-// should be longer.
-constexpr auto kDelayBeforeCollapsingChipForAbusiveOrigins = base::Seconds(18);
 
 class BubbleButtonController : public views::ButtonController {
  public:
@@ -72,37 +53,13 @@
 ChipController::~ChipController() = default;
 
 void ChipController::OnPermissionRequestManagerDestructed() {
-  ResetPermissionPromptChip();
-  if (active_chip_permission_request_manager_.has_value()) {
-    active_chip_permission_request_manager_.value()->RemoveObserver(this);
-    active_chip_permission_request_manager_.reset();
-  }
-}
-
-void ChipController::OnBubbleRemoved() {
-  bool is_tab_hidden = active_chip_permission_request_manager_.value()
-                           ->GetWebContents()
-                           .GetVisibility() == content::Visibility::HIDDEN;
-  if (is_tab_hidden) {
-    ResetPermissionPromptChip();
-  }
-}
-
-void ChipController::OnRequestDecided(
-    permissions::PermissionAction permission_action) {
-  DCHECK(permission_prompt_model_);
-  RemoveBubbleObserverAndResetTimersAndChipCallbacks();
-  permission_prompt_model_->UpdateWithUserDecision(permission_action);
-
-  if (base::FeatureList::IsEnabled(permissions::features::kConfirmationChip)) {
-    HandleConfirmation(permission_action);
-  } else {
-    HideChip();
+  if (permission_prompt_model_) {
+    permission_prompt_model_->ResetDelegate();
   }
 }
 
 bool ChipController::IsBubbleShowing() {
-  return chip_ != nullptr && (GetBubbleWidget() != nullptr);
+  return chip_ != nullptr && GetPromptBubbleWidget() != nullptr;
 }
 
 bool ChipController::IsAnimating() const {
@@ -110,15 +67,9 @@
 }
 
 void ChipController::RestartTimersOnMouseHover() {
-  ResetTimers();
-  if (!permission_prompt_model_ ||
-      (active_chip_permission_request_manager_.has_value() &&
-       !active_chip_permission_request_manager_.value()
-            ->IsRequestInProgress()) ||
-      IsBubbleShowing() || IsAnimating()) {
+  if (!permission_prompt_model_ || IsBubbleShowing() || IsAnimating()) {
     return;
   }
-
   if (chip_->is_fully_collapsed()) {
     StartDismissTimer();
   } else {
@@ -127,7 +78,7 @@
 }
 
 void ChipController::OnWidgetDestroying(views::Widget* widget) {
-  DCHECK_EQ(GetBubbleWidget(), widget);
+  DCHECK_EQ(GetPromptBubbleWidget(), widget);
   ResetTimers();
   if (widget->closed_reason() == views::Widget::ClosedReason::kEscKeyPressed ||
       widget->closed_reason() ==
@@ -136,41 +87,39 @@
   }
 
   widget->RemoveObserver(this);
-  CollapsePrompt(/*allow_restart=*/false);
+
+  // If permission request is still active after the prompt was closed,
+  // collapse the chip.
+  CollapseChip(/*allow_restart=*/false);
 }
 
 void ChipController::ShowPermissionPrompt(
-    content::WebContents* web_contents,
     permissions::PermissionPrompt::Delegate* delegate) {
   DCHECK(delegate);
-  ResetChip();
-
+  ResetTimers();
   permission_prompt_model_ =
       std::make_unique<PermissionPromptChipModel>(delegate);
-  chip_shown_time_ = base::TimeTicks::Now();
-  AnnouncePermissionRequestForAccessibility(
-      permission_prompt_model_->GetAccessibilityChipText());
-  chip_->SetVisible(true);
-  if (active_chip_permission_request_manager_.has_value()) {
-    active_chip_permission_request_manager_.value()->RemoveObserver(this);
-  }
-  active_chip_permission_request_manager_ =
-      permissions::PermissionRequestManager::FromWebContents(web_contents);
 
-  active_chip_permission_request_manager_.value()->AddObserver(this);
-
-  SyncChipWithModel();
-
+  chip_->SetText(permission_prompt_model_->GetPermissionMessage());
+  chip_->SetTheme(permission_prompt_model_->GetChipTheme());
+  chip_->SetChipIcon(permission_prompt_model_->GetAllowedIcon());
   chip_->SetButtonController(std::make_unique<BubbleButtonController>(
       chip_.get(), this,
       std::make_unique<views::Button::DefaultButtonControllerDelegate>(
           chip_.get())));
   chip_->SetCallback(base::BindRepeating(&ChipController::OnChipButtonPressed,
                                          base::Unretained(this)));
-  chip_->ResetAnimation();
+  chip_shown_time_ = base::TimeTicks::Now();
+  chip_->SetVisible(true);
+
   ObservePromptBubble();
 
-  if (permission_prompt_model_->IsExpandAnimationAllowed()) {
+  AnnouncePermissionRequestForAccessibility(l10n_util::GetStringUTF16(
+      IDS_PERMISSIONS_REQUESTED_SCREENREADER_ANNOUNCEMENT));
+
+  if (permission_prompt_model_ && permission_prompt_model_->ShouldExpand() &&
+      (permission_prompt_model_->ShouldBubbleStartOpen() ||
+       (!permission_prompt_model_->WasRequestAlreadyDisplayed()))) {
     AnimateExpand(base::BindRepeating(&ChipController::OnExpandAnimationEnded,
                                       base::Unretained(this)));
   } else {
@@ -178,98 +127,28 @@
   }
 }
 
-void ChipController::ResetChip() {
-  // This is a placeholder method, additional chip functionality will be added
-  // and will be reset here.
-  ResetPermissionPromptChip();
+void ChipController::FinalizeChip() {
+  FinalizePermissionPromptChip();
 }
 
-void ChipController::ResetChipCallbacks() {
-  chip_->SetCallback(
-      base::BindRepeating(&ChipController::DoNothing, base::Unretained(this)));
-  chip_->SetCollapseEndedCallback(
-      base::BindRepeating(&ChipController::DoNothing, base::Unretained(this)));
-  chip_->SetExpandAnimationEndedCallback(
-      base::BindRepeating(&ChipController::DoNothing, base::Unretained(this)));
-  chip_->SetVisibilityChangedCallback(
-      base::BindRepeating(&ChipController::DoNothing, base::Unretained(this)));
-}
+void ChipController::FinalizePermissionPromptChip() {
+  chip_->ResetAnimation();
+  chip_->SetChipIcon(gfx::kNoneIcon);
+  chip_->SetVisible(false);
 
-void ChipController::RemoveBubbleObserverAndResetTimersAndChipCallbacks() {
-  views::Widget* const bubble_widget = GetBubbleWidget();
+  views::Widget* const bubble_widget = GetPromptBubbleWidget();
   if (bubble_widget) {
     bubble_widget->RemoveObserver(this);
     bubble_widget->Close();
   }
 
   ResetTimers();
-  ResetChipCallbacks();
-}
+  permission_prompt_model_.reset();
 
-void ChipController::ResetPermissionPromptChip() {
-  RemoveBubbleObserverAndResetTimersAndChipCallbacks();
-
-  if (permission_prompt_model_) {
-    // permission_request_manager_ is empty if the PermissionRequestManager
-    // instance has destructed, which triggers the observer method
-    // OnPermissionRequestManagerDestructed() implemented by this controller.
-    if (active_chip_permission_request_manager_.has_value()) {
-      active_chip_permission_request_manager_.value()->RemoveObserver(this);
-
-      // When the user starts typing into the location bar we need to inform the
-      // PermissionRequestManager to update the PermissionPrompt reference it
-      // is holding. The typical update is for it to destruct the
-      // PermissionPrompt instance and not to hold any  PermissionPrompt
-      // instance during the edit time.
-      if (GetLocationBarView()->IsEditingOrEmpty() &&
-          active_chip_permission_request_manager_.value()
-              ->IsRequestInProgress()) {
-        active_chip_permission_request_manager_.value()->RecreateView();
-      }
-    }
-    permission_prompt_model_.reset();
+  LocationBarView* lbv = GetLocationBarView();
+  if (lbv) {
+    lbv->InvalidateLayout();
   }
-
-  HideChip();
-}
-
-void ChipController::ShowPageInfoDialog() {
-  content::WebContents* contents = GetLocationBarView()->GetWebContents();
-  if (!contents)
-    return;
-
-  content::NavigationEntry* entry = contents->GetController().GetVisibleEntry();
-  if (!entry || entry->IsInitialEntry())
-    return;
-
-  // prevent chip from collapsing while prompt bubble is open
-  ResetTimers();
-
-  auto initialized_callback =
-      GetPageInfoDialogCreatedCallbackForTesting()
-          ? std::move(GetPageInfoDialogCreatedCallbackForTesting())
-          : base::DoNothing();
-
-  views::BubbleDialogDelegateView* bubble =
-      PageInfoBubbleView::CreatePageInfoBubble(
-          chip_, gfx::Rect(), chip_->GetWidget()->GetNativeWindow(), contents,
-          entry->GetVirtualURL(), std::move(initialized_callback),
-          base::BindOnce(&ChipController::OnPageInfoBubbleClosed,
-                         base::Unretained(this)));
-  bubble->GetWidget()->Show();
-  bubble_tracker.SetView(bubble);
-}
-
-void ChipController::OnPageInfoBubbleClosed(
-    views::Widget::ClosedReason closed_reason,
-    bool reload_prompt) {
-  HideChip();
-}
-
-void ChipController::CollapseConfirmation() {
-  chip_->SetCollapseEndedCallback(
-      base::BindRepeating(&ChipController::HideChip, base::Unretained(this)));
-  chip_->AnimateCollapse(kConfirmationCollapseDuration);
 }
 
 bool ChipController::should_start_open_for_testing() {
@@ -287,24 +166,10 @@
   chip_->SetExpandAnimationEndedCallback(
       std::move(expand_anmiation_ended_callback));
   chip_->ResetAnimation();
-  chip_->AnimateExpand(kExpandDuration);
+  chip_->AnimateExpand();
   chip_->SetVisible(true);
 }
 
-void ChipController::HandleConfirmation(
-    permissions::PermissionAction user_decision) {
-  SyncChipWithModel();
-  if (permission_prompt_model_->CanDisplayConfirmation()) {
-    chip_->AnimateExpand(kExpandDuration);
-    chip_->SetCallback(base::BindRepeating(&ChipController::ShowPageInfoDialog,
-                                           base::Unretained(this)));
-    collapse_timer_.Start(FROM_HERE, kConfirmationDisplayDuration, this,
-                          &ChipController::CollapseConfirmation);
-  } else {
-    ResetChip();
-  }
-}
-
 void ChipController::AnnouncePermissionRequestForAccessibility(
     const std::u16string& text) {
 #if BUILDFLAG(IS_MAC)
@@ -316,24 +181,19 @@
 #endif
 }
 
-void ChipController::CollapsePrompt(bool allow_restart) {
+void ChipController::CollapseChip(bool allow_restart) {
   if (allow_restart && chip_->IsMouseHovered()) {
     StartCollapseTimer();
   } else {
-    chip_->AnimateCollapse(kPromptCollapseDuration);
-    permission_prompt_model_->UpdateAutoCollapsePromptChipState(true);
-    chip_->SetChipIcon(permission_prompt_model_->GetIcon());
-    chip_->SetTheme(permission_prompt_model_->GetChipTheme());
-
+    AnimateCollapse();
+    chip_->SetChipIcon(permission_prompt_model_
+                           ? permission_prompt_model_->GetBlockedIcon()
+                           : gfx::kNoneIcon);
+    chip_->SetTheme(OmniboxChipTheme::kLowVisibility);
     StartDismissTimer();
   }
 }
 
-void ChipController::HideChip() {
-  chip_->SetVisible(false);
-  GetLocationBarView()->InvalidateLayout();
-}
-
 void ChipController::OpenPermissionPromptBubble() {
   DCHECK(!IsBubbleShowing());
   if (!permission_prompt_model_ ||
@@ -352,7 +212,7 @@
             browser_,
             permission_prompt_model_->GetDelegate().value()->GetWeakPtr(),
             chip_shown_time_, PermissionPromptStyle::kChip);
-    bubble_tracker.SetView(prompt_bubble);
+    prompt_bubble_tracker_.SetView(prompt_bubble);
     prompt_bubble->Show();
   } else if (permission_prompt_model_->GetPromptStyle() ==
              PermissionPromptStyle::kQuietChip) {
@@ -373,17 +233,17 @@
       views::Widget* bubble_widget =
           views::BubbleDialogDelegateView::CreateBubble(quiet_request_bubble);
       quiet_request_bubble->set_close_on_deactivate(false);
-      bubble_tracker.SetView(quiet_request_bubble);
+      prompt_bubble_tracker_.SetView(quiet_request_bubble);
       bubble_widget->Show();
     }
   }
   chip_->SetVisibilityChangedCallback(base::BindRepeating(
       &ChipController::OnChipVisibilityChanged, base::Unretained(this)));
 
-  // It is possible that a Chip got reset while the permission prompt
+  // It is possible that a Chip get finalized while the permission prompt
   // bubble was displayed.
   if (permission_prompt_model_ && IsBubbleShowing()) {
-    GetBubbleWidget()->AddObserver(this);
+    GetPromptBubbleWidget()->AddObserver(this);
     permission_prompt_model_->GetDelegate().value()->SetBubbleShown();
   }
 }
@@ -391,7 +251,7 @@
 void ChipController::ClosePermissionPromptBubbleWithReason(
     views::Widget::ClosedReason reason) {
   DCHECK(IsBubbleShowing());
-  GetBubbleWidget()->CloseWithReason(reason);
+  GetPromptBubbleWidget()->CloseWithReason(reason);
 }
 
 void ChipController::RecordChipButtonPressed(const char* recordKey) {
@@ -400,7 +260,7 @@
 }
 
 void ChipController::ObservePromptBubble() {
-  views::Widget* promptBubbleWidget = GetBubbleWidget();
+  views::Widget* promptBubbleWidget = GetPromptBubbleWidget();
   if (promptBubbleWidget) {
     promptBubbleWidget->AddObserver(this);
   }
@@ -471,7 +331,7 @@
 }
 
 void ChipController::OnChipVisibilityChanged() {
-  auto* prompt_bubble = GetBubbleWidget();
+  auto* prompt_bubble = GetPromptBubbleWidget();
   if (!chip_->GetVisible() && prompt_bubble) {
     // In case if the prompt bubble isn't closed on focus loss, manually close
     // it when chip is hidden.
@@ -479,16 +339,11 @@
   }
 }
 
-void ChipController::SyncChipWithModel() {
-  chip_->SetChipIcon(permission_prompt_model_->GetIcon());
-  chip_->SetText(permission_prompt_model_->GetChipText());
-  chip_->SetTheme(permission_prompt_model_->GetChipTheme());
-}
-
 void ChipController::StartCollapseTimer() {
+  constexpr auto kDelayBeforeCollapsingChip = base::Seconds(12);
   collapse_timer_.Start(
       FROM_HERE, kDelayBeforeCollapsingChip,
-      base::BindOnce(&ChipController::CollapsePrompt, base::Unretained(this),
+      base::BindOnce(&ChipController::CollapseChip, base::Unretained(this),
                      /*allow_restart=*/true));
 }
 
@@ -505,8 +360,10 @@
                            &ChipController::OnPromptExpired);
     }
   } else {
-    dismiss_timer_.Start(FROM_HERE, kDelayBeforeCollapsingChipForAbusiveOrigins,
-                         this, &ChipController::OnPromptExpired);
+    // Abusive origins do not support expand animation, hence the dismiss timer
+    // should be longer.
+    dismiss_timer_.Start(FROM_HERE, base::Seconds(18), this,
+                         &ChipController::OnPromptExpired);
   }
 }
 
@@ -520,6 +377,8 @@
   return browser_view ? browser_view->GetLocationBarView() : nullptr;
 }
 
-views::Widget* ChipController::GetBubbleWidget() {
-  return bubble_tracker.view() ? bubble_tracker.view()->GetWidget() : nullptr;
+views::Widget* ChipController::GetPromptBubbleWidget() {
+  return prompt_bubble_tracker_.view()
+             ? prompt_bubble_tracker_.view()->GetWidget()
+             : nullptr;
 }
diff --git a/chrome/browser/ui/views/permissions/chip_controller.h b/chrome/browser/ui/views/permissions/chip_controller.h
index 3a2a3e7..2933caa1 100644
--- a/chrome/browser/ui/views/permissions/chip_controller.h
+++ b/chrome/browser/ui/views/permissions/chip_controller.h
@@ -11,7 +11,6 @@
 #include "chrome/browser/ui/views/location_bar/omnibox_chip_button.h"
 #include "components/permissions/permission_prompt.h"
 #include "components/permissions/permission_request_manager.h"
-#include "components/permissions/permission_util.h"
 #include "ui/views/widget/widget_observer.h"
 
 class PermissionPromptChipModel;
@@ -44,8 +43,6 @@
 
   // PermissionRequestManager::Observer
   void OnPermissionRequestManagerDestructed() override;
-  void OnBubbleRemoved() override;
-  void OnRequestDecided(permissions::PermissionAction permissions) override;
 
   // BubbleOwnerDelegate
   bool IsBubbleShowing() override;
@@ -56,18 +53,16 @@
   void OnWidgetDestroying(views::Widget* widget) override;
 
   // Displays a permission prompt using the chip UI.
-  void ShowPermissionPrompt(content::WebContents* web_contents,
-                            permissions::PermissionPrompt::Delegate* delegate);
+  void ShowPermissionPrompt(permissions::PermissionPrompt::Delegate* delegate);
 
   // Chip View
   OmniboxChipButton* chip() { return chip_; }
 
-  // Hide and clean up the entire chip and associated observers, callback timers
-  // and callbacks
-  void ResetChip();
+  // Hide and clean up the entire chip
+  void FinalizeChip();
 
   // Hide and clean up permission parts of the chip
-  void ResetPermissionPromptChip();
+  void FinalizePermissionPromptChip();
 
   // State
   bool IsPermissionPromptChipVisible() {
@@ -77,7 +72,7 @@
   // Update Browser
   void UpdateBrowser(Browser* browser) { browser_ = browser; }
 
-  views::Widget* GetBubbleWidget();
+  views::Widget* GetPromptBubbleWidget();
 
   // Testing helpers
   bool should_start_open_for_testing();
@@ -88,11 +83,6 @@
     return collapse_timer_.IsRunning();
   }
 
-  void fire_collapse_timer_for_testing() {
-    CHECK_IS_TEST();
-    collapse_timer_.FireNow();
-  }
-
   bool is_dismiss_timer_running_for_testing() {
     CHECK_IS_TEST();
     return dismiss_timer_.IsRunning();
@@ -106,24 +96,18 @@
 
   views::View* get_prompt_bubble_view_for_testing() {
     CHECK_IS_TEST();
-    return bubble_tracker.view();
+    return prompt_bubble_tracker_.view();
   }
 
  private:
   // Animations
   void AnimateExpand(
       base::RepeatingCallback<void()> expand_anmiation_ended_callback);
-
-  // Confirmation chip
-  void HandleConfirmation(permissions::PermissionAction permission_action);
-  void CollapseConfirmation();
+  void AnimateCollapse() { chip_->AnimateCollapse(); }
 
   // Permission prompt chip functionality
   void AnnouncePermissionRequestForAccessibility(const std::u16string& text);
-  void CollapsePrompt(bool allow_restart);
-
-  // Hides the chip and invalidates the layout
-  void HideChip();
+  void CollapseChip(bool allow_restart);
 
   // Permission prompt bubble functiontionality
   void OpenPermissionPromptBubble();
@@ -141,26 +125,6 @@
   void OnExpandAnimationEnded();
   void OnChipVisibilityChanged();
 
-  // Updates chip icon, text and theme with model
-  void SyncChipWithModel();
-
-  // NOP method used for callbacks
-  void DoNothing() {}
-
-  // Opens the Page Info Dialog
-  void ShowPageInfoDialog();
-
-  // Actions executed when the user closes the page info dialog
-  void OnPageInfoBubbleClosed(views::Widget::ClosedReason closed_reason,
-                              bool reload_prompt);
-
-  // Resets all chip callbacks such as click callback, but also
-  // animation-related callbacks.
-  void ResetChipCallbacks();
-
-  // Clean up utility
-  void RemoveBubbleObserverAndResetTimersAndChipCallbacks();
-
   // Timer functionality
   void StartCollapseTimer();
   void StartDismissTimer();
@@ -187,10 +151,7 @@
   // The model of a permission prompt if one is present.
   std::unique_ptr<PermissionPromptChipModel> permission_prompt_model_;
 
-  absl::optional<permissions::PermissionRequestManager*>
-      active_chip_permission_request_manager_;
-
-  views::ViewTracker bubble_tracker;
+  views::ViewTracker prompt_bubble_tracker_;
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_PERMISSIONS_CHIP_CONTROLLER_H_
diff --git a/chrome/browser/ui/views/permissions/permission_bubble_interactive_uitest.cc b/chrome/browser/ui/views/permissions/permission_bubble_interactive_uitest.cc
index fbbdbce..c4e13ac 100644
--- a/chrome/browser/ui/views/permissions/permission_bubble_interactive_uitest.cc
+++ b/chrome/browser/ui/views/permissions/permission_bubble_interactive_uitest.cc
@@ -27,11 +27,6 @@
 #include "ui/views/test/button_test_api.h"
 #include "ui/views/test/widget_test.h"
 
-enum ChipFeatureConfig {
-  REQUEST_CHIP,
-  REQUEST_CHIP_LOCATION_BAR_ICON_OVERRIDE
-};
-
 class PermissionBubbleInteractiveUITest : public InProcessBrowserTest {
  public:
   PermissionBubbleInteractiveUITest() {
diff --git a/chrome/browser/ui/views/permissions/permission_chip_interactive_test.cc b/chrome/browser/ui/views/permissions/permission_chip_interactive_test.cc
index 40f129437..9470591 100644
--- a/chrome/browser/ui/views/permissions/permission_chip_interactive_test.cc
+++ b/chrome/browser/ui/views/permissions/permission_chip_interactive_test.cc
@@ -1,6 +1,7 @@
 // Copyright 2021 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
+
 #include "base/ranges/algorithm.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "chrome/browser/permissions/quiet_notification_permission_ui_config.h"
@@ -14,7 +15,6 @@
 #include "chrome/browser/ui/views/content_setting_bubble_contents.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/location_bar/omnibox_chip_theme.h"
-#include "chrome/browser/ui/views/page_info/page_info_bubble_view.h"
 #include "chrome/browser/ui/views/permissions/chip_controller.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
 #include "chrome/common/chrome_features.h"
@@ -27,7 +27,6 @@
 #include "components/permissions/permission_util.h"
 #include "components/permissions/request_type.h"
 #include "components/permissions/test/mock_permission_request.h"
-#include "components/strings/grit/components_strings.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/test/browser_test.h"
 #include "net/dns/mock_host_resolver.h"
@@ -37,14 +36,6 @@
 #include "ui/views/test/button_test_api.h"
 
 namespace {
-
-enum ChipFeatureConfig {
-  REQUEST_CHIP,
-  REQUEST_CHIP_LOCATION_BAR_ICON_OVERRIDE,
-  REQUEST_AND_CONFIRMATION_CHIP,
-  REQUEST_AND_CONFIRMATION_CHIP_LOCATION_BAR_ICON_OVERRIDE
-};
-
 // Test implementation of PermissionUiSelector that always returns a canned
 // decision.
 class TestQuietNotificationPermissionUiSelector
@@ -129,8 +120,7 @@
   void ClickOnChip(OmniboxChipButton* chip) {
     ASSERT_TRUE(chip != nullptr);
     ASSERT_TRUE(chip->GetVisible());
-    ASSERT_FALSE(GetChipController()->GetBubbleWidget());
-
+    ASSERT_FALSE(GetChipController()->GetPromptBubbleWidget());
     views::test::ButtonTestApi(chip).NotifyClick(
         ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
                        ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0));
@@ -228,272 +218,6 @@
       permissions::PermissionPromptDisposition::LOCATION_BAR_LEFT_CHIP);
 }
 
-class LocationBarIconOverrideTest
-    : public PermissionChipInteractiveTest,
-      public ::testing::WithParamInterface<ChipFeatureConfig> {
- public:
-  LocationBarIconOverrideTest() {
-    std::vector<base::Feature> disabled_features = {
-        permissions::features::kPermissionChipGestureSensitive,
-        permissions::features::kPermissionChipRequestTypeSensitive};
-
-    switch (GetParam()) {
-      case REQUEST_CHIP:
-        scoped_feature_list_.InitWithFeatures(
-            {permissions::features::kPermissionChip}, disabled_features);
-        break;
-      case REQUEST_CHIP_LOCATION_BAR_ICON_OVERRIDE:
-        scoped_feature_list_.InitWithFeatures(
-            {permissions::features::kPermissionChip,
-             permissions::features::kChipLocationBarIconOverride},
-            disabled_features);
-        break;
-      case REQUEST_AND_CONFIRMATION_CHIP:
-        scoped_feature_list_.InitWithFeatures(
-            {permissions::features::kPermissionChip,
-             permissions::features::kConfirmationChip},
-            disabled_features);
-        break;
-      case REQUEST_AND_CONFIRMATION_CHIP_LOCATION_BAR_ICON_OVERRIDE:
-        scoped_feature_list_.InitWithFeatures(
-            {permissions::features::kPermissionChip,
-             permissions::features::kConfirmationChip,
-             permissions::features::kChipLocationBarIconOverride},
-            disabled_features);
-        break;
-    }
-  }
-
-  bool IsLocationIconVisible() {
-    return BrowserView::GetBrowserViewForBrowser(browser())
-        ->GetLocationBarView()
-        ->location_icon_view()
-        ->GetVisible();
-  }
-
-  bool IsTestWithOverridenLocationBarIcon() {
-    return base::FeatureList::IsEnabled(
-        permissions::features::kChipLocationBarIconOverride);
-  }
-
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-IN_PROC_BROWSER_TEST_P(LocationBarIconOverrideTest,
-                       OverrideLocationBarIconDuringChipOnlyForOverrideFlags) {
-  // Initially the location bar icon should be visible for any feature flag
-  // configuration
-  EXPECT_TRUE(IsLocationIconVisible());
-
-  RequestPermission(permissions::RequestType::kGeolocation);
-
-  // After a request, a chip is shown, which should override the lock icon for
-  // feature flags featuring this.
-  if (IsTestWithOverridenLocationBarIcon()) {
-    EXPECT_FALSE(IsLocationIconVisible());
-  } else {
-    EXPECT_TRUE(IsLocationIconVisible());
-  }
-
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(
-      test_api_->manager()->current_request_prompt_disposition_for_testing(),
-      permissions::PermissionPromptDisposition::LOCATION_BAR_LEFT_CHIP);
-
-  test_api_->manager()->Accept();
-
-  base::RunLoop().RunUntilIdle();
-
-  // Force synchronous update of layout values. In the actual code,
-  // InvalidateLayout() is sufficient, but leaves stale visibility values for
-  // testing.
-  BrowserView::GetBrowserViewForBrowser(browser())
-      ->GetLocationBarView()
-      ->Layout();
-
-  if (base::FeatureList::IsEnabled(permissions::features::kConfirmationChip)) {
-    // Test with confirmation chip.
-    // Verify chip is still visible and has the confirmation text
-    EXPECT_TRUE(GetChip()->GetVisible());
-    EXPECT_TRUE(GetChip()->GetText() ==
-                l10n_util::GetStringUTF16(
-                    IDS_PERMISSIONS_PERMISSION_ALLOWED_CONFIRMATION));
-
-    if (IsTestWithOverridenLocationBarIcon()) {
-      EXPECT_FALSE(IsLocationIconVisible());
-    } else {
-      EXPECT_TRUE(IsLocationIconVisible());
-    }
-
-    // Check collapse timer is running and fast forward fire callback. Then,
-    // fast forward animation to trigger callback and wait until it completes.
-    EXPECT_TRUE(GetChipController()->is_collapse_timer_running_for_testing());
-    GetChipController()->fire_collapse_timer_for_testing();
-    GetChip()->animation_for_testing()->End();
-    base::RunLoop().RunUntilIdle();
-
-    // Force synchronous update of layout values. In the actual code,
-    // InvalidateLayout() is sufficient, but leaves stale visibility values for
-    // testing.
-    BrowserView::GetBrowserViewForBrowser(browser())
-        ->GetLocationBarView()
-        ->Layout();
-  }
-
-  // With any feature flag configuration, we have to ensure that the location
-  // bar icon is visible after the chip collapsed.
-  EXPECT_FALSE(GetChip()->GetVisible());
-  EXPECT_TRUE(IsLocationIconVisible());
-}
-
-INSTANTIATE_TEST_SUITE_P(
-    All,
-    LocationBarIconOverrideTest,
-    ::testing::Values(
-        REQUEST_CHIP,
-        REQUEST_CHIP_LOCATION_BAR_ICON_OVERRIDE,
-        REQUEST_AND_CONFIRMATION_CHIP,
-        REQUEST_AND_CONFIRMATION_CHIP_LOCATION_BAR_ICON_OVERRIDE));
-
-class ConfirmationChipEnabledInteractiveTest
-    : public PermissionChipInteractiveTest,
-      public ::testing::WithParamInterface<ChipFeatureConfig> {
- public:
-  ConfirmationChipEnabledInteractiveTest() {
-    std::vector<base::Feature> disabled_features = {
-        permissions::features::kPermissionChipGestureSensitive,
-        permissions::features::kPermissionChipRequestTypeSensitive};
-    switch (GetParam()) {
-      case REQUEST_AND_CONFIRMATION_CHIP:
-        scoped_feature_list_.InitWithFeatures(
-            {permissions::features::kPermissionChip,
-             permissions::features::kConfirmationChip},
-            disabled_features);
-        break;
-      case REQUEST_AND_CONFIRMATION_CHIP_LOCATION_BAR_ICON_OVERRIDE:
-        scoped_feature_list_.InitWithFeatures(
-            {permissions::features::kPermissionChip,
-             permissions::features::kConfirmationChip,
-             permissions::features::kChipLocationBarIconOverride},
-            disabled_features);
-        break;
-      default:
-        NOTREACHED();
-    }
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-IN_PROC_BROWSER_TEST_P(ConfirmationChipEnabledInteractiveTest,
-                       ShouldDisplayAllowAndDenyConfirmationCorrectly) {
-  RequestPermission(permissions::RequestType::kGeolocation);
-  base::RunLoop().RunUntilIdle();
-
-  // Chip should be visible and show geolocation request
-  EXPECT_TRUE(GetChip()->GetVisible());
-  EXPECT_TRUE(GetChip()->GetText() ==
-              l10n_util::GetStringUTF16(IDS_GEOLOCATION_PERMISSION_CHIP));
-
-  test_api_->manager()->Accept();
-
-  // Confirmation chip should be visible
-  EXPECT_TRUE(GetChip()->GetVisible());
-  EXPECT_TRUE(GetChip()->GetText() ==
-              l10n_util::GetStringUTF16(
-                  IDS_PERMISSIONS_PERMISSION_ALLOWED_CONFIRMATION));
-  EXPECT_EQ(GetChip()->get_theme_for_testing(),
-            OmniboxChipTheme::kNormalVisibility);
-
-  // Check collapse timer is running and fast forward fire callback. Then,
-  // fast forward animation to trigger callback and wait until it completes.
-  EXPECT_TRUE(GetChipController()->is_collapse_timer_running_for_testing());
-  GetChipController()->fire_collapse_timer_for_testing();
-  GetChip()->animation_for_testing()->End();
-  base::RunLoop().RunUntilIdle();
-
-  // Chip should no longer be visible.
-  EXPECT_FALSE(GetChip()->GetVisible());
-
-  // Request second permission
-  RequestPermission(permissions::RequestType::kNotifications);
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_TRUE(GetChip()->GetVisible());
-  EXPECT_EQ(GetChip()->GetText(),
-            l10n_util::GetStringUTF16(IDS_NOTIFICATION_PERMISSIONS_CHIP));
-
-  test_api_->manager()->Deny();
-
-  // After deny, the deny confirmation should be displayed
-  EXPECT_TRUE(GetChip()->GetVisible());
-  EXPECT_EQ(GetChip()->GetText(),
-            l10n_util::GetStringUTF16(
-                IDS_PERMISSIONS_PERMISSION_NOT_ALLOWED_CONFIRMATION));
-  EXPECT_EQ(GetChip()->get_theme_for_testing(),
-            OmniboxChipTheme::kLowVisibility);
-}
-
-IN_PROC_BROWSER_TEST_P(ConfirmationChipEnabledInteractiveTest,
-                       IncomingRequestShouldOverrideConfirmation) {
-  RequestPermission(permissions::RequestType::kGeolocation);
-  base::RunLoop().RunUntilIdle();
-
-  test_api_->manager()->Accept();
-
-  RequestPermission(permissions::RequestType::kNotifications);
-  base::RunLoop().RunUntilIdle();
-
-  // Since a new request came in, the new request should be displayed
-  EXPECT_TRUE(GetChip()->GetVisible());
-  EXPECT_EQ(GetChip()->GetText(),
-            l10n_util::GetStringUTF16(IDS_NOTIFICATION_PERMISSIONS_CHIP));
-
-  test_api_->manager()->Deny();
-
-  // After the deny, the deny confirmation should be displayed
-  EXPECT_TRUE(GetChip()->GetVisible());
-  EXPECT_EQ(GetChip()->GetText(),
-            l10n_util::GetStringUTF16(
-                IDS_PERMISSIONS_PERMISSION_NOT_ALLOWED_CONFIRMATION));
-}
-
-IN_PROC_BROWSER_TEST_P(ConfirmationChipEnabledInteractiveTest,
-                       ClickOnConfirmationChipShouldOpenPageInfoDialog) {
-  RequestPermission(permissions::RequestType::kGeolocation);
-  base::RunLoop().RunUntilIdle();
-
-  test_api_->manager()->Accept();
-
-  ClickOnChip(GetChip());
-
-  base::RunLoop().RunUntilIdle();
-  views::View* bubble_view =
-      GetChipController()->get_prompt_bubble_view_for_testing();
-  PageInfoBubbleView* page_info_bubble =
-      static_cast<PageInfoBubbleView*>(bubble_view);
-  ASSERT_NE(page_info_bubble, nullptr);
-
-  // Ensure closing the bubble works, and that this will start the collapse
-  // animation of the chip.
-  page_info_bubble->CloseBubble();
-
-  // Fast forward animation to trigger callback and wait until it completes.
-  GetChip()->animation_for_testing()->End();
-  base::RunLoop().RunUntilIdle();
-
-  ASSERT_FALSE(GetChip()->GetVisible());
-}
-
-INSTANTIATE_TEST_SUITE_P(
-    All,
-    ConfirmationChipEnabledInteractiveTest,
-    ::testing::Values(
-        REQUEST_AND_CONFIRMATION_CHIP,
-        REQUEST_AND_CONFIRMATION_CHIP_LOCATION_BAR_ICON_OVERRIDE));
-
 class ChipGestureSensitiveEnabledInteractiveTest
     : public PermissionChipInteractiveTest {
  public:
@@ -505,6 +229,7 @@
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
 };
+
 IN_PROC_BROWSER_TEST_F(ChipGestureSensitiveEnabledInteractiveTest,
                        ChipAutoPopupBubbleEnabled) {
   ASSERT_TRUE(base::FeatureList::IsEnabled(
diff --git a/chrome/browser/ui/views/permissions/permission_prompt_bubble.cc b/chrome/browser/ui/views/permissions/permission_prompt_bubble.cc
index 110e567..6c5f6c3 100644
--- a/chrome/browser/ui/views/permissions/permission_prompt_bubble.cc
+++ b/chrome/browser/ui/views/permissions/permission_prompt_bubble.cc
@@ -43,13 +43,16 @@
   prompt_bubble_ = nullptr;
 }
 
-bool PermissionPromptBubble::UpdateAnchor() {
+void PermissionPromptBubble::UpdateAnchor() {
   bool was_browser_changed = UpdateBrowser();
+  LocationBarView* lbv = GetLocationBarView();
+  DCHECK(!lbv->chip_controller() ||
+         !lbv->chip_controller()->IsPermissionPromptChipVisible());
   // TODO(crbug.com/1175231): Investigate why prompt_bubble_ can be null
   // here. Early return is preventing the crash from happening but we still
   // don't know the reason why it is null here and cannot reproduce it.
   if (!prompt_bubble_)
-    return true;
+    return;
 
   // If |browser_| changed, recreate bubble for correct browser.
   if (was_browser_changed) {
@@ -58,7 +61,6 @@
   } else {
     prompt_bubble_->UpdateAnchorPosition();
   }
-  return true;
 }
 
 permissions::PermissionPromptDisposition
diff --git a/chrome/browser/ui/views/permissions/permission_prompt_bubble.h b/chrome/browser/ui/views/permissions/permission_prompt_bubble.h
index a7419da..1e3c757 100644
--- a/chrome/browser/ui/views/permissions/permission_prompt_bubble.h
+++ b/chrome/browser/ui/views/permissions/permission_prompt_bubble.h
@@ -31,7 +31,7 @@
   void OnWidgetDestroying(views::Widget* widget) override;
 
   // permissions::PermissionPrompt:
-  bool UpdateAnchor() override;
+  void UpdateAnchor() override;
   permissions::PermissionPromptDisposition GetPromptDisposition()
       const override;
 
diff --git a/chrome/browser/ui/views/permissions/permission_prompt_chip.cc b/chrome/browser/ui/views/permissions/permission_prompt_chip.cc
index b62663c..dbd13f57 100644
--- a/chrome/browser/ui/views/permissions/permission_prompt_chip.cc
+++ b/chrome/browser/ui/views/permissions/permission_prompt_chip.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/views/permissions/permission_prompt_chip.h"
 #include <algorithm>
 #include <memory>
+
 #include "base/bind.h"
 #include "base/containers/contains.h"
 #include "base/metrics/histogram_functions.h"
@@ -28,32 +29,35 @@
     : PermissionPromptDesktop(browser, web_contents, delegate),
       delegate_(delegate) {
   DCHECK(delegate_);
+
   LocationBarView* lbv = GetLocationBarView();
   if (!lbv->chip_controller()->chip()) {
     lbv->CreateChip();
   }
 
   chip_controller_ = lbv->chip_controller();
-  chip_controller_->ShowPermissionPrompt(web_contents, delegate);
+  chip_controller_->ShowPermissionPrompt(delegate_);
 }
 
-PermissionPromptChip::~PermissionPromptChip() = default;
+PermissionPromptChip::~PermissionPromptChip() {
+  chip_controller_->FinalizePermissionPromptChip();
+}
 
-bool PermissionPromptChip::UpdateAnchor() {
+void PermissionPromptChip::UpdateAnchor() {
   UpdateBrowser();
 
   LocationBarView* lbv = GetLocationBarView();
   const bool is_location_bar_drawn =
       lbv && lbv->IsDrawn() && !lbv->GetWidget()->IsFullscreen();
+
   if (chip_controller_->IsPermissionPromptChipVisible() &&
       !is_location_bar_drawn) {
-    chip_controller_->ResetPermissionPromptChip();
+    chip_controller_->FinalizePermissionPromptChip();
     if (delegate_) {
       chip_controller_->UpdateBrowser(browser());
-      return false;
+      delegate_->RecreateView();
     }
   }
-  return true;
 }
 
 permissions::PermissionPromptDisposition
@@ -80,6 +84,6 @@
 
   return chip_controller_->IsPermissionPromptChipVisible() &&
                  lbv->chip_controller()->IsBubbleShowing()
-             ? lbv->chip_controller()->GetBubbleWidget()
+             ? lbv->chip_controller()->GetPromptBubbleWidget()
              : nullptr;
 }
diff --git a/chrome/browser/ui/views/permissions/permission_prompt_chip.h b/chrome/browser/ui/views/permissions/permission_prompt_chip.h
index 533e5297..83e6379 100644
--- a/chrome/browser/ui/views/permissions/permission_prompt_chip.h
+++ b/chrome/browser/ui/views/permissions/permission_prompt_chip.h
@@ -35,7 +35,7 @@
   PermissionPromptChip& operator=(const PermissionPromptChip&) = delete;
 
   // PermissionPrompt:
-  bool UpdateAnchor() override;
+  void UpdateAnchor() override;
   permissions::PermissionPromptDisposition GetPromptDisposition()
       const override;
 
diff --git a/chrome/browser/ui/views/permissions/permission_prompt_chip_model.cc b/chrome/browser/ui/views/permissions/permission_prompt_chip_model.cc
index fd0f149..0740a483 100644
--- a/chrome/browser/ui/views/permissions/permission_prompt_chip_model.cc
+++ b/chrome/browser/ui/views/permissions/permission_prompt_chip_model.cc
@@ -5,11 +5,6 @@
 #include "chrome/browser/ui/views/permissions/permission_prompt_chip_model.h"
 #include "base/check.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/ui/views/location_bar/omnibox_chip_theme.h"
-#include "components/permissions/permission_actions_history.h"
-#include "components/permissions/permission_request_manager.h"
-#include "components/permissions/permission_util.h"
-#include "components/permissions/request_type.h"
 #include "components/strings/grit/components_strings.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/vector_icon_types.h"
@@ -46,10 +41,9 @@
 std::u16string GetQuietPermissionMessage(
     permissions::PermissionPrompt::Delegate* delegate) {
   DCHECK(delegate);
-  auto quiet_request_text = delegate->Requests()[0]->GetRequestChipText(
-      permissions::PermissionRequest::QUIET_REQUEST);
-  DCHECK(quiet_request_text.has_value());
-  return quiet_request_text.value();
+  DCHECK(delegate->Requests()[0]->GetQuietChipText().has_value());
+
+  return delegate->Requests()[0]->GetQuietChipText().value();
 }
 
 std::u16string GetLoudPermissionMessage(
@@ -59,10 +53,7 @@
   auto requests = delegate->Requests();
 
   return requests.size() == 1
-             ? requests[0]
-                   ->GetRequestChipText(
-                       permissions::PermissionRequest::LOUD_REQUEST)
-                   .value()
+             ? requests[0]->GetRequestChipText().value()
              : l10n_util::GetStringUTF16(
                    IDS_MEDIA_CAPTURE_VIDEO_AND_AUDIO_PERMISSION_CHIP);
 }
@@ -96,7 +87,7 @@
         (should_bubble_start_open_ ||
          (!delegate_.value()->WasCurrentRequestAlreadyDisplayed()));
 
-    chip_text_ = GetQuietPermissionMessage(delegate_.value());
+    permission_message_ = GetQuietPermissionMessage(delegate_.value());
     chip_theme_ = OmniboxChipTheme::kLowVisibility;
   } else {
     prompt_style_ = PermissionPromptStyle::kChip;
@@ -106,81 +97,7 @@
 
     should_expand_ = true;
 
-    chip_text_ = GetLoudPermissionMessage(delegate_.value());
+    permission_message_ = GetLoudPermissionMessage(delegate_.value());
     chip_theme_ = OmniboxChipTheme::kNormalVisibility;
   }
-  accessibility_chip_text_ = l10n_util::GetStringUTF16(
-      IDS_PERMISSIONS_REQUESTED_SCREENREADER_ANNOUNCEMENT);
-}
-
-void PermissionPromptChipModel::UpdateAutoCollapsePromptChipState(
-    bool is_collapsed) {
-  should_display_blocked_icon_ = is_collapsed;
-  chip_theme_ = OmniboxChipTheme::kLowVisibility;
-}
-
-bool PermissionPromptChipModel::IsExpandAnimationAllowed() {
-  return ShouldExpand() &&
-         (ShouldBubbleStartOpen() || !WasRequestAlreadyDisplayed());
-}
-
-void PermissionPromptChipModel::UpdateWithUserDecision(
-    permissions::PermissionAction user_decision) {
-  DCHECK(delegate_.has_value());
-  absl::optional<std::u16string> chip_text_opt;
-  absl::optional<std::u16string> accessibility_chip_text_opt;
-
-  switch (user_decision) {
-    case permissions::PermissionAction::GRANTED:
-    case permissions::PermissionAction::GRANTED_ONCE:
-      should_display_blocked_icon_ = false;
-      chip_theme_ = OmniboxChipTheme::kNormalVisibility;
-      chip_text_ =
-          delegate_.value()
-              ->Requests()[0]
-              ->GetRequestChipText(permissions::PermissionRequest::
-                                       ChipTextType::ALLOW_CONFIRMATION)
-              .value_or(u"");
-      if (delegate_.value()->Requests().size() == 1) {
-        accessibility_chip_text_ =
-            delegate_.value()
-                ->Requests()[0]
-                ->GetRequestChipText(
-                    permissions::PermissionRequest::ChipTextType::
-                        ACCESSIBILITY_ALLOWED_CONFIRMATION)
-                .value_or(u"");
-      } else {
-        accessibility_chip_text_ = l10n_util::GetStringUTF16(
-            IDS_PERMISSIONS_CAMERA_AND_MICROPHONE_ALLOWED_CONFIRMATION_SCREENREADER_ANNOUNCEMENT);
-      }
-      break;
-    case permissions::PermissionAction::DENIED:
-    case permissions::PermissionAction::DISMISSED:
-    case permissions::PermissionAction::IGNORED:
-    case permissions::PermissionAction::REVOKED:
-      should_display_blocked_icon_ = true;
-      chip_text_ =
-          delegate_.value()
-              ->Requests()[0]
-              ->GetRequestChipText(permissions::PermissionRequest::
-                                       ChipTextType::BLOCKED_CONFIRMATION)
-              .value_or(u"");
-      if (delegate_.value()->Requests().size() == 1) {
-        accessibility_chip_text_ =
-            delegate_.value()
-                ->Requests()[0]
-                ->GetRequestChipText(
-                    permissions::PermissionRequest::ChipTextType::
-                        ACCESSIBILITY_BLOCKED_CONFIRMATION)
-                .value_or(u"");
-      } else {
-        accessibility_chip_text_ = l10n_util::GetStringUTF16(
-            IDS_PERMISSIONS_CAMERA_AND_MICROPHONE_NOT_ALLOWED_CONFIRMATION_SCREENREADER_ANNOUNCEMENT);
-      }
-      chip_theme_ = OmniboxChipTheme::kLowVisibility;
-      break;
-    case permissions::PermissionAction::NUM:
-      NOTREACHED();
-      break;
-  }
 }
diff --git a/chrome/browser/ui/views/permissions/permission_prompt_chip_model.h b/chrome/browser/ui/views/permissions/permission_prompt_chip_model.h
index 7b5799d9..c32d685 100644
--- a/chrome/browser/ui/views/permissions/permission_prompt_chip_model.h
+++ b/chrome/browser/ui/views/permissions/permission_prompt_chip_model.h
@@ -9,13 +9,7 @@
 #include "base/check.h"
 #include "chrome/browser/ui/views/location_bar/omnibox_chip_theme.h"
 #include "chrome/browser/ui/views/permissions/chip_controller.h"
-#include "chrome/grit/generated_resources.h"
 #include "components/permissions/features.h"
-#include "components/permissions/permission_prompt.h"
-#include "components/permissions/permission_request_manager.h"
-#include "components/permissions/permission_util.h"
-#include "ui/gfx/paint_vector_icon.h"
-#include "ui/gfx/vector_icon_types.h"
 
 class PermissionPromptChipModel {
  public:
@@ -33,12 +27,10 @@
     return delegate_;
   }
 
-  const gfx::VectorIcon& GetIcon() {
-    return should_display_blocked_icon_ ? blocked_icon_ : allowed_icon_;
-  }
-
-  std::u16string GetChipText() { return chip_text_; }
-  std::u16string GetAccessibilityChipText() { return accessibility_chip_text_; }
+  // Permission icons and text
+  const gfx::VectorIcon& GetAllowedIcon() { return allowed_icon_; }
+  const gfx::VectorIcon& GetBlockedIcon() { return blocked_icon_; }
+  std::u16string GetPermissionMessage() { return permission_message_; }
 
   // Chip look
   PermissionPromptStyle GetPromptStyle() { return prompt_style_; }
@@ -50,53 +42,31 @@
 
   // Permission state
   void SetShouldDismiss(bool flag) { should_dismiss_ = flag; }
-
-  // Updates relevant properties of the model according to the chip's collapse
-  // state if it's triggered automatically.
-  void UpdateAutoCollapsePromptChipState(bool is_collapsed);
-
   bool ShouldDismiss() { return should_dismiss_; }
-
-  bool IsExpandAnimationAllowed();
-
-  bool CanDisplayConfirmation() { return chip_text_.length() > 0; }
-
   bool WasRequestAlreadyDisplayed() {
     DCHECK(delegate_.has_value());
     return delegate_.value()->WasCurrentRequestAlreadyDisplayed();
   }
 
-  // Takes a user decision and updates relevant properties of the model
-  void UpdateWithUserDecision(permissions::PermissionAction permission_action);
-
-  permissions::PermissionAction GetUserDecision() { return user_decision_; }
-
  private:
-  // Delegate holding the current request
+  // Delegate representing a permission request
   absl::optional<permissions::PermissionPrompt::Delegate*> delegate_;
 
   // Permission icons and text
   const gfx::VectorIcon& allowed_icon_;
   const gfx::VectorIcon& blocked_icon_;
-
-  std::u16string chip_text_;
-  std::u16string accessibility_chip_text_;
+  std::u16string permission_message_;
 
   // Chip look
   PermissionPromptStyle prompt_style_;
   OmniboxChipTheme chip_theme_;
 
-  bool should_display_blocked_icon_ = false;
-
   // Chip behaviour
   bool should_bubble_start_open_ = false;
   bool should_expand_ = true;
 
   // Permission state
   bool should_dismiss_ = false;
-
-  permissions::PermissionAction user_decision_ =
-      permissions::PermissionAction::NUM;
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_PERMISSIONS_PERMISSION_PROMPT_CHIP_MODEL_H_
diff --git a/chrome/browser/ui/views/permissions/permission_prompt_desktop.cc b/chrome/browser/ui/views/permissions/permission_prompt_desktop.cc
index 9096e0c4..0d4aec10 100644
--- a/chrome/browser/ui/views/permissions/permission_prompt_desktop.cc
+++ b/chrome/browser/ui/views/permissions/permission_prompt_desktop.cc
@@ -31,9 +31,8 @@
   return was_browser_changed;
 }
 
-bool PermissionPromptDesktop::UpdateAnchor() {
+void PermissionPromptDesktop::UpdateAnchor() {
   UpdateBrowser();
-  return true;
 }
 
 permissions::PermissionPrompt::TabSwitchingBehavior
diff --git a/chrome/browser/ui/views/permissions/permission_prompt_desktop.h b/chrome/browser/ui/views/permissions/permission_prompt_desktop.h
index 3716e45..1a98db93 100644
--- a/chrome/browser/ui/views/permissions/permission_prompt_desktop.h
+++ b/chrome/browser/ui/views/permissions/permission_prompt_desktop.h
@@ -34,7 +34,7 @@
   ~PermissionPromptDesktop() override;
 
   // permissions::PermissionPrompt:
-  bool UpdateAnchor() override;
+  void UpdateAnchor() override;
   TabSwitchingBehavior GetTabSwitchingBehavior() override;
   permissions::PermissionPromptDisposition GetPromptDisposition()
       const override = 0;
diff --git a/chrome/browser/ui/views/permissions/permission_prompt_factory.cc b/chrome/browser/ui/views/permissions/permission_prompt_factory.cc
index f045d22..4584985 100644
--- a/chrome/browser/ui/views/permissions/permission_prompt_factory.cc
+++ b/chrome/browser/ui/views/permissions/permission_prompt_factory.cc
@@ -68,10 +68,7 @@
   std::vector<permissions::PermissionRequest*> requests = delegate->Requests();
   return base::ranges::all_of(
       requests, [](permissions::PermissionRequest* request) {
-        return request
-            ->GetRequestChipText(
-                permissions::PermissionRequest::ChipTextType::LOUD_REQUEST)
-            .has_value();
+        return request->GetRequestChipText().has_value();
       });
 }
 
diff --git a/chrome/browser/ui/views/privacy_sandbox/OWNERS b/chrome/browser/ui/views/privacy_sandbox/OWNERS
index 282c715..230e9a1d 100644
--- a/chrome/browser/ui/views/privacy_sandbox/OWNERS
+++ b/chrome/browser/ui/views/privacy_sandbox/OWNERS
@@ -1 +1 @@
-file://chrome/browser/privacy_sandbox/OWNERS
+file://components/privacy_sandbox/OWNERS
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller.cc b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
index 701b6b3..7cfee1e 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
@@ -2059,21 +2059,21 @@
 
 void TabDragController::BringWindowUnderPointToFront(
     const gfx::Point& point_in_screen) {
-  gfx::NativeWindow window;
-  if (GetLocalProcessWindow(point_in_screen, true, &window) ==
+  gfx::NativeWindow native_window;
+  if (GetLocalProcessWindow(point_in_screen, true, &native_window) ==
       Liveness::DELETED) {
     return;
   }
 
   // Only bring browser windows to front - only windows with a
   // TabDragContext can be tab drag targets.
-  if (!CanAttachTo(window))
+  if (!CanAttachTo(native_window))
     return;
 
-  if (window &&
+  if (native_window &&
       !base::FeatureList::IsEnabled(views::features::kWidgetLayering)) {
     views::Widget* widget_window =
-        views::Widget::GetWidgetForNativeWindow(window);
+        views::Widget::GetWidgetForNativeWindow(native_window);
     if (!widget_window)
       return;
 
diff --git a/chrome/browser/ui/views/toolbar/toolbar_action_hover_card_bubble_view_interactive_uitest.cc b/chrome/browser/ui/views/toolbar/toolbar_action_hover_card_bubble_view_interactive_uitest.cc
index 1d1360529..57328819 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_action_hover_card_bubble_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_action_hover_card_bubble_view_interactive_uitest.cc
@@ -28,8 +28,6 @@
 
 // Similar to views::test::WidgetDestroyedWaiter but waiting after the widget
 // has been closed is a no-op rather than an error.
-// TODO(crbug.com/1354661): Move SafeWidgetDestroyedWaiter to a shared file
-// since it's used by multiple tests.
 class SafeWidgetDestroyedWaiter : public views::WidgetObserver {
  public:
   explicit SafeWidgetDestroyedWaiter(views::Widget* widget) {
diff --git a/chrome/browser/ui/views/translate/partial_translate_bubble_view_unittest.cc b/chrome/browser/ui/views/translate/partial_translate_bubble_view_unittest.cc
index 99eea2b..440f8c75 100644
--- a/chrome/browser/ui/views/translate/partial_translate_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/translate/partial_translate_bubble_view_unittest.cc
@@ -84,8 +84,6 @@
     full_page_translate_called_ = true;
   }
 
-  void SetSourceTextTruncated(bool is_truncated) override {}
-
   ViewState current_view_state_;
   std::u16string source_name_ = u"English";
   std::u16string target_name_ = u"English";
diff --git a/chrome/browser/ui/views/translate/translate_bubble_controller.cc b/chrome/browser/ui/views/translate/translate_bubble_controller.cc
index 50bd7c63..f6b8084 100644
--- a/chrome/browser/ui/views/translate/translate_bubble_controller.cc
+++ b/chrome/browser/ui/views/translate/translate_bubble_controller.cc
@@ -197,7 +197,6 @@
       source_text,
       translate::kDesktopPartialTranslateTextSelectionMaxCharacters.Get(),
       gfx::WORD_BREAK);
-  bool is_truncated = (source_text.compare(truncated_source_text) != 0);
 
   if (partial_translate_bubble_view_) {
     PartialTranslateBubbleModel* model =
@@ -205,7 +204,6 @@
     model->SetSourceLanguage(source_language);
     model->SetTargetLanguage(target_language);
     model->SetSourceText(truncated_source_text);
-    model->SetSourceTextTruncated(is_truncated);
     model->SetTargetText(target_text);
     // When the user reads the advanced setting panel, the bubble should not be
     // changed because they are focusing on the bubble.
@@ -241,7 +239,6 @@
     model = std::make_unique<PartialTranslateBubbleModelImpl>(
         view_state, error_type, truncated_source_text, target_text,
         std::move(partial_translate_manager), std::move(ui_delegate));
-    model->SetSourceTextTruncated(is_truncated);
   }
 
   model->AddObserver(this);
diff --git a/chrome/browser/ui/views/translate/translate_bubble_controller_unittest.cc b/chrome/browser/ui/views/translate/translate_bubble_controller_unittest.cc
index 8d5f685..83c0927 100644
--- a/chrome/browser/ui/views/translate/translate_bubble_controller_unittest.cc
+++ b/chrome/browser/ui/views/translate/translate_bubble_controller_unittest.cc
@@ -166,8 +166,6 @@
 
   void TranslateFullPage(content::WebContents* web_contents) override {}
 
-  void SetSourceTextTruncated(bool is_truncated) override {}
-
   void NotifyTranslated() {
     for (PartialTranslateBubbleModel::Observer& obs : observers_) {
       obs.OnPartialTranslateComplete();
diff --git a/chrome/browser/ui/views/web_apps/web_app_tab_strip_browsertest.cc b/chrome/browser/ui/views/web_apps/web_app_tab_strip_browsertest.cc
index ca215c8..7758eb11 100644
--- a/chrome/browser/ui/views/web_apps/web_app_tab_strip_browsertest.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_tab_strip_browsertest.cc
@@ -21,10 +21,9 @@
 #include "chrome/browser/ui/web_applications/app_browser_controller.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
 #include "chrome/browser/ui/web_applications/web_app_launch_utils.h"
-#include "chrome/browser/web_applications/commands/fetch_manifest_and_install_command.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/user_display_mode.h"
-#include "chrome/browser/web_applications/web_app_command_manager.h"
+#include "chrome/browser/web_applications/web_app_command_scheduler.h"
 #include "chrome/browser/web_applications/web_app_constants.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
 #include "chrome/browser/web_applications/web_app_install_params.h"
@@ -163,28 +162,26 @@
     auto* provider = WebAppProvider::GetForTest(browser()->profile());
     DCHECK(provider);
     test::WaitUntilReady(provider);
-    provider->command_manager().ScheduleCommand(
-        std::make_unique<FetchManifestAndInstallCommand>(
-            &provider->install_finalizer(), &provider->registrar(),
-            webapps::WebappInstallSource::MENU_BROWSER_TAB,
-            browser()->tab_strip_model()->GetActiveWebContents()->GetWeakPtr(),
-            /*bypass_service_worker_check=*/false,
-            base::BindLambdaForTesting(
-                [](content::WebContents*,
-                   std::unique_ptr<WebAppInstallInfo> web_app_info,
-                   WebAppInstallationAcceptanceCallback acceptance_callback) {
-                  web_app_info->user_display_mode = UserDisplayMode::kTabbed;
-                  std::move(acceptance_callback)
-                      .Run(/*user_accepted=*/true, std::move(web_app_info));
-                }),
-            base::BindLambdaForTesting([&run_loop, &app_id](
-                                           const AppId& installed_app_id,
-                                           webapps::InstallResultCode code) {
+    provider->scheduler().FetchManifestAndInstall(
+        webapps::WebappInstallSource::MENU_BROWSER_TAB,
+        browser()->tab_strip_model()->GetActiveWebContents()->GetWeakPtr(),
+        /*bypass_service_worker_check=*/false,
+        base::BindLambdaForTesting(
+            [](content::WebContents*,
+               std::unique_ptr<WebAppInstallInfo> web_app_info,
+               WebAppInstallationAcceptanceCallback acceptance_callback) {
+              web_app_info->user_display_mode = UserDisplayMode::kTabbed;
+              std::move(acceptance_callback)
+                  .Run(/*user_accepted=*/true, std::move(web_app_info));
+            }),
+        base::BindLambdaForTesting(
+            [&run_loop, &app_id](const AppId& installed_app_id,
+                                 webapps::InstallResultCode code) {
               DCHECK_EQ(code, webapps::InstallResultCode::kSuccessNewInstall);
               app_id = installed_app_id;
               run_loop.Quit();
             }),
-            /*use_fallback=*/true, WebAppInstallFlow::kInstallSite));
+        /*use_fallback=*/true);
     run_loop.Run();
   }
 
diff --git a/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc b/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc
index eb7a06a..0020d027 100644
--- a/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc
+++ b/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc
@@ -39,6 +39,7 @@
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/user_display_mode.h"
 #include "chrome/browser/web_applications/web_app_command_manager.h"
+#include "chrome/browser/web_applications/web_app_command_scheduler.h"
 #include "chrome/browser/web_applications/web_app_constants.h"
 #include "chrome/browser/web_applications/web_app_install_finalizer.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
@@ -148,21 +149,19 @@
   auto* provider = WebAppProvider::GetForTest(browser->profile());
   DCHECK(provider);
   test::WaitUntilReady(provider);
-  provider->command_manager().ScheduleCommand(
-      std::make_unique<FetchManifestAndInstallCommand>(
-          &provider->install_finalizer(), &provider->registrar(),
-          webapps::WebappInstallSource::MENU_BROWSER_TAB,
-          browser->tab_strip_model()->GetActiveWebContents()->GetWeakPtr(),
-          /*bypass_service_worker_check=*/false,
-          base::BindOnce(&AutoAcceptDialogCallback),
-          base::BindLambdaForTesting(
-              [&run_loop, &app_id](const AppId& installed_app_id,
-                                   webapps::InstallResultCode code) {
-                DCHECK_EQ(code, webapps::InstallResultCode::kSuccessNewInstall);
-                app_id = installed_app_id;
-                run_loop.Quit();
-              }),
-          /*use_fallback=*/true, WebAppInstallFlow::kInstallSite));
+  provider->scheduler().FetchManifestAndInstall(
+      webapps::WebappInstallSource::MENU_BROWSER_TAB,
+      browser->tab_strip_model()->GetActiveWebContents()->GetWeakPtr(),
+      /*bypass_service_worker_check=*/false,
+      base::BindOnce(&AutoAcceptDialogCallback),
+      base::BindLambdaForTesting(
+          [&run_loop, &app_id](const AppId& installed_app_id,
+                               webapps::InstallResultCode code) {
+            DCHECK_EQ(code, webapps::InstallResultCode::kSuccessNewInstall);
+            app_id = installed_app_id;
+            run_loop.Quit();
+          }),
+      /*use_fallback=*/true);
 
   run_loop.Run();
   return app_id;
@@ -200,21 +199,19 @@
   auto* provider = WebAppProvider::GetForTest(browser->profile());
   DCHECK(provider);
   test::WaitUntilReady(provider);
-  provider->command_manager().ScheduleCommand(
-      std::make_unique<FetchManifestAndInstallCommand>(
-          &provider->install_finalizer(), &provider->registrar(),
-          webapps::WebappInstallSource::MENU_BROWSER_TAB,
-          browser->tab_strip_model()->GetActiveWebContents()->GetWeakPtr(),
-          /*bypass_service_worker_check=*/false,
-          base::BindOnce(&AutoAcceptDialogCallback),
-          base::BindLambdaForTesting(
-              [&run_loop, &app_id](const AppId& installed_app_id,
-                                   webapps::InstallResultCode code) {
-                DCHECK_EQ(code, webapps::InstallResultCode::kSuccessNewInstall);
-                app_id = installed_app_id;
-                run_loop.Quit();
-              }),
-          /*use_fallback=*/true, WebAppInstallFlow::kInstallSite));
+  provider->scheduler().FetchManifestAndInstall(
+      webapps::WebappInstallSource::MENU_BROWSER_TAB,
+      browser->tab_strip_model()->GetActiveWebContents()->GetWeakPtr(),
+      /*bypass_service_worker_check=*/false,
+      base::BindOnce(&AutoAcceptDialogCallback),
+      base::BindLambdaForTesting(
+          [&run_loop, &app_id](const AppId& installed_app_id,
+                               webapps::InstallResultCode code) {
+            DCHECK_EQ(code, webapps::InstallResultCode::kSuccessNewInstall);
+            app_id = installed_app_id;
+            run_loop.Quit();
+          }),
+      /*use_fallback=*/true);
 
   run_loop.Run();
   return app_id;
diff --git a/chrome/browser/ui/web_applications/web_app_dialog_utils.cc b/chrome/browser/ui/web_applications/web_app_dialog_utils.cc
index a19fbd77..89d071b9 100644
--- a/chrome/browser/ui/web_applications/web_app_dialog_utils.cc
+++ b/chrome/browser/ui/web_applications/web_app_dialog_utils.cc
@@ -16,9 +16,9 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/web_applications/commands/fetch_manifest_and_install_command.h"
 #include "chrome/browser/web_applications/user_display_mode.h"
 #include "chrome/browser/web_applications/web_app_command_manager.h"
+#include "chrome/browser/web_applications/web_app_command_scheduler.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_info.h"
@@ -140,15 +140,13 @@
 
   WebAppInstalledCallback callback = base::DoNothing();
 
-  provider->command_manager().ScheduleCommand(
-      std::make_unique<FetchManifestAndInstallCommand>(
-          &provider->install_finalizer(), &provider->registrar(),
-          install_source, web_contents->GetWeakPtr(),
-          /*bypass_service_worker_check=*/false,
-          base::BindOnce(OnWebAppInstallShowInstallDialog, flow, install_source,
-                         chrome::PwaInProductHelpState::kNotShown),
-          base::BindOnce(OnWebAppInstalled, std::move(callback)),
-          /*use_fallback=*/true, flow));
+  provider->scheduler().FetchManifestAndInstall(
+      install_source, web_contents->GetWeakPtr(),
+      /*bypass_service_worker_check=*/false,
+      base::BindOnce(OnWebAppInstallShowInstallDialog, flow, install_source,
+                     chrome::PwaInProductHelpState::kNotShown),
+      base::BindOnce(OnWebAppInstalled, std::move(callback)),
+      /*use_fallback=*/true);
 }
 
 bool CreateWebAppFromManifest(content::WebContents* web_contents,
@@ -165,15 +163,13 @@
     return false;
   }
 
-  provider->command_manager().ScheduleCommand(
-      std::make_unique<FetchManifestAndInstallCommand>(
-          &provider->install_finalizer(), &provider->registrar(),
-          install_source, web_contents->GetWeakPtr(),
-          bypass_service_worker_check,
-          base::BindOnce(OnWebAppInstallShowInstallDialog,
-                         WebAppInstallFlow::kInstallSite, install_source,
-                         iph_state),
-          base::BindOnce(OnWebAppInstalled, std::move(installed_callback))));
+  provider->scheduler().FetchManifestAndInstall(
+      install_source, web_contents->GetWeakPtr(), bypass_service_worker_check,
+      base::BindOnce(OnWebAppInstallShowInstallDialog,
+                     WebAppInstallFlow::kInstallSite, install_source,
+                     iph_state),
+      base::BindOnce(OnWebAppInstalled, std::move(installed_callback)),
+      /*use_fallback=*/false);
   return true;
 }
 
diff --git a/chrome/browser/ui/webui/ash/arc_power_control/arc_power_control_handler.cc b/chrome/browser/ui/webui/ash/arc_power_control/arc_power_control_handler.cc
index 6c00e11..e3c9582 100644
--- a/chrome/browser/ui/webui/ash/arc_power_control/arc_power_control_handler.cc
+++ b/chrome/browser/ui/webui/ash/arc_power_control/arc_power_control_handler.cc
@@ -78,18 +78,19 @@
       &common_model.system_model().memory_events(),
       arc::ArcValueEvent::Type::kWakenessfullMode);
   for (const auto& wakefulness_mode_event : wakefulness_mode_events) {
-    const int64_t timestamp =
+    const int64_t wakefulness_mode_timestamp =
         (wakefulness_mode_event.first - base::TimeTicks()).InMicroseconds();
-    wakenessfull_mode.MaybeAdd(timestamp,
+    wakenessfull_mode.MaybeAdd(wakefulness_mode_timestamp,
                                static_cast<int>(wakefulness_mode_event.second));
   }
   arc::ArcValueEventTrimmer throttling(
       &common_model.system_model().memory_events(),
       arc::ArcValueEvent::Type::kThrottlingMode);
   for (const auto& throttling_event : throttling_events) {
-    const int64_t timestamp =
+    const int64_t throttling_timestamp =
         (throttling_event.first - base::TimeTicks()).InMicroseconds();
-    throttling.MaybeAdd(timestamp, static_cast<int>(throttling_event.second));
+    throttling.MaybeAdd(throttling_timestamp,
+                        static_cast<int>(throttling_event.second));
   }
 
   // Flush automatically normalizes the model.
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
index 6e6d3571..c627b9e 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
@@ -175,11 +175,12 @@
     if (email.empty())
       return GaiaScreenHandler::GAIA_SCREEN_MODE_SAML_REDIRECT;
 
+    user_manager::KnownUser known_user(g_browser_process->local_state());
     // If there's a populated email, we must check first that this user is using
-    // SAML in order to decide whether to do the redirect.
-    const user_manager::User* user = user_manager::UserManager::Get()->FindUser(
-        user_manager::known_user::GetAccountId(email, std::string() /* id */,
-                                               AccountType::UNKNOWN));
+    // SAML in order to decide whether to show the interstitial page.
+    const user_manager::User* user =
+        user_manager::UserManager::Get()->FindUser(known_user.GetAccountId(
+            email, std::string() /* id */, AccountType::UNKNOWN));
 
     if (user && user->using_saml())
       return GaiaScreenHandler::GAIA_SCREEN_MODE_SAML_REDIRECT;
@@ -684,10 +685,11 @@
   if (ShouldCheckUserTypeBeforeAllowing())
     return;
 
+  user_manager::KnownUser known_user(g_browser_process->local_state());
   if (LoginDisplayHost::default_host() &&
       !LoginDisplayHost::default_host()->IsUserAllowlisted(
-          user_manager::known_user::GetAccountId(
-              user_email, std::string() /* id */, AccountType::UNKNOWN),
+          known_user.GetAccountId(user_email, std::string() /* id */,
+                                  AccountType::UNKNOWN),
           absl::nullopt)) {
     ShowAllowlistCheckFailedError();
   }
@@ -747,8 +749,9 @@
   const std::string canonicalized_email =
       gaia::CanonicalizeEmail(gaia::SanitizeEmail(authenticated_email));
 
-  const AccountId account_id = user_manager::known_user::GetAccountId(
-      authenticated_email, id, account_type);
+  user_manager::KnownUser known_user(g_browser_process->local_state());
+  const AccountId account_id =
+      known_user.GetAccountId(authenticated_email, id, account_type);
 
   if (account_id.GetUserEmail() != canonicalized_email) {
     LOG(WARNING) << "Existing user '" << account_id.GetUserEmail()
@@ -1005,7 +1008,8 @@
 }
 
 void GaiaScreenHandler::HandleUserRemoved(const std::string& email) {
-  const AccountId account_id = user_manager::known_user::GetAccountId(
+  user_manager::KnownUser known_user(g_browser_process->local_state());
+  const AccountId account_id = known_user.GetAccountId(
       email, /*id=*/std::string(), AccountType::UNKNOWN);
   if (account_id == user_manager::UserManager::Get()->GetOwnerAccountId()) {
     // Shows powerwash UI if the user is device owner.
diff --git a/chrome/browser/ui/webui/chromeos/login/l10n_util.cc b/chrome/browser/ui/webui/chromeos/login/l10n_util.cc
index f671994..c5ccb3bd 100644
--- a/chrome/browser/ui/webui/chromeos/login/l10n_util.cc
+++ b/chrome/browser/ui/webui/chromeos/login/l10n_util.cc
@@ -112,8 +112,7 @@
   std::set<std::string> language_codes;
   // Collect the language codes from the supported input methods.
   for (const auto& descriptor : descriptors) {
-    const std::vector<std::string>& languages = descriptor.language_codes();
-    for (const auto& language : languages)
+    for (const auto& language : descriptor.language_codes())
       language_codes.insert(language);
   }
 
diff --git a/chrome/browser/ui/webui/chromeos/user_image_source.cc b/chrome/browser/ui/webui/chromeos/user_image_source.cc
index e7f62c7..ab892ad 100644
--- a/chrome/browser/ui/webui/chromeos/user_image_source.cc
+++ b/chrome/browser/ui/webui/chromeos/user_image_source.cc
@@ -9,6 +9,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "chrome/browser/ash/login/users/default_user_image/default_user_images.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/common/url_constants.h"
 #include "components/account_id/account_id.h"
 #include "components/user_manager/known_user.h"
@@ -43,7 +44,8 @@
   // migrated.
   if (!status) {
     LOG(WARNING) << "Failed to deserialize account_id.";
-    account_id = user_manager::known_user::GetAccountId(
+    user_manager::KnownUser known_user(g_browser_process->local_state());
+    account_id = known_user.GetAccountId(
         serialized_account_id, std::string() /* id */, AccountType::UNKNOWN);
   }
   *email = account_id.GetUserEmail();
diff --git a/chrome/browser/ui/webui/commander/commander_ui_browsertest.cc b/chrome/browser/ui/webui/commander/commander_ui_browsertest.cc
index bc197f9..7b27db4 100644
--- a/chrome/browser/ui/webui/commander/commander_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/commander/commander_ui_browsertest.cc
@@ -153,21 +153,20 @@
   EXPECT_EQ("view-model-updated", call_data.arg1()->GetString());
 
   const base::Value* arg = call_data.arg2();
-  EXPECT_EQ("Test item", arg->FindPath("options")
-                             ->GetListDeprecated()[0]
-                             .FindPath("title")
-                             ->GetString());
+  EXPECT_EQ(
+      "Test item",
+      arg->FindPath("options")->GetList()[0].FindPath("title")->GetString());
   EXPECT_EQ(0, arg->FindPath("options")
-                   ->GetListDeprecated()[0]
+                   ->GetList()[0]
                    .FindPath("matchedRanges")
-                   ->GetListDeprecated()[0]
-                   .GetListDeprecated()[0]
+                   ->GetList()[0]
+                   .GetList()[0]
                    .GetInt());
   EXPECT_EQ(4, arg->FindPath("options")
-                   ->GetListDeprecated()[0]
+                   ->GetList()[0]
                    .FindPath("matchedRanges")
-                   ->GetListDeprecated()[0]
-                   .GetListDeprecated()[1]
+                   ->GetList()[0]
+                   .GetList()[1]
                    .GetInt());
   EXPECT_EQ(42, arg->FindPath("resultSetId")->GetInt());
 }
diff --git a/chrome/browser/ui/webui/history/browsing_history_handler_unittest.cc b/chrome/browser/ui/webui/history/browsing_history_handler_unittest.cc
index 31449d71..2f01732a 100644
--- a/chrome/browser/ui/webui/history/browsing_history_handler_unittest.cc
+++ b/chrome/browser/ui/webui/history/browsing_history_handler_unittest.cc
@@ -386,7 +386,7 @@
   const base::Value* list = arg3->FindListKey("value");
   ASSERT_TRUE(list->is_list());
 
-  const base::Value& first_entry = list->GetListDeprecated()[0];
+  const base::Value& first_entry = list->GetList()[0];
   ASSERT_TRUE(first_entry.is_dict());
 
   const std::string* title = first_entry.FindStringKey("title");
diff --git a/chrome/browser/ui/webui/privacy_sandbox/OWNERS b/chrome/browser/ui/webui/privacy_sandbox/OWNERS
index e46b4fee..230e9a1d 100644
--- a/chrome/browser/ui/webui/privacy_sandbox/OWNERS
+++ b/chrome/browser/ui/webui/privacy_sandbox/OWNERS
@@ -1,4 +1 @@
-# Chrome Privacy Team
-rainhard@chromium.org
-sauski@google.com
-olesiamarukhno@google.com
+file://components/privacy_sandbox/OWNERS
diff --git a/chrome/browser/ui/webui/settings/OWNERS b/chrome/browser/ui/webui/settings/OWNERS
index f699053..d27a101 100644
--- a/chrome/browser/ui/webui/settings/OWNERS
+++ b/chrome/browser/ui/webui/settings/OWNERS
@@ -4,7 +4,7 @@
 per-file people_handler*=treib@chromium.org
 
 per-file hats_handler*=sauski@google.com
-per-file privacy_sandbox_handler*=sauski@google.com
+per-file privacy_sandbox_handler*=file://components/privacy_sandbox/OWNERS
 per-file *site_settings*=msramek@chromium.org
 per-file *site_settings*=sauski@google.com
 per-file safe_browsing_handler*=msramek@chromium.org
diff --git a/chrome/browser/ui/webui/settings/settings_ui.cc b/chrome/browser/ui/webui/settings/settings_ui.cc
index 70eecc9..486d5be 100644
--- a/chrome/browser/ui/webui/settings/settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/settings_ui.cc
@@ -329,10 +329,6 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   html_source->AddBoolean(
-      "syncSettingsCategorizationEnabled",
-      chromeos::features::IsSyncSettingsCategorizationEnabled());
-
-  html_source->AddBoolean(
       "userCannotManuallyEnterPassword",
       !ash::password_visibility::AccountHasUserFacingPassword(
           g_browser_process->local_state(), ash::ProfileHelper::Get()
diff --git a/chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.cc
index 3ef2ca6..113d8ce 100644
--- a/chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.cc
@@ -301,6 +301,7 @@
     {"existingPassphraseLabelWithDate",
      IDS_SYNC_FULL_ENCRYPTION_BODY_CUSTOM_WITH_DATE},
     {"existingPassphraseLabel", IDS_SYNC_FULL_ENCRYPTION_BODY_CUSTOM},
+    {"themeCheckboxLabel", IDS_SETTINGS_THEME_CHECKBOX_LABEL},
   // Settings warning for Lacros side-by-side mode.
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     {"syncSettingsLacrosSideBySideWarning",
@@ -315,22 +316,6 @@
   };
   html_source->AddLocalizedStrings(kLocalizedStrings);
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-  if (chromeos::features::IsSyncSettingsCategorizationEnabled()) {
-    html_source->AddLocalizedString("themeCheckboxLabel",
-                                    IDS_SETTINGS_THEME_CHECKBOX_LABEL);
-  } else {
-    html_source->AddLocalizedString(
-        "themeCheckboxLabel",
-        IDS_SETTINGS_THEMES_AND_WALLPAPERS_CHECKBOX_LABEL);
-  }
-#else
-  // TODO(https://crbug.com/1249845): Move this string back to the list above
-  //     after launching SyncSettingsCategorization.
-  html_source->AddLocalizedString("themeCheckboxLabel",
-                                  IDS_SETTINGS_THEME_CHECKBOX_LABEL);
-#endif
-
   std::string sync_dashboard_url =
       google_util::AppendGoogleLocaleParam(
           GURL(chrome::kSyncGoogleDashboardURL),
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler.cc b/chrome/browser/ui/webui/settings/site_settings_handler.cc
index fda4bc9..1984be4 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_handler.cc
@@ -482,15 +482,16 @@
     CookiesTreeModel* tree_model) {
   // Used to count unique eTLD+1 owned by a FPS owner.
   std::map<std::string, std::set<std::string>> fps_owner_to_members;
-  auto first_party_sets = privacy_sandbox_service->GetFirstPartySets();
+
   // Count members by unique eTLD+1 for each first party set.
   for (const auto& host_node : tree_model->GetRoot()->children()) {
     std::string etld_plus1 =
         GetEtldPlusOne(host_node->GetDetailedInfo().origin);
     auto schemeful_site = ConvertEtldToSchemefulSite(etld_plus1);
-    if (first_party_sets.count(schemeful_site)) {
-      auto fps_owner = first_party_sets[schemeful_site];
-      fps_owner_to_members[fps_owner.GetURL().host()].insert(etld_plus1);
+    auto fps_owner =
+        privacy_sandbox_service->GetFirstPartySetOwner(schemeful_site.GetURL());
+    if (fps_owner.has_value()) {
+      fps_owner_to_members[fps_owner->GetURL().host()].insert(etld_plus1);
     }
   }
 
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc b/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
index 5aca9e20..6490ca5 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
@@ -634,6 +634,34 @@
     return nodes;
   }
 
+  void SetupDefaultFirstPartySets(MockPrivacySandboxService* mock_service) {
+    EXPECT_CALL(*mock_service, GetFirstPartySetOwner(_))
+        .WillRepeatedly(
+            [&](const GURL& url) -> absl::optional<net::SchemefulSite> {
+              auto first_party_sets = GetTestFirstPartySets();
+              if (first_party_sets.count(net::SchemefulSite(url))) {
+                return first_party_sets[net::SchemefulSite(url)];
+              }
+
+              return absl::nullopt;
+            });
+  }
+
+  base::flat_map<net::SchemefulSite, net::SchemefulSite>
+  GetTestFirstPartySets() {
+    base::flat_map<net::SchemefulSite, net::SchemefulSite> first_party_sets = {
+        {ConvertEtldToSchemefulSite("google.com"),
+         ConvertEtldToSchemefulSite("google.com")},
+        {ConvertEtldToSchemefulSite("google.com.au"),
+         ConvertEtldToSchemefulSite("google.com")},
+        {ConvertEtldToSchemefulSite("example.com"),
+         ConvertEtldToSchemefulSite("example.com")},
+        {ConvertEtldToSchemefulSite("unrelated.com"),
+         ConvertEtldToSchemefulSite("unrelated.com")}};
+
+    return first_party_sets;
+  }
+
   // Content setting group name for the relevant ContentSettingsType.
   const std::string kNotifications;
   const std::string kCookies;
@@ -3012,28 +3040,16 @@
 }
 
 TEST_F(SiteSettingsHandlerTest, HandleGetUsageInfo) {
-  base::flat_map<net::SchemefulSite, net::SchemefulSite> first_party_sets = {
-      {ConvertEtldToSchemefulSite("google.com"),
-       ConvertEtldToSchemefulSite("google.com")},
-      {ConvertEtldToSchemefulSite("google.com.au"),
-       ConvertEtldToSchemefulSite("google.com")},
-      {ConvertEtldToSchemefulSite("unrelated.com"),
-       ConvertEtldToSchemefulSite("unrelated.com")},
-      {ConvertEtldToSchemefulSite("ungrouped.com"),
-       ConvertEtldToSchemefulSite("ungrouped.com")},
-  };
+  SetupDefaultFirstPartySets(mock_privacy_sandbox_service());
 
-  EXPECT_CALL(*mock_privacy_sandbox_service(), GetFirstPartySets())
-      .Times(4)
-      .WillRepeatedly(Return(first_party_sets));
   EXPECT_CALL(*mock_privacy_sandbox_service(), IsPartOfManagedFirstPartySet(_))
       .Times(1)
       .WillOnce(Return(false));
   EXPECT_CALL(
       *mock_privacy_sandbox_service(),
-      IsPartOfManagedFirstPartySet(ConvertEtldToSchemefulSite("ungrouped.com")))
-      .Times(1)
-      .WillOnce(Return(true));
+      IsPartOfManagedFirstPartySet(ConvertEtldToSchemefulSite("example.com")))
+      .Times(2)
+      .WillRepeatedly(Return(true));
 
   // Confirm that usage info only returns unpartitioned storage.
   SetUpCookiesTreeModel();
@@ -3045,13 +3061,15 @@
   args.Append("www.example.com");
   handler()->HandleFetchUsageTotal(args);
   handler()->OnGetUsageInfo();
-  ValidateUsageInfo("www.example.com", "2 B", "1 cookie", "", false);
+  ValidateUsageInfo("www.example.com", "2 B", "1 cookie",
+                    "Allowed for 1 example.com site", true);
 
   args.clear();
   args.Append("example.com");
   handler()->HandleFetchUsageTotal(args);
   handler()->OnGetUsageInfo();
-  ValidateUsageInfo("example.com", "", "1 cookie", "", false);
+  ValidateUsageInfo("example.com", "", "1 cookie",
+                    "Allowed for 1 example.com site", true);
 
   args.clear();
   args.Append("google.com");
@@ -3063,8 +3081,7 @@
   args.Append("ungrouped.com");
   handler()->HandleFetchUsageTotal(args);
   handler()->OnGetUsageInfo();
-  ValidateUsageInfo("ungrouped.com", "", "1 cookie",
-                    "Allowed for 1 ungrouped.com site", true);
+  ValidateUsageInfo("ungrouped.com", "", "1 cookie", "", false);
 }
 
 TEST_F(SiteSettingsHandlerTest, NonTreeModelDeletion) {
@@ -3095,17 +3112,8 @@
 }
 
 TEST_F(SiteSettingsHandlerTest, FirstPartySetsMembership) {
-  base::flat_map<net::SchemefulSite, net::SchemefulSite> first_party_sets = {
-      {ConvertEtldToSchemefulSite("example.com"),
-       ConvertEtldToSchemefulSite("example.com")},
-      {ConvertEtldToSchemefulSite("google.com"),
-       ConvertEtldToSchemefulSite("google.com")},
-      {ConvertEtldToSchemefulSite("google.com.au"),
-       ConvertEtldToSchemefulSite("google.com")},
-  };
+  SetupDefaultFirstPartySets(mock_privacy_sandbox_service());
 
-  EXPECT_CALL(*mock_privacy_sandbox_service(), GetFirstPartySets())
-      .WillOnce(Return(first_party_sets));
   EXPECT_CALL(*mock_privacy_sandbox_service(), IsPartOfManagedFirstPartySet(_))
       .Times(2)
       .WillRepeatedly(Return(false));
@@ -3130,6 +3138,8 @@
   const base::Value::List& storage_and_cookie_list = data.arg2()->GetList();
   EXPECT_EQ(4U, storage_and_cookie_list.size());
 
+  auto first_party_sets = GetTestFirstPartySets();
+
   ValidateSitesWithFps(storage_and_cookie_list, first_party_sets);
 }
 
diff --git a/chrome/browser/ui/window_sizer/window_sizer_chromeos_unittest.cc b/chrome/browser/ui/window_sizer/window_sizer_chromeos_unittest.cc
index 7238ec1..cb60621c 100644
--- a/chrome/browser/ui/window_sizer/window_sizer_chromeos_unittest.cc
+++ b/chrome/browser/ui/window_sizer/window_sizer_chromeos_unittest.cc
@@ -418,8 +418,9 @@
 
   // Make sure that popups do not get changed.
   {
-    Browser::CreateParams params_popup(Browser::TYPE_POPUP, &profile_, true);
-    auto new_popup = CreateWindowlessBrowser(params_popup);
+    Browser::CreateParams params_new_popup(Browser::TYPE_POPUP, &profile_,
+                                           true);
+    auto new_popup = CreateWindowlessBrowser(params_new_popup);
     gfx::Rect window_bounds;
     GetWindowBounds(new_popup.get(), gfx::Rect(), display_id,
                     gfx::Rect(50, 100, 300, 150), bottom_s1600x1200, PERSISTED,
@@ -757,14 +758,16 @@
 TEST_F(WindowSizerChromeOSTest, DefaultBoundsInTargetDisplay) {
   UpdateDisplay("500x500,600x600");
 
-  // By default windows are placed on the primary display.
-  aura::Window* first_root = ash::Shell::GetAllRootWindows()[0];
-  EXPECT_EQ(first_root, ash::Shell::GetRootWindowForNewWindows());
-  gfx::Rect bounds;
-  ui::WindowShowState show_state;
-  WindowSizer::GetBrowserWindowBoundsAndShowState(gfx::Rect(), nullptr, &bounds,
-                                                  &show_state);
-  EXPECT_TRUE(first_root->GetBoundsInScreen().Contains(bounds));
+  {
+    // By default windows are placed on the primary display.
+    aura::Window* first_root = ash::Shell::GetAllRootWindows()[0];
+    EXPECT_EQ(first_root, ash::Shell::GetRootWindowForNewWindows());
+    gfx::Rect bounds;
+    ui::WindowShowState show_state;
+    WindowSizer::GetBrowserWindowBoundsAndShowState(gfx::Rect(), nullptr,
+                                                    &bounds, &show_state);
+    EXPECT_TRUE(first_root->GetBoundsInScreen().Contains(bounds));
+  }
 
   {
     // When the second display is active new windows are placed there.
diff --git a/chrome/browser/web_applications/BUILD.gn b/chrome/browser/web_applications/BUILD.gn
index 4721c75..3d2d0d8 100644
--- a/chrome/browser/web_applications/BUILD.gn
+++ b/chrome/browser/web_applications/BUILD.gn
@@ -153,6 +153,8 @@
     "web_app_chromeos_data.h",
     "web_app_command_manager.cc",
     "web_app_command_manager.h",
+    "web_app_command_scheduler.cc",
+    "web_app_command_scheduler.h",
     "web_app_constants.cc",
     "web_app_constants.h",
     "web_app_data_retriever.cc",
@@ -576,6 +578,7 @@
     "preinstalled_web_apps/preinstalled_web_app_definition_utils_unittest.cc",
     "test/web_app_test.h",
     "web_app_command_manager_unittest.cc",
+    "web_app_command_scheduler_unittest.cc",
     "web_app_data_retriever_unittest.cc",
     "web_app_database_unittest.cc",
     "web_app_helpers_unittest.cc",
diff --git a/chrome/browser/web_applications/commands/fetch_manifest_and_install_command.cc b/chrome/browser/web_applications/commands/fetch_manifest_and_install_command.cc
index 2a0fea7..af73c2d0 100644
--- a/chrome/browser/web_applications/commands/fetch_manifest_and_install_command.cc
+++ b/chrome/browser/web_applications/commands/fetch_manifest_and_install_command.cc
@@ -26,6 +26,7 @@
 #include "chrome/browser/web_applications/web_app_utils.h"
 #include "chrome/common/chrome_features.h"
 #include "components/webapps/browser/features.h"
+#include "components/webapps/browser/installable/installable_metrics.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents.h"
 
@@ -130,71 +131,23 @@
 }  // namespace
 
 FetchManifestAndInstallCommand::FetchManifestAndInstallCommand(
-    WebAppInstallFinalizer* install_finalizer,
-    WebAppRegistrar* registrar,
-    webapps::WebappInstallSource install_surface,
-    base::WeakPtr<content::WebContents> contents,
-    bool bypass_service_worker_check,
-    WebAppInstallDialogCallback dialog_callback,
-    OnceInstallCallback callback)
-    : noop_lock_(std::make_unique<NoopLock>()),
-      install_finalizer_(install_finalizer),
-      registrar_(registrar),
-      install_surface_(install_surface),
-      web_contents_(contents),
-      bypass_service_worker_check_(bypass_service_worker_check),
-      dialog_callback_(std::move(dialog_callback)),
-      install_callback_(std::move(callback)),
-      data_retriever_(std::make_unique<WebAppDataRetriever>()),
-      install_error_log_entry_(/*background_installation=*/false,
-                               install_surface_) {}
-
-FetchManifestAndInstallCommand::FetchManifestAndInstallCommand(
-    WebAppInstallFinalizer* install_finalizer,
-    WebAppRegistrar* registrar,
     webapps::WebappInstallSource install_surface,
     base::WeakPtr<content::WebContents> contents,
     bool bypass_service_worker_check,
     WebAppInstallDialogCallback dialog_callback,
     OnceInstallCallback callback,
     bool use_fallback,
-    WebAppInstallFlow flow)
-    : noop_lock_(std::make_unique<NoopLock>()),
-      install_finalizer_(install_finalizer),
-      registrar_(registrar),
-      install_surface_(install_surface),
-      web_contents_(contents),
-      bypass_service_worker_check_(bypass_service_worker_check),
-      dialog_callback_(std::move(dialog_callback)),
-      install_callback_(std::move(callback)),
-      data_retriever_(std::make_unique<WebAppDataRetriever>()),
-      use_fallback_(use_fallback),
-      flow_(flow),
-      install_error_log_entry_(/*background_installation=*/false,
-                               install_surface_) {}
-
-FetchManifestAndInstallCommand::FetchManifestAndInstallCommand(
     WebAppInstallFinalizer* install_finalizer,
-    WebAppRegistrar* registrar,
-    webapps::WebappInstallSource install_surface,
-    base::WeakPtr<content::WebContents> contents,
-    bool bypass_service_worker_check,
-    WebAppInstallDialogCallback dialog_callback,
-    OnceInstallCallback callback,
-    bool use_fallback,
-    WebAppInstallFlow flow,
     std::unique_ptr<WebAppDataRetriever> data_retriever)
     : noop_lock_(std::make_unique<NoopLock>()),
-      install_finalizer_(install_finalizer),
-      registrar_(registrar),
       install_surface_(install_surface),
       web_contents_(contents),
       bypass_service_worker_check_(bypass_service_worker_check),
       dialog_callback_(std::move(dialog_callback)),
       install_callback_(std::move(callback)),
-      data_retriever_(std::move(data_retriever)),
       use_fallback_(use_fallback),
-      flow_(flow),
+      install_finalizer_(install_finalizer),
+      data_retriever_(std::move(data_retriever)),
       install_error_log_entry_(/*background_installation=*/false,
                                install_surface_) {}
 
@@ -320,7 +273,7 @@
     LogInstallInfo();
   }
 
-  if (flow_ == WebAppInstallFlow::kCreateShortcut &&
+  if (install_surface_ == webapps::WebappInstallSource::MENU_CREATE_SHORTCUT &&
       base::FeatureList::IsEnabled(
           webapps::features::kCreateShortcutIgnoresManifest)) {
     // When creating a shortcut, the |manifest_id| is not part of the App's
@@ -351,7 +304,8 @@
 void FetchManifestAndInstallCommand::CheckForPlayStoreIntentOrGetIcons(
     base::flat_set<GURL> icon_urls,
     bool skip_page_favicons) {
-  bool is_create_shortcut = flow_ == WebAppInstallFlow::kCreateShortcut;
+  bool is_create_shortcut =
+      install_surface_ == webapps::WebappInstallSource::MENU_CREATE_SHORTCUT;
   // Background installations are not a user-triggered installs, and thus
   // cannot be sent to the store.
   bool skip_store = is_create_shortcut || !opt_manifest_;
diff --git a/chrome/browser/web_applications/commands/fetch_manifest_and_install_command.h b/chrome/browser/web_applications/commands/fetch_manifest_and_install_command.h
index 84937ff..562c180 100644
--- a/chrome/browser/web_applications/commands/fetch_manifest_and_install_command.h
+++ b/chrome/browser/web_applications/commands/fetch_manifest_and_install_command.h
@@ -32,51 +32,20 @@
 class NoopLock;
 class WebAppDataRetriever;
 class WebAppInstallFinalizer;
-class WebAppRegistrar;
 
 // Install web app from manifest for current `WebContents`.
 class FetchManifestAndInstallCommand : public WebAppCommand {
  public:
-  // When |dialog_callback| is null (aka |base::NullCallback|) the command
-  // doesn't show installation prompt in UI and installs the application in
-  // background.
-  FetchManifestAndInstallCommand(WebAppInstallFinalizer* install_finalizer,
-                                 WebAppRegistrar* registrar,
-                                 webapps::WebappInstallSource install_surface,
-                                 base::WeakPtr<content::WebContents> contents,
-                                 bool bypass_service_worker_check,
-                                 WebAppInstallDialogCallback dialog_callback,
-                                 OnceInstallCallback callback);
-
   // `use_fallback` allows getting fallback information from current document
   // to enable installing a non-promotable site.
-  //
-  // When |dialog_callback| is null (aka |base::NullCallback|) the command
-  // doesn't show installation prompt in UI and installs the application in
-  // background.
-  FetchManifestAndInstallCommand(WebAppInstallFinalizer* install_finalizer,
-                                 WebAppRegistrar* registrar,
-                                 webapps::WebappInstallSource install_surface,
-                                 base::WeakPtr<content::WebContents> contents,
-                                 bool bypass_service_worker_check,
-                                 WebAppInstallDialogCallback dialog_callback,
-                                 OnceInstallCallback callback,
-                                 bool use_fallback,
-                                 WebAppInstallFlow flow);
-
-  // TODO(https://crbug.com/1364390): Remove the constructors above and fix the
-  // param orders here after replacing callsites to use provider()->scheduler()
-  // interface.
   FetchManifestAndInstallCommand(
-      WebAppInstallFinalizer* install_finalizer,
-      WebAppRegistrar* registrar,
       webapps::WebappInstallSource install_surface,
       base::WeakPtr<content::WebContents> contents,
       bool bypass_service_worker_check,
       WebAppInstallDialogCallback dialog_callback,
       OnceInstallCallback callback,
       bool use_fallback,
-      WebAppInstallFlow flow,
+      WebAppInstallFinalizer* install_finalizer,
       std::unique_ptr<WebAppDataRetriever> data_retriever);
 
   ~FetchManifestAndInstallCommand() override;
@@ -146,29 +115,24 @@
   std::unique_ptr<NoopLock> noop_lock_;
   std::unique_ptr<AppLock> app_lock_;
 
-  raw_ptr<WebAppInstallFinalizer> install_finalizer_;
-  raw_ptr<WebAppRegistrar> registrar_;
   webapps::WebappInstallSource install_surface_;
-
   base::WeakPtr<content::WebContents> web_contents_;
   bool bypass_service_worker_check_;
   WebAppInstallDialogCallback dialog_callback_;
   OnceInstallCallback install_callback_;
+  // Whether using fallback installation data from the document.
+  bool use_fallback_ = false;
 
+  raw_ptr<WebAppInstallFinalizer> install_finalizer_;
   std::unique_ptr<WebAppDataRetriever> data_retriever_;
 
+  InstallErrorLogEntry install_error_log_entry_;
+
   AppId app_id_;
   std::unique_ptr<WebAppInstallInfo> web_app_info_;
   blink::mojom::ManifestPtr opt_manifest_;
-
   base::Value::Dict debug_log_;
 
-  // Whether using fallback installation data from the document.
-  bool use_fallback_ = false;
-  WebAppInstallFlow flow_{};
-
-  InstallErrorLogEntry install_error_log_entry_;
-
   base::WeakPtrFactory<FetchManifestAndInstallCommand> weak_ptr_factory_{this};
 };
 
diff --git a/chrome/browser/web_applications/commands/fetch_manifest_and_install_command_browsertest.cc b/chrome/browser/web_applications/commands/fetch_manifest_and_install_command_browsertest.cc
index 98985557..8eb45a1e 100644
--- a/chrome/browser/web_applications/commands/fetch_manifest_and_install_command_browsertest.cc
+++ b/chrome/browser/web_applications/commands/fetch_manifest_and_install_command_browsertest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <memory>
 #include <utility>
 
 #include "base/run_loop.h"
@@ -9,12 +10,10 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/web_applications/web_app_controller_browsertest.h"
-#include "chrome/browser/web_applications/commands/fetch_manifest_and_install_command.h"
 #include "chrome/browser/web_applications/test/web_app_test_utils.h"
 #include "chrome/browser/web_applications/user_display_mode.h"
 #include "chrome/browser/web_applications/web_app.h"
-#include "chrome/browser/web_applications/web_app_command_manager.h"
-#include "chrome/browser/web_applications/web_app_install_params.h"
+#include "chrome/browser/web_applications/web_app_command_scheduler.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/browser/web_applications/web_app_registry_update.h"
 #include "content/public/browser/web_contents.h"
@@ -43,18 +42,17 @@
   EXPECT_TRUE(NavigateAndAwaitInstallabilityCheck(browser(), test_url));
 
   base::RunLoop loop;
-  provider().command_manager().ScheduleCommand(
-      std::make_unique<FetchManifestAndInstallCommand>(
-          &provider().install_finalizer(), &provider().registrar(),
-          webapps::WebappInstallSource::MENU_BROWSER_TAB,
-          browser()->tab_strip_model()->GetActiveWebContents()->GetWeakPtr(),
-          /*bypass_service_worker_check=*/false, CreateDialogCallback(),
-          base::BindLambdaForTesting(
-              [&](const AppId& app_id, webapps::InstallResultCode code) {
-                EXPECT_EQ(code, webapps::InstallResultCode::kSuccessNewInstall);
-                EXPECT_TRUE(provider().registrar().IsLocallyInstalled(app_id));
-                loop.Quit();
-              })));
+  provider().scheduler().FetchManifestAndInstall(
+      webapps::WebappInstallSource::MENU_BROWSER_TAB,
+      browser()->tab_strip_model()->GetActiveWebContents()->GetWeakPtr(),
+      /*bypass_service_worker_check=*/false, CreateDialogCallback(),
+      base::BindLambdaForTesting(
+          [&](const AppId& app_id, webapps::InstallResultCode code) {
+            EXPECT_EQ(code, webapps::InstallResultCode::kSuccessNewInstall);
+            EXPECT_TRUE(provider().registrar().IsLocallyInstalled(app_id));
+            loop.Quit();
+          }),
+      /*use_fallback=*/false);
   loop.Run();
 }
 
@@ -66,30 +64,28 @@
 
   // Schedule two installs and both succeed.
   base::RunLoop loop;
-  provider().command_manager().ScheduleCommand(
-      std::make_unique<FetchManifestAndInstallCommand>(
-          &provider().install_finalizer(), &provider().registrar(),
-          webapps::WebappInstallSource::MENU_BROWSER_TAB,
-          browser()->tab_strip_model()->GetActiveWebContents()->GetWeakPtr(),
-          /*bypass_service_worker_check=*/false, CreateDialogCallback(),
-          base::BindLambdaForTesting(
-              [&](const AppId& app_id, webapps::InstallResultCode code) {
-                EXPECT_EQ(code, webapps::InstallResultCode::kSuccessNewInstall);
-                EXPECT_TRUE(provider().registrar().IsLocallyInstalled(app_id));
-              })));
+  provider().scheduler().FetchManifestAndInstall(
+      webapps::WebappInstallSource::MENU_BROWSER_TAB,
+      browser()->tab_strip_model()->GetActiveWebContents()->GetWeakPtr(),
+      /*bypass_service_worker_check=*/false, CreateDialogCallback(),
+      base::BindLambdaForTesting(
+          [&](const AppId& app_id, webapps::InstallResultCode code) {
+            EXPECT_EQ(code, webapps::InstallResultCode::kSuccessNewInstall);
+            EXPECT_TRUE(provider().registrar().IsLocallyInstalled(app_id));
+          }),
+      /*use_fallback=*/false);
 
-  provider().command_manager().ScheduleCommand(
-      std::make_unique<FetchManifestAndInstallCommand>(
-          &provider().install_finalizer(), &provider().registrar(),
-          webapps::WebappInstallSource::MENU_BROWSER_TAB,
-          browser()->tab_strip_model()->GetActiveWebContents()->GetWeakPtr(),
-          /*bypass_service_worker_check=*/false, CreateDialogCallback(),
-          base::BindLambdaForTesting(
-              [&](const AppId& app_id, webapps::InstallResultCode code) {
-                EXPECT_EQ(code, webapps::InstallResultCode::kSuccessNewInstall);
-                EXPECT_TRUE(provider().registrar().IsLocallyInstalled(app_id));
-                loop.Quit();
-              })));
+  provider().scheduler().FetchManifestAndInstall(
+      webapps::WebappInstallSource::MENU_BROWSER_TAB,
+      browser()->tab_strip_model()->GetActiveWebContents()->GetWeakPtr(),
+      /*bypass_service_worker_check=*/false, CreateDialogCallback(),
+      base::BindLambdaForTesting(
+          [&](const AppId& app_id, webapps::InstallResultCode code) {
+            EXPECT_EQ(code, webapps::InstallResultCode::kSuccessNewInstall);
+            EXPECT_TRUE(provider().registrar().IsLocallyInstalled(app_id));
+            loop.Quit();
+          }),
+      /*use_fallback=*/false);
   loop.Run();
 }
 
@@ -100,19 +96,18 @@
   EXPECT_FALSE(NavigateAndAwaitInstallabilityCheck(browser(), test_url));
 
   base::RunLoop loop;
-  provider().command_manager().ScheduleCommand(
-      std::make_unique<FetchManifestAndInstallCommand>(
-          &provider().install_finalizer(), &provider().registrar(),
-          webapps::WebappInstallSource::MENU_BROWSER_TAB,
-          browser()->tab_strip_model()->GetActiveWebContents()->GetWeakPtr(),
-          /*bypass_service_worker_check=*/false, CreateDialogCallback(),
-          base::BindLambdaForTesting([&](const AppId& app_id,
-                                         webapps::InstallResultCode code) {
+  provider().scheduler().FetchManifestAndInstall(
+      webapps::WebappInstallSource::MENU_BROWSER_TAB,
+      browser()->tab_strip_model()->GetActiveWebContents()->GetWeakPtr(),
+      /*bypass_service_worker_check=*/false, CreateDialogCallback(),
+      base::BindLambdaForTesting(
+          [&](const AppId& app_id, webapps::InstallResultCode code) {
             EXPECT_EQ(code,
                       webapps::InstallResultCode::kNotValidManifestForWebApp);
             EXPECT_FALSE(provider().registrar().IsLocallyInstalled(app_id));
             loop.Quit();
-          })));
+          }),
+      /*use_fallback=*/false);
   loop.Run();
 }
 
@@ -123,19 +118,18 @@
   EXPECT_TRUE(NavigateAndAwaitInstallabilityCheck(browser(), test_url));
 
   base::RunLoop loop;
-  provider().command_manager().ScheduleCommand(
-      std::make_unique<FetchManifestAndInstallCommand>(
-          &provider().install_finalizer(), &provider().registrar(),
-          webapps::WebappInstallSource::MENU_BROWSER_TAB,
-          browser()->tab_strip_model()->GetActiveWebContents()->GetWeakPtr(),
-          /*bypass_service_worker_check=*/false,
-          CreateDialogCallback(/*accept=*/false),
-          base::BindLambdaForTesting([&](const AppId& app_id,
-                                         webapps::InstallResultCode code) {
+  provider().scheduler().FetchManifestAndInstall(
+      webapps::WebappInstallSource::MENU_BROWSER_TAB,
+      browser()->tab_strip_model()->GetActiveWebContents()->GetWeakPtr(),
+      /*bypass_service_worker_check=*/false,
+      CreateDialogCallback(/*accept=*/false),
+      base::BindLambdaForTesting(
+          [&](const AppId& app_id, webapps::InstallResultCode code) {
             EXPECT_EQ(code, webapps::InstallResultCode::kUserInstallDeclined);
             EXPECT_FALSE(provider().registrar().IsLocallyInstalled(app_id));
             loop.Quit();
-          })));
+          }),
+      /*use_fallback=*/false);
   loop.Run();
 }
 
@@ -149,18 +143,17 @@
   base::RunLoop loop;
   auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
 
-  provider().command_manager().ScheduleCommand(
-      std::make_unique<FetchManifestAndInstallCommand>(
-          &provider().install_finalizer(), &provider().registrar(),
-          webapps::WebappInstallSource::MENU_BROWSER_TAB,
-          web_contents->GetWeakPtr(),
-          /*bypass_service_worker_check=*/false, CreateDialogCallback(),
-          base::BindLambdaForTesting([&](const AppId& app_id,
-                                         webapps::InstallResultCode code) {
+  provider().scheduler().FetchManifestAndInstall(
+      webapps::WebappInstallSource::MENU_BROWSER_TAB,
+      web_contents->GetWeakPtr(),
+      /*bypass_service_worker_check=*/false, CreateDialogCallback(),
+      base::BindLambdaForTesting(
+          [&](const AppId& app_id, webapps::InstallResultCode code) {
             EXPECT_EQ(code, webapps::InstallResultCode::kWebContentsDestroyed);
             EXPECT_FALSE(provider().registrar().IsLocallyInstalled(app_id));
             loop.Quit();
-          })));
+          }),
+      /*use_fallback=*/false);
 
   web_contents->Close();
 
@@ -177,19 +170,17 @@
   base::RunLoop loop;
   auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
 
-  provider().command_manager().ScheduleCommand(
-      std::make_unique<FetchManifestAndInstallCommand>(
-          &provider().install_finalizer(), &provider().registrar(),
-          webapps::WebappInstallSource::MENU_BROWSER_TAB,
-          web_contents->GetWeakPtr(),
-          /*bypass_service_worker_check=*/false, CreateDialogCallback(),
-          base::BindLambdaForTesting(
-              [&](const AppId& app_id, webapps::InstallResultCode code) {
-                EXPECT_EQ(code, webapps::InstallResultCode::kSuccessNewInstall);
-                EXPECT_TRUE(provider().registrar().IsLocallyInstalled(app_id));
-                loop.Quit();
-              }),
-          /*use_fallback=*/true, WebAppInstallFlow::kInstallSite));
+  provider().scheduler().FetchManifestAndInstall(
+      webapps::WebappInstallSource::MENU_BROWSER_TAB,
+      web_contents->GetWeakPtr(),
+      /*bypass_service_worker_check=*/false, CreateDialogCallback(),
+      base::BindLambdaForTesting(
+          [&](const AppId& app_id, webapps::InstallResultCode code) {
+            EXPECT_EQ(code, webapps::InstallResultCode::kSuccessNewInstall);
+            EXPECT_TRUE(provider().registrar().IsLocallyInstalled(app_id));
+            loop.Quit();
+          }),
+      /*use_fallback=*/true);
   loop.Run();
 }
 
@@ -218,18 +209,16 @@
   base::RunLoop loop;
   auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
 
-  provider().command_manager().ScheduleCommand(
-      std::make_unique<FetchManifestAndInstallCommand>(
-          &provider().install_finalizer(), &provider().registrar(),
-          webapps::WebappInstallSource::MENU_BROWSER_TAB,
-          web_contents->GetWeakPtr(),
-          /*bypass_service_worker_check=*/false, CreateDialogCallback(),
-          base::BindLambdaForTesting(
-              [&](const AppId& app_id, webapps::InstallResultCode code) {
-                EXPECT_EQ(code, webapps::InstallResultCode::kSuccessNewInstall);
-                loop.Quit();
-              }),
-          /*use_fallback=*/true, WebAppInstallFlow::kInstallSite));
+  provider().scheduler().FetchManifestAndInstall(
+      webapps::WebappInstallSource::MENU_BROWSER_TAB,
+      web_contents->GetWeakPtr(),
+      /*bypass_service_worker_check=*/false, CreateDialogCallback(),
+      base::BindLambdaForTesting(
+          [&](const AppId& app_id, webapps::InstallResultCode code) {
+            EXPECT_EQ(code, webapps::InstallResultCode::kSuccessNewInstall);
+            loop.Quit();
+          }),
+      /*use_fallback=*/true);
   loop.Run();
   EXPECT_TRUE(provider().registrar().IsLocallyInstalled(app_id));
 
diff --git a/chrome/browser/web_applications/commands/fetch_manifest_and_install_command_unittest.cc b/chrome/browser/web_applications/commands/fetch_manifest_and_install_command_unittest.cc
index 9da0b3a..6013405 100644
--- a/chrome/browser/web_applications/commands/fetch_manifest_and_install_command_unittest.cc
+++ b/chrome/browser/web_applications/commands/fetch_manifest_and_install_command_unittest.cc
@@ -176,13 +176,11 @@
       std::unique_ptr<WebAppDataRetriever> data_retriever,
       webapps::WebappInstallSource install_surface,
       WebAppInstallDialogCallback dialog_callback,
-      WebAppInstallFlow flow,
       bool use_fallback = false) {
     webapps::InstallResultCode result;
     base::RunLoop run_loop;
     provider()->command_manager().ScheduleCommand(
         std::make_unique<FetchManifestAndInstallCommand>(
-            &provider()->install_finalizer(), &provider()->registrar(),
             webapps::WebappInstallSource::MENU_BROWSER_TAB,
             web_contents()->GetWeakPtr(),
             /*bypass_service_worker_check=*/false, std::move(dialog_callback),
@@ -191,7 +189,8 @@
                   result = code;
                   run_loop.Quit();
                 }),
-            use_fallback, flow, std::move(data_retriever)));
+            use_fallback, &provider()->install_finalizer(),
+            std::move(data_retriever)));
     run_loop.Run();
     return result;
   }
@@ -212,8 +211,7 @@
   EXPECT_EQ(
       InstallAndWait(kWebAppId, SetupDefaultFakeDataRetriever(),
                      webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
-                     CreateDialogCallback(true, UserDisplayMode::kStandalone),
-                     WebAppInstallFlow::kInstallSite),
+                     CreateDialogCallback(true, UserDisplayMode::kStandalone)),
       webapps::InstallResultCode::kSuccessNewInstall);
   EXPECT_TRUE(provider()->registrar().IsLocallyInstalled(kWebAppId));
   EXPECT_EQ(1, fake_ui_manager()->num_reparent_tab_calls());
@@ -231,9 +229,9 @@
   data_retriever->SetRendererWebAppInstallInfo(std::move(web_app_info));
   EXPECT_EQ(
       InstallAndWait(kWebAppId, std::move(data_retriever),
-                     webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
+                     webapps::WebappInstallSource::MENU_CREATE_SHORTCUT,
                      CreateDialogCallback(true, UserDisplayMode::kStandalone),
-                     WebAppInstallFlow::kInstallSite, /*use_fallback=*/true),
+                     /*use_fallback=*/true),
       webapps::InstallResultCode::kSuccessNewInstall);
   EXPECT_TRUE(provider()->registrar().IsLocallyInstalled(kWebAppId));
   EXPECT_EQ(1, fake_ui_manager()->num_reparent_tab_calls());
@@ -243,9 +241,9 @@
        FallbackInstallWithFailToGetInstallInfo) {
   EXPECT_EQ(
       InstallAndWait(kWebAppId, std::make_unique<FakeDataRetriever>(),
-                     webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
+                     webapps::WebappInstallSource::MENU_CREATE_SHORTCUT,
                      CreateDialogCallback(true, UserDisplayMode::kStandalone),
-                     WebAppInstallFlow::kInstallSite, /*use_fallback=*/true),
+                     /*use_fallback=*/true),
       webapps::InstallResultCode::kGetWebAppInstallInfoFailed);
   EXPECT_FALSE(provider()->registrar().IsLocallyInstalled(kWebAppId));
   EXPECT_EQ(0, fake_ui_manager()->num_reparent_tab_calls());
@@ -255,9 +253,7 @@
   EXPECT_EQ(
       InstallAndWait(kWebAppId, SetupDefaultFakeDataRetriever(),
                      webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
-                     CreateDialogCallback(true, UserDisplayMode::kBrowser),
-
-                     WebAppInstallFlow::kInstallSite),
+                     CreateDialogCallback(true, UserDisplayMode::kBrowser)),
       webapps::InstallResultCode::kSuccessNewInstall);
   EXPECT_EQ(0, fake_ui_manager()->num_reparent_tab_calls());
 }
@@ -266,9 +262,7 @@
   EXPECT_EQ(
       InstallAndWait(kWebAppId, SetupDefaultFakeDataRetriever(),
                      webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
-                     CreateDialogCallback(false, UserDisplayMode::kStandalone),
-
-                     WebAppInstallFlow::kInstallSite),
+                     CreateDialogCallback(false, UserDisplayMode::kStandalone)),
       webapps::InstallResultCode::kUserInstallDeclined);
   EXPECT_FALSE(provider()->registrar().IsLocallyInstalled(kWebAppId));
   EXPECT_EQ(0, fake_ui_manager()->num_reparent_tab_calls());
@@ -289,7 +283,6 @@
 
   provider()->command_manager().ScheduleCommand(
       std::make_unique<FetchManifestAndInstallCommand>(
-          &provider()->install_finalizer(), &provider()->registrar(),
           webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
           web_contents()->GetWeakPtr(),
           /*bypass_service_worker_check=*/false, std::move(dialog_callback),
@@ -298,7 +291,7 @@
                 result_populated = true;
                 result = code;
               }),
-          /*use_fallback=*/false, WebAppInstallFlow::kInstallSite,
+          /*use_fallback=*/false, &provider()->install_finalizer(),
           SetupDefaultFakeDataRetriever()));
 
   dialog_runloop.Run();
@@ -316,7 +309,6 @@
   base::RunLoop loop;
   provider()->command_manager().ScheduleCommand(
       std::make_unique<FetchManifestAndInstallCommand>(
-          &provider()->install_finalizer(), &provider()->registrar(),
           webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
           web_contents()->GetWeakPtr(),
           /*bypass_service_worker_check=*/false, CreateDialogCallback(),
@@ -326,7 +318,7 @@
                 result = code;
                 loop.Quit();
               }),
-          /*use_fallback=*/false, WebAppInstallFlow::kInstallSite,
+          /*use_fallback=*/false, &provider()->install_finalizer(),
           SetupDefaultFakeDataRetriever()));
 
   DeleteContents();
@@ -400,7 +392,7 @@
                     std::move(icons_map), IconsDownloadedResult::kCompleted,
                     net::HttpStatusCode::HTTP_OK, std::move(manifest)),
                 webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
-                CreateDialogCallback(true), WebAppInstallFlow::kInstallSite),
+                CreateDialogCallback(true)),
             webapps::InstallResultCode::kSuccessNewInstall);
 
   EXPECT_TRUE(file_utils().DirectoryExists(manifest_resources_directory));
@@ -470,15 +462,14 @@
   EXPECT_FALSE(file_utils().DirectoryExists(manifest_resources_directory));
 
   IconsMap icons_map;
-  EXPECT_EQ(
-      InstallAndWait(
-          kWebAppId,
-          SetupFakeDataRetriever(std::move(icons_map),
-                                 IconsDownloadedResult::kPrimaryPageChanged,
-                                 net::HttpStatusCode::HTTP_OK),
-          webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
-          CreateDialogCallback(true), WebAppInstallFlow::kInstallSite),
-      webapps::InstallResultCode::kSuccessNewInstall);
+  EXPECT_EQ(InstallAndWait(kWebAppId,
+                           SetupFakeDataRetriever(
+                               std::move(icons_map),
+                               IconsDownloadedResult::kPrimaryPageChanged,
+                               net::HttpStatusCode::HTTP_OK),
+                           webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
+                           CreateDialogCallback(true)),
+            webapps::InstallResultCode::kSuccessNewInstall);
 
   EXPECT_TRUE(file_utils().DirectoryExists(manifest_resources_directory));
 
@@ -537,9 +528,7 @@
                                        IconsDownloadedResult::kCompleted,
                                        net::HttpStatusCode::HTTP_NOT_FOUND),
                 webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
-                CreateDialogCallback(true),
-
-                WebAppInstallFlow::kInstallSite),
+                CreateDialogCallback(true)),
             webapps::InstallResultCode::kSuccessNewInstall);
 
   EXPECT_TRUE(file_utils().DirectoryExists(manifest_resources_directory));
@@ -599,9 +588,7 @@
                                             IconsDownloadedResult::kCompleted,
                                             net::HttpStatusCode::HTTP_OK),
                      webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
-                     CreateDialogCallback(true),
-
-                     WebAppInstallFlow::kInstallSite),
+                     CreateDialogCallback(true)),
       webapps::InstallResultCode::kWriteDataFailed);
 
   const base::FilePath temp_dir = web_apps_dir.AppendASCII("Temp");
@@ -623,10 +610,10 @@
   related_app.id = u"com.app.id";
   manifest->related_applications.push_back(std::move(related_app));
 
-  EXPECT_EQ(InstallAndWait(
-                kWebAppId, SetupDefaultFakeDataRetriever(std::move(manifest)),
-                webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
-                CreateDialogCallback(true), WebAppInstallFlow::kInstallSite),
+  EXPECT_EQ(InstallAndWait(kWebAppId,
+                           SetupDefaultFakeDataRetriever(std::move(manifest)),
+                           webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
+                           CreateDialogCallback(true)),
             webapps::InstallResultCode::kIntentToPlayStore);
 }
 #endif
diff --git a/chrome/browser/web_applications/manifest_update_manager_browsertest.cc b/chrome/browser/web_applications/manifest_update_manager_browsertest.cc
index 6b245e5..f22d1df 100644
--- a/chrome/browser/web_applications/manifest_update_manager_browsertest.cc
+++ b/chrome/browser/web_applications/manifest_update_manager_browsertest.cc
@@ -40,8 +40,6 @@
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
-#include "chrome/browser/web_applications/commands/fetch_manifest_and_install_command.h"
-#include "chrome/browser/web_applications/commands/web_app_command.h"
 #include "chrome/browser/web_applications/external_install_options.h"
 #include "chrome/browser/web_applications/externally_managed_app_manager.h"
 #include "chrome/browser/web_applications/manifest_update_task.h"
@@ -55,7 +53,7 @@
 #include "chrome/browser/web_applications/user_display_mode.h"
 #include "chrome/browser/web_applications/web_app.h"
 #include "chrome/browser/web_applications/web_app_callback_app_identity.h"
-#include "chrome/browser/web_applications/web_app_command_manager.h"
+#include "chrome/browser/web_applications/web_app_command_scheduler.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_icon_generator.h"
@@ -440,20 +438,18 @@
 
     AppId app_id;
     base::RunLoop run_loop;
-    GetProvider().command_manager().ScheduleCommand(
-        std::make_unique<FetchManifestAndInstallCommand>(
-            &GetProvider().install_finalizer(), &GetProvider().registrar(),
-            webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
-            browser()->tab_strip_model()->GetActiveWebContents()->GetWeakPtr(),
-            /*bypass_service_worker_check=*/false,
-            base::BindOnce(test::TestAcceptDialogCallback),
-            base::BindLambdaForTesting([&](const AppId& new_app_id,
-                                           webapps::InstallResultCode code) {
+    GetProvider().scheduler().FetchManifestAndInstall(
+        webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
+        browser()->tab_strip_model()->GetActiveWebContents()->GetWeakPtr(),
+        /*bypass_service_worker_check=*/false,
+        base::BindOnce(test::TestAcceptDialogCallback),
+        base::BindLambdaForTesting(
+            [&](const AppId& new_app_id, webapps::InstallResultCode code) {
               EXPECT_EQ(code, webapps::InstallResultCode::kSuccessNewInstall);
               app_id = new_app_id;
               run_loop.Quit();
             }),
-            /*use_fallback=*/true, WebAppInstallFlow::kInstallSite));
+        /*use_fallback=*/true);
 
     run_loop.Run();
     return app_id;
diff --git a/chrome/browser/web_applications/web_app_command_scheduler.cc b/chrome/browser/web_applications/web_app_command_scheduler.cc
new file mode 100644
index 0000000..4666442
--- /dev/null
+++ b/chrome/browser/web_applications/web_app_command_scheduler.cc
@@ -0,0 +1,46 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+
+#include "chrome/browser/web_applications/web_app_command_scheduler.h"
+
+#include "chrome/browser/web_applications/commands/fetch_manifest_and_install_command.h"
+#include "chrome/browser/web_applications/web_app_command_manager.h"
+#include "chrome/browser/web_applications/web_app_data_retriever.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
+
+namespace web_app {
+
+WebAppCommandScheduler::WebAppCommandScheduler(WebAppProvider* provider)
+    : provider_(provider) {}
+
+WebAppCommandScheduler::~WebAppCommandScheduler() = default;
+
+void WebAppCommandScheduler::FetchManifestAndInstall(
+    webapps::WebappInstallSource install_surface,
+    base::WeakPtr<content::WebContents> contents,
+    bool bypass_service_worker_check,
+    WebAppInstallDialogCallback dialog_callback,
+    OnceInstallCallback callback,
+    bool use_fallback) {
+  if (!provider_->is_registry_ready()) {
+    provider_->on_registry_ready().Post(
+        FROM_HERE,
+        base::BindOnce(&WebAppCommandScheduler::FetchManifestAndInstall,
+                       weak_ptr_factory_.GetWeakPtr(),
+                       std::move(install_surface), std::move(contents),
+                       bypass_service_worker_check, std::move(dialog_callback),
+                       std::move(callback), use_fallback));
+    return;
+  }
+  provider_->command_manager().ScheduleCommand(
+      std::make_unique<FetchManifestAndInstallCommand>(
+          std::move(install_surface), std::move(contents),
+          bypass_service_worker_check, std::move(dialog_callback),
+          std::move(callback), use_fallback, &provider_->install_finalizer(),
+          std::make_unique<WebAppDataRetriever>()));
+}
+
+}  // namespace web_app
diff --git a/chrome/browser/web_applications/web_app_command_scheduler.h b/chrome/browser/web_applications/web_app_command_scheduler.h
new file mode 100644
index 0000000..811c4ad2
--- /dev/null
+++ b/chrome/browser/web_applications/web_app_command_scheduler.h
@@ -0,0 +1,50 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_COMMAND_SCHEDULER_H_
+#define CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_COMMAND_SCHEDULER_H_
+
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/web_applications/web_app_install_params.h"
+#include "components/webapps/browser/installable/installable_metrics.h"
+
+namespace web_app {
+
+class WebAppProvider;
+
+// The command scheduler is the main API to access the web app system. The
+// scheduler internally ensures:
+// * Operations occur after the WebAppProvider is ready (so you don't have to
+//   manually wait for this).
+// * Operations are isolated from other operations in the system (currently
+//   implemented using `WebAppCommand`s) to prevent race conditions while
+//   reading/writing from the various data storage of the system.
+// * Operations have the necessary dependencies from the WebAppProvider system.
+class WebAppCommandScheduler {
+ public:
+  explicit WebAppCommandScheduler(WebAppProvider* provider);
+  ~WebAppCommandScheduler();
+
+  // User initiated install that uses current `WebContents` to fetch manifest
+  // and install the web app.
+  void FetchManifestAndInstall(webapps::WebappInstallSource install_surface,
+                               base::WeakPtr<content::WebContents> contents,
+                               bool bypass_service_worker_check,
+                               WebAppInstallDialogCallback dialog_callback,
+                               OnceInstallCallback callback,
+                               bool use_fallback);
+
+  // TODO(https://crbug.com/1298130): expose all commands for web app
+  // operations.
+
+ private:
+  raw_ptr<WebAppProvider> provider_;
+
+  base::WeakPtrFactory<WebAppCommandScheduler> weak_ptr_factory_{this};
+};
+
+}  // namespace web_app
+
+#endif  // CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_COMMAND_SCHEDULER_H_
diff --git a/chrome/browser/web_applications/web_app_command_scheduler_unittest.cc b/chrome/browser/web_applications/web_app_command_scheduler_unittest.cc
new file mode 100644
index 0000000..6f422e1
--- /dev/null
+++ b/chrome/browser/web_applications/web_app_command_scheduler_unittest.cc
@@ -0,0 +1,58 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/web_applications/web_app_command_scheduler.h"
+
+#include "base/functional/callback_helpers.h"
+#include "chrome/browser/web_applications/test/fake_web_app_provider.h"
+#include "chrome/browser/web_applications/test/web_app_test.h"
+#include "chrome/browser/web_applications/web_app_command_manager.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
+#include "components/webapps/browser/installable/installable_metrics.h"
+#include "content/public/browser/web_contents.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace web_app {
+namespace {
+
+class WebAppCommandSchedulerTest : public WebAppTest {
+ public:
+  void SetUp() override {
+    WebAppTest::SetUp();
+    provider_ = FakeWebAppProvider::Get(profile());
+  }
+
+  FakeWebAppProvider* provider() { return provider_; }
+
+ private:
+  FakeWebAppProvider* provider_;
+};
+
+TEST_F(WebAppCommandSchedulerTest, FetchManifestAndInstall) {
+  EXPECT_FALSE(provider()->is_registry_ready());
+  provider()->scheduler().FetchManifestAndInstall(
+      webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
+      web_contents()->GetWeakPtr(),
+      /*bypass_service_worker_check=*/true, base::DoNothing(),
+      base::DoNothing(), /*use_fallback=*/true);
+
+  provider()->StartWithSubsystems();
+  EXPECT_EQ(provider()->command_manager().GetCommandCountForTesting(), 0u);
+
+  base::RunLoop run_loop;
+  provider()->on_registry_ready().Post(FROM_HERE, run_loop.QuitClosure());
+  run_loop.Run();
+  EXPECT_EQ(provider()->command_manager().GetCommandCountForTesting(), 1u);
+  base::Value::Dict log =
+      provider()->command_manager().ToDebugValue().TakeDict();
+  base::Value::List* command_queue = log.FindList("command_queue");
+
+  EXPECT_EQ(command_queue->size(), 1u);
+  EXPECT_EQ(*command_queue->front().GetDict().FindDict("value")->FindString(
+                "command_name"),
+            "FetchManifestAndInstallCommand");
+}
+
+}  // namespace
+}  // namespace web_app
diff --git a/chrome/browser/web_applications/web_app_internals_browsertest.cc b/chrome/browser/web_applications/web_app_internals_browsertest.cc
index d1eb239..162ef470 100644
--- a/chrome/browser/web_applications/web_app_internals_browsertest.cc
+++ b/chrome/browser/web_applications/web_app_internals_browsertest.cc
@@ -11,12 +11,11 @@
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
-#include "chrome/browser/web_applications/commands/fetch_manifest_and_install_command.h"
 #include "chrome/browser/web_applications/os_integration/os_integration_manager.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/test/web_app_test_utils.h"
 #include "chrome/browser/web_applications/web_app.h"
-#include "chrome/browser/web_applications/web_app_command_manager.h"
+#include "chrome/browser/web_applications/web_app_command_scheduler.h"
 #include "chrome/browser/web_applications/web_app_install_manager.h"
 #include "chrome/browser/web_applications/web_app_install_params.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
@@ -90,20 +89,18 @@
 
     AppId app_id;
     base::RunLoop run_loop;
-    GetProvider().command_manager().ScheduleCommand(
-        std::make_unique<FetchManifestAndInstallCommand>(
-            &GetProvider().install_finalizer(), &GetProvider().registrar(),
-            webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
-            browser()->tab_strip_model()->GetActiveWebContents()->GetWeakPtr(),
-            /*bypass_service_worker_check=*/false,
-            base::BindOnce(test::TestAcceptDialogCallback),
-            base::BindLambdaForTesting([&](const AppId& new_app_id,
-                                           webapps::InstallResultCode code) {
+    GetProvider().scheduler().FetchManifestAndInstall(
+        webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
+        browser()->tab_strip_model()->GetActiveWebContents()->GetWeakPtr(),
+        /*bypass_service_worker_check=*/false,
+        base::BindOnce(test::TestAcceptDialogCallback),
+        base::BindLambdaForTesting(
+            [&](const AppId& new_app_id, webapps::InstallResultCode code) {
               EXPECT_EQ(code, webapps::InstallResultCode::kSuccessNewInstall);
               app_id = new_app_id;
               run_loop.Quit();
             }),
-            /*use_fallback=*/true, WebAppInstallFlow::kInstallSite));
+        /*use_fallback=*/true);
 
     run_loop.Run();
     return app_id;
diff --git a/chrome/browser/web_applications/web_app_provider.cc b/chrome/browser/web_applications/web_app_provider.cc
index 481fa9c..856b2fb 100644
--- a/chrome/browser/web_applications/web_app_provider.cc
+++ b/chrome/browser/web_applications/web_app_provider.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/web_applications/web_app_provider.h"
 
+#include <memory>
 #include <utility>
 
 #include "base/bind.h"
@@ -28,6 +29,7 @@
 #include "chrome/browser/web_applications/preinstalled_web_app_manager.h"
 #include "chrome/browser/web_applications/web_app_audio_focus_id_map.h"
 #include "chrome/browser/web_applications/web_app_command_manager.h"
+#include "chrome/browser/web_applications/web_app_command_scheduler.h"
 #include "chrome/browser/web_applications/web_app_database_factory.h"
 #include "chrome/browser/web_applications/web_app_icon_manager.h"
 #include "chrome/browser/web_applications/web_app_install_finalizer.h"
@@ -210,6 +212,10 @@
   return *command_manager_;
 }
 
+WebAppCommandScheduler& WebAppProvider::scheduler() {
+  return *command_scheduler_;
+}
+
 void WebAppProvider::Shutdown() {
   command_manager_->Shutdown();
   ui_manager_->Shutdown();
@@ -282,6 +288,7 @@
   }
 
   command_manager_ = std::make_unique<WebAppCommandManager>(profile);
+  command_scheduler_ = std::make_unique<WebAppCommandScheduler>(this);
 
   registrar_ = std::move(registrar);
   sync_bridge_ = std::move(sync_bridge);
diff --git a/chrome/browser/web_applications/web_app_provider.h b/chrome/browser/web_applications/web_app_provider.h
index d183485..e5bc209 100644
--- a/chrome/browser/web_applications/web_app_provider.h
+++ b/chrome/browser/web_applications/web_app_provider.h
@@ -41,6 +41,7 @@
 class OsIntegrationManager;
 class WebAppTranslationManager;
 class WebAppCommandManager;
+class WebAppCommandScheduler;
 
 // WebAppProvider is the heart of Chrome web app code.
 //
@@ -105,6 +106,13 @@
   // Start the Web App system. This will run subsystem startup tasks.
   void Start();
 
+  // Read/write to web app system should use `scheduler()` to guarantee safe
+  // access. This is safe to access even if the `WebAppProvider` is not ready.
+  WebAppCommandScheduler& scheduler();
+
+  // Web App sub components. These should only be accessed after
+  // `on_registry_ready()` is signaled.
+
   // The app registry model.
   WebAppRegistrar& registrar();
   const WebAppRegistrar& registrar() const;
@@ -187,6 +195,7 @@
   std::unique_ptr<WebAppUiManager> ui_manager_;
   std::unique_ptr<OsIntegrationManager> os_integration_manager_;
   std::unique_ptr<WebAppCommandManager> command_manager_;
+  std::unique_ptr<WebAppCommandScheduler> command_scheduler_;
 
   base::OneShotEvent on_registry_ready_;
 
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index ac6c6eb..5d749395 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1664539181-b4635e903d38eb6a57b416c7af54d2620bdda14e.profdata
+chrome-linux-main-1664560771-46519cf6f8911afbee9422153aef6e3a7d06136d.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 5870efe3..54954b0 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1664539181-2294ee1e73a1fc86fc85a53fbb64c8f9c06de14f.profdata
+chrome-win32-main-1664549964-703058a079f627dc4a66ccdfdcd4efbdebe56335.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 6d8398ed..9ec349a7 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1664539181-f4ef3da311d96224b7f84b66f071be01b7079126.profdata
+chrome-win64-main-1664549964-1625700ce22588f8d9733d54d4d908d745a412fe.profdata
diff --git a/chrome/services/sharing/nearby/platform/ble_medium.cc b/chrome/services/sharing/nearby/platform/ble_medium.cc
index e655de1..af44592e 100644
--- a/chrome/services/sharing/nearby/platform/ble_medium.cc
+++ b/chrome/services/sharing/nearby/platform/ble_medium.cc
@@ -289,10 +289,10 @@
   // BlePeripherals are passed by reference to NearbyConnections, if a
   // BlePeripheral already exists with the given address, the reference should
   // not be invalidated, the update functions should be called instead.
-  std::string address = device->address;
-  auto* ble_peripheral = GetDiscoveredBlePeripheral(address);
-  if (ble_peripheral) {
-    ble_peripheral->UpdateDeviceInfo(std::move(device));
+  const std::string address = device->address;
+  auto* existing_ble_peripheral = GetDiscoveredBlePeripheral(address);
+  if (existing_ble_peripheral) {
+    existing_ble_peripheral->UpdateDeviceInfo(std::move(device));
   } else {
     discovered_ble_peripherals_map_.emplace(
         address,
@@ -314,8 +314,9 @@
     if (it == discovered_peripheral_callbacks_map_.end())
       continue;
 
-    // Fetch |ble_peripheral| again because it might have since been invalidated
-    // while we were iterating through IDs.
+    // Fetch the BlePeripheral with the same `address` again because
+    // previously fetched pointers may have been invalidated while iterating
+    // through the IDs.
     auto* ble_peripheral = GetDiscoveredBlePeripheral(address);
     if (!ble_peripheral)
       continue;
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 9eda33ed..a4802f7 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2591,7 +2591,6 @@
         "../browser/ui/test/test_browser_dialog_mac.h",
         "../browser/ui/test/test_browser_dialog_mac.mm",
         "../browser/ui/views/frame/browser_non_client_frame_view_mac_browsertest.mm",
-        "../browser/ui/views/intent_picker_bubble_view_browsertest_mac.cc",
         "../browser/webauthn/chrome_webauthn_mac_browsertest.mm",
         "../browser/webshare/mac/sharing_service_operation_browsertest.cc",
         "../common/mac/app_mode_chrome_locator_browsertest.mm",
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index adc8ab00..3dc1ec9 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -105,7 +105,6 @@
       "ntp4.js",
       "password_manager/password_manager_browsertest.js",
       "privacy_sandbox/privacy_sandbox_dialog_browsertest.js",
-      "resources/webui_resources_browsertest.js",
       "sandboxstatus_browsertest.js",
       "settings/a11y/a11y_browsertest.js",
       "settings/cr_settings_browsertest.js",
@@ -159,6 +158,7 @@
         "../../../browser/ui/webui/chromeos/certificate_manager_dialog_browsertest.js",
         "chromeos/account_manager/account_manager_browsertest.js",
         "chromeos/arc_account_picker/arc_account_picker_browsertest.js",
+        "chromeos/ash_common/ash_common_resources_browsertest.js",
         "chromeos/ash_common/post_message_api/post_message_api_browsertest.js",
         "chromeos/edu_coexistence/edu_coexistence_browsertest.js",
         "chromeos/emoji_picker/emoji_picker_browsertest.js",
@@ -454,7 +454,7 @@
   if (is_chromeos_ash) {
     in_files += [
       "cr_focus_row_behavior_test.ts",
-      "resources/list_property_update_behavior_tests.ts",
+      "chromeos/ash_common/list_property_update_behavior_tests.ts",
     ]
   }
 
diff --git a/chrome/test/data/webui/chromeos/ash_common/ash_common_resources_browsertest.js b/chrome/test/data/webui/chromeos/ash_common/ash_common_resources_browsertest.js
new file mode 100644
index 0000000..bb8c23e
--- /dev/null
+++ b/chrome/test/data/webui/chromeos/ash_common/ash_common_resources_browsertest.js
@@ -0,0 +1,34 @@
+// Copyright 2019 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/** @fileoverview Runs the WebUI resources tests. */
+
+// Polymer BrowserTest fixture.
+GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']);
+
+GEN('#include "content/public/test/browser_test.h"');
+
+var AshCommonResourcesBrowserTest = class extends PolymerTest {
+  /** @override */
+  get browsePreload() {
+    throw new Error('this is abstract and should be overridden by subclasses');
+  }
+
+  /** @override */
+  get webuiHost() {
+    return 'dummyurl';
+  }
+};
+
+var AshCommonResourcesListPropertyUpdateBehaviorTest =
+    class extends AshCommonResourcesBrowserTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://webui-test/test_loader.html?module=chromeos/ash_common/list_property_update_behavior_tests.js';
+  }
+};
+
+TEST_F('AshCommonResourcesListPropertyUpdateBehaviorTest', 'All', function() {
+  mocha.run();
+});
diff --git a/chrome/test/data/webui/resources/list_property_update_behavior_tests.ts b/chrome/test/data/webui/chromeos/ash_common/list_property_update_behavior_tests.ts
similarity index 98%
rename from chrome/test/data/webui/resources/list_property_update_behavior_tests.ts
rename to chrome/test/data/webui/chromeos/ash_common/list_property_update_behavior_tests.ts
index 48a85f3..593d5fa 100644
--- a/chrome/test/data/webui/resources/list_property_update_behavior_tests.ts
+++ b/chrome/test/data/webui/chromeos/ash_common/list_property_update_behavior_tests.ts
@@ -4,7 +4,7 @@
 
 /** @fileoverview Suite of tests for the ListPropertyUpdateBehavior.  */
 
-import {ListPropertyUpdateBehavior} from 'chrome://resources/js/list_property_update_behavior.js';
+import {ListPropertyUpdateBehavior} from 'chrome://resources/ash/common/list_property_update_behavior.js';
 import {mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/share_data_page_test.js b/chrome/test/data/webui/chromeos/os_feedback_ui/share_data_page_test.js
index 4d1e1b3..26006c9 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/share_data_page_test.js
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/share_data_page_test.js
@@ -268,6 +268,21 @@
     assertTrue(isVisible(performanceTraceContainer));
   });
 
+  // Test clicking performanceTraceLink link.
+  test('performanceTraceLink', async () => {
+    await initializePage();
+
+    // Set up performance trace id.
+    page.feedbackContext = fakeFeedbackContext;
+
+    const link = getElement('#performanceTraceLink');
+
+    assertEquals('_blank', link.getAttribute('target'));
+    // Performance trace id is the last number in the URL, which is 1.
+    assertEquals(
+        'chrome://slow_trace/tracing.zip#1', link.getAttribute('href'));
+  });
+
   /**
    * Test that when when the send button is clicked, an on-continue is fired.
    * Case 1: Share pageUrl, do not share system logs.
diff --git a/chrome/test/data/webui/chromeos/shortcut_customization/accelerator_edit_view_test.ts b/chrome/test/data/webui/chromeos/shortcut_customization/accelerator_edit_view_test.ts
index 157dabd..0e48fafa 100644
--- a/chrome/test/data/webui/chromeos/shortcut_customization/accelerator_edit_view_test.ts
+++ b/chrome/test/data/webui/chromeos/shortcut_customization/accelerator_edit_view_test.ts
@@ -11,7 +11,8 @@
 import {fakeAcceleratorConfig, fakeLayoutInfo} from 'chrome://shortcut-customization/js/fake_data.js';
 import {AcceleratorSource, Modifier} from 'chrome://shortcut-customization/js/shortcut_types.js';
 import {assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {flushTasks, isVisible} from 'chrome://webui-test/test_util.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
+import {isVisible} from 'chrome://webui-test/test_util.js';
 
 import {createDefaultAccelerator, createUserAccelerator} from './shortcut_customization_test_util.js';
 
diff --git a/chrome/test/data/webui/cr_components/BUILD.gn b/chrome/test/data/webui/cr_components/BUILD.gn
index 027a710..347820d 100644
--- a/chrome/test/data/webui/cr_components/BUILD.gn
+++ b/chrome/test/data/webui/cr_components/BUILD.gn
@@ -77,7 +77,6 @@
   extra_deps = [
     ":copy",
     ":preprocess",
-    "..:generate_definitions",
   ]
 }
 
diff --git a/chrome/test/data/webui/cr_components/app_management/file_handling_item_test.ts b/chrome/test/data/webui/cr_components/app_management/file_handling_item_test.ts
index 7e15885..d6849805 100644
--- a/chrome/test/data/webui/cr_components/app_management/file_handling_item_test.ts
+++ b/chrome/test/data/webui/cr_components/app_management/file_handling_item_test.ts
@@ -12,8 +12,7 @@
 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 {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
-import {waitAfterNextRender} from 'chrome://webui-test/test_util.js';
+import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
 
 import {createTestApp, TestAppManagementBrowserProxy} from './app_management_test_support.js';
 
diff --git a/chrome/test/data/webui/cr_components/app_management/window_mode_item_test.ts b/chrome/test/data/webui/cr_components/app_management/window_mode_item_test.ts
index 224f65a..65812bd 100644
--- a/chrome/test/data/webui/cr_components/app_management/window_mode_item_test.ts
+++ b/chrome/test/data/webui/cr_components/app_management/window_mode_item_test.ts
@@ -9,7 +9,8 @@
 import {BrowserProxy} from 'chrome://resources/cr_components/app_management/browser_proxy.js';
 import {AppManagementWindowModeElement} from 'chrome://resources/cr_components/app_management/window_mode_item.js';
 import {assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {flushTasks, isVisible} from 'chrome://webui-test/test_util.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
+import {isVisible} from 'chrome://webui-test/test_util.js';
 
 import {createTestApp, TestAppManagementBrowserProxy} from './app_management_test_support.js';
 
diff --git a/chrome/test/data/webui/cr_components/help_bubble_mixin_test.ts b/chrome/test/data/webui/cr_components/help_bubble_mixin_test.ts
index 54f8b0f..5eaaaae8 100644
--- a/chrome/test/data/webui/cr_components/help_bubble_mixin_test.ts
+++ b/chrome/test/data/webui/cr_components/help_bubble_mixin_test.ts
@@ -12,8 +12,9 @@
 import {HelpBubbleProxy, HelpBubbleProxyImpl} from 'chrome://resources/cr_components/help_bubble/help_bubble_proxy.js';
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
-import {isVisible, waitAfterNextRender} from 'chrome://webui-test/test_util.js';
+import {isVisible} from 'chrome://webui-test/test_util.js';
 
 const TITLE_NATIVE_ID: string = 'kHelpBubbleMixinTestTitleElementId';
 const PARAGRAPH_NATIVE_ID: string = 'kHelpBubbleMixinTestParagraphElementId';
diff --git a/chrome/test/data/webui/cr_components/help_bubble_test.ts b/chrome/test/data/webui/cr_components/help_bubble_test.ts
index dbb0fd85..02e65b7 100644
--- a/chrome/test/data/webui/cr_components/help_bubble_test.ts
+++ b/chrome/test/data/webui/cr_components/help_bubble_test.ts
@@ -10,7 +10,8 @@
 import {HELP_BUBBLE_DISMISSED_EVENT, HELP_BUBBLE_TIMED_OUT_EVENT, HelpBubbleDismissedEvent, HelpBubbleElement, HelpBubbleTimedOutEvent} from 'chrome://resources/cr_components/help_bubble/help_bubble.js';
 import {HelpBubbleArrowPosition, HelpBubbleButtonParams} from 'chrome://resources/cr_components/help_bubble/help_bubble.mojom-webui.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {isVisible, waitAfterNextRender} from 'chrome://webui-test/test_util.js';
+import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
+import {isVisible} from 'chrome://webui-test/test_util.js';
 
 interface WaitForSuccessParams {
   retryIntervalMs: number;
diff --git a/chrome/test/data/webui/cr_components/localized_link_test.ts b/chrome/test/data/webui/cr_components/localized_link_test.ts
index 25e6595..8ed1aea 100644
--- a/chrome/test/data/webui/cr_components/localized_link_test.ts
+++ b/chrome/test/data/webui/cr_components/localized_link_test.ts
@@ -6,7 +6,8 @@
 
 import {LocalizedLinkElement} from '//resources/cr_components/localized_link/localized_link.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {eventToPromise, flushTasks} from 'chrome://webui-test/test_util.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 suite('localized_link', function() {
   let localizedStringWithLink: LocalizedLinkElement|null;
diff --git a/chrome/test/data/webui/cr_components/managed_footnote_test.ts b/chrome/test/data/webui/cr_components/managed_footnote_test.ts
index 9f51bd8..f992b8e 100644
--- a/chrome/test/data/webui/cr_components/managed_footnote_test.ts
+++ b/chrome/test/data/webui/cr_components/managed_footnote_test.ts
@@ -11,7 +11,7 @@
 import {webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
 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 { assertEquals,assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {assertEquals,assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
 // clang-format on
 
diff --git a/chrome/test/data/webui/cr_components/most_visited_test.ts b/chrome/test/data/webui/cr_components/most_visited_test.ts
index 4e3770c4..65dd938 100644
--- a/chrome/test/data/webui/cr_components/most_visited_test.ts
+++ b/chrome/test/data/webui/cr_components/most_visited_test.ts
@@ -15,8 +15,9 @@
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {TextDirection} from 'chrome://resources/mojo/mojo/public/mojom/base/text_direction.mojom-webui.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
-import {eventToPromise, flushTasks} from 'chrome://webui-test/test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {$$, assertNotStyle, assertStyle, keydown} from './most_visited_test_support.js';
 
diff --git a/chrome/test/data/webui/cr_elements/BUILD.gn b/chrome/test/data/webui/cr_elements/BUILD.gn
index d65473f8..cefacd9a 100644
--- a/chrome/test/data/webui/cr_elements/BUILD.gn
+++ b/chrome/test/data/webui/cr_elements/BUILD.gn
@@ -103,7 +103,6 @@
   extra_deps = [
     ":copy",
     ":preprocess",
-    "..:generate_definitions",
   ]
 }
 
diff --git a/chrome/test/data/webui/cr_elements/cr_action_menu_test.ts b/chrome/test/data/webui/cr_elements/cr_action_menu_test.ts
index e2d0212..314a354 100644
--- a/chrome/test/data/webui/cr_elements/cr_action_menu_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_action_menu_test.ts
@@ -13,7 +13,8 @@
 import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {eventToPromise, flushTasks} from 'chrome://webui-test/test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 // clang-format on
 
 /**
diff --git a/chrome/test/data/webui/cr_elements/cr_dialog_test.ts b/chrome/test/data/webui/cr_elements/cr_dialog_test.ts
index 8319b99..a230ebd9 100644
--- a/chrome/test/data/webui/cr_elements/cr_dialog_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_dialog_test.ts
@@ -10,7 +10,8 @@
 import {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js';
 import {keyDownOn, keyEventOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {assertEquals, assertFalse, assertNotEquals, assertNotReached, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {eventToPromise, flushTasks} from 'chrome://webui-test/test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
 // clang-format on
 
diff --git a/chrome/test/data/webui/cr_elements/cr_icon_button_tests.ts b/chrome/test/data/webui/cr_elements/cr_icon_button_tests.ts
index 7025e94d..24ff048 100644
--- a/chrome/test/data/webui/cr_elements/cr_icon_button_tests.ts
+++ b/chrome/test/data/webui/cr_elements/cr_icon_button_tests.ts
@@ -10,7 +10,8 @@
 import {CrIconButtonElement} from 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
 import {downAndUp, pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {eventToPromise, flushTasks} from 'chrome://webui-test/test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
 // clang-format on
 
diff --git a/chrome/test/data/webui/cr_elements/cr_slider_test.ts b/chrome/test/data/webui/cr_elements/cr_slider_test.ts
index b2838643..c57af391 100644
--- a/chrome/test/data/webui/cr_elements/cr_slider_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_slider_test.ts
@@ -6,12 +6,11 @@
 import 'chrome://resources/cr_elements/cr_slider/cr_slider.js';
 
 import {CrSliderElement} from 'chrome://resources/cr_elements/cr_slider/cr_slider.js';
-
 import {pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.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, flushTasks} from 'chrome://webui-test/test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 // clang-format on
 
 suite('cr-slider', function() {
diff --git a/chrome/test/data/webui/cr_elements/cr_tabs_test.ts b/chrome/test/data/webui/cr_elements/cr_tabs_test.ts
index 18f0d11..dc31437c 100644
--- a/chrome/test/data/webui/cr_elements/cr_tabs_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_tabs_test.ts
@@ -6,11 +6,10 @@
 import 'chrome://resources/cr_elements/cr_tabs/cr_tabs.js';
 
 import {CrTabsElement} from 'chrome://resources/cr_elements/cr_tabs/cr_tabs.js';
-
 import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
-
 import {assertEquals, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {eventToPromise, flushTasks} from 'chrome://webui-test/test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 // clang-format on
 
 suite('cr_tabs_test', function() {
diff --git a/chrome/test/data/webui/cr_focus_row_behavior_test.ts b/chrome/test/data/webui/cr_focus_row_behavior_test.ts
index e4f1f8e..6a223c0 100644
--- a/chrome/test/data/webui/cr_focus_row_behavior_test.ts
+++ b/chrome/test/data/webui/cr_focus_row_behavior_test.ts
@@ -7,9 +7,11 @@
 import {getDeepActiveElement} from 'chrome://resources/js/util.js';
 import {down, pressAndReleaseKeyOn, up} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {html, PolymerElement, mixinBehaviors} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {eventToPromise, waitAfterNextRender} from 'chrome://webui-test/test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 import {assertFalse, assertTrue, assertEquals} from 'chrome://webui-test/chai_assert.js';
 
+import {waitAfterNextRender} from './polymer_test_util.js';
+
 // clang-format on
 
 class ButtonThreeElement extends PolymerElement {
diff --git a/chrome/test/data/webui/cr_focus_row_mixin_test.ts b/chrome/test/data/webui/cr_focus_row_mixin_test.ts
index 95096e83..8696691 100644
--- a/chrome/test/data/webui/cr_focus_row_mixin_test.ts
+++ b/chrome/test/data/webui/cr_focus_row_mixin_test.ts
@@ -7,7 +7,10 @@
 import {getDeepActiveElement} from 'chrome://resources/js/util.js';
 import {down, pressAndReleaseKeyOn, up} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {eventToPromise, waitAfterNextRender} from 'chrome://webui-test/test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
+
+import {waitAfterNextRender} from './polymer_test_util.js';
+
 import {assertFalse, assertTrue, assertEquals} from 'chrome://webui-test/chai_assert.js';
 
 // clang-format on
diff --git a/chrome/test/data/webui/extensions/pack_dialog_test.ts b/chrome/test/data/webui/extensions/pack_dialog_test.ts
index b2894ff3..050dbb1 100644
--- a/chrome/test/data/webui/extensions/pack_dialog_test.ts
+++ b/chrome/test/data/webui/extensions/pack_dialog_test.ts
@@ -11,7 +11,8 @@
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.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, flushTasks} from 'chrome://webui-test/test_util.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {isElementVisible} from './test_util.js';
 
diff --git a/chrome/test/data/webui/history/history_item_focus_test.ts b/chrome/test/data/webui/history/history_item_focus_test.ts
index 6021fe57..8a7b24d 100644
--- a/chrome/test/data/webui/history/history_item_focus_test.ts
+++ b/chrome/test/data/webui/history/history_item_focus_test.ts
@@ -6,7 +6,8 @@
 
 import {BrowserServiceImpl, HistoryItemElement} from 'chrome://history/history.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {eventToPromise, flushTasks, waitAfterNextRender} from 'chrome://webui-test/test_util.js';
+import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {TestBrowserService} from './test_browser_service.js';
 import {createHistoryEntry} from './test_util.js';
diff --git a/chrome/test/data/webui/history/history_list_focus_test.ts b/chrome/test/data/webui/history/history_list_focus_test.ts
index f636b5d..e3f337d 100644
--- a/chrome/test/data/webui/history/history_list_focus_test.ts
+++ b/chrome/test/data/webui/history/history_list_focus_test.ts
@@ -8,7 +8,8 @@
 import {pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {eventToPromise, flushTasks, waitAfterNextRender} from 'chrome://webui-test/test_util.js';
+import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {TestBrowserService} from './test_browser_service.js';
 import {createHistoryEntry, createHistoryInfo} from './test_util.js';
diff --git a/chrome/test/data/webui/history/history_supervised_user_test.ts b/chrome/test/data/webui/history/history_supervised_user_test.ts
index 648b781..6013fc7 100644
--- a/chrome/test/data/webui/history/history_supervised_user_test.ts
+++ b/chrome/test/data/webui/history/history_supervised_user_test.ts
@@ -4,7 +4,8 @@
 
 import {BrowserServiceImpl, ensureLazyLoaded, HistoryAppElement, HistoryEntry, HistoryListElement, HistoryToolbarElement} from 'chrome://history/history.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {eventToPromise, flushTasks} from 'chrome://webui-test/test_util.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {TestBrowserService} from './test_browser_service.js';
 import {createHistoryEntry, createHistoryInfo} from './test_util.js';
diff --git a/chrome/test/data/webui/new_tab_page/app_test.ts b/chrome/test/data/webui/new_tab_page/app_test.ts
index d54d016..820fe78 100644
--- a/chrome/test/data/webui/new_tab_page/app_test.ts
+++ b/chrome/test/data/webui/new_tab_page/app_test.ts
@@ -12,8 +12,9 @@
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
-import {eventToPromise, flushTasks} from 'chrome://webui-test/test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {fakeMetricsPrivate, MetricsTracker} from './metrics_test_support.js';
 import {assertNotStyle, assertStyle, createBackgroundImage, createTheme, installMock} from './test_support.js';
diff --git a/chrome/test/data/webui/new_tab_page/customize_backgrounds_test.ts b/chrome/test/data/webui/new_tab_page/customize_backgrounds_test.ts
index 4255f5e..900db354 100644
--- a/chrome/test/data/webui/new_tab_page/customize_backgrounds_test.ts
+++ b/chrome/test/data/webui/new_tab_page/customize_backgrounds_test.ts
@@ -9,8 +9,9 @@
 import {NewTabPageProxy, WindowProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {BackgroundCollection, CollectionImage, PageCallbackRouter, PageHandlerRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
-import {eventToPromise, flushTasks, isVisible} from 'chrome://webui-test/test_util.js';
+import {eventToPromise, isVisible} from 'chrome://webui-test/test_util.js';
 
 import {assertNotStyle, assertStyle, createBackgroundImage, createTheme, installMock} from './test_support.js';
 
diff --git a/chrome/test/data/webui/new_tab_page/logo_test.ts b/chrome/test/data/webui/new_tab_page/logo_test.ts
index 6b4bd50..1d6082966 100644
--- a/chrome/test/data/webui/new_tab_page/logo_test.ts
+++ b/chrome/test/data/webui/new_tab_page/logo_test.ts
@@ -9,8 +9,9 @@
 import {hexColorToSkColor, skColorToRgba} from 'chrome://resources/js/color_utils.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertGE, assertLE, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
-import {eventToPromise, flushTasks} from 'chrome://webui-test/test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {assertNotStyle, assertStyle, installMock, keydown} from './test_support.js';
 
diff --git a/chrome/test/data/webui/new_tab_page/modules/cart/module_test.ts b/chrome/test/data/webui/new_tab_page/modules/cart/module_test.ts
index 17a4d650..0d7e72b 100644
--- a/chrome/test/data/webui/new_tab_page/modules/cart/module_test.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/cart/module_test.ts
@@ -9,11 +9,13 @@
 import {$$, CrAutoImgElement} from 'chrome://new-tab-page/new_tab_page.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
-import {eventToPromise, flushTasks, isVisible} from 'chrome://webui-test/test_util.js';
+import {eventToPromise, isVisible} from 'chrome://webui-test/test_util.js';
 
 import {fakeMetricsPrivate, MetricsTracker} from '../../metrics_test_support.js';
 import {assertNotStyle, installMock} from '../../test_support.js';
+
 import {clickAcceptButton, clickCloseButton, clickRejectButton, nextStep} from './discount_consent_card_test_utils.js';
 
 suite('NewTabPageModulesChromeCartModuleTest', () => {
diff --git a/chrome/test/data/webui/new_tab_page/modules/cart_v2/module_test.ts b/chrome/test/data/webui/new_tab_page/modules/cart_v2/module_test.ts
index edd3c28d7..25d6593 100644
--- a/chrome/test/data/webui/new_tab_page/modules/cart_v2/module_test.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/cart_v2/module_test.ts
@@ -9,8 +9,9 @@
 import {$$, CrAutoImgElement} from 'chrome://new-tab-page/new_tab_page.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
-import {eventToPromise, flushTasks, isVisible} from 'chrome://webui-test/test_util.js';
+import {eventToPromise, isVisible} from 'chrome://webui-test/test_util.js';
 
 import {fakeMetricsPrivate, MetricsTracker} from '../../metrics_test_support.js';
 import {assertNotStyle, installMock} from '../../test_support.js';
diff --git a/chrome/test/data/webui/new_tab_page/modules/recipes/module_test.ts b/chrome/test/data/webui/new_tab_page/modules/recipes/module_test.ts
index 98c25b4..a7b03ed9 100644
--- a/chrome/test/data/webui/new_tab_page/modules/recipes/module_test.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/recipes/module_test.ts
@@ -4,13 +4,14 @@
 
 import 'chrome://webui-test/mojo_webui_test_support.js';
 
-import {DismissModuleEvent, RecipesModuleElement, RecipesHandlerProxy, recipeTasksDescriptor} from 'chrome://new-tab-page/lazy_load.js';
+import {DismissModuleEvent, RecipesHandlerProxy, RecipesModuleElement, recipeTasksDescriptor} from 'chrome://new-tab-page/lazy_load.js';
 import {$$, CrAutoImgElement} from 'chrome://new-tab-page/new_tab_page.js';
 import {RecipesHandlerRemote} from 'chrome://new-tab-page/recipes.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
-import {eventToPromise, flushTasks} from 'chrome://webui-test/test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {installMock} from '../../test_support.js';
 
diff --git a/chrome/test/data/webui/new_tab_page/voice_search_overlay_test.ts b/chrome/test/data/webui/new_tab_page/voice_search_overlay_test.ts
index 01a22e16..76b5c729 100644
--- a/chrome/test/data/webui/new_tab_page/voice_search_overlay_test.ts
+++ b/chrome/test/data/webui/new_tab_page/voice_search_overlay_test.ts
@@ -9,8 +9,9 @@
 import {$$, NewTabPageProxy, VoiceAction as Action, VoiceError as Error, WindowProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {PageCallbackRouter, PageHandlerRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
-import {flushTasks, isVisible} from 'chrome://webui-test/test_util.js';
+import {isVisible} from 'chrome://webui-test/test_util.js';
 
 import {assertEquals, assertFalse, assertTrue} from '../chai_assert.js';
 
diff --git a/chrome/test/data/webui/password_manager/checkup_section_test.ts b/chrome/test/data/webui/password_manager/checkup_section_test.ts
index 144775a..95cd044a 100644
--- a/chrome/test/data/webui/password_manager/checkup_section_test.ts
+++ b/chrome/test/data/webui/password_manager/checkup_section_test.ts
@@ -6,7 +6,8 @@
 
 import {PasswordCheckInteraction, PasswordManagerImpl} from 'chrome://password-manager/password_manager.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {flushTasks, isVisible} from 'chrome://webui-test/test_util.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
+import {isVisible} from 'chrome://webui-test/test_util.js';
 
 import {TestPasswordManagerProxy} from './test_password_manager_proxy.js';
 import {makePasswordCheckStatus} from './test_util.js';
diff --git a/chrome/test/data/webui/password_manager/password_manager_app_test.ts b/chrome/test/data/webui/password_manager/password_manager_app_test.ts
index 3bcbf9b..c3c24a1 100644
--- a/chrome/test/data/webui/password_manager/password_manager_app_test.ts
+++ b/chrome/test/data/webui/password_manager/password_manager_app_test.ts
@@ -4,10 +4,11 @@
 
 import 'chrome://password-manager/password_manager.js';
 
-import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {Page, PasswordManagerAppElement, Router, UrlParam} from 'chrome://password-manager/password_manager.js';
-import {assertEquals, assertTrue, assertFalse} from 'chrome://webui-test/chai_assert.js';
-import {flushTasks, isVisible, eventToPromise} from 'chrome://webui-test/test_util.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 {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
+import {eventToPromise, isVisible} from 'chrome://webui-test/test_util.js';
 
 suite('PasswordManagerAppTest', function() {
   let app: PasswordManagerAppElement;
diff --git a/chrome/test/data/webui/password_manager/password_manager_side_bar_test.ts b/chrome/test/data/webui/password_manager/password_manager_side_bar_test.ts
index 73a4b39..530c6d44 100644
--- a/chrome/test/data/webui/password_manager/password_manager_side_bar_test.ts
+++ b/chrome/test/data/webui/password_manager/password_manager_side_bar_test.ts
@@ -6,7 +6,8 @@
 
 import {Page, PasswordManagerSideBarElement, Router} from 'chrome://password-manager/password_manager.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {flushTasks, isVisible} from 'chrome://webui-test/test_util.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
+import {isVisible} from 'chrome://webui-test/test_util.js';
 
 suite('PasswordManagerSideBarTest', function() {
   let sidebar: PasswordManagerSideBarElement;
diff --git a/chrome/test/data/webui/print_preview/advanced_dialog_test.ts b/chrome/test/data/webui/print_preview/advanced_dialog_test.ts
index 5df0d2e..643e88d 100644
--- a/chrome/test/data/webui/print_preview/advanced_dialog_test.ts
+++ b/chrome/test/data/webui/print_preview/advanced_dialog_test.ts
@@ -6,9 +6,9 @@
 import {assert} from 'chrome://resources/js/assert.js';
 import {keyEventOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.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';
+import {fakeDataBind} from 'chrome://webui-test/polymer_test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {getCddTemplateWithAdvancedSettings} from './print_preview_test_utils.js';
 
diff --git a/chrome/test/data/webui/print_preview/color_settings_test.ts b/chrome/test/data/webui/print_preview/color_settings_test.ts
index e222731c..e7b3656d 100644
--- a/chrome/test/data/webui/print_preview/color_settings_test.ts
+++ b/chrome/test/data/webui/print_preview/color_settings_test.ts
@@ -5,9 +5,9 @@
 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 {fakeDataBind} from 'chrome://webui-test/polymer_test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {selectOption} from './print_preview_test_utils.js';
 
diff --git a/chrome/test/data/webui/print_preview/custom_margins_test.ts b/chrome/test/data/webui/print_preview/custom_margins_test.ts
index 95133e5c..c3b9867b 100644
--- a/chrome/test/data/webui/print_preview/custom_margins_test.ts
+++ b/chrome/test/data/webui/print_preview/custom_margins_test.ts
@@ -5,9 +5,9 @@
 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.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';
+import {fakeDataBind} from 'chrome://webui-test/polymer_test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 const custom_margins_test = {
   suiteName: 'CustomMarginsTest',
diff --git a/chrome/test/data/webui/print_preview/destination_dialog_cros_interactive_test.ts b/chrome/test/data/webui/print_preview/destination_dialog_cros_interactive_test.ts
index 1033e1f..339f05c 100644
--- a/chrome/test/data/webui/print_preview/destination_dialog_cros_interactive_test.ts
+++ b/chrome/test/data/webui/print_preview/destination_dialog_cros_interactive_test.ts
@@ -5,9 +5,9 @@
 import {NativeLayerImpl, PrintPreviewDestinationDialogCrosElement, State} from 'chrome://print/print_preview.js';
 import {assert} from 'chrome://resources/js/assert.js';
 import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
-
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {eventToPromise, fakeDataBind} from 'chrome://webui-test/test_util.js';
+import {fakeDataBind} from 'chrome://webui-test/polymer_test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {setNativeLayerCrosInstance} from './native_layer_cros_stub.js';
 import {NativeLayerStub} from './native_layer_stub.js';
diff --git a/chrome/test/data/webui/print_preview/destination_dialog_cros_test.ts b/chrome/test/data/webui/print_preview/destination_dialog_cros_test.ts
index b76a684..d7e712e 100644
--- a/chrome/test/data/webui/print_preview/destination_dialog_cros_test.ts
+++ b/chrome/test/data/webui/print_preview/destination_dialog_cros_test.ts
@@ -8,7 +8,8 @@
 import {keyEventOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {eventToPromise, waitAfterNextRender} from 'chrome://webui-test/test_util.js';
+import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {NativeLayerCrosStub, setNativeLayerCrosInstance} from './native_layer_cros_stub.js';
 import {NativeLayerStub} from './native_layer_stub.js';
diff --git a/chrome/test/data/webui/print_preview/destination_dialog_interactive_test.ts b/chrome/test/data/webui/print_preview/destination_dialog_interactive_test.ts
index 4d050d37..6c92392 100644
--- a/chrome/test/data/webui/print_preview/destination_dialog_interactive_test.ts
+++ b/chrome/test/data/webui/print_preview/destination_dialog_interactive_test.ts
@@ -5,9 +5,9 @@
 import {NativeLayerImpl, PrintPreviewDestinationDialogElement, State} from 'chrome://print/print_preview.js';
 import {assert} from 'chrome://resources/js/assert.js';
 import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
-
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {eventToPromise, fakeDataBind} from 'chrome://webui-test/test_util.js';
+import {fakeDataBind} from 'chrome://webui-test/polymer_test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {NativeLayerStub} from './native_layer_stub.js';
 import {setupTestListenerElement} from './print_preview_test_utils.js';
diff --git a/chrome/test/data/webui/print_preview/destination_settings_test.ts b/chrome/test/data/webui/print_preview/destination_settings_test.ts
index 73d6c68..53e2dee8 100644
--- a/chrome/test/data/webui/print_preview/destination_settings_test.ts
+++ b/chrome/test/data/webui/print_preview/destination_settings_test.ts
@@ -6,8 +6,8 @@
 import {assert} from 'chrome://resources/js/assert.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 {waitBeforeNextRender} from 'chrome://webui-test/polymer_test_util.js';
-import {eventToPromise, fakeDataBind} from 'chrome://webui-test/test_util.js';
+import {fakeDataBind, waitBeforeNextRender} from 'chrome://webui-test/polymer_test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 // <if expr="is_chromeos">
 import {NativeLayerCrosStub, setNativeLayerCrosInstance} from './native_layer_cros_stub.js';
diff --git a/chrome/test/data/webui/print_preview/duplex_settings_test.ts b/chrome/test/data/webui/print_preview/duplex_settings_test.ts
index 7a85c41..5ba43b0 100644
--- a/chrome/test/data/webui/print_preview/duplex_settings_test.ts
+++ b/chrome/test/data/webui/print_preview/duplex_settings_test.ts
@@ -4,9 +4,9 @@
 
 import {DuplexMode, PrintPreviewDuplexSettingsElement, PrintPreviewModelElement} from 'chrome://print/print_preview.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';
+import {fakeDataBind} from 'chrome://webui-test/polymer_test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {selectOption} from './print_preview_test_utils.js';
 
diff --git a/chrome/test/data/webui/print_preview/key_event_test.ts b/chrome/test/data/webui/print_preview/key_event_test.ts
index 2f60002..f436e1b 100644
--- a/chrome/test/data/webui/print_preview/key_event_test.ts
+++ b/chrome/test/data/webui/print_preview/key_event_test.ts
@@ -7,9 +7,9 @@
 import {isChromeOS, isLacros, isMac, isWindows} from 'chrome://resources/js/cr.m.js';
 import {keyEventOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {eventToPromise, flushTasks} from 'chrome://webui-test/test_util.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 // <if expr="is_chromeos">
 import {setNativeLayerCrosInstance} from './native_layer_cros_stub.js';
diff --git a/chrome/test/data/webui/print_preview/layout_settings_test.ts b/chrome/test/data/webui/print_preview/layout_settings_test.ts
index a3ad4afc..9ad3c24 100644
--- a/chrome/test/data/webui/print_preview/layout_settings_test.ts
+++ b/chrome/test/data/webui/print_preview/layout_settings_test.ts
@@ -3,10 +3,11 @@
 // found in the LICENSE file.
 
 import 'chrome://print/print_preview.js';
-import {PrintPreviewLayoutSettingsElement} from 'chrome://print/print_preview.js';
 
+import {PrintPreviewLayoutSettingsElement} 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 {fakeDataBind} from 'chrome://webui-test/polymer_test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {selectOption} from './print_preview_test_utils.js';
 
diff --git a/chrome/test/data/webui/print_preview/margins_settings_test.ts b/chrome/test/data/webui/print_preview/margins_settings_test.ts
index b6f61c64..4b85d35 100644
--- a/chrome/test/data/webui/print_preview/margins_settings_test.ts
+++ b/chrome/test/data/webui/print_preview/margins_settings_test.ts
@@ -3,9 +3,9 @@
 // found in the LICENSE file.
 
 import {MarginsType, PrintPreviewMarginsSettingsElement, PrintPreviewModelElement, State} 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 {fakeDataBind} from 'chrome://webui-test/polymer_test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {selectOption} from './print_preview_test_utils.js';
 
diff --git a/chrome/test/data/webui/print_preview/other_options_settings_test.ts b/chrome/test/data/webui/print_preview/other_options_settings_test.ts
index 47aafdb..379f92a 100644
--- a/chrome/test/data/webui/print_preview/other_options_settings_test.ts
+++ b/chrome/test/data/webui/print_preview/other_options_settings_test.ts
@@ -7,7 +7,8 @@
 import {CrCheckboxElement, PrintPreviewModelElement, PrintPreviewOtherOptionsSettingsElement} from 'chrome://print/print_preview.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';
+import {fakeDataBind} from 'chrome://webui-test/polymer_test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 suite('OtherOptionsSettingsTest', function() {
   let otherOptionsSection: PrintPreviewOtherOptionsSettingsElement;
diff --git a/chrome/test/data/webui/print_preview/pages_per_sheet_settings_test.ts b/chrome/test/data/webui/print_preview/pages_per_sheet_settings_test.ts
index 8d74dc5..2ea1f33 100644
--- a/chrome/test/data/webui/print_preview/pages_per_sheet_settings_test.ts
+++ b/chrome/test/data/webui/print_preview/pages_per_sheet_settings_test.ts
@@ -3,9 +3,12 @@
 // found in the LICENSE file.
 
 import 'chrome://print/print_preview.js';
+
 import {PrintPreviewPagesPerSheetSettingsElement} 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 {fakeDataBind} from 'chrome://webui-test/polymer_test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
+
 import {selectOption} from './print_preview_test_utils.js';
 
 suite('PagesPerSheetSettingsTest', function() {
diff --git a/chrome/test/data/webui/print_preview/pages_settings_test.ts b/chrome/test/data/webui/print_preview/pages_settings_test.ts
index 20402e2..d45674a 100644
--- a/chrome/test/data/webui/print_preview/pages_settings_test.ts
+++ b/chrome/test/data/webui/print_preview/pages_settings_test.ts
@@ -9,7 +9,8 @@
 import {keyEventOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.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';
+import {fakeDataBind} from 'chrome://webui-test/polymer_test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {selectOption, triggerInputEvent} from './print_preview_test_utils.js';
 
diff --git a/chrome/test/data/webui/print_preview/pdf_viewer_test.ts b/chrome/test/data/webui/print_preview/pdf_viewer_test.ts
index 9af336f8..990ef4a 100644
--- a/chrome/test/data/webui/print_preview/pdf_viewer_test.ts
+++ b/chrome/test/data/webui/print_preview/pdf_viewer_test.ts
@@ -11,7 +11,8 @@
 import {pdfCreateOutOfProcessPlugin} from 'chrome://print/pdf/pdf_scripting_api.js';
 import {PDFViewerPPElement} from 'chrome://print/pdf/pdf_viewer_pp.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {eventToPromise, waitAfterNextRender} from 'chrome://webui-test/test_util.js';
+import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 const pdf_viewer_test = {
   suiteName: 'PdfViewerTest',
diff --git a/chrome/test/data/webui/print_preview/scaling_settings_interactive_test.ts b/chrome/test/data/webui/print_preview/scaling_settings_interactive_test.ts
index db9802b..23b5c4f 100644
--- a/chrome/test/data/webui/print_preview/scaling_settings_interactive_test.ts
+++ b/chrome/test/data/webui/print_preview/scaling_settings_interactive_test.ts
@@ -6,7 +6,8 @@
 import {assert} from 'chrome://resources/js/assert.js';
 import {getDeepActiveElement} from 'chrome://resources/js/util.js';
 import {assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {eventToPromise, fakeDataBind} from 'chrome://webui-test/test_util.js';
+import {fakeDataBind} from 'chrome://webui-test/polymer_test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {selectOption} from './print_preview_test_utils.js';
 
diff --git a/chrome/test/data/webui/print_preview/settings_select_test.ts b/chrome/test/data/webui/print_preview/settings_select_test.ts
index 076cbd3..4c7ab25 100644
--- a/chrome/test/data/webui/print_preview/settings_select_test.ts
+++ b/chrome/test/data/webui/print_preview/settings_select_test.ts
@@ -7,7 +7,8 @@
 import {PrintPreviewModelElement, PrintPreviewSettingsSelectElement} from 'chrome://print/print_preview.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';
+import {fakeDataBind} from 'chrome://webui-test/polymer_test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {getMediaSizeCapabilityWithCustomNames, selectOption} from './print_preview_test_utils.js';
 
diff --git a/chrome/test/data/webui/resources/webui_resources_browsertest.js b/chrome/test/data/webui/resources/webui_resources_browsertest.js
deleted file mode 100644
index 9c67075..0000000
--- a/chrome/test/data/webui/resources/webui_resources_browsertest.js
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/** @fileoverview Runs the WebUI resources tests. */
-
-// Polymer BrowserTest fixture.
-GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']);
-
-GEN('#include "build/chromeos_buildflags.h"');
-GEN('#include "content/public/test/browser_test.h"');
-
-var WebUIResourcesBrowserTest = class extends PolymerTest {
-  /** @override */
-  get browsePreload() {
-    throw new Error('this is abstract and should be overridden by subclasses');
-  }
-
-  /** @override */
-  get webuiHost() {
-    return 'dummyurl';
-  }
-};
-
-GEN('#if BUILDFLAG(IS_CHROMEOS_ASH)');
-var WebUIResourcesListPropertyUpdateBehaviorTest =
-    class extends WebUIResourcesBrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://webui-test/test_loader.html?module=resources/list_property_update_behavior_tests.js';
-  }
-};
-
-TEST_F('WebUIResourcesListPropertyUpdateBehaviorTest', 'All', function() {
-  mocha.run();
-});
-GEN('#endif');
diff --git a/chrome/test/data/webui/settings/all_sites_tests.ts b/chrome/test/data/webui/settings/all_sites_tests.ts
index fe6ec86..cb6b46d 100644
--- a/chrome/test/data/webui/settings/all_sites_tests.ts
+++ b/chrome/test/data/webui/settings/all_sites_tests.ts
@@ -8,7 +8,8 @@
 import {AllSitesElement, ContentSetting, ContentSettingsTypes, LocalDataBrowserProxyImpl, SiteGroup, SiteSettingsPrefsBrowserProxyImpl, SortMethod} from 'chrome://settings/lazy_load.js';
 import {CrSettingsPrefs, Router, routes} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {flushTasks, isChildVisible} from 'chrome://webui-test/test_util.js';
+import {isChildVisible} from 'chrome://webui-test/test_util.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
 import {TestLocalDataBrowserProxy} from './test_local_data_browser_proxy.js';
 import {TestSiteSettingsPrefsBrowserProxy} from './test_site_settings_prefs_browser_proxy.js';
@@ -387,6 +388,28 @@
     }
   });
 
+  test('clear data "no sites" string', async function() {
+    testElement.siteGroupMap.set(
+        TEST_MULTIPLE_SITE_GROUP.etldPlus1,
+        JSON.parse(JSON.stringify(TEST_MULTIPLE_SITE_GROUP)));
+    const googleSiteGroup = createSiteGroup('google.com', [
+      'https://www.google.com',
+      'https://docs.google.com',
+      'https://mail.google.com',
+    ]);
+    testElement.siteGroupMap.set(googleSiteGroup.etldPlus1, googleSiteGroup);
+    testElement.filter = 'google';
+    testElement.forceListUpdateForTesting();
+    await flushTasks();
+
+    assertFalse(isChildVisible(testElement, '#noSitesFoundText'));
+
+    clearDataViaClearAllButton('action-button');
+    await flushTasks();
+
+    assertTrue(isChildVisible(testElement, '#noSitesFoundText'));
+  });
+
   test('can be sorted by storage', async function() {
     setUpAllSites(prefsVarious);
     testElement.currentRouteChanged(routes.SITE_SETTINGS_ALL);
diff --git a/chrome/test/data/webui/settings/appearance_fonts_page_test.ts b/chrome/test/data/webui/settings/appearance_fonts_page_test.ts
index 15ca1de7..4bb8ab1 100644
--- a/chrome/test/data/webui/settings/appearance_fonts_page_test.ts
+++ b/chrome/test/data/webui/settings/appearance_fonts_page_test.ts
@@ -7,7 +7,7 @@
 
 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 { FontsBrowserProxy, FontsBrowserProxyImpl, FontsData,SettingsAppearanceFontsPageElement} from 'chrome://settings/lazy_load.js';
+import {FontsBrowserProxy, FontsBrowserProxyImpl, FontsData,SettingsAppearanceFontsPageElement} from 'chrome://settings/lazy_load.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
diff --git a/chrome/test/data/webui/settings/appearance_page_test.ts b/chrome/test/data/webui/settings/appearance_page_test.ts
index 2332fff..877dff8 100644
--- a/chrome/test/data/webui/settings/appearance_page_test.ts
+++ b/chrome/test/data/webui/settings/appearance_page_test.ts
@@ -4,8 +4,8 @@
 
 // clang-format off
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import { AppearanceBrowserProxy, AppearanceBrowserProxyImpl,HomeUrlInputElement, SettingsAppearancePageElement, SystemTheme} from 'chrome://settings/settings.js';
-import { assertEquals,assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {AppearanceBrowserProxy, AppearanceBrowserProxyImpl,HomeUrlInputElement, SettingsAppearancePageElement, SystemTheme} from 'chrome://settings/settings.js';
+import {assertEquals,assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 // clang-format on
diff --git a/chrome/test/data/webui/settings/basic_page_test.ts b/chrome/test/data/webui/settings/basic_page_test.ts
index 3b14446..f9430203 100644
--- a/chrome/test/data/webui/settings/basic_page_test.ts
+++ b/chrome/test/data/webui/settings/basic_page_test.ts
@@ -14,7 +14,8 @@
 import {CrSettingsPrefs, MetricsBrowserProxyImpl, pageVisibility, PrivacyGuideBrowserProxy, PrivacyGuideBrowserProxyImpl, PrivacyGuideInteractions, Router, routes, SettingsBasicPageElement, SettingsIdleLoadElement, SettingsPrefsElement, SettingsSectionElement, StatusAction, SyncStatus} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
-import {eventToPromise, flushTasks, isChildVisible, isVisible} from 'chrome://webui-test/test_util.js';
+import {eventToPromise, isChildVisible, isVisible} from 'chrome://webui-test/test_util.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
 import {TestMetricsBrowserProxy} from './test_metrics_browser_proxy.js';
 
diff --git a/chrome/test/data/webui/settings/chooser_exception_list_tests.ts b/chrome/test/data/webui/settings/chooser_exception_list_tests.ts
index 6bfae68..9f1750b 100644
--- a/chrome/test/data/webui/settings/chooser_exception_list_tests.ts
+++ b/chrome/test/data/webui/settings/chooser_exception_list_tests.ts
@@ -13,7 +13,7 @@
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
 import {TestSiteSettingsPrefsBrowserProxy} from './test_site_settings_prefs_browser_proxy.js';
-import { createContentSettingTypeToValuePair,createRawChooserException,createRawSiteException,createSiteSettingsPrefs,SiteSettingsPref} from './test_util.js';
+import {createContentSettingTypeToValuePair,createRawChooserException,createRawSiteException,createSiteSettingsPrefs,SiteSettingsPref} from './test_util.js';
 // clang-format on
 
 /** @fileoverview Suite of tests for chooser-exception-list. */
diff --git a/chrome/test/data/webui/settings/chromeos/settings_traffic_counters_test.js b/chrome/test/data/webui/settings/chromeos/settings_traffic_counters_test.js
index d7fed32..e296e6f 100644
--- a/chrome/test/data/webui/settings/chromeos/settings_traffic_counters_test.js
+++ b/chrome/test/data/webui/settings/chromeos/settings_traffic_counters_test.js
@@ -5,7 +5,7 @@
 // clang-format off
 import 'chrome://os-settings/chromeos/os_settings.js';
 
-import { ConnectionStateType, NetworkType } from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-webui.js';
+import {ConnectionStateType, NetworkType} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-webui.js';
 import {MojoInterfaceProviderImpl} from 'chrome://resources/ash/common/network/mojo_interface_provider.js';
 import {OncMojo} from 'chrome://resources/ash/common/network/onc_mojo.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/chrome/test/data/webui/settings/clear_browsing_data_test.ts b/chrome/test/data/webui/settings/clear_browsing_data_test.ts
index eceabf8a..3ff10e8 100644
--- a/chrome/test/data/webui/settings/clear_browsing_data_test.ts
+++ b/chrome/test/data/webui/settings/clear_browsing_data_test.ts
@@ -6,7 +6,7 @@
 import {webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import { ClearBrowsingDataBrowserProxyImpl, ClearBrowsingDataResult,InstalledApp, SettingsCheckboxElement, SettingsClearBrowsingDataDialogElement, SettingsHistoryDeletionDialogElement, SettingsPasswordsDeletionDialogElement} from 'chrome://settings/lazy_load.js';
+import {ClearBrowsingDataBrowserProxyImpl, ClearBrowsingDataResult,InstalledApp, SettingsCheckboxElement, SettingsClearBrowsingDataDialogElement, SettingsHistoryDeletionDialogElement, SettingsPasswordsDeletionDialogElement} from 'chrome://settings/lazy_load.js';
 import {CrButtonElement, loadTimeData, StatusAction, SyncBrowserProxyImpl} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise, isVisible, whenAttributeIs} from 'chrome://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/settings/cookies_page_test.ts b/chrome/test/data/webui/settings/cookies_page_test.ts
index ac644a4..ff01abc 100644
--- a/chrome/test/data/webui/settings/cookies_page_test.ts
+++ b/chrome/test/data/webui/settings/cookies_page_test.ts
@@ -8,7 +8,8 @@
 import {ContentSetting, ContentSettingsTypes,CookiePrimarySetting, SettingsCookiesPageElement, SiteSettingsPrefsBrowserProxyImpl} from 'chrome://settings/lazy_load.js';
 import {CrLinkRowElement, CrSettingsPrefs, MetricsBrowserProxyImpl, PrivacyElementInteractions, Router, routes, SettingsPrefsElement, SettingsToggleButtonElement} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {flushTasks, isChildVisible} from 'chrome://webui-test/test_util.js';
+import {isChildVisible} from 'chrome://webui-test/test_util.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
 import {TestMetricsBrowserProxy} from './test_metrics_browser_proxy.js';
 import {TestSiteSettingsPrefsBrowserProxy} from './test_site_settings_prefs_browser_proxy.js';
diff --git a/chrome/test/data/webui/settings/languages_page_tests.ts b/chrome/test/data/webui/settings/languages_page_tests.ts
index a3ab7f90..c375921 100644
--- a/chrome/test/data/webui/settings/languages_page_tests.ts
+++ b/chrome/test/data/webui/settings/languages_page_tests.ts
@@ -10,7 +10,8 @@
 import {CrCheckboxElement, kMenuCloseDelay, LanguageHelper, LanguagesBrowserProxyImpl, SettingsAddLanguagesDialogElement, SettingsLanguagesPageElement} from 'chrome://settings/lazy_load.js';
 import {CrActionMenuElement, CrButtonElement, CrSettingsPrefs, loadTimeData} from 'chrome://settings/settings.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertGE, assertGT, assertLT, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {eventToPromise, fakeDataBind} from 'chrome://webui-test/test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
+import {fakeDataBind} from 'chrome://webui-test/polymer_test_util.js';
 
 import {FakeLanguageSettingsPrivate, getFakeLanguagePrefs} from './fake_language_settings_private.js';
 import {FakeSettingsPrivate} from './fake_settings_private.js';
diff --git a/chrome/test/data/webui/settings/password_check_test.ts b/chrome/test/data/webui/settings/password_check_test.ts
index a3e61d8..883a45cc 100644
--- a/chrome/test/data/webui/settings/password_check_test.ts
+++ b/chrome/test/data/webui/settings/password_check_test.ts
@@ -15,10 +15,12 @@
 import {PasswordCheckListItemElement, SettingsPasswordCheckElement, SettingsPasswordRemoveConfirmationDialogElement} from 'chrome://settings/lazy_load.js';
 import {OpenWindowProxyImpl, PasswordCheckInteraction, PasswordManagerImpl, Router, routes, StatusAction, SyncBrowserProxyImpl} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
+
 // <if expr="chromeos_ash">
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 // </if>
-import {flushTasks} from 'chrome://webui-test/test_util.js';
+
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
 import {makeInsecureCredential, makePasswordCheckStatus} from './passwords_and_autofill_fake_data.js';
 import {getSyncAllPrefs, simulateSyncStatus} from './sync_test_util.js';
diff --git a/chrome/test/data/webui/settings/password_edit_dialog_test.ts b/chrome/test/data/webui/settings/password_edit_dialog_test.ts
index 66bbdc5..82a86ad8 100644
--- a/chrome/test/data/webui/settings/password_edit_dialog_test.ts
+++ b/chrome/test/data/webui/settings/password_edit_dialog_test.ts
@@ -12,7 +12,8 @@
 import {PasswordDialogMode, PasswordEditDialogElement, SettingsTextareaElement} from 'chrome://settings/lazy_load.js';
 import {PasswordManagerImpl} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {eventToPromise, flushTasks, isVisible} from 'chrome://webui-test/test_util.js';
+import {eventToPromise, isVisible} from 'chrome://webui-test/test_util.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
 import {createPasswordEntry, PasswordSectionElementFactory} from './passwords_and_autofill_fake_data.js';
 import {TestPasswordManagerProxy} from './test_password_manager_proxy.js';
diff --git a/chrome/test/data/webui/settings/password_view_test.ts b/chrome/test/data/webui/settings/password_view_test.ts
index a6ddba8..0c607e1 100644
--- a/chrome/test/data/webui/settings/password_view_test.ts
+++ b/chrome/test/data/webui/settings/password_view_test.ts
@@ -13,7 +13,8 @@
 import {buildRouter, PasswordManagerImpl, Router, routes} from 'chrome://settings/settings.js';
 import {SettingsRoutes} from 'chrome://settings/settings_routes.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {flushTasks, isVisible} from 'chrome://webui-test/test_util.js';
+import {isVisible} from 'chrome://webui-test/test_util.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
 import {createPasswordEntry} from './passwords_and_autofill_fake_data.js';
 import {TestPasswordManagerProxy} from './test_password_manager_proxy.js';
diff --git a/chrome/test/data/webui/settings/passwords_section_test.ts b/chrome/test/data/webui/settings/passwords_section_test.ts
index ab398eb..699b843 100644
--- a/chrome/test/data/webui/settings/passwords_section_test.ts
+++ b/chrome/test/data/webui/settings/passwords_section_test.ts
@@ -13,10 +13,11 @@
 import {PasswordsSectionElement} from 'chrome://settings/lazy_load.js';
 import {buildRouter, HatsBrowserProxyImpl, PasswordCheckReferrer, PasswordManagerImpl, Router, routes, SettingsPluralStringProxyImpl,StatusAction, TrustedVaultBannerState, TrustSafetyInteraction} from 'chrome://settings/settings.js';
 import {SettingsRoutes} from 'chrome://settings/settings_routes.js';
+import {SettingsToggleButtonElement} from 'chrome://settings/settings.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue, assertNotEquals} from 'chrome://webui-test/chai_assert.js';
 import {TestPluralStringProxy} from 'chrome://webui-test/test_plural_string_proxy.js';
-import {eventToPromise, flushTasks, isVisible} from 'chrome://webui-test/test_util.js';
-import {SettingsToggleButtonElement} from 'chrome://settings/settings.js';
+import {eventToPromise, isVisible} from 'chrome://webui-test/test_util.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
 import {createExceptionEntry, createPasswordEntry, makeInsecureCredential, makePasswordCheckStatus, PasswordSectionElementFactory} from './passwords_and_autofill_fake_data.js';
 import {getSyncAllPrefs, simulateStoredAccounts, simulateSyncStatus} from './sync_test_util.js';
diff --git a/chrome/test/data/webui/settings/privacy_guide_page_test.ts b/chrome/test/data/webui/settings/privacy_guide_page_test.ts
index e3fe4b8..f55baee0 100644
--- a/chrome/test/data/webui/settings/privacy_guide_page_test.ts
+++ b/chrome/test/data/webui/settings/privacy_guide_page_test.ts
@@ -9,9 +9,10 @@
 import {CookiePrimarySetting, PrivacyGuideCompletionFragmentElement, PrivacyGuideHistorySyncFragmentElement, PrivacyGuideStep, PrivacyGuideWelcomeFragmentElement, SafeBrowsingSetting, SettingsPrivacyGuideDialogElement, SettingsPrivacyGuidePageElement, SettingsRadioGroupElement} from 'chrome://settings/lazy_load.js';
 import {CrSettingsPrefs, MetricsBrowserProxyImpl, PrivacyGuideInteractions, PrivacyGuideSettingsStates, Router, routes, SettingsPrefsElement, StatusAction, SyncBrowserProxyImpl, SyncPrefs, syncPrefsIndividualDataTypes, SyncStatus} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {eventToPromise, flushTasks, isChildVisible} from 'chrome://webui-test/test_util.js';
-import {getSyncAllPrefs} from './sync_test_util.js';
+import {eventToPromise, isChildVisible} from 'chrome://webui-test/test_util.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
+import {getSyncAllPrefs} from './sync_test_util.js';
 import {TestMetricsBrowserProxy} from './test_metrics_browser_proxy.js';
 import {TestSyncBrowserProxy} from './test_sync_browser_proxy.js';
 
diff --git a/chrome/test/data/webui/settings/privacy_page_test.ts b/chrome/test/data/webui/settings/privacy_page_test.ts
index aac90ba..9ac8dba 100644
--- a/chrome/test/data/webui/settings/privacy_page_test.ts
+++ b/chrome/test/data/webui/settings/privacy_page_test.ts
@@ -9,7 +9,8 @@
 import {ClearBrowsingDataBrowserProxyImpl, ContentSettingsTypes, SiteSettingsPrefsBrowserProxyImpl} from 'chrome://settings/lazy_load.js';
 import {CrLinkRowElement, CrSettingsPrefs, HatsBrowserProxyImpl, MetricsBrowserProxyImpl, PrivacyGuideInteractions, PrivacyPageBrowserProxyImpl, Route, Router, routes, SettingsPrefsElement, SettingsPrivacyPageElement, StatusAction, SyncStatus, TrustSafetyInteraction} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {flushTasks, isChildVisible, isVisible} from 'chrome://webui-test/test_util.js';
+import {isChildVisible, isVisible} from 'chrome://webui-test/test_util.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
 import {TestClearBrowsingDataBrowserProxy} from './test_clear_browsing_data_browser_proxy.js';
 import {TestHatsBrowserProxy} from './test_hats_browser_proxy.js';
diff --git a/chrome/test/data/webui/settings/privacy_sandbox_test.ts b/chrome/test/data/webui/settings/privacy_sandbox_test.ts
index 160f550..991d5ef5 100644
--- a/chrome/test/data/webui/settings/privacy_sandbox_test.ts
+++ b/chrome/test/data/webui/settings/privacy_sandbox_test.ts
@@ -10,8 +10,9 @@
 import {CanonicalTopic, PrivacySandboxBrowserProxy, PrivacySandboxBrowserProxyImpl} from 'chrome://settings/privacy_sandbox/privacy_sandbox_browser_proxy.js';
 import {CrSettingsPrefs, HatsBrowserProxyImpl, MetricsBrowserProxyImpl, TrustSafetyInteraction} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
-import {eventToPromise, flushTasks, isVisible} from 'chrome://webui-test/test_util.js';
+import {eventToPromise, isVisible} from 'chrome://webui-test/test_util.js';
 
 import {TestHatsBrowserProxy} from './test_hats_browser_proxy.js';
 import {TestMetricsBrowserProxy} from './test_metrics_browser_proxy.js';
diff --git a/chrome/test/data/webui/settings/recent_site_permissions_test.ts b/chrome/test/data/webui/settings/recent_site_permissions_test.ts
index 1f387b25..2762887 100644
--- a/chrome/test/data/webui/settings/recent_site_permissions_test.ts
+++ b/chrome/test/data/webui/settings/recent_site_permissions_test.ts
@@ -7,7 +7,8 @@
 import {ContentSetting, ContentSettingsTypes, SettingsRecentSitePermissionsElement, SiteSettingSource, SiteSettingsPrefsBrowserProxyImpl} 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 {flushTasks, isChildVisible, isVisible} from 'chrome://webui-test/test_util.js';
+import {isChildVisible, isVisible} from 'chrome://webui-test/test_util.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
 import {TestSiteSettingsPrefsBrowserProxy} from './test_site_settings_prefs_browser_proxy.js';
 import {createRawSiteException} from './test_util.js';
diff --git a/chrome/test/data/webui/settings/security_page_test.ts b/chrome/test/data/webui/settings/security_page_test.ts
index fe4a839..2bb1e291 100644
--- a/chrome/test/data/webui/settings/security_page_test.ts
+++ b/chrome/test/data/webui/settings/security_page_test.ts
@@ -11,7 +11,8 @@
 import {OpenWindowProxyImpl} from 'chrome://settings/settings.js';
 // </if>
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {flushTasks, isChildVisible} from 'chrome://webui-test/test_util.js';
+import {isChildVisible} from 'chrome://webui-test/test_util.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
 import {TestMetricsBrowserProxy} from './test_metrics_browser_proxy.js';
 // <if expr="chrome_root_store_supported">
diff --git a/chrome/test/data/webui/settings/settings_slider_tests.ts b/chrome/test/data/webui/settings/settings_slider_tests.ts
index bd32b27..4625471 100644
--- a/chrome/test/data/webui/settings/settings_slider_tests.ts
+++ b/chrome/test/data/webui/settings/settings_slider_tests.ts
@@ -7,9 +7,10 @@
 
 import {keyDownOn, keyUpOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import { CrSliderElement,SettingsSliderElement} from 'chrome://settings/lazy_load.js';
+import {CrSliderElement,SettingsSliderElement} from 'chrome://settings/lazy_load.js';
 import {assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {eventToPromise, flushTasks} from 'chrome://webui-test/test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 // clang-format on
 
 /** @fileoverview Suite of tests for settings-slider. */
diff --git a/chrome/test/data/webui/settings/settings_subpage_test.ts b/chrome/test/data/webui/settings/settings_subpage_test.ts
index 5ebdbbc3..1395fc0 100644
--- a/chrome/test/data/webui/settings/settings_subpage_test.ts
+++ b/chrome/test/data/webui/settings/settings_subpage_test.ts
@@ -6,7 +6,8 @@
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {loadTimeData, Route, Router} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {eventToPromise, flushTasks} from 'chrome://webui-test/test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
 import {setupPopstateListener} from './test_util.js';
 
diff --git a/chrome/test/data/webui/settings/site_data_test.ts b/chrome/test/data/webui/settings/site_data_test.ts
index a9a6e69..1925440 100644
--- a/chrome/test/data/webui/settings/site_data_test.ts
+++ b/chrome/test/data/webui/settings/site_data_test.ts
@@ -7,7 +7,8 @@
 import {LocalDataBrowserProxyImpl, SiteDataElement} from 'chrome://settings/lazy_load.js';
 import {MetricsBrowserProxyImpl, PrivacyElementInteractions, Router,routes} from 'chrome://settings/settings.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {eventToPromise, flushTasks} from 'chrome://webui-test/test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
 import {TestLocalDataBrowserProxy} from './test_local_data_browser_proxy.js';
 import {TestMetricsBrowserProxy} from './test_metrics_browser_proxy.js';
diff --git a/chrome/test/data/webui/settings/site_entry_tests.ts b/chrome/test/data/webui/settings/site_entry_tests.ts
index 13392f2..266455b9 100644
--- a/chrome/test/data/webui/settings/site_entry_tests.ts
+++ b/chrome/test/data/webui/settings/site_entry_tests.ts
@@ -14,7 +14,7 @@
 
 import {TestLocalDataBrowserProxy} from './test_local_data_browser_proxy.js';
 import {TestSiteSettingsPrefsBrowserProxy} from './test_site_settings_prefs_browser_proxy.js';
-import { createOriginInfo,createSiteGroup} from './test_util.js';
+import {createOriginInfo,createSiteGroup} from './test_util.js';
 
 // clang-format on
 
diff --git a/chrome/test/data/webui/settings/site_settings_page_test.ts b/chrome/test/data/webui/settings/site_settings_page_test.ts
index d0b77b59..b6906791 100644
--- a/chrome/test/data/webui/settings/site_settings_page_test.ts
+++ b/chrome/test/data/webui/settings/site_settings_page_test.ts
@@ -8,9 +8,9 @@
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {ContentSetting, defaultSettingLabel, NotificationSetting, SettingsSiteSettingsPageElement, SiteSettingsPrefsBrowserProxyImpl} from 'chrome://settings/lazy_load.js';
 import {CrLinkRowElement} from 'chrome://settings/settings.js';
-
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {flushTasks, isChildVisible} from 'chrome://webui-test/test_util.js';
+import {isChildVisible} from 'chrome://webui-test/test_util.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
 import {TestSiteSettingsPrefsBrowserProxy} from './test_site_settings_prefs_browser_proxy.js';
 
diff --git a/chrome/test/data/webui/settings/startup_urls_page_test.ts b/chrome/test/data/webui/settings/startup_urls_page_test.ts
index e8e01257..f9a7a18a 100644
--- a/chrome/test/data/webui/settings/startup_urls_page_test.ts
+++ b/chrome/test/data/webui/settings/startup_urls_page_test.ts
@@ -8,7 +8,7 @@
 import {webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
 import {keyEventOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import { EDIT_STARTUP_URL_EVENT,SettingsStartupUrlDialogElement,SettingsStartupUrlEntryElement, SettingsStartupUrlsPageElement, StartupUrlsPageBrowserProxy, StartupUrlsPageBrowserProxyImpl} from 'chrome://settings/settings.js';
+import {EDIT_STARTUP_URL_EVENT,SettingsStartupUrlDialogElement,SettingsStartupUrlEntryElement, SettingsStartupUrlsPageElement, StartupUrlsPageBrowserProxy, StartupUrlsPageBrowserProxyImpl} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
diff --git a/chrome/test/data/webui/settings/translate_page_tests.ts b/chrome/test/data/webui/settings/translate_page_tests.ts
index 619f7e27..f55e6c3a 100644
--- a/chrome/test/data/webui/settings/translate_page_tests.ts
+++ b/chrome/test/data/webui/settings/translate_page_tests.ts
@@ -8,7 +8,8 @@
 import {CrIconButtonElement, LanguageHelper, LanguagesBrowserProxyImpl, SettingsAddLanguagesDialogElement, SettingsTranslatePageElement} from 'chrome://settings/lazy_load.js';
 import {CrSettingsPrefs, loadTimeData} from 'chrome://settings/settings.js';
 import {assertDeepEquals, assertEquals, assertTrue, assertFalse} from 'chrome://webui-test/chai_assert.js';
-import {eventToPromise, fakeDataBind} from 'chrome://webui-test/test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
+import {fakeDataBind} from 'chrome://webui-test/polymer_test_util.js';
 
 import {FakeLanguageSettingsPrivate, getFakeLanguagePrefs} from './fake_language_settings_private.js';
 import {FakeSettingsPrivate} from './fake_settings_private.js';
diff --git a/chrome/test/data/webui/side_panel/bookmarks/bookmark_folder_test.ts b/chrome/test/data/webui/side_panel/bookmarks/bookmark_folder_test.ts
index 54aa7eed..5f7bc44a 100644
--- a/chrome/test/data/webui/side_panel/bookmarks/bookmark_folder_test.ts
+++ b/chrome/test/data/webui/side_panel/bookmarks/bookmark_folder_test.ts
@@ -10,7 +10,8 @@
 import {BookmarksApiProxyImpl} from 'chrome://read-later.top-chrome/bookmarks/bookmarks_api_proxy.js';
 import {getFaviconForPageURL} from 'chrome://resources/js/icon.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {eventToPromise, flushTasks, waitAfterNextRender} from 'chrome://webui-test/test_util.js';
+import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {TestBookmarksApiProxy} from './test_bookmarks_api_proxy.js';
 
diff --git a/chrome/test/data/webui/side_panel/bookmarks/bookmarks_list_test.ts b/chrome/test/data/webui/side_panel/bookmarks/bookmarks_list_test.ts
index a1c9b7a..19ed093 100644
--- a/chrome/test/data/webui/side_panel/bookmarks/bookmarks_list_test.ts
+++ b/chrome/test/data/webui/side_panel/bookmarks/bookmarks_list_test.ts
@@ -12,7 +12,8 @@
 import {ShoppingListApiProxyImpl} from 'chrome://read-later.top-chrome/bookmarks/commerce/shopping_list_api_proxy.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {flushTasks, isVisible} from 'chrome://webui-test/test_util.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
+import {isVisible} from 'chrome://webui-test/test_util.js';
 
 import {fakeMetricsPrivate, MetricsTracker} from '../metrics_test_support.js';
 
diff --git a/chrome/test/data/webui/side_panel/bookmarks/commerce/shopping_list_test.ts b/chrome/test/data/webui/side_panel/bookmarks/commerce/shopping_list_test.ts
index 8b8cb1d..2a14039 100644
--- a/chrome/test/data/webui/side_panel/bookmarks/commerce/shopping_list_test.ts
+++ b/chrome/test/data/webui/side_panel/bookmarks/commerce/shopping_list_test.ts
@@ -13,7 +13,8 @@
 import {ShoppingListApiProxyImpl} from 'chrome://read-later.top-chrome/bookmarks/commerce/shopping_list_api_proxy.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {flushTasks, isVisible} from 'chrome://webui-test/test_util.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
+import {isVisible} from 'chrome://webui-test/test_util.js';
 
 import {fakeMetricsPrivate, MetricsTracker} from '../../metrics_test_support.js';
 import {TestBookmarksApiProxy} from '../test_bookmarks_api_proxy.js';
diff --git a/chrome/test/data/webui/signin/account_selection_lacros_test.ts b/chrome/test/data/webui/signin/account_selection_lacros_test.ts
index 2775a52..c2e106a 100644
--- a/chrome/test/data/webui/signin/account_selection_lacros_test.ts
+++ b/chrome/test/data/webui/signin/account_selection_lacros_test.ts
@@ -8,8 +8,8 @@
 import {AvailableAccount, ensureLazyLoaded, ManageProfilesBrowserProxyImpl} from 'chrome://profile-picker/profile_picker.js';
 import {webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {waitBeforeNextRender} from 'chrome://webui-test/polymer_test_util.js';
-import {flushTasks, isChildVisible} from 'chrome://webui-test/test_util.js';
+import {flushTasks, waitBeforeNextRender} from 'chrome://webui-test/polymer_test_util.js';
+import {isChildVisible} from 'chrome://webui-test/test_util.js';
 
 import {TestManageProfilesBrowserProxy} from './test_manage_profiles_browser_proxy.js';
 
diff --git a/chrome/test/data/webui/signin/dice_web_signin_intercept_test.ts b/chrome/test/data/webui/signin/dice_web_signin_intercept_test.ts
index c604c3ec..bb5e394 100644
--- a/chrome/test/data/webui/signin/dice_web_signin_intercept_test.ts
+++ b/chrome/test/data/webui/signin/dice_web_signin_intercept_test.ts
@@ -7,9 +7,9 @@
 import {webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
 import {DiceWebSigninInterceptAppElement} from 'chrome://signin-dice-web-intercept/dice_web_signin_intercept_app.js';
 import {DiceWebSigninInterceptBrowserProxyImpl, InterceptionParameters} from 'chrome://signin-dice-web-intercept/dice_web_signin_intercept_browser_proxy.js';
-
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {isChildVisible, waitAfterNextRender} from 'chrome://webui-test/test_util.js';
+import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
+import {isChildVisible} from 'chrome://webui-test/test_util.js';
 
 import {TestDiceWebSigninInterceptBrowserProxy} from './test_dice_web_signin_intercept_browser_proxy.js';
 
diff --git a/chrome/test/data/webui/signin/enterprise_profile_welcome_test.ts b/chrome/test/data/webui/signin/enterprise_profile_welcome_test.ts
index 5f2dae8..252dc70 100644
--- a/chrome/test/data/webui/signin/enterprise_profile_welcome_test.ts
+++ b/chrome/test/data/webui/signin/enterprise_profile_welcome_test.ts
@@ -10,7 +10,8 @@
 import {webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {isChildVisible, waitAfterNextRender} from 'chrome://webui-test/test_util.js';
+import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
+import {isChildVisible} from 'chrome://webui-test/test_util.js';
 
 import {TestEnterpriseProfileWelcomeBrowserProxy} from './test_enterprise_profile_welcome_browser_proxy.js';
 
diff --git a/chrome/test/data/webui/signin/local_profile_customization_focus_test.ts b/chrome/test/data/webui/signin/local_profile_customization_focus_test.ts
index bb91464..a24af611 100644
--- a/chrome/test/data/webui/signin/local_profile_customization_focus_test.ts
+++ b/chrome/test/data/webui/signin/local_profile_customization_focus_test.ts
@@ -9,8 +9,8 @@
 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 {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {waitBeforeNextRender} from 'chrome://webui-test/polymer_test_util.js';
-import {flushTasks, whenAttributeIs, whenCheck} from 'chrome://webui-test/test_util.js';
+import {flushTasks, waitBeforeNextRender} from 'chrome://webui-test/polymer_test_util.js';
+import {whenAttributeIs, whenCheck} from 'chrome://webui-test/test_util.js';
 
 import {TestManageProfilesBrowserProxy} from './test_manage_profiles_browser_proxy.js';
 
diff --git a/chrome/test/data/webui/signin/profile_picker_app_test.ts b/chrome/test/data/webui/signin/profile_picker_app_test.ts
index 88ae271..6d4006b3 100644
--- a/chrome/test/data/webui/signin/profile_picker_app_test.ts
+++ b/chrome/test/data/webui/signin/profile_picker_app_test.ts
@@ -15,8 +15,8 @@
 import {webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {flushTasks, whenCheck} from 'chrome://webui-test/test_util.js';
-import {waitBeforeNextRender} from 'chrome://webui-test/polymer_test_util.js';
+import {whenCheck} from 'chrome://webui-test/test_util.js';
+import {flushTasks, waitBeforeNextRender} from 'chrome://webui-test/polymer_test_util.js';
 
 import {TestManageProfilesBrowserProxy} from './test_manage_profiles_browser_proxy.js';
 
diff --git a/chrome/test/data/webui/welcome/BUILD.gn b/chrome/test/data/webui/welcome/BUILD.gn
index 677831e..ee3c5df 100644
--- a/chrome/test/data/webui/welcome/BUILD.gn
+++ b/chrome/test/data/webui/welcome/BUILD.gn
@@ -42,7 +42,6 @@
     "..:build_ts",
     "//chrome/browser/resources/welcome:build_ts",
   ]
-  extra_deps = [ "..:generate_definitions" ]
 }
 
 generate_grd("build_grdp") {
diff --git a/chrome/test/data/webui/whats_new/BUILD.gn b/chrome/test/data/webui/whats_new/BUILD.gn
index ad9294f6..78a362f 100644
--- a/chrome/test/data/webui/whats_new/BUILD.gn
+++ b/chrome/test/data/webui/whats_new/BUILD.gn
@@ -20,8 +20,10 @@
                     target_gen_dir),
   ]
   in_files = [ "whats_new_app_test.ts" ]
-  deps = [ "//chrome/browser/resources/whats_new:build_ts" ]
-  extra_deps = [ "..:generate_definitions" ]
+  deps = [
+    "..:build_ts",
+    "//chrome/browser/resources/whats_new:build_ts",
+  ]
 }
 
 generate_grd("build_grdp") {
diff --git a/chrome/test/data/webui/whats_new/whats_new_app_test.ts b/chrome/test/data/webui/whats_new/whats_new_app_test.ts
index b986a32..f484aff 100644
--- a/chrome/test/data/webui/whats_new/whats_new_app_test.ts
+++ b/chrome/test/data/webui/whats_new/whats_new_app_test.ts
@@ -8,8 +8,9 @@
 import {BrowserCommandProxy} from 'chrome://resources/js/browser_command/browser_command_proxy.js';
 import {isChromeOS} from 'chrome://resources/js/cr.m.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
-import {eventToPromise, flushTasks} from 'chrome://webui-test/test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 import {WhatsNewProxy, WhatsNewProxyImpl} from 'chrome://whats-new/whats_new_proxy.js';
 
 const whatsNewURL = 'chrome://webui-test/whats_new/test.html';
diff --git a/chromecast/metrics/cast_event_builder.h b/chromecast/metrics/cast_event_builder.h
index 7f3cb93..2c796b5 100644
--- a/chromecast/metrics/cast_event_builder.h
+++ b/chromecast/metrics/cast_event_builder.h
@@ -74,6 +74,8 @@
   virtual CastEventBuilder& SetAoghRequestId(const std::string& request_id) = 0;
   virtual CastEventBuilder& SetAoghLocalDeviceId(int64_t local_id) = 0;
   virtual CastEventBuilder& SetAoghAgentId(const std::string& agent_id) = 0;
+  virtual CastEventBuilder& SetAoghStandardAgentId(
+      const std::string& standard_agent_id) = 0;
   virtual CastEventBuilder& SetUiVersion(const std::string& ui_version) = 0;
   virtual CastEventBuilder& SetAuditReport(const std::string& audit_report) = 0;
   virtual CastEventBuilder& SetDuoCoreVersion(int64_t version) = 0;
diff --git a/chromecast/metrics/cast_event_builder_impl.cc b/chromecast/metrics/cast_event_builder_impl.cc
index 7a6b825a..678b80f 100644
--- a/chromecast/metrics/cast_event_builder_impl.cc
+++ b/chromecast/metrics/cast_event_builder_impl.cc
@@ -138,6 +138,12 @@
   return *this;
 }
 
+CastEventBuilder& CastEventBuilderImpl::SetAoghStandardAgentId(
+    const std::string& standard_agent_id) {
+  event_proto_->set_aogh_standard_agent_id(standard_agent_id);
+  return *this;
+}
+
 CastEventBuilder& CastEventBuilderImpl::SetUiVersion(const std::string& value) {
   event_proto_->set_ui_version(value);
   return *this;
diff --git a/chromecast/metrics/cast_event_builder_impl.h b/chromecast/metrics/cast_event_builder_impl.h
index 2b5fd3d..7d134ce 100644
--- a/chromecast/metrics/cast_event_builder_impl.h
+++ b/chromecast/metrics/cast_event_builder_impl.h
@@ -42,6 +42,8 @@
   CastEventBuilder& SetAoghRequestId(const std::string& request_id) override;
   CastEventBuilder& SetAoghLocalDeviceId(int64_t local_id) override;
   CastEventBuilder& SetAoghAgentId(const std::string& agent_id) override;
+  CastEventBuilder& SetAoghStandardAgentId(
+      const std::string& standard_agent_id) override;
   CastEventBuilder& SetUiVersion(const std::string& ui_version) override;
   CastEventBuilder& SetAuditReport(const std::string& audit_report) override;
   CastEventBuilder& SetDuoCoreVersion(int64_t version) override;
diff --git a/chromecast/metrics/cast_event_builder_simple.cc b/chromecast/metrics/cast_event_builder_simple.cc
index f6fc33e..95951631 100644
--- a/chromecast/metrics/cast_event_builder_simple.cc
+++ b/chromecast/metrics/cast_event_builder_simple.cc
@@ -102,6 +102,11 @@
   return *this;
 }
 
+CastEventBuilder& CastEventBuilderSimple::SetAoghStandardAgentId(
+    const std::string& standard_agent_id) {
+  return *this;
+}
+
 CastEventBuilder& CastEventBuilderSimple::SetUiVersion(
     const std::string& value) {
   return *this;
diff --git a/chromecast/metrics/cast_event_builder_simple.h b/chromecast/metrics/cast_event_builder_simple.h
index 4a938e3..60b225c4 100644
--- a/chromecast/metrics/cast_event_builder_simple.h
+++ b/chromecast/metrics/cast_event_builder_simple.h
@@ -40,6 +40,8 @@
   CastEventBuilder& SetAoghRequestId(const std::string& request_id) override;
   CastEventBuilder& SetAoghLocalDeviceId(int64_t local_id) override;
   CastEventBuilder& SetAoghAgentId(const std::string& agent_id) override;
+  CastEventBuilder& SetAoghStandardAgentId(
+      const std::string& standard_agent_id) override;
   CastEventBuilder& SetUiVersion(const std::string& ui_version) override;
   CastEventBuilder& SetAuditReport(const std::string& audit_report) override;
   CastEventBuilder& SetDuoCoreVersion(int64_t version) override;
diff --git a/chromecast/metrics/metrics_recorder.cc b/chromecast/metrics/metrics_recorder.cc
index d405593..21bde7a 100644
--- a/chromecast/metrics/metrics_recorder.cc
+++ b/chromecast/metrics/metrics_recorder.cc
@@ -111,6 +111,11 @@
     return *this;
   }
 
+  CastEventBuilder& SetAoghStandardAgentId(
+      const std::string& agent_id) override {
+    return *this;
+  }
+
   CastEventBuilder& SetUiVersion(const std::string& ui_version) override {
     return *this;
   }
diff --git a/components/breadcrumbs/core/application_breadcrumbs_logger.cc b/components/breadcrumbs/core/application_breadcrumbs_logger.cc
index 38b1273..28909b12 100644
--- a/components/breadcrumbs/core/application_breadcrumbs_logger.cc
+++ b/components/breadcrumbs/core/application_breadcrumbs_logger.cc
@@ -11,7 +11,6 @@
 #include "components/breadcrumbs/core/application_breadcrumbs_not_user_action.inc"
 #include "components/breadcrumbs/core/breadcrumb_manager.h"
 #include "components/breadcrumbs/core/breadcrumb_persistent_storage_manager.h"
-#include "components/breadcrumbs/core/crash_reporter_breadcrumb_observer.h"
 
 namespace breadcrumbs {
 
@@ -30,11 +29,6 @@
               storage_dir,
               std::move(is_metrics_enabled_callback))) {
   base::AddActionCallback(user_action_callback_);
-
-  // Start crash reporter listening for breadcrumb events. Collected breadcrumbs
-  // will be attached to crash reports.
-  CrashReporterBreadcrumbObserver::GetInstance();
-
   AddEvent("Startup");
 }
 
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectableListToolbar.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectableListToolbar.java
index fff7ad0..b013a8c 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectableListToolbar.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectableListToolbar.java
@@ -408,7 +408,7 @@
 
         if (mIsDestroyed) return;
 
-        mSelectionDelegate.clearSelection();
+        if (mSelectionDelegate != null) mSelectionDelegate.clearSelection();
         if (mIsSearching) hideSearchView();
     }
 
diff --git a/components/desks_storage/core/local_desk_data_manager.cc b/components/desks_storage/core/local_desk_data_manager.cc
index 4ae0cf185..455a64c6 100644
--- a/components/desks_storage/core/local_desk_data_manager.cc
+++ b/components/desks_storage/core/local_desk_data_manager.cc
@@ -444,14 +444,29 @@
     // This local storage cannot load any entry of `type` from disk.
     return {CacheStatus::kInvalidPath, std::move(entries)};
   }
+
   while (dir_reader.Next()) {
     if (!IsValidTemplateFileName(dir_reader.name())) {
       continue;
     }
+
     base::FilePath fully_qualified_path =
         base::FilePath(local_saved_desk_path.Append(dir_reader.name()));
     std::unique_ptr<ash::DeskTemplate> entry =
         ReadFileToTemplate(fully_qualified_path);
+
+    // Rename file for saved desk if uuid in file and file name are different.
+    std::string entry_uuid_string = entry->uuid().AsLowercaseString();
+    entry_uuid_string.append(kFileExtension);
+
+    if (dir_reader.name() != entry_uuid_string) {
+      const base::FilePath renamed_fully_qualified_path =
+          GetFullyQualifiedPath(local_saved_desk_path, entry->uuid());
+      if (!base::Move(fully_qualified_path, renamed_fully_qualified_path)) {
+        DVLOG(1) << "Fail to rename saved desk template to proper UUID";
+      }
+    }
+
     // TODO(crbug/1359398): Record metrics about files that failed to parse.
     if (entry) {
       entries.push_back(std::move(entry));
diff --git a/components/desks_storage/core/local_desk_data_manager_unittests.cc b/components/desks_storage/core/local_desk_data_manager_unittests.cc
index 70687fc..300ab66 100644
--- a/components/desks_storage/core/local_desk_data_manager_unittests.cc
+++ b/components/desks_storage/core/local_desk_data_manager_unittests.cc
@@ -61,6 +61,27 @@
                             GetTestUuid(uuid_id).AsLowercaseString().c_str());
 }
 
+std::string GetTestSaveDeskFileNameString(TestUuidId uuid_id) {
+  return base::StringPrintf("%s.saveddesk",
+                            GetTestUuid(uuid_id).AsLowercaseString().c_str());
+}
+
+std::string GetTestTemplateJSONData() {
+  return "{\"version\":1,\"uuid\":\"" +
+         GetTestUuid(TestUuidId(1)).AsLowercaseString() +
+         "\",\"name\":\""
+         "Saved Desk Template 1"
+         "\",\"created_time_usec\":\"1633535632\",\"updated_time_usec\": "
+         "\"1633535632\",\"desk\":{\"apps\":[{\"window_"
+         "bound\":{\"left\":0,\"top\":1,\"height\":121,\"width\":120},\"window_"
+         "state\":\"NORMAL\",\"z_index\":1,\"app_type\":\"BROWSER\",\"tabs\":[{"
+         "\"url\":\"https://"
+         "example.com\",\"title\":\"Example\"},{\"url\":\"https://"
+         "example.com/"
+         "2\",\"title\":\"Example2\"}],\"active_tab_index\":1,\"window_id\":0,"
+         "\"display_id\":\"100\",\"pre_minimized_window_state\":\"NORMAL\"}]}}";
+}
+
 std::string GetPolicyWithOneTemplate() {
   return "[{\"version\":1,\"uuid\":\"" +
          GetTestUuid(TestUuidId(9)).AsLowercaseString() +
@@ -107,6 +128,17 @@
   EXPECT_TRUE(base::WriteFile(full_path, "This is not valid template data."));
 }
 
+void WriteIncorrectlyNamedData(const base::FilePath& temp_dir) {
+  base::FilePath saved_desk_path = temp_dir.Append("saveddesk");
+  base::CreateDirectory(saved_desk_path);
+  base::FilePath wrong_filename_full_path =
+      saved_desk_path.Append(GetTestSaveDeskFileNameString(TestUuidId(2)));
+  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
+                                                base::BlockingType::MAY_BLOCK);
+  EXPECT_TRUE(
+      base::WriteFile(wrong_filename_full_path, GetTestTemplateJSONData()));
+}
+
 // Make test template with ID containing the index. Defaults to desk template
 // type if a type is not specified.
 std::unique_ptr<ash::DeskTemplate> MakeTestDeskTemplate(
@@ -148,6 +180,7 @@
   entry->set_desk_restore_data(std::make_unique<app_restore::RestoreData>());
   return entry;
 }
+
 }  // namespace
 
 // TODO(crbug:1320940): Clean up tests to move on from std::string.
@@ -494,6 +527,48 @@
   EXPECT_EQ(DeskModel::GetEntryByUuidStatus::kFailure, result.status);
 }
 
+TEST_F(LocalDeskDataManagerTest,
+       CanRenameSavedDeskTemplateIfFilenameDoesNotMatchUUID) {
+  // Initialize new local temp directory
+  base::ScopedTempDir local_temp_dir_;
+  EXPECT_TRUE(local_temp_dir_.CreateUniqueTempDir());
+
+  // Pre-write file into directory for correcting
+  auto task_runner = task_environment_.GetMainThreadTaskRunner();
+  task_runner->PostTask(FROM_HERE, base::BindOnce(&WriteIncorrectlyNamedData,
+                                                  local_temp_dir_.GetPath()));
+  task_environment_.RunUntilIdle();
+
+  EXPECT_TRUE(base::PathExists(
+      local_temp_dir_.GetPath()
+          .Append("saveddesk")
+          .Append(GetTestSaveDeskFileNameString(TestUuidId(2)))));
+  EXPECT_FALSE(base::PathExists(
+      local_temp_dir_.GetPath()
+          .Append("saveddesk")
+          .Append(GetTestSaveDeskFileNameString(TestUuidId(1)))));
+
+  // Initialize temp data manager
+  std::unique_ptr<LocalDeskDataManager> temp_data_manager_;
+  temp_data_manager_ = std::make_unique<LocalDeskDataManager>(
+      local_temp_dir_.GetPath(), account_id_);
+  task_environment_.RunUntilIdle();
+
+  // Retrieve entry from test directory
+  auto result = temp_data_manager_->GetEntryByUUID(GetTestUuid(TestUuidId(1)));
+  EXPECT_EQ(DeskModel::GetEntryByUuidStatus::kOk, result.status);
+  EXPECT_EQ(result.entry->uuid(), GetTestUuid(TestUuidId(1)));
+  EXPECT_EQ(temp_data_manager_->GetEntryCount(), 1u);
+  EXPECT_TRUE(base::PathExists(
+      local_temp_dir_.GetPath()
+          .Append("saveddesk")
+          .Append(GetTestSaveDeskFileNameString(TestUuidId(1)))));
+  EXPECT_FALSE(base::PathExists(
+      local_temp_dir_.GetPath()
+          .Append("saveddesk")
+          .Append(GetTestSaveDeskFileNameString(TestUuidId(2)))));
+}
+
 TEST_F(LocalDeskDataManagerTest, CanUpdateEntry) {
   data_manager_->AddOrUpdateEntry(std::move(sample_desk_template_one_),
                                   base::BindOnce(&VerifyEntryAddedCorrectly));
diff --git a/components/device_signals/core/common/signals_features.cc b/components/device_signals/core/common/signals_features.cc
index 7b04058..ef8157a 100644
--- a/components/device_signals/core/common/signals_features.cc
+++ b/components/device_signals/core/common/signals_features.cc
@@ -8,7 +8,7 @@
 
 BASE_FEATURE(kNewEvSignalsEnabled,
              "NewEvSignalsEnabled",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 const base::FeatureParam<bool> kDisableFileSystemInfo{
     &kNewEvSignalsEnabled, "DisableFileSystemInfo", false};
@@ -42,9 +42,11 @@
       disable_function = kDisableSettings.Get();
       break;
     case NewEvFunction::kAntiVirus:
-      return !kDisableAntiVirus.Get();
+      disable_function = kDisableAntiVirus.Get();
+      break;
     case NewEvFunction::kHotfix:
-      return !kDisableHotfix.Get();
+      disable_function = kDisableHotfix.Get();
+      break;
   }
 
   if (!base::FeatureList::IsEnabled(kNewEvSignalsEnabled)) {
diff --git a/components/device_signals/core/common/signals_features_unittest.cc b/components/device_signals/core/common/signals_features_unittest.cc
index 0cadfdf..82b26b4 100644
--- a/components/device_signals/core/common/signals_features_unittest.cc
+++ b/components/device_signals/core/common/signals_features_unittest.cc
@@ -9,11 +9,6 @@
 
 namespace enterprise_signals::features {
 
-bool IsHotfixOrAntiVirus(int i) {
-  return i == static_cast<int>(NewEvFunction::kHotfix) ||
-         i == static_cast<int>(NewEvFunction::kAntiVirus);
-}
-
 class SignalsFeaturesTest : public testing::Test {
  protected:
   const int min_enum_value_ = static_cast<int>(NewEvFunction::kFileSystemInfo);
@@ -26,8 +21,7 @@
 TEST_F(SignalsFeaturesTest, DisabledFeature) {
   scoped_features_.InitAndDisableFeature(kNewEvSignalsEnabled);
   for (int i = min_enum_value_; i <= max_enum_value_; i++) {
-    EXPECT_EQ(IsNewFunctionEnabled(static_cast<NewEvFunction>(i)),
-              IsHotfixOrAntiVirus(i));
+    EXPECT_FALSE(IsNewFunctionEnabled(static_cast<NewEvFunction>(i)));
   }
 }
 
diff --git a/components/messages/android/internal/BUILD.gn b/components/messages/android/internal/BUILD.gn
index 38c9da2..fbdcfdb8 100644
--- a/components/messages/android/internal/BUILD.gn
+++ b/components/messages/android/internal/BUILD.gn
@@ -43,6 +43,7 @@
 
 robolectric_library("junit") {
   sources = [
+    "java/src/org/chromium/components/messages/MessageAnimationCoordinatorUnitTest.java",
     "java/src/org/chromium/components/messages/MessageAutoDismissTimerTest.java",
     "java/src/org/chromium/components/messages/MessageBannerCoordinatorUnitTest.java",
     "java/src/org/chromium/components/messages/MessageBannerMediatorUnitTest.java",
diff --git a/components/messages/android/internal/java/src/org/chromium/components/messages/MessageAnimationCoordinator.java b/components/messages/android/internal/java/src/org/chromium/components/messages/MessageAnimationCoordinator.java
index 05350140..061e4d1 100644
--- a/components/messages/android/internal/java/src/org/chromium/components/messages/MessageAnimationCoordinator.java
+++ b/components/messages/android/internal/java/src/org/chromium/components/messages/MessageAnimationCoordinator.java
@@ -16,6 +16,8 @@
 import org.chromium.components.messages.MessageQueueManager.MessageState;
 import org.chromium.components.messages.MessageStateHandler.Position;
 
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -30,11 +32,13 @@
      */
     @Nullable
     private MessageState mCurrentDisplayedMessage;
-    @Nullable
-    private List<MessageState> mCurrentDisplayedMessages;
+    @NonNull
+    private List<MessageState> mCurrentDisplayedMessages = Arrays.asList(null, null);
     private MessageState mLastShownMessage;
     private MessageQueueDelegate mMessageQueueDelegate;
     private AnimatorSet mAnimatorSet = new AnimatorSet();
+    private Animator mFrontAnimator;
+    private Animator mBackAnimator;
     private final MessageContainer mContainer;
     private final Callback<Animator> mAnimatorStartCallback;
 
@@ -106,9 +110,131 @@
         }
     }
 
-    // TODO(crbug.com/1200974): Add support for stacking animation.
+    // TODO(crbug.com/1200974): Compare current shown messages with last shown ones.
+    /**
+     * cf: Current front message.
+     * cb: Current back message.
+     * nf: Next front message.
+     * nb: Next back message.
+     * Null represents no view at that position.
+     * 1. If candidates and current displayed messages are internally equal, do nothing.
+     * 2. If cf is null, which implies cb is also null, show candidates.
+     * 3. If cf is not found in candidates, it must be hidden.
+     *    In the meantime, if current back message is displayed, check if it should be hidden or
+     *    moved to front.
+     * 4. If only back message is changed:
+     *    Hide current back message if possible; otherwise, show the candidate.
+     * 5. The current front message must be moved back and a new message is moved to front.
+     *
+     * Note: Assume current displayed messages are [m1, m2]; Then the candidates won't be [m3, m2].
+     * If m3 is a higher priority message, then the candidates should be [m3, m1].
+     * Otherwise, m1 is usually hidden because of dismissing or inactive scope, the candidates
+     * should be [m2, null/m3].
+     *
+     * [m1, m2] -> [m3, m4] should also be impossible, because message is designed to be dismissed
+     * one by one. If both are hiding by queue suspending, it should look like:
+     * [m1, m2] -> [null, null] -> [m3, m4]
+     *
+     * @param candidates The candidates supposed to be displayed next. Not all candidates are
+     *                   guaranteed to be displayed after update. The content may be changed to
+     *                   reflect the actual change in this update.
+     * @param isSuspended Whether the queue is suspended.
+     * @param onFinished Runnable triggered after animation is finished.
+     */
     public void updateWithStacking(
-            @NonNull List<MessageState> candidates, boolean isSuspended, Runnable onFinished) {}
+            @NonNull List<MessageState> candidates, boolean isSuspended, Runnable onFinished) {
+        // Wait until the current animation is done, unless we need to hide them immediately.
+        if (!isSuspended && mAnimatorSet.isStarted()) {
+            return;
+        }
+        var cf = mCurrentDisplayedMessages.get(0); // Currently front.
+        var cb = mCurrentDisplayedMessages.get(1); // Currently back.
+        var nf = candidates.get(0); // Next front.
+        var nb = candidates.get(1); // Next back.
+        mFrontAnimator = mBackAnimator = null;
+        boolean animate = !isSuspended;
+
+        // If front message is null, then the back one is definitely null.
+        assert !(nf == null && nb != null);
+        assert !(cf == null && cb != null);
+        if (cf == nf && cb == nb) return;
+        if (cf == null) { // Implies that currently back is also null.
+            mFrontAnimator = nf.handler.show(Position.INVISIBLE, Position.FRONT);
+            if (nb != null) mBackAnimator = nb.handler.show(Position.FRONT, Position.BACK);
+        } else if (cf != nf && cf != nb) {
+            // Current displayed front message will be hidden.
+            mFrontAnimator = cf.handler.hide(Position.FRONT, Position.INVISIBLE, animate);
+            if (cb != null) {
+                if (cb == nf) { // Visible front will be dismissed and back one is moved to front.
+                    mBackAnimator = cb.handler.show(Position.BACK, Position.FRONT);
+                    // Show nb in the next round.
+                    nb = null;
+                    candidates.set(1, null);
+                } else { // Both visible front and back messages will be replaced.
+                    mBackAnimator = cb.handler.hide(Position.BACK, Position.FRONT, animate);
+                    // Hide current displayed two messages and then show other messages
+                    // in the next round.
+                    nf = nb = null;
+                    candidates.set(0, null);
+                    candidates.set(1, null);
+                }
+            }
+        } else if (cf == nf) {
+            if (cb != null) { // Hide the current back one.
+                mBackAnimator = cb.handler.hide(Position.BACK, Position.FRONT, animate);
+            } else {
+                // If nb is null, it means candidates and current displayed messages are equal.
+                assert nb != null;
+                mBackAnimator = nb.handler.show(Position.FRONT, Position.BACK);
+            }
+        } else {
+            assert cf == nb;
+            if (cb != null) {
+                mBackAnimator = cb.handler.hide(Position.BACK, Position.FRONT, animate);
+                // [m1, m2] -> [m1, null] -> [m3, m1]
+                // In this case, we complete this in 2 steps to avoid manipulating 3 handlers
+                // at any single moment.
+                candidates.set(0, cf);
+                candidates.set(1, null);
+            } else { // Moved the current front to back and show a new front view.
+                mBackAnimator = cf.handler.show(Position.FRONT, Position.BACK);
+                mFrontAnimator = nf.handler.show(Position.INVISIBLE, Position.FRONT);
+            }
+        }
+        if (cf == null) {
+            // No message is being displayed now: trigger #onStartShowing.
+            mMessageQueueDelegate.onStartShowing(
+                    () -> { triggerStackingAnimation(candidates, onFinished); });
+        } else if (nf == null) {
+            // All messages will be hidden: trigger #onFinishHiding.
+            Runnable runnable = () -> {
+                mMessageQueueDelegate.onFinishHiding();
+                mCurrentDisplayedMessage = mLastShownMessage = null;
+                onFinished.run();
+            };
+            triggerStackingAnimation(candidates, runnable);
+        } else {
+            triggerStackingAnimation(candidates, onFinished);
+        }
+    }
+
+    private void triggerStackingAnimation(List<MessageState> candidates, Runnable onFinished) {
+        mCurrentDisplayedMessages = new ArrayList<>(candidates);
+        Runnable runnable = () -> {
+            mAnimatorSet.cancel();
+            mAnimatorSet.removeAllListeners();
+            mAnimatorSet = new AnimatorSet();
+            mAnimatorSet.play(mFrontAnimator);
+            mAnimatorSet.play(mBackAnimator);
+            mAnimatorSet.addListener(new MessageAnimationListener(onFinished));
+            mAnimatorStartCallback.onResult(mAnimatorSet);
+        };
+        if (candidates.get(0) == null) {
+            runnable.run();
+        } else {
+            mContainer.runAfterInitialMessageLayout(runnable);
+        }
+    }
 
     void setMessageQueueDelegate(MessageQueueDelegate delegate) {
         mMessageQueueDelegate = delegate;
@@ -119,6 +245,12 @@
         return mCurrentDisplayedMessage;
     }
 
+    // Return a list of two messages which should be displayed when stacking animation is enabled.
+    @NonNull
+    List<MessageState> getCurrentDisplayedMessages() {
+        return mCurrentDisplayedMessages;
+    }
+
     class MessageAnimationListener extends CancelAwareAnimatorListener {
         private final Runnable mOnFinished;
 
@@ -132,9 +264,4 @@
             mOnFinished.run();
         }
     }
-
-    // Return a list of two messages which should be displayed when stacking animation is enabled.
-    List<MessageState> getCurrentDisplayedMessages() {
-        return mCurrentDisplayedMessages;
-    }
 }
diff --git a/components/messages/android/internal/java/src/org/chromium/components/messages/MessageAnimationCoordinatorUnitTest.java b/components/messages/android/internal/java/src/org/chromium/components/messages/MessageAnimationCoordinatorUnitTest.java
new file mode 100644
index 0000000..fb86398
--- /dev/null
+++ b/components/messages/android/internal/java/src/org/chromium/components/messages/MessageAnimationCoordinatorUnitTest.java
@@ -0,0 +1,181 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.components.messages;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.animation.Animator;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import org.chromium.base.Callback;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.components.messages.MessageQueueManager.MessageState;
+import org.chromium.components.messages.MessageStateHandler.Position;
+
+import java.util.Arrays;
+
+/**
+ * Unit tests for {@link MessageAnimationCoordinator}.
+ */
+@RunWith(BaseRobolectricTestRunner.class)
+public class MessageAnimationCoordinatorUnitTest {
+    private MessageQueueDelegate mQueueDelegate = new MessageQueueDelegate() {
+        @Override
+        public void onStartShowing(Runnable callback) {
+            callback.run();
+        }
+
+        @Override
+        public void onFinishHiding() {}
+    };
+
+    @Rule
+    public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    @Mock
+    private MessageContainer mContainer;
+
+    @Mock
+    private Callback<Animator> mAnimatorStartCallback;
+
+    private MessageAnimationCoordinator mAnimationCoordinator;
+
+    @Before
+    public void setUp() {
+        mAnimationCoordinator = new MessageAnimationCoordinator(mContainer, mAnimatorStartCallback);
+        doNothing().when(mAnimatorStartCallback).onResult(any());
+        mAnimationCoordinator.setMessageQueueDelegate(mQueueDelegate);
+    }
+
+    // Test incoming candidates are same with current displayed ones.
+    // [m1, m2] -> [m1, m2]
+    @Test
+    @SmallTest
+    public void testDoNothing() {
+        MessageState m1 = buildMessageState();
+        MessageState m2 = buildMessageState();
+        // Initial setup
+        mAnimationCoordinator.updateWithStacking(Arrays.asList(m1, m2), false, () -> {});
+
+        // Again with same candidates.
+        mAnimationCoordinator.updateWithStacking(Arrays.asList(m1, m2), false, () -> {});
+
+        verify(m1.handler, never()).hide(anyInt(), anyInt(), anyBoolean());
+        verify(m2.handler, never()).hide(anyInt(), anyInt(), anyBoolean());
+        verify(m1.handler).show(anyInt(), anyInt());
+        verify(m2.handler).show(anyInt(), anyInt());
+    }
+
+    // Test showing messages.
+    // [null, null] -> [m1, m2]
+    @Test
+    @SmallTest
+    public void testShowMessages() {
+        // Initial values should be null.
+        var currentMessages = mAnimationCoordinator.getCurrentDisplayedMessages();
+        Assert.assertArrayEquals(new MessageState[] {null, null}, currentMessages.toArray());
+
+        MessageState m1 = buildMessageState();
+        MessageState m2 = buildMessageState();
+        mAnimationCoordinator.updateWithStacking(Arrays.asList(m1, m2), false, () -> {});
+
+        verify(m1.handler).show(Position.INVISIBLE, Position.FRONT);
+        verify(m2.handler).show(Position.FRONT, Position.BACK);
+        verify(m1.handler, never()).hide(anyInt(), anyInt(), anyBoolean());
+        verify(m2.handler, never()).hide(anyInt(), anyInt(), anyBoolean());
+
+        currentMessages = mAnimationCoordinator.getCurrentDisplayedMessages();
+        Assert.assertArrayEquals(new MessageState[] {m1, m2}, currentMessages.toArray());
+    }
+
+    // Test only front message becomes hidden.
+    // [m1, m2] -> [m2, null]
+    @Test
+    @SmallTest
+    public void testHideFrontMessageOnly() {
+        MessageState m1 = buildMessageState();
+        MessageState m2 = buildMessageState();
+        mAnimationCoordinator.updateWithStacking(Arrays.asList(m1, m2), false, () -> {});
+
+        verify(m1.handler).show(Position.INVISIBLE, Position.FRONT);
+        verify(m2.handler).show(Position.FRONT, Position.BACK);
+
+        // Hide the front one so that the back one is brought to front.
+        mAnimationCoordinator.updateWithStacking(Arrays.asList(m2, null), false, () -> {});
+        verify(m1.handler).hide(Position.FRONT, Position.INVISIBLE, true);
+        verify(m2.handler).show(Position.BACK, Position.FRONT);
+
+        var currentMessages = mAnimationCoordinator.getCurrentDisplayedMessages();
+        Assert.assertArrayEquals(new MessageState[] {m2, null}, currentMessages.toArray());
+    }
+
+    // Test hiding front one and then showing a new one.
+    // [m1, m2] -> [m2, m3] is done in two steps.
+    // [m1, m2] -> [m2, null], then [m2, null] -> [m2, m3]
+    @Test
+    @SmallTest
+    public void testDismissFrontAndEnqueueNew() {
+        MessageState m1 = buildMessageState();
+        MessageState m2 = buildMessageState();
+        mAnimationCoordinator.updateWithStacking(Arrays.asList(m1, m2), false, () -> {});
+
+        verify(m1.handler).show(Position.INVISIBLE, Position.FRONT);
+        verify(m2.handler).show(Position.FRONT, Position.BACK);
+
+        MessageState m3 = buildMessageState();
+        // Hide the front one so that the back one is brought to front.
+        mAnimationCoordinator.updateWithStacking(Arrays.asList(m2, m3), false, () -> {});
+        verify(m1.handler).hide(Position.FRONT, Position.INVISIBLE, true);
+        verify(m2.handler).show(Position.BACK, Position.FRONT);
+
+        var currentMessages = mAnimationCoordinator.getCurrentDisplayedMessages();
+        Assert.assertArrayEquals(new MessageState[] {m2, null}, currentMessages.toArray());
+
+        mAnimationCoordinator.updateWithStacking(Arrays.asList(m2, m3), false, () -> {});
+        verify(m3.handler).show(Position.FRONT, Position.BACK);
+        currentMessages = mAnimationCoordinator.getCurrentDisplayedMessages();
+        Assert.assertArrayEquals(new MessageState[] {m2, m3}, currentMessages.toArray());
+    }
+
+    // Test only back message becomes hidden.
+    // [m1, m2] -> [m1, null]
+    @Test
+    @SmallTest
+    public void testHiddenBackMessageOnly() {
+        MessageState m1 = buildMessageState();
+        MessageState m2 = buildMessageState();
+        mAnimationCoordinator.updateWithStacking(Arrays.asList(m1, m2), false, () -> {});
+
+        verify(m1.handler).show(Position.INVISIBLE, Position.FRONT);
+        verify(m2.handler).show(Position.FRONT, Position.BACK);
+
+        mAnimationCoordinator.updateWithStacking(Arrays.asList(m1, null), false, () -> {});
+        verify(m1.handler, never()).hide(anyInt(), anyInt(), anyBoolean());
+        verify(m2.handler).hide(Position.BACK, Position.FRONT, true);
+
+        var currentMessages = mAnimationCoordinator.getCurrentDisplayedMessages();
+        Assert.assertArrayEquals(new MessageState[] {m1, null}, currentMessages.toArray());
+    }
+
+    private MessageState buildMessageState() {
+        return new MessageState(null, null, Mockito.mock(MessageStateHandler.class), false);
+    }
+}
diff --git a/components/permissions/android/permission_prompt/permission_prompt_android.cc b/components/permissions/android/permission_prompt/permission_prompt_android.cc
index 9d822ae..fee484b2 100644
--- a/components/permissions/android/permission_prompt/permission_prompt_android.cc
+++ b/components/permissions/android/permission_prompt/permission_prompt_android.cc
@@ -23,9 +23,8 @@
 
 PermissionPromptAndroid::~PermissionPromptAndroid() = default;
 
-bool PermissionPromptAndroid::UpdateAnchor() {
+void PermissionPromptAndroid::UpdateAnchor() {
   NOTIMPLEMENTED();
-  return false;
 }
 
 PermissionPrompt::TabSwitchingBehavior
diff --git a/components/permissions/android/permission_prompt/permission_prompt_android.h b/components/permissions/android/permission_prompt/permission_prompt_android.h
index 532ede4..33c6c38 100644
--- a/components/permissions/android/permission_prompt/permission_prompt_android.h
+++ b/components/permissions/android/permission_prompt/permission_prompt_android.h
@@ -38,7 +38,7 @@
   ~PermissionPromptAndroid() override;
 
   // PermissionPrompt:
-  bool UpdateAnchor() override;
+  void UpdateAnchor() override;
   TabSwitchingBehavior GetTabSwitchingBehavior() override;
 
   void Closing();
diff --git a/components/permissions/permission_manager.cc b/components/permissions/permission_manager.cc
index f5e67107..9f74544 100644
--- a/components/permissions/permission_manager.cc
+++ b/components/permissions/permission_manager.cc
@@ -292,14 +292,6 @@
       continue;
     }
 
-    auto status = GetPermissionOverrideForDevTools(
-        url::Origin::Create(canonical_requesting_origin), permission);
-    if (status != CONTENT_SETTING_DEFAULT) {
-      response_callback->OnPermissionsRequestResponseStatus(
-          CONTENT_SETTING_ALLOW);
-      continue;
-    }
-
     PermissionContextBase* context = GetPermissionContext(permission);
     DCHECK(context);
 
@@ -602,10 +594,6 @@
 
   GURL canonical_requesting_origin = PermissionUtil::GetCanonicalOrigin(
       permission, requesting_origin, embedding_origin);
-  auto status = GetPermissionOverrideForDevTools(
-      url::Origin::Create(canonical_requesting_origin), permission);
-  if (status != CONTENT_SETTING_DEFAULT)
-    return PermissionResult(status, PermissionStatusSource::UNSPECIFIED);
   PermissionContextBase* context = GetPermissionContext(permission);
   PermissionResult result = context->GetPermissionStatus(
       render_frame_host, canonical_requesting_origin.DeprecatedGetOriginAsURL(),
@@ -618,40 +606,4 @@
   return result;
 }
 
-ContentSetting PermissionManager::GetPermissionOverrideForDevTools(
-    const url::Origin& origin,
-    ContentSettingsType permission) {
-  auto it = devtools_permission_overrides_.find(origin);
-  if (it == devtools_permission_overrides_.end())
-    it = devtools_permission_overrides_.find(devtools_global_overrides_origin_);
-  if (it == devtools_permission_overrides_.end())
-    return CONTENT_SETTING_DEFAULT;
-
-  auto setting_it = it->second.find(permission);
-  if (setting_it == it->second.end())
-    return CONTENT_SETTING_DEFAULT;
-
-  return setting_it->second;
-}
-
-void PermissionManager::SetPermissionOverridesForDevTools(
-    const absl::optional<url::Origin>& optional_origin,
-    const PermissionOverrides& overrides) {
-  ContentSettingsTypeOverrides result;
-  for (const auto& item : overrides) {
-    ContentSettingsType content_setting =
-        PermissionUtil::PermissionTypeToContentSettingTypeSafe(item.first);
-    if (content_setting != ContentSettingsType::DEFAULT)
-      result[content_setting] =
-          PermissionUtil::PermissionStatusToContentSetting(item.second);
-  }
-  const url::Origin& origin =
-      optional_origin.value_or(devtools_global_overrides_origin_);
-  devtools_permission_overrides_[origin] = std::move(result);
-}
-
-void PermissionManager::ResetPermissionOverridesForDevTools() {
-  devtools_permission_overrides_.clear();
-}
-
 }  // namespace permissions
diff --git a/components/permissions/permission_manager.h b/components/permissions/permission_manager.h
index 19126bcf..35a1d4e2 100644
--- a/components/permissions/permission_manager.h
+++ b/components/permissions/permission_manager.h
@@ -183,18 +183,6 @@
       const GURL& requesting_origin,
       const GURL& embedding_origin);
 
-  ContentSetting GetPermissionOverrideForDevTools(
-      const url::Origin& origin,
-      ContentSettingsType permission);
-
-  // content::PermissionControllerDelegate implementation.
-  // For the given |origin|, overrides permissions that belong to |overrides|.
-  // These permissions are in-sync with the PermissionController.
-  void SetPermissionOverridesForDevTools(
-      const absl::optional<url::Origin>& origin,
-      const PermissionOverrides& overrides) override;
-  void ResetPermissionOverridesForDevTools() override;
-
   raw_ptr<content::BrowserContext> browser_context_;
 
   PendingRequestsMap pending_requests_;
@@ -211,11 +199,6 @@
   SubscriptionTypeCounts subscription_type_counts_;
 
   PermissionContextMap permission_contexts_;
-  using ContentSettingsTypeOverrides =
-      base::flat_map<ContentSettingsType, ContentSetting>;
-  std::map<url::Origin, ContentSettingsTypeOverrides>
-      devtools_permission_overrides_;
-  url::Origin devtools_global_overrides_origin_;
 
   bool is_shutting_down_ = false;
 };
diff --git a/components/permissions/permission_prompt.h b/components/permissions/permission_prompt.h
index 0efc6f5..4c7df73f 100644
--- a/components/permissions/permission_prompt.h
+++ b/components/permissions/permission_prompt.h
@@ -118,9 +118,7 @@
   virtual ~PermissionPrompt() {}
 
   // Updates where the prompt should be anchored. ex: fullscreen toggle.
-  // Returns true, if the update was successful, and false if the caller should
-  // recreate the view instead.
-  virtual bool UpdateAnchor() = 0;
+  virtual void UpdateAnchor() = 0;
 
   // Get the behavior of this prompt when the user switches away from the
   // associated tab.
diff --git a/components/permissions/permission_request.cc b/components/permissions/permission_request.cc
index 6537a61c..3baf995 100644
--- a/components/permissions/permission_request.cc
+++ b/components/permissions/permission_request.cc
@@ -4,7 +4,6 @@
 
 #include "components/permissions/permission_request.h"
 
-#include "base/no_destructor.h"
 #include "base/notreached.h"
 #include "build/build_config.h"
 #include "components/permissions/permission_util.h"
@@ -112,50 +111,58 @@
   return permissions::GetBlockedIconId(request_type_);
 }
 
-absl::optional<std::u16string> PermissionRequest::GetRequestChipText(
-    ChipTextType type) const {
-  static base::NoDestructor<std::map<RequestType, std::vector<int>>> kMessageIds(
-      {{RequestType::kArSession, {IDS_AR_PERMISSION_CHIP, -1, -1, -1, -1, -1}},
-       {RequestType::kCameraStream,
-        {IDS_MEDIA_CAPTURE_VIDEO_ONLY_PERMISSION_CHIP, -1,
-         IDS_PERMISSIONS_PERMISSION_ALLOWED_CONFIRMATION,
-         IDS_PERMISSIONS_PERMISSION_NOT_ALLOWED_CONFIRMATION,
-         IDS_PERMISSIONS_CAMERA_ALLOWED_CONFIRMATION_SCREENREADER_ANNOUNCEMENT,
-         IDS_PERMISSIONS_CAMERA_NOT_ALLOWED_CONFIRMATION_SCREENREADER_ANNOUNCEMENT}},
-       {RequestType::kClipboard,
-        {IDS_CLIPBOARD_PERMISSION_CHIP, -1, -1, -1, -1, -1}},
-       {RequestType::kGeolocation,
-        {IDS_GEOLOCATION_PERMISSION_CHIP,
-         IDS_GEOLOCATION_PERMISSION_BLOCKED_CHIP,
-         IDS_PERMISSIONS_PERMISSION_ALLOWED_CONFIRMATION,
-         IDS_PERMISSIONS_PERMISSION_NOT_ALLOWED_CONFIRMATION,
-         IDS_PERMISSIONS_GEOLOCATION_ALLOWED_CONFIRMATION_SCREENREADER_ANNOUNCEMENT,
-         IDS_PERMISSIONS_GEOLOCATION_NOT_ALLOWED_CONFIRMATION_SCREENREADER_ANNOUNCEMENT}},
-       {RequestType::kIdleDetection,
-        {IDS_IDLE_DETECTION_PERMISSION_CHIP, -1, -1, -1, -1, -1}},
-       {RequestType::kMicStream,
-        {IDS_MEDIA_CAPTURE_AUDIO_ONLY_PERMISSION_CHIP, -1,
-         IDS_PERMISSIONS_PERMISSION_ALLOWED_CONFIRMATION,
-         IDS_PERMISSIONS_PERMISSION_NOT_ALLOWED_CONFIRMATION,
-         IDS_PERMISSIONS_MICROPHONE_ALLOWED_CONFIRMATION_SCREENREADER_ANNOUNCEMENT,
-         IDS_PERMISSIONS_MICROPHONE_NOT_ALLOWED_CONFIRMATION_SCREENREADER_ANNOUNCEMENT}},
-       {RequestType::kMidiSysex,
-        {IDS_MIDI_SYSEX_PERMISSION_CHIP, -1, -1, -1, -1, -1}},
-       {RequestType::kNotifications,
-        {IDS_NOTIFICATION_PERMISSIONS_CHIP,
-         IDS_NOTIFICATION_PERMISSIONS_BLOCKED_CHIP,
-         IDS_PERMISSIONS_PERMISSION_ALLOWED_CONFIRMATION,
-         IDS_PERMISSIONS_PERMISSION_NOT_ALLOWED_CONFIRMATION,
-         IDS_PERMISSIONS_NOTIFICATION_ALLOWED_CONFIRMATION_SCREENREADER_ANNOUNCEMENT,
-         IDS_PERMISSIONS_NOTIFICATION_NOT_ALLOWED_CONFIRMATION_SCREENREADER_ANNOUNCEMENT}},
-       {RequestType::kVrSession,
-        {IDS_VR_PERMISSION_CHIP, -1, -1, -1, -1, -1}}});
+absl::optional<std::u16string> PermissionRequest::GetRequestChipText() const {
+  int message_id;
+  switch (request_type_) {
+    case RequestType::kArSession:
+      message_id = IDS_AR_PERMISSION_CHIP;
+      break;
+    case RequestType::kCameraStream:
+      message_id = IDS_MEDIA_CAPTURE_VIDEO_ONLY_PERMISSION_CHIP;
+      break;
+    case RequestType::kClipboard:
+      message_id = IDS_CLIPBOARD_PERMISSION_CHIP;
+      break;
+    case RequestType::kGeolocation:
+      message_id = IDS_GEOLOCATION_PERMISSION_CHIP;
+      break;
+    case RequestType::kIdleDetection:
+      message_id = IDS_IDLE_DETECTION_PERMISSION_CHIP;
+      break;
+    case RequestType::kMicStream:
+      message_id = IDS_MEDIA_CAPTURE_AUDIO_ONLY_PERMISSION_CHIP;
+      break;
+    case RequestType::kMidiSysex:
+      message_id = IDS_MIDI_SYSEX_PERMISSION_CHIP;
+      break;
+    case RequestType::kNotifications:
+      message_id = IDS_NOTIFICATION_PERMISSIONS_CHIP;
+      break;
+    case RequestType::kVrSession:
+      message_id = IDS_VR_PERMISSION_CHIP;
+      break;
+    default:
+      // TODO(bsep): We don't actually want to support having no string in the
+      // long term, but writing them takes time. In the meantime, we fall back
+      // to the existing UI when the string is missing.
+      return absl::nullopt;
+  }
+  return l10n_util::GetStringUTF16(message_id);
+}
 
-  auto messages = kMessageIds->find(request_type_);
-  if (messages != kMessageIds->end() && messages->second[type] != -1)
-    return l10n_util::GetStringUTF16(messages->second[type]);
-
-  return absl::nullopt;
+absl::optional<std::u16string> PermissionRequest::GetQuietChipText() const {
+  int message_id;
+  switch (request_type_) {
+    case RequestType::kGeolocation:
+      message_id = IDS_GEOLOCATION_PERMISSION_BLOCKED_CHIP;
+      break;
+    case RequestType::kNotifications:
+      message_id = IDS_NOTIFICATION_PERMISSIONS_BLOCKED_CHIP;
+      break;
+    default:
+      return absl::nullopt;
+  }
+  return l10n_util::GetStringUTF16(message_id);
 }
 
 std::u16string PermissionRequest::GetMessageTextFragment() const {
diff --git a/components/permissions/permission_request.h b/components/permissions/permission_request.h
index 9220e83..e9179c6 100644
--- a/components/permissions/permission_request.h
+++ b/components/permissions/permission_request.h
@@ -57,15 +57,6 @@
   PermissionRequest(const PermissionRequest&) = delete;
   PermissionRequest& operator=(const PermissionRequest&) = delete;
 
-  enum ChipTextType {
-    LOUD_REQUEST,
-    QUIET_REQUEST,
-    ALLOW_CONFIRMATION,
-    BLOCKED_CONFIRMATION,
-    ACCESSIBILITY_ALLOWED_CONFIRMATION,
-    ACCESSIBILITY_BLOCKED_CONFIRMATION
-  };
-
   virtual ~PermissionRequest();
 
   GURL requesting_origin() const { return requesting_origin_; }
@@ -91,7 +82,11 @@
 
   // Returns prompt text appropriate for displaying on the chip button in the
   // location bar.
-  absl::optional<std::u16string> GetRequestChipText(ChipTextType type) const;
+  absl::optional<std::u16string> GetRequestChipText() const;
+
+  // Returns prompt text appropriate for displaying on the quiet chip button in
+  // the location bar.
+  absl::optional<std::u16string> GetQuietChipText() const;
 
   // Returns prompt text appropriate for displaying under the dialog title
   // "[domain] wants to:".
diff --git a/components/permissions/permission_request_manager.cc b/components/permissions/permission_request_manager.cc
index e32337c2..b39df7f 100644
--- a/components/permissions/permission_request_manager.cc
+++ b/components/permissions/permission_request_manager.cc
@@ -212,7 +212,7 @@
     return;
   }
 
-  // TODO(tsergeant): change the UMA to no longer mention bubble.
+  // TODO(tsergeant): change the UMA to no longer mention bubbles.
   base::RecordAction(base::UserMetricsAction("PermissionBubbleRequest"));
 
   // TODO(gbillock): is there a race between an early request on a
@@ -349,8 +349,7 @@
     // recreated for the new browser. Because of that, ignore prompt callbacks
     // while doing that.
     base::AutoReset<bool> ignore(&ignore_callbacks_from_prompt_, true);
-    if (!view_->UpdateAnchor())
-      RecreateView();
+    view_->UpdateAnchor();
   }
 }
 
@@ -401,7 +400,7 @@
   // issued at DOMContentLoaded, they may be bouncing around in scheduled
   // callbacks finding the UI thread still. This makes sure we allow those
   // scheduled calls to AddRequest to complete before we show the page-load
-  // permissions prompt.
+  // permissions bubble.
   ScheduleDequeueRequestIfNeeded();
 }
 
@@ -411,12 +410,12 @@
 }
 
 void PermissionRequestManager::WebContentsDestroyed() {
-  // If the web contents has been destroyed, treat the prompt as cancelled.
+  // If the web contents has been destroyed, treat the bubble as cancelled.
   CleanUpRequests();
 
   // The WebContents is going away; be aggressively paranoid and delete
   // ourselves lest other parts of the system attempt to add permission
-  // prompts or use us otherwise during the destruction.
+  // bubbles or use us otherwise during the destruction.
   web_contents()->RemoveUserData(UserDataKey());
   // That was the equivalent of "delete this". This object is now destroyed;
   // returning from this function is the only safe thing to do.
@@ -494,8 +493,6 @@
     PermissionGrantedIncludingDuplicates(*requests_iter,
                                          /*is_one_time=*/false);
   }
-
-  NotifyRequestDecided(PermissionAction::GRANTED);
   FinalizeCurrentRequests(PermissionAction::GRANTED);
 }
 
@@ -509,8 +506,6 @@
     PermissionGrantedIncludingDuplicates(*requests_iter,
                                          /*is_one_time=*/true);
   }
-
-  NotifyRequestDecided(PermissionAction::GRANTED_ONCE);
   FinalizeCurrentRequests(PermissionAction::GRANTED_ONCE);
 }
 
@@ -536,8 +531,6 @@
        requests_iter++) {
     PermissionDeniedIncludingDuplicates(*requests_iter);
   }
-
-  NotifyRequestDecided(PermissionAction::DENIED);
   FinalizeCurrentRequests(PermissionAction::DENIED);
 }
 
@@ -550,8 +543,6 @@
        requests_iter++) {
     CancelledIncludingDuplicates(*requests_iter);
   }
-
-  NotifyRequestDecided(PermissionAction::DISMISSED);
   FinalizeCurrentRequests(PermissionAction::DISMISSED);
 }
 
@@ -564,8 +555,6 @@
        requests_iter++) {
     CancelledIncludingDuplicates(*requests_iter);
   }
-
-  NotifyRequestDecided(PermissionAction::IGNORED);
   FinalizeCurrentRequests(PermissionAction::IGNORED);
 }
 
@@ -716,7 +705,7 @@
   // There is a race condition where the request might have been removed
   // already so double-checking that there is a request in progress.
   //
-  // There is no need to show a new prompt if the previous one still exists.
+  // There is no need to show a new bubble if the previous one still exists.
   if (!IsRequestInProgress() || view_)
     return;
 
@@ -1011,12 +1000,6 @@
     observer.OnBubbleRemoved();
 }
 
-void PermissionRequestManager::NotifyRequestDecided(
-    permissions::PermissionAction permission_action) {
-  for (Observer& observer : observer_list_)
-    observer.OnRequestDecided(permission_action);
-}
-
 void PermissionRequestManager::OnPermissionUiSelectorDone(
     size_t selector_index,
     const UiDecision& decision) {
diff --git a/components/permissions/permission_request_manager.h b/components/permissions/permission_request_manager.h
index 9a2aac27..2a5ea98 100644
--- a/components/permissions/permission_request_manager.h
+++ b/components/permissions/permission_request_manager.h
@@ -81,7 +81,6 @@
     virtual void OnRequestsFinalized() {}
 
     virtual void OnPermissionRequestManagerDestructed() {}
-    virtual void OnRequestDecided(permissions::PermissionAction action) {}
 
    protected:
     virtual ~Observer() = default;
@@ -285,7 +284,6 @@
 
   void NotifyBubbleAdded();
   void NotifyBubbleRemoved();
-  void NotifyRequestDecided(permissions::PermissionAction permission_action);
 
   void OnPermissionUiSelectorDone(size_t selector_index,
                                   const UiDecision& decision);
diff --git a/components/permissions/test/mock_permission_prompt.cc b/components/permissions/test/mock_permission_prompt.cc
index 3117a8e..dd5f2b5 100644
--- a/components/permissions/test/mock_permission_prompt.cc
+++ b/components/permissions/test/mock_permission_prompt.cc
@@ -23,9 +23,7 @@
     factory_->HideView(this);
 }
 
-bool MockPermissionPrompt::UpdateAnchor() {
-  return true;
-}
+void MockPermissionPrompt::UpdateAnchor() {}
 
 PermissionPrompt::TabSwitchingBehavior
 MockPermissionPrompt::GetTabSwitchingBehavior() {
diff --git a/components/permissions/test/mock_permission_prompt.h b/components/permissions/test/mock_permission_prompt.h
index 579e093..4e9c8479 100644
--- a/components/permissions/test/mock_permission_prompt.h
+++ b/components/permissions/test/mock_permission_prompt.h
@@ -20,7 +20,7 @@
   ~MockPermissionPrompt() override;
 
   // PermissionPrompt:
-  bool UpdateAnchor() override;
+  void UpdateAnchor() override;
   TabSwitchingBehavior GetTabSwitchingBehavior() override;
   PermissionPromptDisposition GetPromptDisposition() const override;
 
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index b016278..c6509d3b 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -22049,6 +22049,16 @@
         'dynamic_refresh': False,
         'per_profile': False,
       },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Require site isolation for all websites',
+        },
+        {
+          'value': False,
+          'caption': 'Enable site isolation for all websites, but allow the user to opt out',
+        },
+      ],
       'example_value': True,
       'id': 399,
       'caption': '''Require Site Isolation for every site''',
@@ -22095,6 +22105,16 @@
         'dynamic_refresh': False,
         'per_profile': False,
       },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Require site isolation for the sign-in screen',
+        },
+        {
+          'value': False,
+          'caption': 'Use the default site isolation settings for the sign-in screen',
+        },
+      ],
       'deprecated': True,
       'example_value': True,
       'id': 418,
@@ -22143,7 +22163,22 @@
         'dynamic_refresh': False,
         'per_profile': False,
       },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Require site isolation for all websites',
+        },
+        {
+          'value': False,
+          'caption': 'Disable site isolation for all websites, but allow the user to enable it',
+        },
+        {
+          'value': None,
+          'caption': 'Allow the user to decide',
+        },
+      ],
       'example_value': True,
+      'default': None,
       'id': 446,
       'caption': '''Enable Site Isolation for every site''',
       'tags': ['system-security'],
@@ -22313,6 +22348,16 @@
         'dynamic_refresh': False,
         'per_profile': False,
       },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Allow WebDriver to override incompatible policies',
+        },
+        {
+          'value': False,
+          'caption': 'Do not allow WebDriver to override incompatible policies',
+        },
+      ],
       'deprecated': True,
       'example_value': True,
       'id': 414,
@@ -22577,6 +22622,16 @@
         'dynamic_refresh': False,
         'per_profile': False,
       },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Prevent third party code from being injected into Chrome',
+        },
+        {
+          'value': False,
+          'caption': 'Allow third party code to be injected into Chrome',
+        },
+      ],
       'example_value': False,
       'id': 407,
       'caption': '''Enable third party software injection blocking''',
@@ -22892,6 +22947,16 @@
       'features': {
         'dynamic_refresh': True,
       },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Allow the device to run virtual machines',
+        },
+        {
+          'value': False,
+          'caption': 'Do not allow the device to run virtual machines',
+        },
+      ],
       'example_value': True,
       'default_for_managed_devices_doc_only': False,
       'id': 421,
@@ -22911,6 +22976,16 @@
         'dynamic_refresh': True,
         'per_profile': False,
       },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Allow users to use virtual machines needed to support Linux apps',
+        },
+        {
+          'value': False,
+          'caption': 'Do not allow users to use virtual machines needed to support Linux apps',
+        },
+      ],
       'example_value': False,
       'id': 481,
       'caption': '''User is enabled to run Crostini''',
@@ -22927,6 +23002,16 @@
       'features': {
         'dynamic_refresh': True,
       },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Allow unaffiliated users to use virtual machines needed to support Linux apps',
+        },
+        {
+          'value': False,
+          'caption': 'Do not allow unaffiliated users to use virtual machines needed to support Linux apps',
+        },
+      ],
       'example_value': False,
       'id': 482,
       'caption': '''Allow unaffiliated users to use Crostini''',
@@ -22943,6 +23028,16 @@
         'dynamic_refresh': True,
         'per_profile': False,
       },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Enable Linux virtual machine backup and restore',
+        },
+        {
+          'value': False,
+          'caption': 'Disable Linux virtual machine backup and restore',
+        },
+      ],
       'example_value': False,
       'id': 523,
       'caption': '''User is enabled to export / import Crostini containers via the UI''',
@@ -22959,6 +23054,16 @@
         'dynamic_refresh': True,
         'per_profile': False,
       },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Enable root access to Linux virtual machines',
+        },
+        {
+          'value': False,
+          'caption': 'Disable root access to Linux virtual machines',
+        },
+      ],
       'example_value': False,
       'id': 576,
       'caption': '''User is allowed to have root access to Crostini containers''',
@@ -24840,7 +24945,6 @@
       },
       'example_value': '${ie}',
       'supported_on': ['chrome.*:71-'],
-      'future_on': ['fuchsia'],
       'caption': '''Alternative browser to launch for configured websites.''',
       'tags': [],
       'desc': '''Setting the policy controls which command to use to open URLs in an alternative browser. The policy can be set to one of <ph name="INTERNET_EXPLORER_VALUE_PLACEHOLDER">${ie}</ph>, <ph name="FIREFOX_VALUE_PLACEHOLDER">${firefox}</ph>, <ph name="SAFARI_VALUE_PLACEHOLDER">${safari}</ph>, <ph name="OPERA_VALUE_PLACEHOLDER">${opera}</ph>, <ph name="EDGE_VALUE_PLACEHOLDER">${edge}</ph> or a file path. When this policy is set to a file path, that file is used as an executable file. <ph name="INTERNET_EXPLORER_VALUE_PLACEHOLDER">${ie}</ph> is only available on <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>. <ph name="SAFARI_VALUE_PLACEHOLDER">${safari}</ph> and <ph name="EDGE_VALUE_PLACEHOLDER">${edge}</ph> are only available on <ph name="MS_WIN_NAME">Microsoft® Windows®</ph> and <ph name="MAC_OS_NAME">macOS</ph>.
@@ -24868,7 +24972,6 @@
         'per_profile': True,
       },
       'supported_on': ['chrome.*:71-'],
-      'future_on': ['fuchsia'],
       'caption': '''Command-line parameters for the alternative browser.''',
       'tags': [],
       'desc': '''Setting the policy to a list of strings means each string is passed to the alternative browser as separate command-line parameters. On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, the parameters are joined with spaces. On <ph name="MAC_OS_NAME">macOS</ph> and <ph name="LINUX_OS_NAME">Linux®</ph>, a parameter can have spaces and still be treated as a single parameter.
@@ -24945,7 +25048,6 @@
         'per_profile': True,
       },
       'supported_on': ['chrome.*:71-'],
-      'future_on': ['fuchsia'],
       'caption': '''Websites to open in alternative browser''',
       'tags': [],
       'desc': '''Setting the policy controls the list of websites to open in an alternative browser. Each item is treated as a rule for something to open in an alternative browser. <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> uses those rules when choosing if a URL should open in an alternative browser. When the <ph name="IE_PRODUCT_NAME">Internet Explorer®</ph> add-in is on, <ph name="IE_PRODUCT_NAME">Internet Explorer®</ph> switches back to <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> when the rules don't match. If rules contradict each other, <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> uses the most specific rule.
@@ -25037,7 +25139,6 @@
         'per_profile': True,
       },
       'supported_on': ['chrome.*:77-'],
-      'future_on': ['fuchsia'],
       'caption': '''URL of an XML file that contains URLs that should never trigger a browser switch.''',
       'tags': [],
       'desc': '''Setting the policy to a valid URL has <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> download the site list from that URL and apply the rules as if they were set up with the <ph name="BROWSER_SWITCHER_URL_GREYLIST_POLICY_NAME">BrowserSwitcherUrlGreylist</ph> policy. These policies prevent <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> and the alternative browser from opening one another.
@@ -25058,7 +25159,6 @@
         'per_profile': True,
       },
       'supported_on': ['chrome.*:74-'],
-      'future_on': ['fuchsia'],
       'caption': '''Delay before launching alternative browser (milliseconds)''',
       'tags': [],
       'desc': '''Setting the policy to a number has <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> show a message for that number of milliseconds, then it opens an alternative browser.
@@ -25077,7 +25177,6 @@
         'per_profile': True,
       },
       'supported_on': ['chrome.*:73-'],
-      'future_on': ['fuchsia'],
       'caption': '''Enable the Legacy Browser Support feature.''',
       'tags': [],
       'desc': '''Setting the policy to Enabled means <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> will try to launch some URLs in an alternate browser, such as <ph name="IE_PRODUCT_NAME">Internet Explorer®</ph>. This feature is set using the policies in the <ph name="LEGACY_BROWSER_SUPPORT_POLICY_GROUP">Legacy Browser support</ph> group.
@@ -25111,7 +25210,6 @@
       },
       'default': 0,
       'supported_on': ['chrome.*:95-'],
-      'future_on': ['fuchsia'],
       'caption': '''Sitelist parsing mode''',
       'tags': [],
       'desc': '''This policy controls how <ph name="PRODUCT_NAME">Google Chrome</ph> interprets sitelist/greylist policies for the Legacy Browser Support feature. It affects the following policies: <ph name="URL_LIST_POLICY_NAME">BrowserSwitcherUrlList</ph>, <ph name="URL_GREYLIST_POLICY_NAME">BrowserSwitcherUrlGreylist</ph>, <ph name="USE_IE_SITELIST_POLICY_NAME">BrowserSwitcherUseIeSitelist</ph>, <ph name="EXTERNAL_SITELIST_POLICY_NAME">BrowserSwitcherExternalSitelistUrl</ph>, and <ph name="EXTERNAL_GREYLIST_POLICY_NAME">BrowserSwitcherExternalGreylistUrl</ph>.
@@ -25140,7 +25238,6 @@
         'per_profile': True,
       },
       'supported_on': ['chrome.*:74-'],
-      'future_on': ['fuchsia'],
       'caption': '''Keep last tab open in Chrome.''',
       'tags': [],
       'desc': '''Setting the policy to Enabled or leaving it unset has <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> keep at least one tab open, after switching to an alternate browser.
@@ -25573,7 +25670,22 @@
         'dynamic_refresh': True,
         'per_profile': True,
       },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Allow Google Assistant to access screen context',
+        },
+        {
+          'value': False,
+          'caption': 'Block Google Assistant from accessing screen context during interactions',
+        },
+        {
+          'value': None,
+          'caption': 'Allow the user to decide',
+        },
+      ],
       'example_value': True,
+      'default': None,
       'id': 527,
       'caption': '''Allow Google Assistant to access screen context''',
       'desc': '''Setting the policy to Enabled lets Google Assistant access screen context and send that data to a server. Setting the policy to Disabled keeps Google Assistant from screen context.
@@ -25591,7 +25703,22 @@
         'dynamic_refresh': True,
         'per_profile': True,
       },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Enable the Google Assistant hotword',
+        },
+        {
+          'value': False,
+          'caption': 'Disable the Google Assistant hotword',
+        },
+        {
+          'value': None,
+          'caption': 'Allow the user to decide',
+        },
+      ],
       'example_value': True,
+      'default': None,
       'id': 529,
       'caption': '''Allow Google Assistant to listen for the voice activation phrase''',
       'desc': '''Setting the policy to Enabled lets Google Assistant listen for the voice activation phrase. Setting the policy to Disabled keeps Google Assistant from listening for the phrase.
@@ -25822,6 +25949,16 @@
         'dynamic_refresh': True,
         'per_profile': False,
       },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Enable the wilco diagnostics and telemetry controller',
+        },
+        {
+          'value': False,
+          'caption': 'Disable the wilco diagnostics and telemetry controller',
+        },
+      ],
       'example_value': False,
       'id': 532,
       'caption': '''Allows wilco diagnostics and telemetry controller''',
@@ -25895,6 +26032,16 @@
         'dynamic_refresh': True,
         'per_profile': False
       },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Enable peak shift power management',
+        },
+        {
+          'value': False,
+          'caption': 'Disable peak shift power management',
+        },
+      ],
       'example_value': False,
       'id': 538,
       'caption': '''Enable peak shift power management''',
@@ -26018,6 +26165,16 @@
         'dynamic_refresh': True,
         'per_profile': False
       },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Enable boot on AC',
+        },
+        {
+          'value': False,
+          'caption': 'Disable boot on AC',
+        },
+      ],
       'example_value': False,
       'id': 541,
       'caption': '''Enable boot on AC (alternating current)''',
@@ -26039,6 +26196,16 @@
         'dynamic_refresh': True,
         'per_profile': False
       },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Enable USB power share',
+        },
+        {
+          'value': False,
+          'caption': 'Disable USB power share',
+        },
+      ],
       'example_value': True,
       'id': 553,
       'caption': '''Enable USB power share''',
@@ -26216,6 +26383,16 @@
         'dynamic_refresh': True,
         'per_profile': False,
       },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Enforce online logins on the login and lock screens',
+        },
+        {
+          'value': False,
+          'caption': 'Only enforce online logins on the login screen',
+        },
+      ],
       'example_value': True,
       'id': 719,
       'caption': '''Enables online re-authentication on lock screen for SAML users''',
@@ -26292,6 +26469,16 @@
         'dynamic_refresh': True,
         'per_profile': False,
       },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Enable advanced battery charge mode',
+        },
+        {
+          'value': False,
+          'caption': 'Disable advanced battery charge mode',
+        },
+      ],
       'example_value': False,
       'id': 548,
       'caption': '''Enable advanced battery charge mode''',
@@ -26577,6 +26764,16 @@
         'dynamic_refresh': True,
         'per_profile': False,
       },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Enable Kerberos',
+        },
+        {
+          'value': False,
+          'caption': 'Disable Kerberos',
+        },
+      ],
       'example_value': True,
       'id': 557,
       'caption': '''Enable Kerberos functionality''',
@@ -26598,6 +26795,16 @@
         'dynamic_refresh': True,
         'per_profile': False,
       },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Allow users to remember Kerberos passwords',
+        },
+        {
+          'value': False,
+          'caption': 'Do not allow users to remember Kerberos passwords',
+        },
+      ],
       'example_value': True,
       'id': 558,
       'caption': '''Enable 'Remember password' feature''',
@@ -26619,6 +26826,16 @@
         'dynamic_refresh': True,
         'per_profile': False,
       },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Allow users to add Kerberos accounts',
+        },
+        {
+          'value': False,
+          'caption': 'Do not allow users to add Kerberos accounts',
+        },
+      ],
       'example_value': True,
       'id': 559,
       'caption': '''Users can add Kerberos accounts''',
@@ -26791,6 +27008,16 @@
         'dynamic_refresh': False,
         'per_profile': True,
       },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Do not launch the browser on startup',
+        },
+        {
+          'value': False,
+          'caption': 'Automatically launch the browser on startup',
+        },
+      ],
       'example_value': True,
       'id': 568,
       'caption': '''Suppress launching of browser window''',
@@ -26814,6 +27041,16 @@
         'dynamic_refresh': True,
         'per_profile': False,
       },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Allow the device to request powerwash',
+        },
+        {
+          'value': False,
+          'caption': 'Do not allow the device to request powerwash',
+        },
+      ],
       'supported_on': ['chrome_os:77-'],
       'caption': '''Allow the device to request powerwash''',
       'tags': [],
@@ -26925,6 +27162,16 @@
         'dynamic_refresh': True,
         'per_profile': True,
       },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Enable virtual machine command line access',
+        },
+        {
+          'value': False,
+          'caption': 'Disable virtual machine command line access',
+        },
+      ],
       'example_value': False,
       'id': 577,
       'caption': '''Specify VM CLI permission''',
@@ -26980,6 +27227,16 @@
       'schema': {
         'type': 'boolean',
       },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Allow users to play media when the device is locked',
+        },
+        {
+          'value': False,
+          'caption': 'Do not allow users to play media when the device is locked',
+        },
+      ],
       'example_value': True,
       'default': True,
       'features': {
@@ -27929,6 +28186,16 @@
         'dynamic_refresh': False,
         'per_profile': False,
       },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Enable Renderer Code Integrity',
+        },
+        {
+          'value': False,
+          'caption': 'Disable Renderer Code Integrity',
+        },
+      ],
       'example_value': False,
       'id': 598,
       'caption': '''Enable Renderer Code Integrity''',
@@ -28116,7 +28383,22 @@
         'dynamic_refresh': False,
         'per_profile': False,
       },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Always sandbox the audio process',
+        },
+        {
+          'value': False,
+          'caption': 'Never sandbox the audio process',
+        },
+        {
+          'value': None,
+          'caption': 'Use the default configuration for the audio sandbox',
+        },
+      ],
       'example_value': True,
+      'default': None,
       'id': 627,
       'caption': '''Allow the audio sandbox to run''',
       'tags': ['system-security'],
diff --git a/components/privacy_sandbox/OWNERS b/components/privacy_sandbox/OWNERS
index 4ef0333e..50cd622 100644
--- a/components/privacy_sandbox/OWNERS
+++ b/components/privacy_sandbox/OWNERS
@@ -1,4 +1,5 @@
-andzaytsev@google.com
-harrisonsean@chromium.org
-msramek@chromium.org
-sauski@google.com
+sauski@google.com # Primary for general logic, primary for web UI, catch-all
+andzaytsev@google.com # Primary for android UI
+msramek@chromium.org # Secondary for general logic
+olesiamarukhno@google.com # Primary for desktop UI
+rainhard@chromium.org # Secondary for web UI
diff --git a/components/sync/engine/sync_scheduler_impl.cc b/components/sync/engine/sync_scheduler_impl.cc
index 7ea925da..5f5ae8ee9 100644
--- a/components/sync/engine/sync_scheduler_impl.cc
+++ b/components/sync/engine/sync_scheduler_impl.cc
@@ -10,7 +10,7 @@
 #include "base/bind.h"
 #include "base/location.h"
 #include "base/logging.h"
-#include "base/metrics/histogram_macros.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/observer_list.h"
 #include "base/rand_util.h"
 #include "base/task/sequenced_task_runner.h"
@@ -779,6 +779,7 @@
 
 void SyncSchedulerImpl::OnThrottled(const base::TimeDelta& throttle_duration) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  base::UmaHistogramBoolean("Sync.ThrottledAllModelTypes", true);
   wait_interval_ = std::make_unique<WaitInterval>(
       WaitInterval::BlockingMode::kThrottled, throttle_duration);
   for (SyncEngineEventListener& observer : *cycle_context_->listeners()) {
@@ -793,6 +794,10 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   SDVLOG(1) << "Throttling " << ModelTypeSetToDebugString(types) << " for "
             << throttle_duration.InSeconds() << " seconds.";
+  for (ModelType type : types) {
+    base::UmaHistogramEnumeration("Sync.ThrottledSomeModelTypes",
+                                  ModelTypeHistogramValue(type));
+  }
   nudge_tracker_.SetTypesThrottledUntil(types, throttle_duration,
                                         TimeTicks::Now());
   RestartWaiting();
@@ -801,6 +806,8 @@
 void SyncSchedulerImpl::OnTypesBackedOff(ModelTypeSet types) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   for (ModelType type : types) {
+    base::UmaHistogramEnumeration("Sync.BackedOffModelType",
+                                  ModelTypeHistogramValue(type));
     base::TimeDelta last_backoff_time = kInitialBackoffRetryTime;
     if (nudge_tracker_.GetTypeBlockingMode(type) ==
         WaitInterval::BlockingMode::kExponentialBackoffRetrying) {
diff --git a/content/browser/accessibility/accessibility_tree_formatter_win.cc b/content/browser/accessibility/accessibility_tree_formatter_win.cc
index 15caf7d..5f6e16aa 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_win.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_win.cc
@@ -98,7 +98,7 @@
       BrowserAccessibility::FromAXPlatformNodeDelegate(node);
   DCHECK(node_internal);
   BrowserAccessibilityManager* root_manager =
-      node_internal->manager()->GetRootManager();
+      node_internal->manager()->GetManagerForRootFrame();
   DCHECK(root_manager);
 
   base::win::ScopedVariant variant_self(CHILDID_SELF);
diff --git a/content/browser/accessibility/browser_accessibility.cc b/content/browser/accessibility/browser_accessibility.cc
index a50ec15..e7e9531 100644
--- a/content/browser/accessibility/browser_accessibility.cc
+++ b/content/browser/accessibility/browser_accessibility.cc
@@ -826,7 +826,8 @@
     // TODO(crbug.com/1074116): this should probably apply visual viewport
     // offset as well.
     if (!content::AXShouldIncludePageScaleFactorInRoot()) {
-      BrowserAccessibilityManager* root_manager = manager()->GetRootManager();
+      BrowserAccessibilityManager* root_manager =
+          manager()->GetManagerForRootFrame();
       if (root_manager)
         bounds.Scale(root_manager->GetPageScaleFactor());
     }
diff --git a/content/browser/accessibility/browser_accessibility_cocoa.mm b/content/browser/accessibility/browser_accessibility_cocoa.mm
index b39acfac..ad324f6 100644
--- a/content/browser/accessibility/browser_accessibility_cocoa.mm
+++ b/content/browser/accessibility/browser_accessibility_cocoa.mm
@@ -1095,7 +1095,9 @@
   // A nil parent means we're the root.
   // Hook back up to RenderWidgetHostViewCocoa.
   BrowserAccessibilityManagerMac* manager =
-      _owner->manager()->GetRootManager()->ToBrowserAccessibilityManagerMac();
+      _owner->manager()
+          ->GetManagerForRootFrame()
+          ->ToBrowserAccessibilityManagerMac();
   if (!manager) {
     // TODO(accessibility) Determine why this is happening.
     SANITIZER_NOTREACHED();
@@ -1669,7 +1671,9 @@
     return nil;
 
   BrowserAccessibilityManagerMac* root_manager =
-      _owner->manager()->GetRootManager()->ToBrowserAccessibilityManagerMac();
+      _owner->manager()
+          ->GetManagerForRootFrame()
+          ->ToBrowserAccessibilityManagerMac();
   CHECK(root_manager) << "There should always be a root manager whenever an "
                          "object is instanceActive.";
   CHECK(root_manager->GetParentView());
diff --git a/content/browser/accessibility/browser_accessibility_fuchsia.cc b/content/browser/accessibility/browser_accessibility_fuchsia.cc
index d5894489..c9ca00bc 100644
--- a/content/browser/accessibility/browser_accessibility_fuchsia.cc
+++ b/content/browser/accessibility/browser_accessibility_fuchsia.cc
@@ -72,7 +72,7 @@
 
   // Declare this node as the fuchsia tree root if it's the root of the main
   // frame's tree.
-  if (manager()->IsRootTree() &&
+  if (manager()->IsRootFrameManager() &&
       manager()->GetBrowserAccessibilityRoot() == this) {
     ui::AccessibilityBridgeFuchsia* accessibility_bridge =
         GetAccessibilityBridge();
@@ -426,7 +426,8 @@
 bool BrowserAccessibilityFuchsia::AccessibilityPerformAction(
     const ui::AXActionData& action_data) {
   if (action_data.action == ax::mojom::Action::kHitTest) {
-    BrowserAccessibilityManager* root_manager = manager()->GetRootManager();
+    BrowserAccessibilityManager* root_manager =
+        manager()->GetManagerForRootFrame();
     DCHECK(root_manager);
 
     ui::AccessibilityBridgeFuchsia* accessibility_bridge =
diff --git a/content/browser/accessibility/browser_accessibility_manager.cc b/content/browser/accessibility/browser_accessibility_manager.cc
index c490e12e..e9ba4e6 100644
--- a/content/browser/accessibility/browser_accessibility_manager.cc
+++ b/content/browser/accessibility/browser_accessibility_manager.cc
@@ -280,13 +280,13 @@
   // Fire events only when the root of the tree is reachable, to avoid a bug
   // in AppKit that gets stuck in an infinite loop trying to find the root,
   // causing VoiceOver to get stuck announcing "Chrome is not responding".
-  BrowserAccessibilityManager* root_manager = GetRootManager();
+  BrowserAccessibilityManager* root_manager = GetManagerForRootFrame();
   if (!root_manager)
     return false;
 
   // Make sure that nodes can be traversed to the root.
   const BrowserAccessibilityManager* ancestor_manager = this;
-  while (!ancestor_manager->IsRootTree()) {
+  while (!ancestor_manager->IsRootFrameManager()) {
     BrowserAccessibility* host_node =
         ancestor_manager->GetParentNodeFromParentTree();
     if (!host_node)
@@ -398,7 +398,7 @@
   if (parent) {
     if (!connected_to_parent_tree_node_)
       ParentConnectionChanged(parent);
-    SANITIZER_CHECK(!IsRootTree());
+    SANITIZER_CHECK(!IsRootFrameManager());
     return;
   }
 
@@ -414,7 +414,7 @@
     // an existing document. Due to race conditions, in some cases, |this| is
     // destroyed first, and this condition is not reached; while in other cases
     // the parent node is destroyed first (this case).
-    DCHECK(IsRootTree() || !CanFireEvents());
+    DCHECK(IsRootFrameManager() || !CanFireEvents());
   }
 }
 
@@ -431,12 +431,12 @@
 }
 
 void BrowserAccessibilityManager::OnWindowFocused() {
-  if (IsRootTree())
+  if (IsRootFrameManager())
     FireFocusEventsIfNeeded();
 }
 
 void BrowserAccessibilityManager::OnWindowBlurred() {
-  if (IsRootTree())
+  if (IsRootFrameManager())
     SetLastFocusedNode(nullptr);
 }
 
@@ -502,6 +502,9 @@
       // This is a fatal error, but if there is a delegate, it will handle the
       // error result and recover by re-creating the manager. After a max
       // threshold number of errors is reached, it will crash the browser.
+      CHECK(!ax_tree()->error().empty())
+          << "A failed serialization didn't supply the error via "
+             "AXTree::RecordError().";
       if (!delegate_)
         CHECK(false) << ax_tree()->error();
       return false;
@@ -532,7 +535,7 @@
     return true;
   }
 
-  BrowserAccessibilityManager* root_manager = GetRootManager();
+  BrowserAccessibilityManager* root_manager = GetManagerForRootFrame();
   DCHECK(root_manager) << "Cannot have detached document here, as "
                           "CanFireEvents() must return false in that case.";
 
@@ -541,7 +544,7 @@
   bool has_parent_id = parent_id != ui::AXTreeIDUnknown();
   BrowserAccessibilityManager* parent_manager =
       has_parent_id ? BrowserAccessibilityManager::FromID(parent_id) : nullptr;
-  if (IsRootTree()) {
+  if (IsRootFrameManager()) {
     CHECK(!has_parent_id) << "The root frame must be parentless, root url = "
                           << GetTreeData().url << "\nSupposed parent = "
                           << (parent_manager
@@ -853,7 +856,7 @@
 }
 
 BrowserAccessibility* BrowserAccessibilityManager::GetFocus() const {
-  BrowserAccessibilityManager* root_manager = GetRootManager();
+  BrowserAccessibilityManager* root_manager = GetManagerForRootFrame();
   if (!root_manager) {
     // We can't retrieved the globally focused object since we don't have access
     // to the top document. If we return the focus in the current or a
@@ -1709,9 +1712,9 @@
   return GetBrowserAccessibilityRoot();
 }
 
-BrowserAccessibilityManager* BrowserAccessibilityManager::GetRootManager()
-    const {
-  if (IsRootTree())
+BrowserAccessibilityManager*
+BrowserAccessibilityManager::GetManagerForRootFrame() const {
+  if (IsRootFrameManager())
     return const_cast<BrowserAccessibilityManager*>(this);
 
   BrowserAccessibilityManager* parent_manager =
@@ -1726,7 +1729,7 @@
     return nullptr;
   }
 
-  return parent_manager->GetRootManager();
+  return parent_manager->GetManagerForRootFrame();
 }
 
 ui::AXTreeManager* BrowserAccessibilityManager::GetParentManager() const {
@@ -1739,7 +1742,7 @@
   if (!parent)
     return nullptr;
 
-  DCHECK(!IsRootTree());
+  DCHECK(!IsRootFrameManager());
 
 #if DCHECK_IS_ON()
   BrowserAccessibilityManager* browser_accessibility_manager_parent =
@@ -1761,18 +1764,18 @@
 
 BrowserAccessibilityDelegate*
 BrowserAccessibilityManager::GetDelegateFromRootManager() const {
-  BrowserAccessibilityManager* root_manager = GetRootManager();
+  BrowserAccessibilityManager* root_manager = GetManagerForRootFrame();
   if (root_manager)
     return root_manager->delegate();
   return nullptr;
 }
 
-bool BrowserAccessibilityManager::IsRootTree() const {
+bool BrowserAccessibilityManager::IsRootFrameManager() const {
   // delegate_ can be null in unit tests.
   if (!delegate_)
     return GetTreeData().parent_tree_id == ui::AXTreeIDUnknown();
 
-  bool is_root_tree = delegate_->AccessibilityIsMainFrame();
+  bool is_root_tree = delegate_->AccessibilityIsRootFrame();
   DCHECK(!is_root_tree || GetParentTreeID() == ui::AXTreeIDUnknown())
       << "Root tree has parent tree id of: " << GetParentTreeID();
   return is_root_tree;
@@ -1800,7 +1803,7 @@
   // hit test result, but AXPlatformNodeDelegate says that it's only supposed
   // to return a descendant, so this isn't correctly fulfilling the contract.
   // Unchecked it can even lead to an infinite loop.
-  BrowserAccessibilityManager* root_manager = GetRootManager();
+  BrowserAccessibilityManager* root_manager = GetManagerForRootFrame();
   if (root_manager && root_manager != this)
     return root_manager->CachingAsyncHitTest(physical_pixel_point);
 
@@ -2008,7 +2011,7 @@
   BrowserAccessibilityDelegate* root_delegate = GetDelegateFromRootManager();
   if (!root_delegate)
     return false;
-  if (!root_delegate->AccessibilityIsMainFrame())
+  if (!root_delegate->AccessibilityIsRootFrame())
     return false;
 
   // Don't fire events when this document might be stale as the user has
diff --git a/content/browser/accessibility/browser_accessibility_manager.h b/content/browser/accessibility/browser_accessibility_manager.h
index 3f53868..411143c6 100644
--- a/content/browser/accessibility/browser_accessibility_manager.h
+++ b/content/browser/accessibility/browser_accessibility_manager.h
@@ -107,9 +107,9 @@
       base::OnceCallback<void(BrowserAccessibilityManager* hit_manager,
                               int hit_node_id)> opt_callback) = 0;
 
-  // Returns true if this delegate represents the main (topmost) frame in a
+  // Returns true if this delegate represents the root (outermost) frame in a
   // tree of frames.
-  virtual bool AccessibilityIsMainFrame() = 0;
+  virtual bool AccessibilityIsRootFrame() = 0;
   virtual WebContentsAccessibility*
   AccessibilityGetWebContentsAccessibility() = 0;
 };
@@ -471,18 +471,20 @@
   BrowserAccessibilityDelegate* delegate() const { return delegate_; }
 
   // If this BrowserAccessibilityManager is a child frame or guest frame,
-  // returns the BrowserAccessibilityManager from the top document in the frame
-  // tree. If the current frame is not connected to its parent frame yet, or if
-  // it got disconnected after being reparented, return nullptr to indicate that
-  // we don't have access to the root manager yet.
-  BrowserAccessibilityManager* GetRootManager() const;
+  // returns the BrowserAccessibilityManager from the root frame. The root frame
+  // is the outermost frame, so this method will walk up to any parents (in the
+  // case of subframes), any outer documents (e.g. fenced frame owners), and any
+  // GuestViews. If the current frame is not connected to its parent frame yet,
+  // or if it got disconnected after being reparented, return nullptr to
+  // indicate that we don't have access to the manager of the root frame yet.
+  BrowserAccessibilityManager* GetManagerForRootFrame() const;
 
   // Returns the BrowserAccessibilityDelegate from |GetRootManager| above, or
   // returns nullptr in case we don't have access to the root manager yet.
   BrowserAccessibilityDelegate* GetDelegateFromRootManager() const;
 
-  // Returns whether this is the top document.
-  bool IsRootTree() const;
+  // Returns whether this is the root frame.
+  bool IsRootFrameManager() const;
 
   // Get a snapshot of the current tree as an AXTreeUpdate.
   ui::AXTreeUpdate SnapshotAXTreeForTesting();
diff --git a/content/browser/accessibility/browser_accessibility_manager_android.cc b/content/browser/accessibility/browser_accessibility_manager_android.cc
index 6028698..f79767dc 100644
--- a/content/browser/accessibility/browser_accessibility_manager_android.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_android.cc
@@ -25,7 +25,7 @@
                                                   nullptr);
 
   WebContentsAccessibilityAndroid* wcax = nullptr;
-  if (delegate->AccessibilityIsMainFrame()) {
+  if (delegate->AccessibilityIsRootFrame()) {
     wcax = static_cast<WebContentsAccessibilityAndroid*>(
         delegate->AccessibilityGetWebContentsAccessibility());
   }
@@ -580,8 +580,8 @@
 
   // When the root changes, send the new root id and a navigate signal to Java.
   if (root_changed) {
-    auto* root_manager =
-        static_cast<BrowserAccessibilityManagerAndroid*>(GetRootManager());
+    auto* root_manager = static_cast<BrowserAccessibilityManagerAndroid*>(
+        GetManagerForRootFrame());
     DCHECK(root_manager);
 
     auto* root = static_cast<BrowserAccessibilityAndroid*>(
@@ -600,8 +600,8 @@
   BrowserAccessibilityManager::OnNodeCreated(tree, node);
   if (node->data().GetBoolAttribute(
           ax::mojom::BoolAttribute::kTouchPassthrough)) {
-    auto* root =
-        static_cast<BrowserAccessibilityManagerAndroid*>(GetRootManager());
+    auto* root = static_cast<BrowserAccessibilityManagerAndroid*>(
+        GetManagerForRootFrame());
     if (root)
       root->EnableTouchPassthrough();
     else
@@ -618,10 +618,10 @@
                                                       new_value);
   if (new_value && attr == ax::mojom::BoolAttribute::kTouchPassthrough) {
     // TODO(accessibility): there's a tiny chance we could get this
-    // called on an iframe before it's attached to the root manager.
+    // called on an iframe before it's attached to the root frame manager.
     // If this ever becomes an issue in practice, make this more robust.
-    auto* root =
-        static_cast<BrowserAccessibilityManagerAndroid*>(GetRootManager());
+    auto* root = static_cast<BrowserAccessibilityManagerAndroid*>(
+        GetManagerForRootFrame());
     if (root)
       root->EnableTouchPassthrough();
     else
diff --git a/content/browser/accessibility/browser_accessibility_manager_mac.mm b/content/browser/accessibility/browser_accessibility_manager_mac.mm
index fda9114..304cab9 100644
--- a/content/browser/accessibility/browser_accessibility_manager_mac.mm
+++ b/content/browser/accessibility/browser_accessibility_manager_mac.mm
@@ -192,7 +192,7 @@
 
       NSDictionary* user_info = GetUserInfoForSelectedTextChangedNotification();
 
-      BrowserAccessibilityManager* root_manager = GetRootManager();
+      BrowserAccessibilityManager* root_manager = GetManagerForRootFrame();
       if (!root_manager)
         return;
       BrowserAccessibility* root = root_manager->GetBrowserAccessibilityRoot();
@@ -266,7 +266,8 @@
       // subsequent changes to the user. WebKit seems to address this by firing
       // AXMenuClosed on the document itself when an accessible menu is being
       // detached. See WebKit's AccessibilityObject::detachRemoteParts
-      if (BrowserAccessibilityManager* root_manager = GetRootManager()) {
+      if (BrowserAccessibilityManager* root_manager =
+              GetManagerForRootFrame()) {
         if (BrowserAccessibility* root =
                 root_manager->GetBrowserAccessibilityRoot())
           FireNativeMacNotification((NSString*)kAXMenuClosedNotification, root);
@@ -576,7 +577,7 @@
 }
 
 bool BrowserAccessibilityManagerMac::IsChromeNewTabPage() {
-  if (!delegate() || !IsRootTree())
+  if (!delegate() || !IsRootFrameManager())
     return false;
   content::WebContents* web_contents = WebContents::FromRenderFrameHost(
       delegate()->AccessibilityRenderFrameHost());
@@ -590,7 +591,7 @@
 
 bool BrowserAccessibilityManagerMac::ShouldFireLoadCompleteNotification() {
   // If it's not the top-level document, we shouldn't fire AXLoadComplete.
-  if (!IsRootTree())
+  if (!IsRootFrameManager())
     return false;
 
   // On MacOS 10.15, firing AXLoadComplete causes focus to move to the
diff --git a/content/browser/accessibility/browser_accessibility_manager_unittest.cc b/content/browser/accessibility/browser_accessibility_manager_unittest.cc
index 3616afe..b0c8691 100644
--- a/content/browser/accessibility/browser_accessibility_manager_unittest.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_unittest.cc
@@ -1455,7 +1455,7 @@
   std::unique_ptr<BrowserAccessibilityManager> child_manager(
       BrowserAccessibilityManager::Create(child_update, nullptr));
 
-  ASSERT_EQ(parent_manager.get(), child_manager->GetRootManager());
+  ASSERT_EQ(parent_manager.get(), child_manager->GetManagerForRootFrame());
 
   // Set scaling factor for testing to be 200%
   parent_manager->UseCustomDeviceScaleFactorForTesting(2.0f);
diff --git a/content/browser/accessibility/test_browser_accessibility_delegate.cc b/content/browser/accessibility/test_browser_accessibility_delegate.cc
index b11ebd6..526e981d 100644
--- a/content/browser/accessibility/test_browser_accessibility_delegate.cc
+++ b/content/browser/accessibility/test_browser_accessibility_delegate.cc
@@ -48,7 +48,7 @@
   return nullptr;
 }
 
-bool TestBrowserAccessibilityDelegate::AccessibilityIsMainFrame() {
+bool TestBrowserAccessibilityDelegate::AccessibilityIsRootFrame() {
   return is_root_frame_;
 }
 
diff --git a/content/browser/accessibility/test_browser_accessibility_delegate.h b/content/browser/accessibility/test_browser_accessibility_delegate.h
index 7bb7fca7..33b02fbb 100644
--- a/content/browser/accessibility/test_browser_accessibility_delegate.h
+++ b/content/browser/accessibility/test_browser_accessibility_delegate.h
@@ -24,7 +24,7 @@
   gfx::NativeViewAccessible AccessibilityGetNativeViewAccessibleForWindow()
       override;
   RenderFrameHostImpl* AccessibilityRenderFrameHost() override;
-  bool AccessibilityIsMainFrame() override;
+  bool AccessibilityIsRootFrame() override;
   void AccessibilityHitTest(
       const gfx::Point& point_in_frame_pixels,
       ax::mojom::Event opt_event_to_fire,
diff --git a/content/browser/first_party_sets/database/first_party_sets_database.cc b/content/browser/first_party_sets/database/first_party_sets_database.cc
index 0799a7c..e3c58903 100644
--- a/content/browser/first_party_sets/database/first_party_sets_database.cc
+++ b/content/browser/first_party_sets/database/first_party_sets_database.cc
@@ -312,7 +312,7 @@
   return transaction.Commit();
 }
 
-net::GlobalFirstPartySets FirstPartySetsDatabase::GetPublicSets(
+net::GlobalFirstPartySets FirstPartySetsDatabase::GetGlobalSets(
     const std::string& browser_context_id) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
diff --git a/content/browser/first_party_sets/database/first_party_sets_database.h b/content/browser/first_party_sets/database/first_party_sets_database.h
index e901606..865148e 100644
--- a/content/browser/first_party_sets/database/first_party_sets_database.h
+++ b/content/browser/first_party_sets/database/first_party_sets_database.h
@@ -103,8 +103,8 @@
   // TODO(crbug.com/1219656): Consider returning absl::nullopt for all the
   // fetching methods when having query errors
 
-  // Gets the public First-Party Sets used by `browser_context_id`.
-  [[nodiscard]] net::GlobalFirstPartySets GetPublicSets(
+  // Gets the global First-Party Sets used by `browser_context_id`.
+  [[nodiscard]] net::GlobalFirstPartySets GetGlobalSets(
       const std::string& browser_context_id);
 
   // Gets the list of sites to clear for the `browser_context_id`.
diff --git a/content/browser/first_party_sets/database/first_party_sets_database_unittest.cc b/content/browser/first_party_sets/database/first_party_sets_database_unittest.cc
index a0cac1a4..afbb3a06 100644
--- a/content/browser/first_party_sets/database/first_party_sets_database_unittest.cc
+++ b/content/browser/first_party_sets/database/first_party_sets_database_unittest.cc
@@ -902,15 +902,15 @@
   EXPECT_THAT(db()->FetchPolicyModifications("b2"), res);
 }
 
-TEST_F(FirstPartySetsDatabaseTest, GetPublicSets_NoPreExistingDB) {
+TEST_F(FirstPartySetsDatabaseTest, GetGlobalSets_NoPreExistingDB) {
   OpenDatabase();
-  EXPECT_THAT(db()->GetPublicSets("b").FindEntries(
+  EXPECT_THAT(db()->GetGlobalSets("b").FindEntries(
                   {net::SchemefulSite(GURL("https://example.test"))},
                   /*config=*/nullptr),
               IsEmpty());
 }
 
-TEST_F(FirstPartySetsDatabaseTest, GetPublicSets) {
+TEST_F(FirstPartySetsDatabaseTest, GetGlobalSets) {
   ASSERT_TRUE(
       sql::test::CreateDatabaseFromSQL(db_path(), GetSqlFilePath("v1.sql")));
 
@@ -925,7 +925,7 @@
   const net::SchemefulSite bbb(GURL("https://bbb.test"));
   OpenDatabase();
   EXPECT_THAT(
-      db()->GetPublicSets("b0").FindEntries({aaa, bbb},
+      db()->GetGlobalSets("b0").FindEntries({aaa, bbb},
                                             /*config=*/nullptr),
       UnorderedElementsAre(
           Pair(aaa, net::FirstPartySetEntry(bbb, net::SiteType::kAssociated,
diff --git a/content/browser/first_party_sets/first_party_sets_handler_database_helper.cc b/content/browser/first_party_sets/first_party_sets_handler_database_helper.cc
index 1bf66b6..a5f856d 100644
--- a/content/browser/first_party_sets/first_party_sets_handler_database_helper.cc
+++ b/content/browser/first_party_sets/first_party_sets_handler_database_helper.cc
@@ -76,7 +76,7 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!browser_context_id.empty());
   base::flat_set<net::SchemefulSite> diff =
-      ComputeSetsDiff(db_->GetPublicSets(browser_context_id),
+      ComputeSetsDiff(db_->GetGlobalSets(browser_context_id),
                       net::FirstPartySetsContextConfig(
                           db_->FetchPolicyModifications(browser_context_id)),
                       current_sets, current_config);
@@ -106,15 +106,15 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!browser_context_id.empty());
   if (!db_->PersistSets(browser_context_id, version, sets, config))
-    DVLOG(1) << "Failed to write public sets into the database.";
+    DVLOG(1) << "Failed to write sets into the database.";
 }
 
 net::GlobalFirstPartySets
-FirstPartySetsHandlerDatabaseHelper::GetPersistedPublicSets(
+FirstPartySetsHandlerDatabaseHelper::GetPersistedGlobalSets(
     const std::string& browser_context_id) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!browser_context_id.empty());
-  return db_->GetPublicSets(browser_context_id);
+  return db_->GetGlobalSets(browser_context_id);
 }
 
 }  // namespace content
diff --git a/content/browser/first_party_sets/first_party_sets_handler_database_helper.h b/content/browser/first_party_sets/first_party_sets_handler_database_helper.h
index 0d1f8f06..f78f174 100644
--- a/content/browser/first_party_sets/first_party_sets_handler_database_helper.h
+++ b/content/browser/first_party_sets/first_party_sets_handler_database_helper.h
@@ -65,7 +65,7 @@
       const net::FirstPartySetsContextConfig& current_config);
 
   // Gets the list of sites to clear for the `browser_context_id`. This method
-  // wraps a few DB operations: reads the old public sets and policy
+  // wraps a few DB operations: reads the old global sets and policy
   // customization from DB, call `ComputeSetsDiff` with required inputs to
   // compute the list of sites to clear, stores the sites into DB, then reads
   // the final list of sites to be cleared from DB, which can include sites
@@ -86,8 +86,8 @@
                    const net::GlobalFirstPartySets& sets,
                    const net::FirstPartySetsContextConfig& config);
 
-  // Wraps FirstPartySetsDatabase::GetPublicSets.
-  net::GlobalFirstPartySets GetPersistedPublicSets(
+  // Wraps FirstPartySetsDatabase::GetGlobalSets.
+  net::GlobalFirstPartySets GetPersistedGlobalSets(
       const std::string& browser_context_id);
 
  private:
diff --git a/content/browser/first_party_sets/first_party_sets_handler_impl.cc b/content/browser/first_party_sets/first_party_sets_handler_impl.cc
index 70c95c7..36c6193 100644
--- a/content/browser/first_party_sets/first_party_sets_handler_impl.cc
+++ b/content/browser/first_party_sets/first_party_sets_handler_impl.cc
@@ -179,7 +179,7 @@
   db_helper_.Reset();
 }
 
-void FirstPartySetsHandlerImpl::GetPersistedPublicSetsForTesting(
+void FirstPartySetsHandlerImpl::GetPersistedGlobalSetsForTesting(
     const std::string& browser_context_id,
     base::OnceCallback<void(absl::optional<net::GlobalFirstPartySets>)>
         callback) {
@@ -190,7 +190,7 @@
     return;
   }
   db_helper_
-      .AsyncCall(&FirstPartySetsHandlerDatabaseHelper::GetPersistedPublicSets)
+      .AsyncCall(&FirstPartySetsHandlerDatabaseHelper::GetPersistedGlobalSets)
       .WithArgs(browser_context_id)
       .Then(std::move(callback));
 }
diff --git a/content/browser/first_party_sets/first_party_sets_handler_impl.h b/content/browser/first_party_sets/first_party_sets_handler_impl.h
index 55b5589..d8d580f 100644
--- a/content/browser/first_party_sets/first_party_sets_handler_impl.h
+++ b/content/browser/first_party_sets/first_party_sets_handler_impl.h
@@ -104,7 +104,7 @@
     embedder_will_provide_public_sets_ = enabled_ && will_provide;
   }
 
-  void GetPersistedPublicSetsForTesting(
+  void GetPersistedGlobalSetsForTesting(
       const std::string& browser_context_id,
       base::OnceCallback<void(absl::optional<net::GlobalFirstPartySets>)>
           callback);
diff --git a/content/browser/first_party_sets/first_party_sets_handler_impl_unittest.cc b/content/browser/first_party_sets/first_party_sets_handler_impl_unittest.cc
index 2292f11..c6f934d 100644
--- a/content/browser/first_party_sets/first_party_sets_handler_impl_unittest.cc
+++ b/content/browser/first_party_sets/first_party_sets_handler_impl_unittest.cc
@@ -58,10 +58,10 @@
   return result.has_value() ? std::move(result).value() : future.Take();
 }
 
-absl::optional<net::GlobalFirstPartySets> GetPersistedPublicSetsAndWait(
+absl::optional<net::GlobalFirstPartySets> GetPersistedGlobalSetsAndWait(
     const std::string& browser_context_id) {
   base::test::TestFuture<absl::optional<net::GlobalFirstPartySets>> future;
-  FirstPartySetsHandlerImpl::GetInstance()->GetPersistedPublicSetsForTesting(
+  FirstPartySetsHandlerImpl::GetInstance()->GetPersistedGlobalSetsForTesting(
       browser_context_id, future.GetCallback());
   return future.Take();
 }
@@ -247,7 +247,7 @@
           /*context_config=*/nullptr, run_loop.QuitClosure());
   run_loop.Run();
 
-  EXPECT_THAT(GetPersistedPublicSetsAndWait(browser_context_id)
+  EXPECT_THAT(GetPersistedGlobalSetsAndWait(browser_context_id)
                   ->FindEntries({foo, associated}, /*config=*/nullptr),
               UnorderedElementsAre(
                   Pair(foo, net::FirstPartySetEntry(
@@ -289,7 +289,7 @@
           /*context_config=*/nullptr, run_loop.QuitClosure());
   run_loop.Run();
 
-  EXPECT_EQ(GetPersistedPublicSetsAndWait(browser_context_id), absl::nullopt);
+  EXPECT_EQ(GetPersistedGlobalSetsAndWait(browser_context_id), absl::nullopt);
 }
 
 TEST_F(FirstPartySetsHandlerImplEnabledTest,
diff --git a/content/browser/permissions/permission_controller_impl.cc b/content/browser/permissions/permission_controller_impl.cc
index 4ebca92..dbdfa09 100644
--- a/content/browser/permissions/permission_controller_impl.cc
+++ b/content/browser/permissions/permission_controller_impl.cc
@@ -221,7 +221,6 @@
   devtools_permission_overrides_.Set(origin, permission, status);
   NotifyChangedSubscriptions(old_statuses);
 
-  UpdateDelegateOverridesForDevTools(origin);
   return OverrideStatus::kOverrideSet;
 }
 
@@ -246,7 +245,6 @@
   // notified manually.
   NotifyChangedSubscriptions(old_statuses);
 
-  UpdateDelegateOverridesForDevTools(origin);
   return OverrideStatus::kOverrideSet;
 }
 
@@ -254,29 +252,11 @@
   const auto old_statuses = GetSubscriptionsStatuses();
   devtools_permission_overrides_ = DevToolsPermissionOverrides();
 
-  PermissionControllerDelegate* delegate =
-      browser_context_->GetPermissionControllerDelegate();
-  if (delegate)
-    delegate->ResetPermissionOverridesForDevTools();
-
   // If any statuses changed because they lost their overrides, the subscribers
   // must be notified manually.
   NotifyChangedSubscriptions(old_statuses);
 }
 
-void PermissionControllerImpl::UpdateDelegateOverridesForDevTools(
-    const absl::optional<url::Origin>& origin) {
-  PermissionControllerDelegate* delegate =
-      browser_context_->GetPermissionControllerDelegate();
-  if (!delegate)
-    return;
-
-  // If no overrides exist, still want to update with "blank" overrides.
-  PermissionOverrides current_overrides =
-      devtools_permission_overrides_.GetAll(origin);
-  delegate->SetPermissionOverridesForDevTools(origin, current_overrides);
-}
-
 void PermissionControllerImpl::RequestPermissions(
     const std::vector<blink::PermissionType>& permissions,
     RenderFrameHost* render_frame_host,
diff --git a/content/browser/permissions/permission_controller_impl.h b/content/browser/permissions/permission_controller_impl.h
index 9caf1d1..96ce357 100644
--- a/content/browser/permissions/permission_controller_impl.h
+++ b/content/browser/permissions/permission_controller_impl.h
@@ -147,8 +147,6 @@
   void NotifyChangedSubscriptions(const SubscriptionsStatusMap& old_statuses);
   void OnDelegatePermissionStatusChange(SubscriptionId subscription_id,
                                         blink::mojom::PermissionStatus status);
-  void UpdateDelegateOverridesForDevTools(
-      const absl::optional<url::Origin>& origin);
 
   DevToolsPermissionOverrides devtools_permission_overrides_;
 
diff --git a/content/browser/permissions/permission_controller_impl_unittest.cc b/content/browser/permissions/permission_controller_impl_unittest.cc
index f0037621..56ffd73 100644
--- a/content/browser/permissions/permission_controller_impl_unittest.cc
+++ b/content/browser/permissions/permission_controller_impl_unittest.cc
@@ -59,12 +59,6 @@
        const base::OnceCallback<
            void(const std::vector<blink::mojom::PermissionStatus>&)> callback),
       (override));
-  MOCK_METHOD(void,
-              SetPermissionOverridesForDevTools,
-              (const absl::optional<url::Origin>& origin,
-               const PermissionOverrides& overrides),
-              (override));
-  MOCK_METHOD(void, ResetPermissionOverridesForDevTools, (), (override));
   MOCK_METHOD(bool,
               IsPermissionOverridableByDevTools,
               (PermissionType, const absl::optional<url::Origin>&),
@@ -217,23 +211,6 @@
   std::unique_ptr<PermissionControllerImpl> permission_controller_;
 };
 
-TEST_F(PermissionControllerImplTest, ResettingOverridesForwardsReset) {
-  EXPECT_CALL(*mock_manager(), ResetPermissionOverridesForDevTools());
-  permission_controller()->ResetOverridesForDevTools();
-}
-
-TEST_F(PermissionControllerImplTest, SettingOverridesForwardsUpdates) {
-  auto kTestOrigin = absl::make_optional(url::Origin::Create(GURL(kTestUrl)));
-  EXPECT_CALL(*mock_manager(),
-              SetPermissionOverridesForDevTools(
-                  kTestOrigin, testing::ElementsAre(testing::Pair(
-                                   PermissionType::GEOLOCATION,
-                                   blink::mojom::PermissionStatus::GRANTED))));
-  permission_controller()->SetOverrideForDevTools(
-      kTestOrigin, PermissionType::GEOLOCATION,
-      blink::mojom::PermissionStatus::GRANTED);
-}
-
 TEST_F(PermissionControllerImplTest,
        RequestPermissionsFromCurrentDocumentDelegatesIffMissingOverrides) {
   url::Origin kTestOrigin = url::Origin::Create(GURL(kTestUrl));
diff --git a/content/browser/portal/portal_browsertest.cc b/content/browser/portal/portal_browsertest.cc
index fd718278..5d00af6c 100644
--- a/content/browser/portal/portal_browsertest.cc
+++ b/content/browser/portal/portal_browsertest.cc
@@ -2029,12 +2029,13 @@
   RenderFrameHostImpl* portal_frame = portal_contents->GetPrimaryMainFrame();
   WaitForAccessibilityTree(portal_contents);
   if (!main_frame->browser_accessibility_manager() ||
-      !portal_frame->browser_accessibility_manager()->GetRootManager())
+      !portal_frame->browser_accessibility_manager()->GetManagerForRootFrame())
     WaitForAccessibilityTree(web_contents_impl);
 
   EXPECT_NE(nullptr, portal_frame->browser_accessibility_manager());
-  EXPECT_EQ(main_frame->browser_accessibility_manager(),
-            portal_frame->browser_accessibility_manager()->GetRootManager());
+  EXPECT_EQ(
+      main_frame->browser_accessibility_manager(),
+      portal_frame->browser_accessibility_manager()->GetManagerForRootFrame());
   // Activate portal and adopt predecessor.
   EXPECT_TRUE(ExecJs(portal_frame,
                      "window.addEventListener('portalactivate', e => { "
@@ -2054,8 +2055,9 @@
     adoption_observer.WaitUntilPortalCreated();
   }
 
-  EXPECT_EQ(portal_frame->browser_accessibility_manager()->GetRootManager(),
-            portal_frame->browser_accessibility_manager());
+  EXPECT_EQ(
+      portal_frame->browser_accessibility_manager()->GetManagerForRootFrame(),
+      portal_frame->browser_accessibility_manager());
 }
 
 IN_PROC_BROWSER_TEST_F(PortalBrowserTest, OrphanedPortalAccessibilityReset) {
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 4350980..1468c71e 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -2910,7 +2910,7 @@
 
 gfx::AcceleratedWidget
 RenderFrameHostImpl::AccessibilityGetAcceleratedWidget() {
-  DCHECK(AccessibilityIsMainFrame());
+  DCHECK(AccessibilityIsRootFrame());
   // Only the active RenderFrameHost is connected to the native widget tree for
   // accessibility, so return null if this is queried on any other frame.
   if (!IsActive())
@@ -2985,7 +2985,7 @@
                      opt_event_to_fire, std::move(opt_callback)));
 }
 
-bool RenderFrameHostImpl::AccessibilityIsMainFrame() {
+bool RenderFrameHostImpl::AccessibilityIsRootFrame() {
   // Do not use is_main_frame() or IsOutermostMainFrame().
   // Frame trees may be nested so it can be the case that is_main_frame() is
   // true, but is not the outermost RenderFrameHost (it only checks for nullity
@@ -2998,7 +2998,7 @@
 
 WebContentsAccessibility*
 RenderFrameHostImpl::AccessibilityGetWebContentsAccessibility() {
-  DCHECK(AccessibilityIsMainFrame());
+  DCHECK(AccessibilityIsRootFrame());
   auto* view = static_cast<RenderWidgetHostViewBase*>(GetView());
   if (!view)
     return nullptr;
@@ -10250,7 +10250,7 @@
 ui::AXTreeID RenderFrameHostImpl::GetParentAXTreeID() {
   auto* parent = GetParentOrOuterDocumentOrEmbedder();
   if (!parent) {
-    DCHECK(AccessibilityIsMainFrame())
+    DCHECK(AccessibilityIsRootFrame())
         << "Child frame requires a parent, root=" << GetLastCommittedURL();
     return ui::AXTreeIDUnknown();
   }
@@ -10263,7 +10263,7 @@
   //     << "Parent frame must have an id, child url = " <<
   //     GetLastCommittedURL()
   //     << "    parent url = " << parent->GetLastCommittedURL();
-  DCHECK(!AccessibilityIsMainFrame())
+  DCHECK(!AccessibilityIsRootFrame())
       << "Root frame must not have a parent, root=" << GetLastCommittedURL()
       << "  parent=" << parent->GetLastCommittedURL();
   return parent->GetAXTreeID();
@@ -10271,7 +10271,7 @@
 
 ui::AXTreeID RenderFrameHostImpl::GetFocusedAXTreeID() {
   // If this is not the root frame tree node, we're done.
-  if (!AccessibilityIsMainFrame())
+  if (!AccessibilityIsRootFrame())
     return ui::AXTreeIDUnknown();
 
   RenderFrameHostImpl* focused_frame = delegate_->GetFocusedFrame();
diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h
index ed79f43..b04b3a2 100644
--- a/content/browser/renderer_host/render_frame_host_impl.h
+++ b/content/browser/renderer_host/render_frame_host_impl.h
@@ -602,7 +602,7 @@
       base::OnceCallback<void(BrowserAccessibilityManager* hit_manager,
                               int hit_node_id)> opt_callback) override;
   // Return true if this is the root -- there are no parent frames of any kind.
-  bool AccessibilityIsMainFrame() override;
+  bool AccessibilityIsRootFrame() override;
   WebContentsAccessibility* AccessibilityGetWebContentsAccessibility() override;
 
   // SiteInstanceGroup::Observer
diff --git a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
index 09db033..37845d5 100644
--- a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
+++ b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
@@ -3114,11 +3114,11 @@
   ASSERT_TRUE(NavigateToURL(shell(), main_url));
 
   RenderFrameHostImpl* main_frame = web_contents()->GetPrimaryMainFrame();
-  EXPECT_TRUE(main_frame->AccessibilityIsMainFrame());
+  EXPECT_TRUE(main_frame->AccessibilityIsRootFrame());
 
   ASSERT_EQ(1u, main_frame->child_count());
   RenderFrameHostImpl* iframe = main_frame->child_at(0)->current_frame_host();
-  EXPECT_FALSE(iframe->AccessibilityIsMainFrame());
+  EXPECT_FALSE(iframe->AccessibilityIsRootFrame());
 }
 
 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
diff --git a/content/public/browser/permission_controller_delegate.h b/content/public/browser/permission_controller_delegate.h
index be0afcb6..b589708 100644
--- a/content/public/browser/permission_controller_delegate.h
+++ b/content/public/browser/permission_controller_delegate.h
@@ -23,8 +23,6 @@
 
 class CONTENT_EXPORT PermissionControllerDelegate {
  public:
-  using PermissionOverrides = DevToolsPermissionOverrides::PermissionOverrides;
-
   // Identifier for an active subscription.
   using SubscriptionId = base::IdType64<PermissionControllerDelegate>;
 
@@ -131,17 +129,6 @@
   virtual void UnsubscribePermissionStatusChange(
       SubscriptionId subscription_id) = 0;
 
-  // Manually overrides default permission settings of delegate, if overrides
-  // are tracked by the delegate. This method should only be called by the
-  // PermissionController owning the delegate.
-  virtual void SetPermissionOverridesForDevTools(
-      const absl::optional<url::Origin>& origin,
-      const PermissionOverrides& overrides) {}
-
-  // Removes overrides that have been set, if any, for all origins. If delegate
-  // does not maintain own permission set, then nothing happens.
-  virtual void ResetPermissionOverridesForDevTools() {}
-
   // Returns whether permission can be overridden by
   // DevToolsPermissionOverrides.
   virtual bool IsPermissionOverridableByDevTools(
diff --git a/content/renderer/accessibility/render_accessibility_impl_browsertest.cc b/content/renderer/accessibility/render_accessibility_impl_browsertest.cc
index 679ff870..24fb2593 100644
--- a/content/renderer/accessibility/render_accessibility_impl_browsertest.cc
+++ b/content/renderer/accessibility/render_accessibility_impl_browsertest.cc
@@ -1147,11 +1147,6 @@
   }
   EXPECT_TRUE(input_range.IsFocused());
 
-  // Increment/decrement actions produce synthesized keydown and keyup events,
-  // and the keyup event is delayed 100ms to look more natural. We need to wait
-  // for them to happen to finish the test cleanly in the TearDown phase.
-  task_environment_.FastForwardBy(base::Seconds(1));
-
   gfx::RectF expected_bounds;
   blink::WebAXObject offset_container;
   gfx::Transform container_transform;
diff --git a/content/test/data/accessibility/html/design-mode-expected-blink.txt b/content/test/data/accessibility/html/design-mode-expected-blink.txt
index ccab1de8..a5646013 100644
--- a/content/test/data/accessibility/html/design-mode-expected-blink.txt
+++ b/content/test/data/accessibility/html/design-mode-expected-blink.txt
@@ -3,4 +3,4 @@
 ++++genericContainer editable richlyEditable htmlTag='body' value='Hello' nonAtomicTextFieldRoot=true
 ++++++paragraph editable richlyEditable htmlTag='p'
 ++++++++staticText editable richlyEditable name='Hello'
-++++++++++inlineTextBox editable richlyEditable name='Hello'
\ No newline at end of file
+++++++++++inlineTextBox editable richlyEditable name='Hello'
diff --git a/infra/config/generated/builders/ci/ios-simulator/properties.json b/infra/config/generated/builders/ci/ios-simulator/properties.json
index 892e654..9e5e9d7e 100644
--- a/infra/config/generated/builders/ci/ios-simulator/properties.json
+++ b/infra/config/generated/builders/ci/ios-simulator/properties.json
@@ -50,10 +50,6 @@
           "group": "tryserver.chromium.mac"
         },
         {
-          "builder": "ios-simulator-orchestrator",
-          "group": "tryserver.chromium.mac"
-        },
-        {
           "builder": "ios-simulator-rts",
           "group": "tryserver.chromium.mac"
         }
diff --git a/infra/config/generated/builders/try/ios-simulator-orchestrator/properties.json b/infra/config/generated/builders/try/ios-simulator-orchestrator/properties.json
deleted file mode 100644
index 08ab21d..0000000
--- a/infra/config/generated/builders/try/ios-simulator-orchestrator/properties.json
+++ /dev/null
@@ -1,69 +0,0 @@
-{
-  "$build/chromium_orchestrator": {
-    "compilator": "ios-simulator-compilator",
-    "compilator_watcher_git_revision": "7809a690bbd935bcb3b4d922e24cabe168aaabc8"
-  },
-  "$build/chromium_tests_builder_config": {
-    "builder_config": {
-      "builder_db": {
-        "entries": [
-          {
-            "builder_id": {
-              "bucket": "ci",
-              "builder": "ios-simulator",
-              "project": "chromium"
-            },
-            "builder_spec": {
-              "build_gs_bucket": "chromium-mac-archive",
-              "builder_group": "chromium.mac",
-              "execution_mode": "COMPILE_AND_TEST",
-              "legacy_chromium_config": {
-                "apply_configs": [
-                  "mb",
-                  "mac_toolchain"
-                ],
-                "build_config": "Debug",
-                "config": "chromium",
-                "target_bits": 64,
-                "target_platform": "ios"
-              },
-              "legacy_gclient_config": {
-                "apply_configs": [
-                  "use_clang_coverage"
-                ],
-                "config": "ios"
-              }
-            }
-          }
-        ]
-      },
-      "builder_ids": [
-        {
-          "bucket": "ci",
-          "builder": "ios-simulator",
-          "project": "chromium"
-        }
-      ]
-    }
-  },
-  "$build/code_coverage": {
-    "coverage_exclude_sources": "ios_test_files_and_test_utils",
-    "coverage_test_types": [
-      "overall",
-      "unit"
-    ],
-    "use_clang_coverage": true
-  },
-  "$build/flakiness": {
-    "check_for_flakiness": true
-  },
-  "$recipe_engine/resultdb/test_presentation": {
-    "column_keys": [],
-    "grouping_keys": [
-      "status",
-      "v.test_suite"
-    ]
-  },
-  "builder_group": "tryserver.chromium.mac",
-  "recipe": "chromium/orchestrator"
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/ios-simulator/properties.json b/infra/config/generated/builders/try/ios-simulator/properties.json
index 88c2b26..08ab21d 100644
--- a/infra/config/generated/builders/try/ios-simulator/properties.json
+++ b/infra/config/generated/builders/try/ios-simulator/properties.json
@@ -1,4 +1,8 @@
 {
+  "$build/chromium_orchestrator": {
+    "compilator": "ios-simulator-compilator",
+    "compilator_watcher_git_revision": "7809a690bbd935bcb3b4d922e24cabe168aaabc8"
+  },
   "$build/chromium_tests_builder_config": {
     "builder_config": {
       "builder_db": {
@@ -53,11 +57,6 @@
   "$build/flakiness": {
     "check_for_flakiness": true
   },
-  "$build/goma": {
-    "rpc_extra_params": "?prod",
-    "server_host": "goma.chromium.org",
-    "use_luci_auth": true
-  },
   "$recipe_engine/resultdb/test_presentation": {
     "column_keys": [],
     "grouping_keys": [
@@ -66,6 +65,5 @@
     ]
   },
   "builder_group": "tryserver.chromium.mac",
-  "recipe": "chromium_trybot",
-  "xcode_build_version": "14a309"
+  "recipe": "chromium/orchestrator"
 }
\ No newline at end of file
diff --git a/infra/config/generated/luci/commit-queue.cfg b/infra/config/generated/luci/commit-queue.cfg
index 3c3fd75..a252ffe 100644
--- a/infra/config/generated/luci/commit-queue.cfg
+++ b/infra/config/generated/luci/commit-queue.cfg
@@ -2396,10 +2396,6 @@
         }
       }
       builders {
-        name: "chromium/try/ios-simulator-orchestrator"
-        includable_only: true
-      }
-      builders {
         name: "chromium/try/ios-simulator-rts"
         includable_only: true
       }
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 0509de8..f21e04a 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -68,6 +68,10 @@
       build_numbers: YES
       service_account: "chromium-cipd-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -137,6 +141,10 @@
       build_numbers: YES
       service_account: "chromium-cipd-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -217,6 +225,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -297,6 +309,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -377,6 +393,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -457,6 +477,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -537,6 +561,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -617,6 +645,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -689,6 +721,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -772,6 +808,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -853,6 +893,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -936,6 +980,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -1019,6 +1067,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -1102,6 +1154,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -1185,6 +1241,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -1268,6 +1328,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -1351,6 +1415,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -1434,6 +1502,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -1517,6 +1589,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -1600,6 +1676,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -1683,6 +1763,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -1766,6 +1850,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -1849,6 +1937,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -1932,6 +2024,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -2015,6 +2111,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -2098,6 +2198,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -2178,6 +2282,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -2258,6 +2366,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -2341,6 +2453,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -2424,6 +2540,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -2503,6 +2623,10 @@
       build_numbers: YES
       service_account: "chromium-automated-expectation@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -2586,6 +2710,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -2669,6 +2797,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -2752,6 +2884,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -2835,6 +2971,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -2922,6 +3062,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -3009,6 +3153,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -3096,6 +3244,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -3183,6 +3335,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -3266,6 +3422,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -3349,6 +3509,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -3429,6 +3593,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -3510,6 +3678,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -3595,6 +3767,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -3677,6 +3853,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -3759,6 +3939,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -3840,6 +4024,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -3919,6 +4107,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -3998,6 +4190,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -4077,6 +4273,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -4155,6 +4355,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -4236,6 +4440,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -4317,6 +4525,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -4399,6 +4611,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -4480,6 +4696,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -4564,6 +4784,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -4645,6 +4869,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -4729,6 +4957,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -4813,6 +5045,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -4897,6 +5133,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -4980,6 +5220,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -5060,6 +5304,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -5140,6 +5388,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -5220,6 +5472,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -5300,6 +5556,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -5380,6 +5640,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -5460,6 +5724,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -5538,6 +5806,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -5616,6 +5888,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -5696,6 +5972,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -5776,6 +6056,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -5856,6 +6140,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -5936,6 +6224,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -6016,6 +6308,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -6096,6 +6392,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -6176,6 +6476,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -6256,6 +6560,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -6336,6 +6644,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -6416,6 +6728,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -6496,6 +6812,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -6576,6 +6896,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -6656,6 +6980,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -6736,6 +7064,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -6816,6 +7148,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -6896,6 +7232,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -6976,6 +7316,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -7056,6 +7400,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -7136,6 +7484,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -7211,6 +7563,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -7286,6 +7642,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -7362,6 +7722,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -7437,6 +7801,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -7516,6 +7884,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -7603,6 +7975,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -7686,6 +8062,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -7769,6 +8149,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -7852,6 +8236,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -7935,6 +8323,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -8018,6 +8410,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -8101,6 +8497,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -8182,6 +8582,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -8263,6 +8667,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -8344,6 +8752,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -8425,6 +8837,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -8508,6 +8924,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -8591,6 +9011,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -8674,6 +9098,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -8757,6 +9185,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -8840,6 +9272,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -8923,6 +9359,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -9006,6 +9446,10 @@
       build_numbers: YES
       service_account: "chromium-automated-expectation@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -9089,6 +9533,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -9169,6 +9617,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -9250,6 +9702,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -9328,6 +9784,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -9406,6 +9866,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -9489,6 +9953,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -9569,6 +10037,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -9652,6 +10124,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -9735,6 +10211,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -9815,6 +10295,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -9891,6 +10375,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -9963,6 +10451,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -10035,6 +10527,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -10107,6 +10603,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -10177,6 +10677,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -10249,6 +10753,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -10321,6 +10829,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -10393,6 +10905,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -10465,6 +10981,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -10537,6 +11057,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -10609,6 +11133,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -10679,6 +11207,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -10749,6 +11281,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -10824,6 +11360,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -10907,6 +11447,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -10987,6 +11531,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -11067,6 +11615,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -11150,6 +11702,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -11233,6 +11789,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -11316,6 +11876,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -11403,6 +11967,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -11490,6 +12058,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -11575,6 +12147,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -11656,6 +12232,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -11739,6 +12319,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -11820,6 +12404,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -11903,6 +12491,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -11986,6 +12578,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -12069,6 +12665,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -12152,6 +12752,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -12233,6 +12837,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -12316,6 +12924,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -12396,6 +13008,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -12479,6 +13095,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -12562,6 +13182,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -12645,6 +13269,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -12728,6 +13356,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -12811,6 +13443,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -12894,6 +13530,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -12977,6 +13617,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -13060,6 +13704,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -13141,6 +13789,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -13224,6 +13876,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -13307,6 +13963,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -13390,6 +14050,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -13473,6 +14137,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -13556,6 +14224,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -13653,6 +14325,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -13740,6 +14416,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -13825,6 +14505,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -13905,6 +14589,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -13985,6 +14673,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -14063,6 +14755,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -14141,6 +14837,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -14221,6 +14921,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -14302,6 +15006,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -14382,6 +15090,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -14466,6 +15178,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -14549,6 +15265,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -14629,6 +15349,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -14707,6 +15431,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -14787,6 +15515,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -14870,6 +15602,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -14953,6 +15689,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -15036,6 +15776,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -15119,6 +15863,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -15202,6 +15950,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -15285,6 +16037,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -15368,6 +16124,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -15451,6 +16211,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -15534,6 +16298,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -15617,6 +16385,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -15700,6 +16472,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -15783,6 +16559,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -15866,6 +16646,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -15949,6 +16733,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -16032,6 +16820,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -16115,6 +16907,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -16198,6 +16994,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -16268,6 +17068,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -16338,6 +17142,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -16409,6 +17217,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -16492,6 +17304,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -16579,6 +17395,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -16666,6 +17486,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -16753,6 +17577,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -16840,6 +17668,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -16924,6 +17756,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -17011,6 +17847,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -17094,6 +17934,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -17177,6 +18021,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -17264,6 +18112,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -17344,6 +18196,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -17425,6 +18281,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -17505,6 +18365,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -17585,6 +18449,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -17668,6 +18536,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -17751,6 +18623,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -17834,6 +18710,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -17917,6 +18797,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -18000,6 +18884,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -18083,6 +18971,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -18166,6 +19058,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -18249,6 +19145,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -18332,6 +19232,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -18415,6 +19319,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -18498,6 +19406,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -18581,6 +19493,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -18664,6 +19580,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -18747,6 +19667,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -18830,6 +19754,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -18911,6 +19839,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -18982,6 +19914,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -19065,6 +20001,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -19148,6 +20088,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -19231,6 +20175,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -19314,6 +20262,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -19396,6 +20348,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -19478,6 +20434,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -19560,6 +20520,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -19631,6 +20595,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -19714,6 +20682,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -19795,6 +20767,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -19878,6 +20854,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -19961,6 +20941,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -20044,6 +21028,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -20127,6 +21115,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -20210,6 +21202,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -20291,6 +21287,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -20362,6 +21362,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -20447,6 +21451,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -20532,6 +21540,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -20612,6 +21624,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -20692,6 +21708,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -20773,6 +21793,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -20856,6 +21880,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -20939,6 +21967,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -21022,6 +22054,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -21103,6 +22139,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -21186,6 +22226,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -21269,6 +22313,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -21351,6 +22399,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -21429,6 +22481,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -21510,6 +22566,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -21595,6 +22655,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -21680,6 +22744,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -21767,6 +22835,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -21852,6 +22924,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -21934,6 +23010,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -22015,6 +23095,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -22098,6 +23182,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -22181,6 +23269,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -22264,6 +23356,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -22347,6 +23443,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -22430,6 +23530,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -22513,6 +23617,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -22596,6 +23704,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -22679,6 +23791,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -22762,6 +23878,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -22845,6 +23965,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -22926,6 +24050,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -23008,6 +24136,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -23095,6 +24227,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -23178,6 +24314,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -23258,6 +24398,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -23345,6 +24489,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -23432,6 +24580,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -23509,6 +24661,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -23592,6 +24748,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -23673,6 +24833,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -23753,6 +24917,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -23834,6 +25002,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -23915,6 +25087,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -23995,6 +25171,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -24075,6 +25255,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -24145,6 +25329,10 @@
       build_numbers: YES
       service_account: "chromium-cipd-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -24221,6 +25409,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -24292,6 +25484,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -24373,6 +25569,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -24456,6 +25656,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -24539,6 +25743,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -24622,6 +25830,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -24702,6 +25914,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -24791,6 +26007,10 @@
       build_numbers: YES
       service_account: "chromium-cipd-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -24872,6 +26092,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -24953,6 +26177,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -25036,6 +26264,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -25110,6 +26342,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -25191,6 +26427,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -25272,6 +26512,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -25353,6 +26597,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -25433,6 +26681,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -25513,6 +26765,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -25593,6 +26849,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -25673,6 +26933,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -25753,6 +27017,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -25834,6 +27102,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -25902,6 +27174,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -25982,6 +27258,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -26062,6 +27342,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -26142,6 +27426,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -26222,6 +27510,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -26302,6 +27594,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -26382,6 +27678,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -26462,6 +27762,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -26542,6 +27846,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -26622,6 +27930,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -26701,6 +28013,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -26784,6 +28100,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -26865,6 +28185,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -26948,6 +28272,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -27031,6 +28359,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -27111,6 +28443,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -27189,6 +28525,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -27270,6 +28610,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -27350,6 +28694,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -27433,6 +28781,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -27516,6 +28868,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -27597,6 +28953,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -27678,6 +29038,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -27759,6 +29123,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -27842,6 +29210,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -27920,6 +29292,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -27998,6 +29374,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -28195,6 +29575,10 @@
       build_numbers: YES
       service_account: "chromium-cipd-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -28276,6 +29660,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -28357,6 +29745,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -28440,6 +29832,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -28521,6 +29917,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -28602,6 +30002,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -28685,6 +30089,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -28810,6 +30218,10 @@
       build_numbers: YES
       service_account: "chromium-build-perf-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -28932,6 +30344,10 @@
       build_numbers: YES
       service_account: "chromium-build-perf-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -29054,6 +30470,10 @@
       build_numbers: YES
       service_account: "chromium-build-perf-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -29138,6 +30558,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -29221,6 +30645,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -29304,6 +30732,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -29387,6 +30819,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -29470,6 +30906,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -29551,6 +30991,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -29632,6 +31076,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -29714,6 +31162,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -29797,6 +31249,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -29880,6 +31336,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -29963,6 +31423,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -30043,6 +31507,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -30126,6 +31594,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -30206,6 +31678,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -30283,6 +31759,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -30366,6 +31846,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -30449,6 +31933,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -30532,6 +32020,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -30613,6 +32105,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -30694,6 +32190,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -30777,6 +32277,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -30858,6 +32362,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -30939,6 +32447,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -31020,6 +32532,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -31103,6 +32619,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -31186,6 +32706,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -31267,6 +32791,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -31348,6 +32876,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -31426,6 +32958,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -31509,6 +33045,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -31592,6 +33132,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -31675,6 +33219,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -31758,6 +33306,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -31841,6 +33393,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -31924,6 +33480,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -32007,6 +33567,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -32178,6 +33742,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -32334,6 +33902,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -32418,6 +33990,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -32503,6 +34079,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -32592,6 +34172,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -32677,6 +34261,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -32759,6 +34347,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -32841,6 +34433,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -32924,6 +34520,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -33010,6 +34610,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -33097,6 +34701,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -33178,6 +34786,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -33263,6 +34875,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -33348,6 +34964,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -33433,6 +35053,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -33518,6 +35142,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -33599,6 +35227,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -33681,6 +35313,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -33763,6 +35399,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -33845,6 +35485,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -33927,6 +35571,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -34009,6 +35657,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -34092,6 +35744,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -34175,6 +35831,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -34256,6 +35916,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -34337,6 +36001,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -34418,6 +36086,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -34499,6 +36171,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -34582,6 +36258,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -34665,6 +36345,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -34745,6 +36429,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -34825,6 +36513,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -34906,6 +36598,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -34989,6 +36685,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -35065,6 +36765,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -35136,6 +36840,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -35207,6 +36915,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -35288,6 +37000,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -35371,6 +37087,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -35454,6 +37174,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -35535,6 +37259,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -35624,6 +37352,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -35705,6 +37437,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -35788,6 +37524,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -35873,6 +37613,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -35954,6 +37698,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -36035,6 +37783,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -36115,6 +37867,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -36198,6 +37954,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -36279,6 +38039,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -36360,6 +38124,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -36443,6 +38211,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -36524,6 +38296,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -36605,6 +38381,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -36686,6 +38466,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -36767,6 +38551,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -36850,6 +38638,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -36935,6 +38727,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -37018,6 +38814,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -37103,6 +38903,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -37183,6 +38987,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -37264,6 +39072,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -37345,6 +39157,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -37426,6 +39242,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -37507,6 +39327,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -37588,6 +39412,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -37669,6 +39497,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -37750,6 +39582,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -37831,6 +39667,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -37912,6 +39752,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -37995,6 +39839,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -38076,6 +39924,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -38157,6 +40009,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -38235,6 +40091,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -38316,6 +40176,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -38386,6 +40250,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -38465,6 +40333,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -38543,6 +40415,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -38621,6 +40497,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -38704,6 +40584,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -38779,6 +40663,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -38862,6 +40750,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -38945,6 +40837,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -39026,6 +40922,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -39109,6 +41009,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -39191,6 +41095,10 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experimental: YES
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -39273,6 +41181,10 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experimental: YES
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -39355,6 +41267,10 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experimental: YES
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -39437,6 +41353,10 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experimental: YES
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -39508,6 +41428,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -39582,6 +41506,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -39653,6 +41581,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -39734,6 +41666,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -39815,6 +41751,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -39896,6 +41836,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -39977,6 +41921,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -40057,6 +42005,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -40139,6 +42091,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -40220,6 +42176,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -40302,6 +42262,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -40386,6 +42350,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -40466,6 +42434,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -40546,6 +42518,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -40627,6 +42603,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -40705,6 +42685,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -40784,6 +42768,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -40864,6 +42852,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -40943,6 +42935,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -41023,6 +43019,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -41104,6 +43104,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -41182,6 +43186,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -41260,6 +43268,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -41338,6 +43350,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -41416,6 +43432,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -41495,6 +43515,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -41575,6 +43599,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -41655,6 +43683,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -41735,6 +43767,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -41815,6 +43851,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -41895,6 +43935,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -41975,6 +44019,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -42058,6 +44106,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -42142,6 +44194,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -42222,6 +44278,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -42305,6 +44365,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -42375,6 +44439,10 @@
       build_numbers: YES
       service_account: "component-mapping-updater@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -42440,6 +44508,10 @@
       build_numbers: YES
       service_account: "chromium-cipd-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -42516,6 +44588,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -42592,6 +44668,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -42673,6 +44753,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -42754,6 +44838,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -42835,6 +44923,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -42918,6 +45010,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -42999,6 +45095,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -43080,6 +45180,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -43155,6 +45259,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -43231,6 +45339,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -43310,6 +45422,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -43391,6 +45507,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -43472,6 +45592,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -43555,6 +45679,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -43630,6 +45758,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -43705,6 +45837,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -43788,6 +45924,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -43871,6 +46011,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -43951,6 +46095,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -44031,6 +46179,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -44112,6 +46264,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -44192,6 +46348,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -44263,6 +46423,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -44334,6 +46498,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -44415,6 +46583,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -44494,6 +46666,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -44574,6 +46750,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -44654,6 +46834,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -44734,6 +46918,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -44816,6 +47004,10 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experimental: YES
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -44896,6 +47088,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -44977,6 +47173,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -45058,6 +47258,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -45137,6 +47341,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -45218,6 +47426,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -45298,6 +47510,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -45378,6 +47594,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -45458,6 +47678,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -45538,6 +47762,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -45632,7 +47860,7 @@
       service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -45713,7 +47941,7 @@
       service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -45794,7 +48022,7 @@
       service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -45875,7 +48103,7 @@
       service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -45956,7 +48184,7 @@
       service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -46035,7 +48263,7 @@
       service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -46116,7 +48344,7 @@
       service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -46185,7 +48413,10 @@
         '    ]'
         '  },'
         '  "builder_group": "tryserver.chromium.codesearch",'
-        '  "recipe": "chromium_codesearch"'
+        '  "recipe": "chromium_codesearch",'
+        '  "recipe_properties": {'
+        '    "platform": "win"'
+        '  }'
         '}'
       execution_timeout_secs: 32400
       expiration_secs: 7200
@@ -46197,7 +48428,7 @@
       service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -46417,6 +48648,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -49328,6 +51563,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -49409,6 +51648,10 @@
       build_numbers: YES
       service_account: "chromium-cq-staging-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -49490,6 +51733,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -49571,6 +51818,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -49651,6 +51902,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -49731,6 +51986,10 @@
       build_numbers: YES
       service_account: "chromium-cq-staging-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -49811,6 +52070,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -49891,6 +52154,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -49972,6 +52239,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -50053,6 +52324,10 @@
       build_numbers: YES
       service_account: "chromium-cq-staging-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -50134,6 +52409,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -50215,6 +52494,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -50296,6 +52579,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -50377,6 +52664,10 @@
       build_numbers: YES
       service_account: "chromium-cq-staging-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -50458,6 +52749,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -50539,6 +52834,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -50623,6 +52922,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -50707,6 +53010,10 @@
       build_numbers: YES
       service_account: "chromium-cq-staging-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -50791,6 +53098,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -50875,6 +53186,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -50955,6 +53270,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -51035,6 +53354,10 @@
       build_numbers: YES
       service_account: "chromium-cq-staging-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -51115,6 +53438,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -51195,6 +53522,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 10
+      }
+      experiments {
         key: "luci.recipes.use_python3"
         value: 100
       }
@@ -51522,7 +53853,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -51606,7 +53937,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -51700,7 +54031,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -51794,7 +54125,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -51888,7 +54219,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -51981,7 +54312,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -52088,7 +54419,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -52183,7 +54514,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -52275,7 +54606,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -52369,7 +54700,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -52463,7 +54794,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -52557,7 +54888,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -52659,7 +54990,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -52765,7 +55096,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -52858,7 +55189,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -52952,7 +55283,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -53046,7 +55377,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -53140,7 +55471,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -53234,7 +55565,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -53328,7 +55659,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -53422,7 +55753,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -53516,7 +55847,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -53610,7 +55941,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -53704,7 +56035,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -53798,7 +56129,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -53892,7 +56223,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -53979,7 +56310,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -54066,7 +56397,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -54160,7 +56491,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -54254,7 +56585,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -54350,7 +56681,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -54461,7 +56792,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -54556,7 +56887,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -54652,7 +56983,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -54763,7 +57094,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -54858,7 +57189,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -54951,7 +57282,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -55058,7 +57389,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -55155,7 +57486,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -55266,7 +57597,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -55361,7 +57692,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -55455,7 +57786,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -55549,7 +57880,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -55643,7 +57974,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -55737,7 +58068,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -55831,7 +58162,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -55924,7 +58255,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -56020,7 +58351,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -56131,7 +58462,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -56226,7 +58557,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -56320,7 +58651,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -56414,7 +58745,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -56507,7 +58838,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -56600,7 +58931,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -56694,7 +59025,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -56788,7 +59119,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -56882,7 +59213,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -56976,7 +59307,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -57070,7 +59401,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -57164,7 +59495,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -57258,7 +59589,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -57352,7 +59683,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -57446,7 +59777,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -57540,7 +59871,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -57633,7 +59964,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -57735,7 +60066,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -57829,7 +60160,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -57923,7 +60254,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -58016,7 +60347,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -58110,7 +60441,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -58204,7 +60535,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -58297,7 +60628,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -58403,7 +60734,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -58496,7 +60827,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -58590,7 +60921,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -58731,7 +61062,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -58813,7 +61144,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -58907,7 +61238,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -59013,7 +61344,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -59106,7 +61437,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -59200,7 +61531,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -59294,7 +61625,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -59388,7 +61719,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -59482,7 +61813,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -59576,7 +61907,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -59672,7 +62003,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 1
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -59783,7 +62114,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -59878,7 +62209,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -59971,7 +62302,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -60077,7 +62408,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -60171,7 +62502,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -60265,7 +62596,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -60359,7 +62690,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -60453,7 +62784,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -60539,7 +62870,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -60631,7 +62962,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -60723,7 +63054,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -60815,7 +63146,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -60907,7 +63238,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -60999,7 +63330,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -61091,7 +63422,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -61183,7 +63514,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -61275,7 +63606,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -61367,7 +63698,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -61461,7 +63792,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -61567,7 +63898,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -61661,7 +63992,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -61768,7 +64099,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -61861,7 +64192,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -61955,7 +64286,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -62042,7 +64373,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -62136,7 +64467,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -62230,7 +64561,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -62324,7 +64655,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -62418,7 +64749,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -62512,7 +64843,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -62605,7 +64936,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -62711,7 +65042,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -62817,7 +65148,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -62922,7 +65253,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -63027,7 +65358,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -63129,7 +65460,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -63219,7 +65550,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -63309,7 +65640,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -63399,7 +65730,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -63489,7 +65820,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -63579,7 +65910,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -63669,7 +66000,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -63759,7 +66090,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -63849,7 +66180,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -63939,7 +66270,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -64029,7 +66360,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -64119,7 +66450,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -64209,7 +66540,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -64299,7 +66630,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -64389,7 +66720,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -64479,7 +66810,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -64569,7 +66900,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -64659,7 +66990,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -64749,7 +67080,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -64839,7 +67170,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -64928,7 +67259,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -65017,7 +67348,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -65106,7 +67437,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -65195,7 +67526,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -65284,7 +67615,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -65373,7 +67704,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -65462,7 +67793,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -65551,7 +67882,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -65640,7 +67971,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -65729,7 +68060,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -65818,7 +68149,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -65907,7 +68238,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -65996,7 +68327,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -66085,7 +68416,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -66175,7 +68506,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -66265,7 +68596,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -66355,7 +68686,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -66445,7 +68776,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -66535,7 +68866,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -66625,7 +68956,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -66715,7 +69046,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -66805,7 +69136,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -66895,7 +69226,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -66985,7 +69316,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -67075,7 +69406,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -67165,7 +69496,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -67254,7 +69585,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -67343,7 +69674,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -67433,7 +69764,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -67606,7 +69937,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -67702,7 +70033,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -67798,7 +70129,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -67893,7 +70224,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -67989,7 +70320,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -68085,7 +70416,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -68181,7 +70512,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -68277,7 +70608,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -68319,10 +70650,10 @@
     builders {
       name: "ios-simulator"
       swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builder:ios-simulator"
+      dimensions: "cores:2"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Mac-12"
-      dimensions: "pool:luci.chromium.try"
+      dimensions: "os:Ubuntu-18.04"
+      dimensions: "pool:luci.chromium.try.orchestrator"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -68351,7 +70682,7 @@
         '  },'
         '  "builder_group": "tryserver.chromium.mac",'
         '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium_trybot"'
+        '  "recipe": "chromium/orchestrator"'
         '}'
       execution_timeout_secs: 14400
       expiration_secs: 7200
@@ -68359,21 +70690,21 @@
         seconds: 120
       }
       caches {
+        name: "unused_builder_cache"
+        path: "builder"
+      }
+      caches {
         name: "win_toolchain"
         path: "win_toolchain"
       }
-      caches {
-        name: "xcode_ios_14a309"
-        path: "xcode_ios_14a309.app"
-      }
       build_numbers: YES
-      service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
+      service_account: "chromium-orchestrator@chops-service-accounts.iam.gserviceaccount.com"
       task_template_canary_percentage {
         value: 5
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -68384,6 +70715,10 @@
         value: 100
       }
       experiments {
+        key: "remove_src_checkout_experiment"
+        value: 100
+      }
+      experiments {
         key: "weetbix.enable_weetbix_exonerations"
         value: 100
       }
@@ -68423,6 +70758,7 @@
           use_invocation_timestamp: true
         }
       }
+      description_html: "This is the orchestrator half of an orchestrator + compilator pair of builders. The compilator is <a href=\"https://ci.chromium.org/p/chromium/builders/try/ios-simulator-compilator\">ios-simulator-compilator</a>."
     }
     builders {
       name: "ios-simulator-compilator"
@@ -68481,7 +70817,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.buildbucket.omit_python2"
@@ -68523,7 +70859,7 @@
           use_invocation_timestamp: true
         }
       }
-      description_html: "This is the compilator half of an orchestrator + compilator pair of builders. The orchestrator is <a href=\"https://ci.chromium.org/p/chromium/builders/try/ios-simulator-orchestrator\">ios-simulator-orchestrator</a>."
+      description_html: "This is the compilator half of an orchestrator + compilator pair of builders. The orchestrator is <a href=\"https://ci.chromium.org/p/chromium/builders/try/ios-simulator\">ios-simulator</a>."
     }
     builders {
       name: "ios-simulator-cronet"
@@ -68582,7 +70918,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -68690,7 +71026,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -68798,7 +71134,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -68894,7 +71230,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -68990,7 +71326,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -69046,119 +71382,6 @@
       }
     }
     builders {
-      name: "ios-simulator-orchestrator"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "cores:2"
-      dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
-      dimensions: "pool:luci.chromium.try.orchestrator"
-      exe {
-        cipd_package: "infra/chromium/bootstrapper/${platform}"
-        cipd_version: "latest"
-        cmd: "bootstrapper"
-      }
-      properties:
-        '{'
-        '  "$bootstrap/exe": {'
-        '    "exe": {'
-        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
-        '      "cipd_version": "refs/heads/main",'
-        '      "cmd": ['
-        '        "luciexe"'
-        '      ]'
-        '    }'
-        '  },'
-        '  "$bootstrap/properties": {'
-        '    "properties_file": "infra/config/generated/builders/try/ios-simulator-orchestrator/properties.json",'
-        '    "top_level_project": {'
-        '      "ref": "refs/heads/main",'
-        '      "repo": {'
-        '        "host": "chromium.googlesource.com",'
-        '        "project": "chromium/src"'
-        '      }'
-        '    }'
-        '  },'
-        '  "builder_group": "tryserver.chromium.mac",'
-        '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium/orchestrator"'
-        '}'
-      execution_timeout_secs: 14400
-      expiration_secs: 7200
-      grace_period {
-        seconds: 120
-      }
-      caches {
-        name: "unused_builder_cache"
-        path: "builder"
-      }
-      caches {
-        name: "win_toolchain"
-        path: "win_toolchain"
-      }
-      build_numbers: YES
-      service_account: "chromium-orchestrator@chops-service-accounts.iam.gserviceaccount.com"
-      task_template_canary_percentage {
-        value: 5
-      }
-      experiments {
-        key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
-      }
-      experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
-        key: "luci.recipes.use_python3"
-        value: 100
-      }
-      experiments {
-        key: "remove_src_checkout_experiment"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
-      resultdb {
-        enable: true
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "try_test_results"
-          test_results {}
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "gpu_try_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+"
-            }
-          }
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "blink_web_tests_try_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*blink_wpt_tests/.+)"
-            }
-          }
-        }
-        history_options {
-          use_invocation_timestamp: true
-        }
-      }
-      description_html: "This is the orchestrator half of an orchestrator + compilator pair of builders. The compilator is <a href=\"https://ci.chromium.org/p/chromium/builders/try/ios-simulator-compilator\">ios-simulator-compilator</a>."
-    }
-    builders {
       name: "ios-simulator-rts"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builder:ios-simulator-rts"
@@ -69215,7 +71438,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -69311,7 +71534,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -69407,7 +71630,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -69503,7 +71726,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -69599,7 +71822,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -69692,7 +71915,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -69797,7 +72020,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -69890,7 +72113,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -69996,7 +72219,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -70090,7 +72313,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -70184,7 +72407,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -70277,7 +72500,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -70369,7 +72592,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -70463,7 +72686,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -70557,7 +72780,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -70651,7 +72874,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -70745,7 +72968,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -70839,7 +73062,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -70933,7 +73156,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -71027,7 +73250,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -71121,7 +73344,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -71215,7 +73438,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -71309,7 +73532,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -71402,7 +73625,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -71495,7 +73718,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -71589,7 +73812,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -71683,7 +73906,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -71779,7 +74002,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -71890,7 +74113,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -71984,7 +74207,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -72077,7 +74300,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -72170,7 +74393,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -72262,7 +74485,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -72356,7 +74579,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -72450,7 +74673,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -72544,7 +74767,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -72638,7 +74861,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -72732,7 +74955,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -72826,7 +75049,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -72920,7 +75143,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -73014,7 +75237,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -73107,7 +75330,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -73201,7 +75424,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -73295,7 +75518,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -73389,7 +75612,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -73495,7 +75718,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -73589,7 +75812,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -73683,7 +75906,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -73773,7 +75996,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -73879,7 +76102,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -73973,7 +76196,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -74067,7 +76290,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -74161,7 +76384,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -74257,7 +76480,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -74368,7 +76591,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -74469,7 +76692,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -74571,7 +76794,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -74664,7 +76887,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -74764,7 +76987,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -74858,7 +77081,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -74951,7 +77174,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -75044,7 +77267,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -75135,7 +77358,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -75219,7 +77442,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -75310,7 +77533,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -75404,7 +77627,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -75497,7 +77720,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -75603,7 +77826,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -75697,7 +77920,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -75791,7 +78014,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -75885,7 +78108,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -75979,7 +78202,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -76072,7 +78295,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -76166,7 +78389,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -76260,7 +78483,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -76354,7 +78577,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -76448,7 +78671,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -76542,7 +78765,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -76638,7 +78861,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -76749,7 +78972,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -76844,7 +79067,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -76938,7 +79161,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -77032,7 +79255,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -77126,7 +79349,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -77217,7 +79440,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -77311,7 +79534,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -77408,7 +79631,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -77502,7 +79725,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -77600,7 +79823,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -77694,7 +79917,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -77788,7 +80011,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -77884,7 +80107,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -77995,7 +80218,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -78090,7 +80313,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -78184,7 +80407,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -78277,7 +80500,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -78362,7 +80585,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -78456,7 +80679,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -78546,7 +80769,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -78639,7 +80862,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -78732,7 +80955,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -78828,7 +81051,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -78920,7 +81143,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -79013,7 +81236,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -79106,7 +81329,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -79198,7 +81421,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -79291,7 +81514,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -79384,7 +81607,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -79480,7 +81703,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -79590,7 +81813,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.buildbucket.omit_python2"
@@ -79684,7 +81907,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -79776,7 +81999,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -79868,7 +82091,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -79961,7 +82184,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -80054,7 +82277,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -80147,7 +82370,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -80240,7 +82463,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -80333,7 +82556,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -80426,7 +82649,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -80519,7 +82742,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -80625,7 +82848,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.buildbucket.omit_python2"
@@ -80723,7 +82946,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -80816,7 +83039,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -80909,7 +83132,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -81002,7 +83225,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -81095,7 +83318,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -81188,7 +83411,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -81281,7 +83504,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -81374,7 +83597,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -81467,7 +83690,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -81560,7 +83783,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -81653,7 +83876,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -81746,7 +83969,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -81838,7 +84061,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -81918,7 +84141,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -81998,7 +84221,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -82092,7 +84315,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -82185,7 +84408,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -82266,7 +84489,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -82356,7 +84579,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -82447,7 +84670,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -82538,7 +84761,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -82629,7 +84852,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -82723,7 +84946,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -82815,7 +85038,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -82907,7 +85130,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -83001,7 +85224,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -83095,7 +85318,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -83186,7 +85409,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -83278,7 +85501,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -83372,7 +85595,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -83458,7 +85681,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -83564,7 +85787,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -83658,7 +85881,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -83749,7 +85972,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -83833,7 +86056,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -83917,7 +86140,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -84008,7 +86231,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -84099,7 +86322,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -84193,7 +86416,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -84287,7 +86510,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -84380,7 +86603,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -84474,7 +86697,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -84568,7 +86791,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -84662,7 +86885,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -84758,7 +86981,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 1
+        value: 20
       }
       experiments {
         key: "enable_weetbix_queries"
@@ -84869,7 +87092,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -84963,7 +87186,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -85057,7 +87280,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -85151,7 +87374,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -85245,7 +87468,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -85339,7 +87562,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -85433,7 +87656,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -85527,7 +87750,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -85621,7 +87844,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -85715,7 +87938,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -85809,7 +88032,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -85889,7 +88112,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -85983,7 +88206,7 @@
       }
       experiments {
         key: "chromium_swarming.expose_merge_script_failures"
-        value: 10
+        value: 20
       }
       experiments {
         key: "luci.recipes.use_python3"
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg
index 49be736..ff19d11 100644
--- a/infra/config/generated/luci/luci-milo.cfg
+++ b/infra/config/generated/luci/luci-milo.cfg
@@ -2746,9 +2746,6 @@
     name: "buildbucket/luci.chromium.try/ios-simulator-full-configs"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/ios-simulator-orchestrator"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/lacros-amd64-generic-rel"
   }
   builders {
@@ -17009,9 +17006,6 @@
     name: "buildbucket/luci.chromium.try/ios-simulator-noncq"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/ios-simulator-orchestrator"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/ios-simulator-rts"
   }
   builders {
@@ -18462,9 +18456,6 @@
     name: "buildbucket/luci.chromium.try/ios-simulator-noncq"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/ios-simulator-orchestrator"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/ios-simulator-rts"
   }
   builders {
diff --git a/infra/config/lib/ci.star b/infra/config/lib/ci.star
index 871eee0..6f3b8637 100644
--- a/infra/config/lib/ci.star
+++ b/infra/config/lib/ci.star
@@ -87,6 +87,11 @@
     if not branches.matches(branch_selector):
         return
 
+    experiments = experiments or {}
+
+    # TODO(crbug.com/1346781): Enable everywhere.
+    experiments.setdefault("chromium_swarming.expose_merge_script_failures", 10)
+
     try_only_kwargs = [k for k in ("mirrors", "try_settings") if k in kwargs]
     if try_only_kwargs:
         fail("CI builders cannot specify the following try-only arguments: {}".format(try_only_kwargs))
diff --git a/infra/config/lib/try.star b/infra/config/lib/try.star
index 2ad9d413..a7b1db9 100644
--- a/infra/config/lib/try.star
+++ b/infra/config/lib/try.star
@@ -145,7 +145,7 @@
     experiments = experiments or {}
 
     # TODO(crbug.com/1346781): Enable everywhere.
-    experiments.setdefault("chromium_swarming.expose_merge_script_failures", 10)
+    experiments.setdefault("chromium_swarming.expose_merge_script_failures", 20)
 
     merged_resultdb_bigquery_exports = [
         resultdb.export_test_results(
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star b/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star
index 7539d1a..240766b 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star
@@ -61,7 +61,6 @@
     main_list_view = "try",
     tryjob = try_.job(),
     experiments = {
-        "chromium_swarming.expose_merge_script_failures": 1,
         "remove_src_checkout_experiment": 100,
         "enable_weetbix_queries": 100,
         "weetbix.retry_weak_exonerations": 100,
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star b/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
index afdf15b..c1d5601 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
@@ -324,29 +324,8 @@
     cpu = cpu.ARM64,
 )
 
-ios_builder(
-    name = "ios-simulator",
-    branch_selector = branches.STANDARD_MILESTONE,
-    mirrors = [
-        "ci/ios-simulator",
-    ],
-    check_for_flakiness = True,
-    main_list_view = "try",
-    use_clang_coverage = True,
-    coverage_exclude_sources = "ios_test_files_and_test_utils",
-    coverage_test_types = ["overall", "unit"],
-    tryjob = try_.job(),
-    experiments = {
-        "enable_weetbix_queries": 100,
-        "weetbix.retry_weak_exonerations": 100,
-        "weetbix.enable_weetbix_exonerations": 100,
-    },
-)
-
-# TODO (crbug.com/1298112): Change name to ios-simulator once we verify that
-# it's safe to move
 try_.orchestrator_builder(
-    name = "ios-simulator-orchestrator",
+    name = "ios-simulator",
     compilator = "ios-simulator-compilator",
     branch_selector = branches.STANDARD_MILESTONE,
     mirrors = [
@@ -357,6 +336,7 @@
     use_clang_coverage = True,
     coverage_exclude_sources = "ios_test_files_and_test_utils",
     coverage_test_types = ["overall", "unit"],
+    tryjob = try_.job(),
     experiments = {
         "remove_src_checkout_experiment": 100,
         "enable_weetbix_queries": 100,
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.win.star b/infra/config/subprojects/chromium/try/tryserver.chromium.win.star
index 53dc198..8e9b81f 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.win.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.win.star
@@ -189,7 +189,6 @@
     main_list_view = "try",
     tryjob = try_.job(),
     experiments = {
-        "chromium_swarming.expose_merge_script_failures": 1,
         "remove_src_checkout_experiment": 100,
         "enable_weetbix_queries": 100,
         "weetbix.retry_weak_exonerations": 100,
diff --git a/infra/config/subprojects/codesearch/codesearch.star b/infra/config/subprojects/codesearch/codesearch.star
index e585d406..995eabc 100644
--- a/infra/config/subprojects/codesearch/codesearch.star
+++ b/infra/config/subprojects/codesearch/codesearch.star
@@ -85,4 +85,9 @@
 try_.builder(
     name = "gen-win-try",
     os = os.WINDOWS_10,
+    properties = {
+        "recipe_properties": {
+            "platform": "win",
+        },
+    },
 )
diff --git a/ios/chrome/browser/application_context/application_context_impl.mm b/ios/chrome/browser/application_context/application_context_impl.mm
index 68413b7..b5cbfc84 100644
--- a/ios/chrome/browser/application_context/application_context_impl.mm
+++ b/ios/chrome/browser/application_context/application_context_impl.mm
@@ -21,6 +21,7 @@
 #import "base/time/default_clock.h"
 #import "base/time/default_tick_clock.h"
 #import "components/breadcrumbs/core/breadcrumb_persistent_storage_manager.h"
+#import "components/breadcrumbs/core/crash_reporter_breadcrumb_observer.h"
 #import "components/breadcrumbs/core/features.h"
 #import "components/component_updater/component_updater_service.h"
 #import "components/component_updater/timer_update_scheduler.h"
@@ -159,6 +160,10 @@
   }
 
   if (base::FeatureList::IsEnabled(breadcrumbs::kLogBreadcrumbs)) {
+    // Start crash reporter listening for breadcrumb events. Collected
+    // breadcrumbs will be attached to crash reports.
+    breadcrumbs::CrashReporterBreadcrumbObserver::GetInstance();
+
     base::FilePath storage_dir;
     bool result = base::PathService::Get(ios::DIR_USER_DATA, &storage_dir);
     DCHECK(result);
diff --git a/ios/chrome/browser/ui/activity_services/activities/bookmark_activity.mm b/ios/chrome/browser/ui/activity_services/activities/bookmark_activity.mm
index 07fe306..da1f95a4 100644
--- a/ios/chrome/browser/ui/activity_services/activities/bookmark_activity.mm
+++ b/ios/chrome/browser/ui/activity_services/activities/bookmark_activity.mm
@@ -80,8 +80,7 @@
 - (UIImage*)activityImage {
   if (UseSymbols()) {
     if (self.bookmarked) {
-      // TODO(crbug.com/1315544): Update this one once created.
-      return DefaultSymbolWithPointSize(kAddBookmarkActionSymbol,
+      return DefaultSymbolWithPointSize(kEditActionSymbol,
                                         kSymbolActionPointSize);
     }
     return DefaultSymbolWithPointSize(kAddBookmarkActionSymbol,
diff --git a/ios/chrome/browser/ui/omnibox/popup/autocomplete_result_consumer.h b/ios/chrome/browser/ui/omnibox/popup/autocomplete_result_consumer.h
index 912728e..387c213 100644
--- a/ios/chrome/browser/ui/omnibox/popup/autocomplete_result_consumer.h
+++ b/ios/chrome/browser/ui/omnibox/popup/autocomplete_result_consumer.h
@@ -65,7 +65,7 @@
 // user doesn't have to scroll or hide the keyboard to see those `n` first
 // suggestions.
 - (void)requestResultsWithVisibleSuggestionCount:
-    (NSInteger)visibleSuggestionCount
+    (NSUInteger)visibleSuggestionCount
     __attribute__((swift_name("requestResults(visibleSuggestionCount:)")));
 ;
 
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_mediator.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_mediator.mm
index a3ab7c6..98d94dc 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_mediator.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_mediator.mm
@@ -115,9 +115,9 @@
 #pragma mark - AutocompleteResultDataSource
 
 - (void)requestResultsWithVisibleSuggestionCount:
-    (NSInteger)visibleSuggestionCount {
-  size_t visibleSuggestions =
-      MIN(visibleSuggestionCount, (NSInteger)_currentResult.size());
+    (NSUInteger)visibleSuggestionCount {
+  NSUInteger visibleSuggestions =
+      MIN(visibleSuggestionCount, _currentResult.size());
   if (visibleSuggestions > 0) {
     // Groups visible suggestions by search vs url. Skip the first suggestion
     // because it's the omnibox content.
@@ -402,6 +402,8 @@
 }
 
 - (void)groupCurrentSuggestionsFrom:(NSUInteger)begin to:(NSUInteger)end {
+  DCHECK(begin <= _currentResult.size());
+  DCHECK(end <= _currentResult.size());
   AutocompleteResult::GroupSuggestionsBySearchVsURL(
       std::next(_currentResult.begin(), begin),
       std::next(_currentResult.begin(), end));
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm
index fd80fa512..e6a6d638 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm
@@ -90,7 +90,7 @@
 
 // Estimated maximum number of visible suggestions.
 // Only updated in `newResultsAvailable` method, were the value is used.
-@property(nonatomic, assign) NSInteger visibleSuggestionCount;
+@property(nonatomic, assign) NSUInteger visibleSuggestionCount;
 
 // Boolean to update visible suggestion count only once on event such as device
 // orientation change or multitasking window change, where multiple keyboard and
@@ -117,6 +117,7 @@
   if (self = [super initWithNibName:nil bundle:nil]) {
     _forwardsScrollEvents = YES;
     _preselectedMatchGroupIndex = 0;
+    _visibleSuggestionCount = 0;
     NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter];
     if (ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET) {
       // The iPad keyboard can cover some of the rows of the scroll view. The
diff --git a/ios/chrome/browser/ui/omnibox/popup/shared/popup_model.swift b/ios/chrome/browser/ui/omnibox/popup/shared/popup_model.swift
index d5b90125..d579373 100644
--- a/ios/chrome/browser/ui/omnibox/popup/shared/popup_model.swift
+++ b/ios/chrome/browser/ui/omnibox/popup/shared/popup_model.swift
@@ -24,7 +24,7 @@
 
   /// Number of suggestions that can be visible in the `popup_view`.
   /// This variable is modified by an observer and should NOT be published.
-  var visibleSuggestionCount: Int
+  var visibleSuggestionCount: UInt
 
   /// Index of the preselected section when no row is highlighted.
   var preselectedSectionIndex: Int
diff --git a/ios/chrome/browser/ui/omnibox/popup/shared/popup_visible_suggestion_count.swift b/ios/chrome/browser/ui/omnibox/popup/shared/popup_visible_suggestion_count.swift
index 94d093aa..62ad5dd 100644
--- a/ios/chrome/browser/ui/omnibox/popup/shared/popup_visible_suggestion_count.swift
+++ b/ios/chrome/browser/ui/omnibox/popup/shared/popup_visible_suggestion_count.swift
@@ -88,7 +88,7 @@
       return
     }
     let visibleCellCount = self.visibleHeight! / fakeCellHeight
-    self.model?.visibleSuggestionCount = Int(floor(visibleCellCount))
+    self.model?.visibleSuggestionCount = UInt(max(0, floor(visibleCellCount)))
   }
 }
 
diff --git a/ios/chrome/browser/ui/promos_manager/promos_manager_scene_agent.h b/ios/chrome/browser/ui/promos_manager/promos_manager_scene_agent.h
index acd1885..9e760129 100644
--- a/ios/chrome/browser/ui/promos_manager/promos_manager_scene_agent.h
+++ b/ios/chrome/browser/ui/promos_manager/promos_manager_scene_agent.h
@@ -19,8 +19,7 @@
 // (2) the scene is in the foreground,
 // (3) there is no UI blocker,
 // (4) the app isn't shutting down,
-// (5) there are no launch intents, and
-// (6) the last session wasn't a crash.
+// (5) there are no launch intents.
 //
 // There are 3 events that can trigger a promo:
 //
diff --git a/ios/chrome/browser/ui/promos_manager/promos_manager_scene_agent.mm b/ios/chrome/browser/ui/promos_manager/promos_manager_scene_agent.mm
index 72aa39f..8cb5724 100644
--- a/ios/chrome/browser/ui/promos_manager/promos_manager_scene_agent.mm
+++ b/ios/chrome/browser/ui/promos_manager/promos_manager_scene_agent.mm
@@ -102,17 +102,13 @@
   if (self.sceneState.startupHadExternalIntent)
     return NO;
 
-  // (6) The app isn't launching after a crash.
-  if (self.sceneState.appState.postCrashLaunch)
-    return NO;
-
   // Additional, sensible checks to add to minimize user annoyance:
 
-  // (7) The user isn't currently signing in.
+  // (6) The user isn't currently signing in.
   if (self.sceneState.signinInProgress)
     return NO;
 
-  // (8) The user isn't currently looking at a modal overlay.
+  // (7) The user isn't currently looking at a modal overlay.
   if (self.sceneState.presentingModalOverlay)
     return NO;
 
diff --git a/ios/chrome/browser/ui/settings/password/password_settings/password_settings_constants.h b/ios/chrome/browser/ui/settings/password/password_settings/password_settings_constants.h
index 90f2cbf..e031f8a 100644
--- a/ios/chrome/browser/ui/settings/password/password_settings/password_settings_constants.h
+++ b/ios/chrome/browser/ui/settings/password/password_settings/password_settings_constants.h
@@ -23,4 +23,10 @@
 // Accessibility ID for the row showing status of Passwords in Other Apps.
 extern NSString* const kPasswordSettingsPasswordsInOtherAppsRowId;
 
+// Accessibility IDs for on-device encryption elements.
+extern NSString* const kPasswordSettingsOnDeviceEncryptionOptInId;
+extern NSString* const kPasswordSettingsOnDeviceEncryptionLearnMoreId;
+extern NSString* const kPasswordSettingsOnDeviceEncryptionOptedInTextId;
+extern NSString* const kPasswordSettingsOnDeviceEncryptionSetUpId;
+
 #endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_SETTINGS_PASSWORD_SETTINGS_CONSTANTS_H_
diff --git a/ios/chrome/browser/ui/settings/password/password_settings/password_settings_constants.mm b/ios/chrome/browser/ui/settings/password/password_settings/password_settings_constants.mm
index 3844c18..5a59b32f 100644
--- a/ios/chrome/browser/ui/settings/password/password_settings/password_settings_constants.mm
+++ b/ios/chrome/browser/ui/settings/password/password_settings/password_settings_constants.mm
@@ -21,3 +21,12 @@
 
 NSString* const kPasswordSettingsPasswordsInOtherAppsRowId =
     @"PasswordSettingsPasswordsInOtherAppsRowId";
+
+NSString* const kPasswordSettingsOnDeviceEncryptionOptInId =
+    @"PasswordSettingsOnDeviceEncryptionOptInId";
+NSString* const kPasswordSettingsOnDeviceEncryptionLearnMoreId =
+    @"PasswordSettingsOnDeviceEncryptionLearnMoreId";
+NSString* const kPasswordSettingsOnDeviceEncryptionOptedInTextId =
+    @"PasswordSettingsOnDeviceEncryptionOptedInTextId";
+NSString* const kPasswordSettingsOnDeviceEncryptionSetUpId =
+    @"PasswordSettingsOnDeviceEncryptionSetUpId";
diff --git a/ios/chrome/browser/ui/settings/password/password_settings/password_settings_consumer.h b/ios/chrome/browser/ui/settings/password/password_settings/password_settings_consumer.h
index 46ed8686..02e553d 100644
--- a/ios/chrome/browser/ui/settings/password/password_settings/password_settings_consumer.h
+++ b/ios/chrome/browser/ui/settings/password/password_settings/password_settings_consumer.h
@@ -5,6 +5,17 @@
 #ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_SETTINGS_PASSWORD_SETTINGS_CONSUMER_H_
 #define IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORD_SETTINGS_PASSWORD_SETTINGS_CONSUMER_H_
 
+// State of on-device encryption.
+typedef NS_ENUM(NSInteger, PasswordSettingsOnDeviceEncryptionState) {
+  // User can not opt-in in their current state, so the section should not be
+  // shown.
+  PasswordSettingsOnDeviceEncryptionStateNotShown = 0,
+  // On-device encryption is on.
+  PasswordSettingsOnDeviceEncryptionStateOptedIn,
+  // User can opt-in to on-device encryption.
+  PasswordSettingsOnDeviceEncryptionStateOfferOptIn,
+};
+
 // Interface for passing state from the mediator to the ViewController showing
 // the password settings submenu.
 @protocol PasswordSettingsConsumer
@@ -24,6 +35,10 @@
 // at the iOS level.
 - (void)setPasswordsInOtherAppsEnabled:(BOOL)enabled;
 
+// Indicates the on-device encryption state according to the sync service.
+- (void)setOnDeviceEncryptionState:
+    (PasswordSettingsOnDeviceEncryptionState)onDeviceEncryptionState;
+
 // Enables/disables the "Export Passwords..." button based on the current state.
 - (void)updateExportPasswordsButton;
 
diff --git a/ios/chrome/browser/ui/settings/password/password_settings/password_settings_mediator.mm b/ios/chrome/browser/ui/settings/password/password_settings/password_settings_mediator.mm
index 5e3bf84..77c2636a 100644
--- a/ios/chrome/browser/ui/settings/password/password_settings/password_settings_mediator.mm
+++ b/ios/chrome/browser/ui/settings/password/password_settings/password_settings_mediator.mm
@@ -110,6 +110,11 @@
                                         kCredentialsEnableService)];
 
   [self passwordAutoFillStatusDidChange];
+
+  // TODO(crbug.com/1335156): Read the actual state and push to the consumer;
+  // this is placeholder behavior.
+  [self.consumer setOnDeviceEncryptionState:
+                     PasswordSettingsOnDeviceEncryptionStateOptedIn];
 }
 
 - (void)userDidStartExportFlow {
diff --git a/ios/chrome/browser/ui/settings/password/password_settings/password_settings_view_controller.mm b/ios/chrome/browser/ui/settings/password/password_settings/password_settings_view_controller.mm
index 7083ff3..c5a3f4b 100644
--- a/ios/chrome/browser/ui/settings/password/password_settings/password_settings_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/password/password_settings/password_settings_view_controller.mm
@@ -5,11 +5,13 @@
 #import "ios/chrome/browser/ui/settings/password/password_settings/password_settings_view_controller.h"
 
 #import "base/check.h"
+#import "base/check_op.h"
 #import "base/mac/foundation_util.h"
 #import "base/notreached.h"
 #import "components/strings/grit/components_strings.h"
 #import "ios/chrome/browser/ui/settings/password/password_settings/password_settings_constants.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_detail_icon_item.h"
+#import "ios/chrome/browser/ui/table_view/cells/table_view_image_item.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_info_button_cell.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_info_button_item.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_switch_cell.h"
@@ -34,6 +36,7 @@
 typedef NS_ENUM(NSInteger, SectionIdentifier) {
   SectionIdentifierSavePasswordsSwitch = kSectionIdentifierEnumZero,
   SectionIdentifierPasswordsInOtherApps,
+  SectionIdentifierOnDeviceEncryption,
   SectionIdentifierExportPasswordsButton,
 };
 
@@ -43,6 +46,18 @@
   ItemTypeManagedSavePasswords,
   ItemTypePasswordsInOtherApps,
   ItemTypeExportPasswordsButton,
+  ItemTypeOnDeviceEncryptionOptInDescription,
+  ItemTypeOnDeviceEncryptionOptedInDescription,
+  ItemTypeOnDeviceEncryptionOptedInLearnMore,
+  ItemTypeOnDeviceEncryptionSetUp,
+};
+
+// Indicates whether the model has not started loading, is in the process of
+// loading, or has completed loading.
+typedef NS_ENUM(NSInteger, ModelLoadStatus) {
+  ModelNotLoaded = 0,
+  ModelIsLoading,
+  ModelLoadComplete,
 };
 
 }  // namespace
@@ -51,9 +66,6 @@
   // The item related to the button for exporting passwords.
   TableViewTextItem* _exportPasswordsItem;
 
-  // Whether or not the model has been loaded.
-  BOOL _isModelLoaded;
-
   // Whether or not Chromium has been enabled as a credential provider at the
   // iOS level. This may not be known at load time; the detail text showing on
   // or off status will be omitted until this is populated.
@@ -62,6 +74,9 @@
 
 // State
 
+// Tracks whether or not the model has loaded.
+@property(nonatomic, assign) ModelLoadStatus modelLoadStatus;
+
 // Whether or not the exporter should be enabled.
 @property(nonatomic, assign) BOOL canExportPasswords;
 
@@ -72,6 +87,10 @@
 @property(nonatomic, assign, getter=isSavePasswordsEnabled)
     BOOL savePasswordsEnabled;
 
+// On-device encryption state according to the sync service.
+@property(nonatomic, assign)
+    PasswordSettingsOnDeviceEncryptionState onDeviceEncryptionState;
+
 // UI elements
 
 // The item related to the switch for the password manager setting.
@@ -86,6 +105,24 @@
 @property(nonatomic, readonly)
     TableViewDetailIconItem* passwordsInOtherAppsItem;
 
+// Descriptive text shown when the user has the option of enabling on-device
+// encryption.
+@property(nonatomic, readonly)
+    TableViewImageItem* onDeviceEncryptionOptInDescriptionItem;
+
+// Descriptive text shown when the user has already enabled on-device
+// encryption.
+@property(nonatomic, readonly)
+    TableViewImageItem* onDeviceEncryptionOptedInDescription;
+
+// A button giving the user more information about on-device encrpytion, shown
+// when they have already enabled it.
+@property(nonatomic, readonly)
+    TableViewTextItem* onDeviceEncryptionOptedInLearnMore;
+
+// A button which triggers the setup of on-device encryption.
+@property(nonatomic, readonly) TableViewTextItem* setUpOnDeviceEncryptionItem;
+
 @end
 
 @implementation PasswordSettingsViewController
@@ -93,6 +130,13 @@
 @synthesize savePasswordsItem = _savePasswordsItem;
 @synthesize managedSavePasswordsItem = _managedSavePasswordsItem;
 @synthesize passwordsInOtherAppsItem = _passwordsInOtherAppsItem;
+@synthesize onDeviceEncryptionOptInDescriptionItem =
+    _onDeviceEncryptionOptInDescriptionItem;
+@synthesize onDeviceEncryptionOptedInDescription =
+    _onDeviceEncryptionOptedInDescription;
+@synthesize onDeviceEncryptionOptedInLearnMore =
+    _onDeviceEncryptionOptedInLearnMore;
+@synthesize setUpOnDeviceEncryptionItem = _setUpOnDeviceEncryptionItem;
 
 - (instancetype)init {
   self = [super initWithStyle:ChromeTableViewStyle()];
@@ -126,9 +170,9 @@
 - (void)loadModel {
   [super loadModel];
 
-  TableViewModel* model = self.tableViewModel;
+  self.modelLoadStatus = ModelIsLoading;
 
-  _isModelLoaded = YES;
+  TableViewModel* model = self.tableViewModel;
 
   [model addSectionWithIdentifier:SectionIdentifierSavePasswordsSwitch];
   [self addSavePasswordsSwitchOrManagedInfo];
@@ -137,12 +181,20 @@
   [model addItem:[self passwordsInOtherAppsItem]
       toSectionWithIdentifier:SectionIdentifierPasswordsInOtherApps];
 
+  if (self.onDeviceEncryptionState !=
+      PasswordSettingsOnDeviceEncryptionStateNotShown) {
+    [self updateOnDeviceEncryptionSectionWithOldState:
+              PasswordSettingsOnDeviceEncryptionStateNotShown];
+  }
+
   // Export passwords button.
   [model addSectionWithIdentifier:SectionIdentifierExportPasswordsButton];
   _exportPasswordsItem = [self makeExportPasswordsItem];
   [self updateExportPasswordsButton];
   [model addItem:_exportPasswordsItem
       toSectionWithIdentifier:SectionIdentifierExportPasswordsButton];
+
+  self.modelLoadStatus = ModelLoadComplete;
 }
 
 #pragma mark - UITableViewDataSource
@@ -190,6 +242,16 @@
       }
       break;
     }
+    case ItemTypeOnDeviceEncryptionSetUp: {
+      // TODO(crbug.com/1335156): Trigger setup.
+      break;
+    }
+    case ItemTypeOnDeviceEncryptionOptedInLearnMore: {
+      // TODO(crbug.com/1335156): Open appropriate URL.
+      break;
+    }
+    case ItemTypeOnDeviceEncryptionOptedInDescription:
+    case ItemTypeOnDeviceEncryptionOptInDescription:
     case ItemTypeSavePasswordsSwitch:
     case ItemTypeManagedSavePasswords: {
       NOTREACHED();
@@ -264,6 +326,77 @@
   return _passwordsInOtherAppsItem;
 }
 
+- (TableViewImageItem*)onDeviceEncryptionOptInDescriptionItem {
+  if (_onDeviceEncryptionOptInDescriptionItem) {
+    return _onDeviceEncryptionOptInDescriptionItem;
+  }
+
+  _onDeviceEncryptionOptInDescriptionItem = [[TableViewImageItem alloc]
+      initWithType:ItemTypeOnDeviceEncryptionOptInDescription];
+  _onDeviceEncryptionOptInDescriptionItem.title =
+      l10n_util::GetNSString(IDS_IOS_PASSWORD_SETTINGS_ON_DEVICE_ENCRYPTION);
+  _onDeviceEncryptionOptInDescriptionItem.detailText = l10n_util::GetNSString(
+      IDS_IOS_PASSWORD_SETTINGS_ON_DEVICE_ENCRYPTION_OPT_IN);
+  _onDeviceEncryptionOptInDescriptionItem.enabled = NO;
+  _onDeviceEncryptionOptInDescriptionItem.accessibilityIdentifier =
+      kPasswordSettingsOnDeviceEncryptionOptInId;
+  _onDeviceEncryptionOptInDescriptionItem.accessibilityTraits |=
+      UIAccessibilityTraitLink;
+  return _onDeviceEncryptionOptInDescriptionItem;
+}
+
+- (TableViewImageItem*)onDeviceEncryptionOptedInDescription {
+  if (_onDeviceEncryptionOptedInDescription) {
+    return _onDeviceEncryptionOptedInDescription;
+  }
+
+  _onDeviceEncryptionOptedInDescription = [[TableViewImageItem alloc]
+      initWithType:ItemTypeOnDeviceEncryptionOptedInDescription];
+  _onDeviceEncryptionOptedInDescription.title =
+      l10n_util::GetNSString(IDS_IOS_PASSWORD_SETTINGS_ON_DEVICE_ENCRYPTION);
+  _onDeviceEncryptionOptedInDescription.detailText = l10n_util::GetNSString(
+      IDS_IOS_PASSWORD_SETTINGS_ON_DEVICE_ENCRYPTION_LEARN_MORE);
+  _onDeviceEncryptionOptedInDescription.enabled = NO;
+  _onDeviceEncryptionOptedInDescription.accessibilityIdentifier =
+      kPasswordSettingsOnDeviceEncryptionOptedInTextId;
+  return _onDeviceEncryptionOptedInDescription;
+}
+
+- (TableViewTextItem*)onDeviceEncryptionOptedInLearnMore {
+  if (_onDeviceEncryptionOptedInLearnMore) {
+    return _onDeviceEncryptionOptedInLearnMore;
+  }
+
+  _onDeviceEncryptionOptedInLearnMore = [[TableViewTextItem alloc]
+      initWithType:ItemTypeOnDeviceEncryptionOptedInLearnMore];
+  _onDeviceEncryptionOptedInLearnMore.text = l10n_util::GetNSString(
+      IDS_IOS_PASSWORD_SETTINGS_ON_DEVICE_ENCRYPTION_OPTED_IN_LEARN_MORE);
+  _onDeviceEncryptionOptedInLearnMore.textColor =
+      [UIColor colorNamed:kBlueColor];
+  _onDeviceEncryptionOptedInLearnMore.accessibilityTraits =
+      UIAccessibilityTraitButton;
+  _onDeviceEncryptionOptedInLearnMore.accessibilityIdentifier =
+      kPasswordSettingsOnDeviceEncryptionLearnMoreId;
+  return _onDeviceEncryptionOptedInLearnMore;
+}
+
+- (TableViewTextItem*)setUpOnDeviceEncryptionItem {
+  if (_setUpOnDeviceEncryptionItem) {
+    return _setUpOnDeviceEncryptionItem;
+  }
+
+  _setUpOnDeviceEncryptionItem =
+      [[TableViewTextItem alloc] initWithType:ItemTypeOnDeviceEncryptionSetUp];
+  _setUpOnDeviceEncryptionItem.text = l10n_util::GetNSString(
+      IDS_IOS_PASSWORD_SETTINGS_ON_DEVICE_ENCRYPTION_SET_UP);
+  _setUpOnDeviceEncryptionItem.textColor = [UIColor colorNamed:kBlueColor];
+  _setUpOnDeviceEncryptionItem.accessibilityTraits = UIAccessibilityTraitButton;
+  _setUpOnDeviceEncryptionItem.accessibilityIdentifier =
+      kPasswordSettingsOnDeviceEncryptionSetUpId;
+  _setUpOnDeviceEncryptionItem.accessibilityTraits |= UIAccessibilityTraitLink;
+  return _setUpOnDeviceEncryptionItem;
+}
+
 // Creates the "Export Passwords..." button. Coloring and enabled/disabled state
 // are handled by `updateExportPasswordsButton`, which should be called as soon
 // as the mediator has provided the necessary state.
@@ -287,7 +420,7 @@
 
   _managedByPolicy = managedByPolicy;
 
-  if (!_isModelLoaded) {
+  if (self.modelLoadStatus == ModelNotLoaded) {
     return;
   }
 
@@ -311,7 +444,7 @@
 
   _savePasswordsEnabled = enabled;
 
-  if (!_isModelLoaded) {
+  if (self.modelLoadStatus == ModelNotLoaded) {
     return;
   }
 
@@ -330,17 +463,32 @@
 
   _passwordsInOtherAppsEnabled = enabled;
 
-  if (!_isModelLoaded) {
+  if (self.modelLoadStatus == ModelNotLoaded) {
     return;
   }
 
   [self updatePasswordsInOtherAppsItem];
 }
 
+- (void)setOnDeviceEncryptionState:
+    (PasswordSettingsOnDeviceEncryptionState)onDeviceEncryptionState {
+  PasswordSettingsOnDeviceEncryptionState oldState = _onDeviceEncryptionState;
+  if (oldState == onDeviceEncryptionState) {
+    return;
+  }
+  _onDeviceEncryptionState = onDeviceEncryptionState;
+
+  if (self.modelLoadStatus == ModelNotLoaded) {
+    return;
+  }
+
+  [self updateOnDeviceEncryptionSectionWithOldState:oldState];
+}
+
 - (void)updateExportPasswordsButton {
   // This can be invoked before the item is ready when passwords are received
   // too early.
-  if (!_isModelLoaded) {
+  if (self.modelLoadStatus == ModelNotLoaded) {
     return;
   }
   if (self.canExportPasswords) {
@@ -408,4 +556,87 @@
   }
 }
 
+// Updates the UI to present the correct elements for the user's current
+// on-device encryption state. `oldState` indicates the currently-displayed UI
+// at the time of invocation and is used to determine if we need to add a new
+// section or clear (and possibly reload) an existing one.
+- (void)updateOnDeviceEncryptionSectionWithOldState:
+    (PasswordSettingsOnDeviceEncryptionState)oldState {
+  // Easy case: the section just needs to be removed.
+  if (self.onDeviceEncryptionState ==
+          PasswordSettingsOnDeviceEncryptionStateNotShown &&
+      [self.tableViewModel
+          hasSectionForSectionIdentifier:SectionIdentifierOnDeviceEncryption]) {
+    NSInteger section = [self.tableViewModel
+        sectionForSectionIdentifier:SectionIdentifierOnDeviceEncryption];
+    [self.tableViewModel
+        removeSectionWithIdentifier:SectionIdentifierOnDeviceEncryption];
+    if (self.modelLoadStatus == ModelLoadComplete) {
+      [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:section]
+                    withRowAnimation:UITableViewRowAnimationAutomatic];
+    }
+    return;
+  }
+
+  // Prepare the section in the model, either by clearing or adding it.
+  if ([self.tableViewModel
+          hasSectionForSectionIdentifier:SectionIdentifierOnDeviceEncryption]) {
+    [self.tableViewModel deleteAllItemsFromSectionWithIdentifier:
+                             SectionIdentifierOnDeviceEncryption];
+  } else {
+    // Find the section that's supposed to be before On-Device Encryption, and
+    // insert after that.
+    NSInteger priorSectionIndex = [self.tableViewModel
+        sectionForSectionIdentifier:SectionIdentifierPasswordsInOtherApps];
+    NSInteger onDeviceEncryptionSectionIndex = priorSectionIndex + 1;
+    [self.tableViewModel
+        insertSectionWithIdentifier:SectionIdentifierOnDeviceEncryption
+                            atIndex:onDeviceEncryptionSectionIndex];
+  }
+
+  // Actually populate the section.
+  switch (self.onDeviceEncryptionState) {
+    case PasswordSettingsOnDeviceEncryptionStateOptedIn: {
+      [self.tableViewModel addItem:self.onDeviceEncryptionOptedInDescription
+           toSectionWithIdentifier:SectionIdentifierOnDeviceEncryption];
+      [self.tableViewModel addItem:self.onDeviceEncryptionOptedInLearnMore
+           toSectionWithIdentifier:SectionIdentifierOnDeviceEncryption];
+      break;
+    }
+    case PasswordSettingsOnDeviceEncryptionStateOfferOptIn: {
+      [self.tableViewModel addItem:self.onDeviceEncryptionOptInDescriptionItem
+           toSectionWithIdentifier:SectionIdentifierOnDeviceEncryption];
+      [self.tableViewModel addItem:self.setUpOnDeviceEncryptionItem
+           toSectionWithIdentifier:SectionIdentifierOnDeviceEncryption];
+      break;
+    }
+    default: {
+      // If the state is PasswordSettingsOnDeviceEncryptionStateNotShown, then
+      // we shouldn't be trying to populate this section. If it's some other
+      // value, then this switch needs to be updated.
+      NOTREACHED();
+      break;
+    }
+  }
+
+  // If the model hasn't finished loading, there's no need to update the table
+  // view.
+  if (self.modelLoadStatus != ModelLoadComplete) {
+    return;
+  }
+
+  NSIndexSet* indexSet = [NSIndexSet
+      indexSetWithIndex:
+          [self.tableViewModel
+              sectionForSectionIdentifier:SectionIdentifierOnDeviceEncryption]];
+
+  if (oldState == PasswordSettingsOnDeviceEncryptionStateNotShown) {
+    [self.tableView insertSections:indexSet
+                  withRowAnimation:UITableViewRowAnimationAutomatic];
+  } else {
+    [self.tableView reloadSections:indexSet
+                  withRowAnimation:UITableViewRowAnimationAutomatic];
+  }
+}
+
 @end
diff --git a/media/gpu/windows/d3d11_copying_texture_wrapper.cc b/media/gpu/windows/d3d11_copying_texture_wrapper.cc
index 22f1ed7a..15162d0 100644
--- a/media/gpu/windows/d3d11_copying_texture_wrapper.cc
+++ b/media/gpu/windows/d3d11_copying_texture_wrapper.cc
@@ -77,18 +77,7 @@
       *previous_input_color_space_ != input_color_space) {
     previous_input_color_space_ = input_color_space;
 
-    // The VideoProcessor doesn't support tone mapping of HLG content, so treat
-    // treat it as gamma 2.2 since HLG is designed to look okay that way.
-    auto adjusted_color_space = input_color_space;
-    if (input_color_space.GetTransferID() == gfx::ColorSpace::TransferID::HLG &&
-        !copy_color_space.IsHDR()) {
-      adjusted_color_space = gfx::ColorSpace(
-          input_color_space.GetPrimaryID(),
-          gfx::ColorSpace::TransferID::GAMMA22, input_color_space.GetMatrixID(),
-          input_color_space.GetRangeID());
-    }
-
-    video_processor_->SetStreamColorSpace(adjusted_color_space);
+    video_processor_->SetStreamColorSpace(input_color_space);
     video_processor_->SetOutputColorSpace(copy_color_space);
   }
 
diff --git a/media/gpu/windows/d3d11_texture_selector.cc b/media/gpu/windows/d3d11_texture_selector.cc
index 6c0beec..cd12c5c 100644
--- a/media/gpu/windows/d3d11_texture_selector.cc
+++ b/media/gpu/windows/d3d11_texture_selector.cc
@@ -122,23 +122,10 @@
         output_dxgi_format = DXGI_FORMAT_B8G8R8A8_UNORM;
         output_pixel_format = PIXEL_FORMAT_ARGB;
 
-        if (input_color_space.GetTransferID() ==
-            gfx::ColorSpace::TransferID::HLG) {
-          // VideoProcessor do good HLG tone mappping between different gpu
-          // vendors if we change input transfer from hlg to Gamma2.2 (Windows
-          // does not support DXGI_COLOR_SPACE_YCBCR_STUDIO_GHLG_TOPLEFT_P2020
-          // well, see: https://crbug.com/1144260#c6) and output color space
-          // to sRGB.
-          output_color_space = gfx::ColorSpace::CreateSRGB();
-        } else {
-          // VideoProcessor do poor PQ tone mapping between different
-          // gpu vendors, no matter if
-          // D3D11_VIDEO_PROCESSOR_FEATURE_CAPS_METADATA_HDR10 feature caps is
-          // supported or not. but gfx::ColorTransform indeed handle PQ content
-          // well, so reset colorspace to use gfx do tone mapping and the result
-          // is pretty good indeed.
-          output_color_space.reset();
-        }
+        // Gfx::ColorTransform now can handle both PQ/HLG content well for
+        // all gpu vendors and also has a better performance when compared with
+        // video processor, reset colorspace to use gfx do tone mapping.
+        output_color_space.reset();
 
         MEDIA_LOG(INFO, media_log) << "D3D11VideoDecoder: Selected ARGB";
       } else if (!needs_texture_copy || supports_fmt(DXGI_FORMAT_P010)) {
diff --git a/net/base/data_url.cc b/net/base/data_url.cc
index 8b6b018..9125ba36e 100644
--- a/net/base/data_url.cc
+++ b/net/base/data_url.cc
@@ -4,14 +4,13 @@
 
 // NOTE: based loosely on mozilla's nsDataChannel.cpp
 
-#include <algorithm>
-
 #include "net/base/data_url.h"
 
 #include "base/base64.h"
 #include "base/containers/cxx20_erase.h"
 #include "base/feature_list.h"
 #include "base/features.h"
+#include "base/ranges/algorithm.h"
 #include "base/strings/escape.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
@@ -64,16 +63,12 @@
     content = content_string;
   }
 
-  base::StringPiece::const_iterator begin = content.begin();
-  base::StringPiece::const_iterator end = content.end();
-
-  base::StringPiece::const_iterator comma = std::find(begin, end, ',');
-
-  if (comma == end)
+  base::StringPiece::const_iterator comma = base::ranges::find(content, ',');
+  if (comma == content.end())
     return false;
 
   std::vector<base::StringPiece> meta_data =
-      base::SplitStringPiece(base::MakeStringPiece(begin, comma), ";",
+      base::SplitStringPiece(base::MakeStringPiece(content.begin(), comma), ";",
                              base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
 
   // These are moved to |mime_type| and |charset| on success.
@@ -134,7 +129,7 @@
     // spaces itself, anyways. Should we just trim leading spaces instead?
     // Allowing random intermediary spaces seems unnecessary.
 
-    auto raw_body = base::MakeStringPiece(comma + 1, end);
+    auto raw_body = base::MakeStringPiece(comma + 1, content.end());
 
     // For base64, we may have url-escaped whitespace which is not part
     // of the data, and should be stripped. Otherwise, the escaped whitespace
diff --git a/net/cert/ev_root_ca_metadata.cc b/net/cert/ev_root_ca_metadata.cc
index 1e033952..343c6483 100644
--- a/net/cert/ev_root_ca_metadata.cc
+++ b/net/cert/ev_root_ca_metadata.cc
@@ -10,8 +10,7 @@
 #include <stdlib.h>
 #endif
 
-#include <algorithm>
-
+#include "base/containers/contains.h"
 #include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/strings/string_piece.h"
@@ -73,9 +72,7 @@
 
 bool EVRootCAMetadata::IsEVPolicyOID(PolicyOID policy_oid) const {
   for (const auto& ev_root : kEvRootCaMetadata) {
-    if (std::find(std::begin(ev_root.policy_oids),
-                  std::end(ev_root.policy_oids),
-                  policy_oid) != std::end(ev_root.policy_oids)) {
+    if (base::Contains(ev_root.policy_oids, policy_oid)) {
       return true;
     }
   }
@@ -100,9 +97,7 @@
   for (const auto& ev_root : kEvRootCaMetadata) {
     if (fingerprint != ev_root.fingerprint)
       continue;
-    return std::find(std::begin(ev_root.policy_oids),
-                     std::end(ev_root.policy_oids),
-                     policy_oid) != std::end(ev_root.policy_oids);
+    return base::Contains(ev_root.policy_oids, policy_oid);
   }
 
   auto it = extra_cas_.find(fingerprint);
diff --git a/net/cert/pki/ocsp.cc b/net/cert/pki/ocsp.cc
index 5dcc82d..dcecebbb 100644
--- a/net/cert/pki/ocsp.cc
+++ b/net/cert/pki/ocsp.cc
@@ -4,9 +4,8 @@
 
 #include "net/cert/pki/ocsp.h"
 
-#include <algorithm>
-
 #include "base/base64.h"
+#include "base/containers/contains.h"
 #include "base/strings/string_util.h"
 #include "base/time/time.h"
 #include "net/cert/asn1_util.h"
@@ -590,14 +589,9 @@
   // part of the extended key usage extension.
   if (!responder_certificate->has_extended_key_usage())
     return false;
-  const std::vector<der::Input>& ekus =
-      responder_certificate->extended_key_usage();
-  if (std::find(ekus.begin(), ekus.end(), der::Input(kOCSPSigning)) ==
-      ekus.end()) {
-    return false;
-  }
 
-  return true;
+  return base::Contains(responder_certificate->extended_key_usage(),
+                        der::Input(kOCSPSigning));
 }
 
 [[nodiscard]] bool VerifyOCSPResponseSignatureGivenCert(
diff --git a/net/cert_net/cert_net_fetcher_url_request.cc b/net/cert_net/cert_net_fetcher_url_request.cc
index 843dcb7..fad520b 100644
--- a/net/cert_net/cert_net_fetcher_url_request.cc
+++ b/net/cert_net/cert_net_fetcher_url_request.cc
@@ -68,6 +68,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/memory/raw_ptr.h"
 #include "base/numerics/safe_math.h"
+#include "base/ranges/algorithm.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -461,7 +462,7 @@
 void Job::DetachRequest(CertNetFetcherURLRequest::RequestCore* request) {
   std::unique_ptr<Job> delete_this;
 
-  auto it = std::find(requests_.begin(), requests_.end(), request);
+  auto it = base::ranges::find(requests_, request);
   DCHECK(it != requests_.end());
   requests_.erase(it);
 
diff --git a/net/cookies/cookie_monster.cc b/net/cookies/cookie_monster.cc
index b81a7fb..74e3e868 100644
--- a/net/cookies/cookie_monster.cc
+++ b/net/cookies/cookie_monster.cc
@@ -64,6 +64,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/task/single_thread_task_runner.h"
+#include "base/threading/thread_checker.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "net/base/features.h"
 #include "net/base/isolation_info.h"
@@ -394,7 +395,7 @@
 // Asynchronous CookieMonster API
 
 void CookieMonster::FlushStore(base::OnceClosure callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   if (initialized_ && store_.get()) {
     store_->Flush(std::move(callback));
@@ -405,7 +406,7 @@
 }
 
 void CookieMonster::SetForceKeepSessionState() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   if (store_)
     store_->SetForceKeepSessionState();
@@ -540,7 +541,7 @@
 void CookieMonster::SetCookieableSchemes(
     const std::vector<std::string>& schemes,
     SetCookieableSchemesCallback callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   // Calls to this method will have no effect if made after a WebView or
   // CookieManager instance has been created.
@@ -555,7 +556,7 @@
 
 // This function must be called before the CookieMonster is used.
 void CookieMonster::SetPersistSessionCookies(bool persist_session_cookies) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(!initialized_);
   net_log_.AddEntryWithBoolParams(
       NetLogEventType::COOKIE_STORE_SESSION_PERSISTENCE, NetLogEventPhase::NONE,
@@ -573,7 +574,7 @@
 }
 
 CookieMonster::~CookieMonster() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   net_log_.EndEvent(NetLogEventType::COOKIE_STORE_ALIVE);
 }
 
@@ -589,7 +590,7 @@
 }
 
 void CookieMonster::GetAllCookies(GetAllCookiesCallback callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   // This function is being called to scrape the cookie list for management UI
   // or similar.  We shouldn't show expired cookies in this list since it will
@@ -641,7 +642,7 @@
     const CookieOptions& options,
     const CookiePartitionKeyCollection& cookie_partition_key_collection,
     GetCookieListCallback callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   CookieAccessResultList included_cookies;
   CookieAccessResultList excluded_cookies;
@@ -684,7 +685,7 @@
 
 void CookieMonster::DeleteAllCreatedInTimeRange(const TimeRange& creation_range,
                                                 DeleteCallback callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   uint32_t num_deleted = 0;
   for (auto it = cookies_.begin(); it != cookies_.end();) {
@@ -751,7 +752,7 @@
 
 void CookieMonster::DeleteCanonicalCookie(const CanonicalCookie& cookie,
                                           DeleteCallback callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   uint32_t result = 0u;
   CookieMap* cookie_map = nullptr;
   PartitionedCookieMap::iterator cookie_partition_it;
@@ -794,7 +795,7 @@
 void CookieMonster::DeleteMatchingCookies(DeletePredicate predicate,
                                           DeletionCause cause,
                                           DeleteCallback callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(predicate);
 
   uint32_t num_deleted = 0;
@@ -836,12 +837,12 @@
 }
 
 void CookieMonster::MarkCookieStoreAsInitialized() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   initialized_ = true;
 }
 
 void CookieMonster::FetchAllCookiesIfNecessary() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (store_.get() && !started_fetching_all_cookies_) {
     started_fetching_all_cookies_ = true;
     FetchAllCookies();
@@ -849,7 +850,7 @@
 }
 
 void CookieMonster::FetchAllCookies() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(store_.get()) << "Store must exist to initialize";
   DCHECK(!finished_fetching_all_cookies_)
       << "All cookies have already been fetched.";
@@ -864,7 +865,7 @@
 void CookieMonster::OnLoaded(
     TimeTicks beginning_time,
     std::vector<std::unique_ptr<CanonicalCookie>> cookies) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   StoreLoadedCookies(std::move(cookies));
   base::UmaHistogramCustomTimes("Cookie.TimeBlockedOnLoad",
                                 base::TimeTicks::Now() - beginning_time,
@@ -877,7 +878,7 @@
 void CookieMonster::OnKeyLoaded(
     const std::string& key,
     std::vector<std::unique_ptr<CanonicalCookie>> cookies) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   StoreLoadedCookies(std::move(cookies));
 
@@ -904,7 +905,7 @@
 
 void CookieMonster::StoreLoadedCookies(
     std::vector<std::unique_ptr<CanonicalCookie>> cookies) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   // Even if a key is expired, insert it so it can be garbage collected,
   // removed, and sync'd.
@@ -974,7 +975,7 @@
 }
 
 void CookieMonster::InvokeQueue() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   // Move all per-key tasks into the global queue, if there are any.  This is
   // protection about a race where the store learns about all cookies loading
@@ -1003,7 +1004,7 @@
 }
 
 void CookieMonster::EnsureCookiesMapIsValid() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   // Iterate through all the of the cookies, grouped by host.
   for (auto next = cookies_.begin(); next != cookies_.end();) {
@@ -1049,7 +1050,7 @@
     CookieMap::iterator begin,
     CookieMap::iterator end,
     absl::optional<PartitionedCookieMap::iterator> cookie_partition_it) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   // Set of cookies ordered by creation time.
   typedef std::multiset<CookieMap::iterator, OrderByCreationTimeDesc> CookieSet;
@@ -1133,7 +1134,7 @@
     const GURL& url,
     CookieMap* cookie_map,
     CookieMonster::PartitionedCookieMap::iterator* partition_it) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   if (!cookie_map)
     cookie_map = &cookies_;
@@ -1171,7 +1172,7 @@
 CookieMonster::FindPartitionedCookiesForRegistryControlledHost(
     const CookiePartitionKey& cookie_partition_key,
     const GURL& url) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   PartitionedCookieMap::iterator it =
       partitioned_cookies_.find(cookie_partition_key);
@@ -1187,7 +1188,7 @@
     std::vector<CanonicalCookie*>* cookie_ptrs,
     CookieAccessResultList* included_cookies,
     CookieAccessResultList* excluded_cookies) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   // Probe to save statistics relatively frequently.  We do it here rather
   // than in the set path as many websites won't set cookies, and we
@@ -1275,7 +1276,7 @@
     base::Time* creation_date_to_inherit,
     CookieInclusionStatus* status,
     absl::optional<PartitionedCookieMap::iterator> cookie_partition_it) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(!status->HasExclusionReason(
       CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE));
   DCHECK(!status->HasExclusionReason(
@@ -1387,7 +1388,7 @@
     bool sync_to_store,
     const CookieAccessResult& access_result,
     bool dispatch_change) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   CanonicalCookie* cc_ptr = cc.get();
 
   net_log_.AddEvent(NetLogEventType::COOKIE_STORE_COOKIE_ADDED,
@@ -1450,7 +1451,7 @@
     const CookieAccessResult& access_result,
     bool dispatch_change) {
   DCHECK(cc->IsPartitioned());
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   CanonicalCookie* cc_ptr = cc.get();
 
   net_log_.AddEvent(NetLogEventType::COOKIE_STORE_COOKIE_ADDED,
@@ -1494,7 +1495,7 @@
     const CookieOptions& options,
     SetCookiesCallback callback,
     absl::optional<CookieAccessResult> cookie_access_result) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   bool delegate_treats_url_as_trustworthy =
       cookie_access_delegate() &&
@@ -1652,7 +1653,7 @@
 
 void CookieMonster::SetAllCookies(CookieList list,
                                   SetCookiesCallback callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   // Nuke the existing store.
   while (!cookies_.empty()) {
@@ -1693,7 +1694,7 @@
 
 void CookieMonster::InternalUpdateCookieAccessTime(CanonicalCookie* cc,
                                                    const Time& current) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   // Based off the Mozilla code.  When a cookie has been accessed recently,
   // don't bother updating its access time again.  This reduces the number of
@@ -1712,7 +1713,7 @@
 void CookieMonster::InternalDeleteCookie(CookieMap::iterator it,
                                          bool sync_to_store,
                                          DeletionCause deletion_cause) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   // Ideally, this would be asserted up where we define kChangeCauseMapping,
   // but DeletionCause's visibility (or lack thereof) forces us to make
@@ -1764,7 +1765,7 @@
     CookieMap::iterator cookie_it,
     bool sync_to_store,
     DeletionCause deletion_cause) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   // Ideally, this would be asserted up where we define kChangeCauseMapping,
   // but DeletionCause's visibility (or lack thereof) forces us to make
@@ -1811,7 +1812,7 @@
 // meaning of the key is different, but that's not visible to this routine).
 size_t CookieMonster::GarbageCollect(const Time& current,
                                      const std::string& key) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   size_t num_deleted = 0;
   Time safe_date(Time::Now() - base::Days(kSafeFromGlobalPurgeDays));
@@ -1979,7 +1980,7 @@
     const base::Time& current,
     const CookiePartitionKey& cookie_partition_key,
     const std::string& key) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   size_t num_deleted = 0;
   PartitionedCookieMap::iterator cookie_partition_it =
@@ -2026,7 +2027,7 @@
                                               size_t to_protect,
                                               size_t purge_goal,
                                               bool protect_secure_cookies) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   // 1. Count number of the cookies at |priority|
   size_t cookies_count_possibly_to_be_deleted = CountCookiesForPossibleDeletion(
@@ -2076,7 +2077,7 @@
 size_t CookieMonster::GarbageCollectExpired(const Time& current,
                                             const CookieMapItPair& itpair,
                                             CookieItVector* cookie_its) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   int num_deleted = 0;
   for (CookieMap::iterator it = itpair.first, end = itpair.second; it != end;) {
@@ -2099,7 +2100,7 @@
     const PartitionedCookieMap::iterator& cookie_partition_it,
     const CookieMapItPair& itpair,
     CookieItVector* cookie_its) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   int num_deleted = 0;
   for (CookieMap::iterator it = itpair.first, end = itpair.second; it != end;) {
@@ -2120,6 +2121,7 @@
 
 void CookieMonster::GarbageCollectAllExpiredPartitionedCookies(
     const Time& current) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   for (auto it = partitioned_cookies_.begin();
        it != partitioned_cookies_.end();) {
     // GarbageCollectExpiredPartitionedCookies calls
@@ -2140,7 +2142,7 @@
     DeletionCause cause,
     CookieItVector::iterator it_begin,
     CookieItVector::iterator it_end) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   for (auto it = it_begin; it != it_end; it++) {
     InternalDeleteCookie((*it), true, cause);
@@ -2155,7 +2157,7 @@
     CookieItVector cookie_its,
     base::Time* earliest_time) {
   DCHECK_LE(purge_goal, cookie_its.size());
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   // Sorts up to *and including* |cookie_its[purge_goal]| (if it exists), so
   // |earliest_time| will be properly assigned even if
@@ -2211,7 +2213,7 @@
 }
 
 bool CookieMonster::HasCookieableScheme(const GURL& url) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   // Make sure the request is on a cookie-able url scheme.
   bool is_cookieable = base::ranges::any_of(
@@ -2243,7 +2245,7 @@
 // in the constructor so that we won't take statistics right after
 // startup, to avoid bias from browsers that are started but not used.
 void CookieMonster::RecordPeriodicStats(const base::Time& current_time) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   const base::TimeDelta kRecordStatisticsIntervalTime(
       base::Seconds(kRecordStatisticsIntervalSeconds));
@@ -2259,6 +2261,7 @@
 }
 
 bool CookieMonster::DoRecordPeriodicStats() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   // These values are all bogus if we have only partially loaded the cookies.
   if (started_fetching_all_cookies_ && !finished_fetching_all_cookies_)
     return false;
@@ -2327,6 +2330,7 @@
     int sample = std::accumulate(
         set.second.begin(), set.second.end(), 0,
         [this](int acc, const net::SchemefulSite& site) -> int {
+          DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
           if (!site.has_registrable_domain_or_host())
             return acc;
           return acc + cookies_.count(site.registrable_domain_or_host());
@@ -2337,7 +2341,7 @@
 }
 
 void CookieMonster::DoCookieCallback(base::OnceClosure callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   MarkCookieStoreAsInitialized();
   FetchAllCookiesIfNecessary();
@@ -2359,6 +2363,7 @@
 void CookieMonster::DoCookieCallbackForHostOrDomain(
     base::OnceClosure callback,
     base::StringPiece host_or_domain) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   MarkCookieStoreAsInitialized();
   FetchAllCookiesIfNecessary();
 
@@ -2448,7 +2453,7 @@
 
 void CookieMonster::OnConvertPartitionedCookiesToUnpartitioned(
     const GURL& url) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   std::vector<CanonicalCookie*> cookie_ptrs_for_site =
       FindCookiesForRegistryControlledHost(url);
diff --git a/net/cookies/cookie_monster.h b/net/cookies/cookie_monster.h
index 385b6c0..59bf8a1 100644
--- a/net/cookies/cookie_monster.h
+++ b/net/cookies/cookie_monster.h
@@ -23,6 +23,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/strings/string_piece.h"
+#include "base/thread_annotations.h"
 #include "base/threading/thread_checker.h"
 #include "base/time/time.h"
 #include "net/base/net_export.h"
@@ -725,15 +726,15 @@
   // Set of keys (eTLD+1's) for which non-expired cookies have
   // been evicted for hitting the per-domain max. The size of this set is
   // histogrammed periodically. The size is limited to |kMaxDomainPurgedKeys|.
-  std::set<std::string> domain_purged_keys_;
+  std::set<std::string> domain_purged_keys_ GUARDED_BY_CONTEXT(thread_checker_);
 
   // The number of distinct keys (eTLD+1's) currently present in the |cookies_|
   // multimap. This is histogrammed periodically.
   size_t num_keys_ = 0u;
 
-  CookieMap cookies_;
+  CookieMap cookies_ GUARDED_BY_CONTEXT(thread_checker_);
 
-  PartitionedCookieMap partitioned_cookies_;
+  PartitionedCookieMap partitioned_cookies_ GUARDED_BY_CONTEXT(thread_checker_);
 
   // Number of distinct partitioned cookies globally. This is used to enforce a
   // global maximum on the number of partitioned cookies.
@@ -758,11 +759,12 @@
   // until all cookies for the associated domain key eTLD+1 are loaded from the
   // backend store.
   std::map<std::string, base::circular_deque<base::OnceClosure>>
-      tasks_pending_for_key_;
+      tasks_pending_for_key_ GUARDED_BY_CONTEXT(thread_checker_);
 
   // Queues tasks that are blocked until all cookies are loaded from the backend
   // store.
-  base::circular_deque<base::OnceClosure> tasks_pending_;
+  base::circular_deque<base::OnceClosure> tasks_pending_
+      GUARDED_BY_CONTEXT(thread_checker_);
 
   // Once a global cookie task has been seen, all per-key tasks must be put in
   // |tasks_pending_| instead of |tasks_pending_for_key_| to ensure a reasonable
@@ -798,7 +800,7 @@
 
   bool first_party_sets_enabled_;
 
-  base::ThreadChecker thread_checker_;
+  THREAD_CHECKER(thread_checker_);
 
   base::WeakPtrFactory<CookieMonster> weak_ptr_factory_{this};
 };
diff --git a/net/dns/address_sorter_posix.cc b/net/dns/address_sorter_posix.cc
index 4e2b0b3..70d4838 100644
--- a/net/dns/address_sorter_posix.cc
+++ b/net/dns/address_sorter_posix.cc
@@ -25,6 +25,8 @@
 #include <algorithm>
 #include <vector>
 
+#include "base/containers/cxx20_erase_vector.h"
+#include "base/containers/unique_ptr_adapters.h"
 #include "base/cxx17_backports.h"
 #include "base/logging.h"
 #include "net/base/ip_endpoint.h"
@@ -38,12 +40,11 @@
 #endif
 
 namespace net {
-
 namespace {
-
 // Address sorting is performed according to RFC3484 with revisions.
 // http://tools.ietf.org/html/draft-ietf-6man-rfc3484bis-06
-// Precedence and label are separate to support override through /etc/gai.conf.
+// Precedence and label are separate to support override through
+// /etc/gai.conf.
 
 // Returns true if |p1| should precede |p2| in the table.
 // Sorts table by decreasing prefix size to allow longest prefix matching.
@@ -138,7 +139,8 @@
     {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF}, 96, 35},
     // 2002::/16 -- 6to4
     {{
-         0x20, 0x02,
+         0x20,
+         0x02,
      },
      16,
      30},
@@ -163,7 +165,8 @@
     {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF}, 96, 4},
     // 2002::/16 -- 6to4
     {{
-         0x20, 0x02,
+         0x20,
+         0x02,
      },
      16,
      2},
@@ -196,7 +199,9 @@
   unsigned precedence;
   unsigned label;
   raw_ptr<const AddressSorterPosix::SourceAddressInfo> src;
+  std::unique_ptr<DatagramClientSocket> socket;
   size_t common_prefix_length;
+  bool failed = false;
 };
 
 // Returns true iff |dst_a| should precede |dst_b| in the address list.
@@ -253,6 +258,84 @@
 
 }  // namespace
 
+class AddressSorterPosix::SortContext {
+ public:
+  SortContext(size_t in_num_endpoints,
+              AddressSorter::CallbackType callback,
+              const AddressSorterPosix* sorter)
+      : num_endpoints_(in_num_endpoints),
+        callback_(std::move(callback)),
+        sorter_(sorter) {}
+  ~SortContext() = default;
+  void DidCompleteConnect(IPEndPoint dest, size_t info_index, int rv) {
+    ++num_completed_;
+    if (rv != OK) {
+      VLOG(1) << "Could not connect to " << dest.ToStringWithoutPort()
+              << " reason " << rv;
+      sort_list_[info_index]->failed = true;
+      MaybeFinishSort();
+      return;
+    }
+    // Filter out unusable destinations.
+    IPEndPoint src;
+    rv = sort_list_[info_index]->socket->GetLocalAddress(&src);
+    if (rv != OK) {
+      LOG(WARNING) << "Could not get local address for "
+                   << dest.ToStringWithoutPort() << " reason " << rv;
+      sort_list_[info_index]->failed = true;
+      MaybeFinishSort();
+      return;
+    }
+
+    AddressSorterPosix::SourceAddressInfo& src_info =
+        sorter_->source_map_[src.address()];
+    if (src_info.scope == AddressSorterPosix::SCOPE_UNDEFINED) {
+      // If |source_info_| is out of date, |src| might be missing, but we still
+      // want to sort, even though the HostCache will be cleared soon.
+      sorter_->FillPolicy(src.address(), &src_info);
+    }
+    sort_list_[info_index]->src = &src_info;
+
+    if (sort_list_[info_index]->endpoint.address().size() ==
+        src.address().size()) {
+      sort_list_[info_index]->common_prefix_length = std::min(
+          CommonPrefixLength(sort_list_[info_index]->endpoint.address(),
+                             src.address()),
+          sort_list_[info_index]->src->prefix_length);
+    }
+    MaybeFinishSort();
+  }
+
+  std::vector<std::unique_ptr<DestinationInfo>>& sort_list() {
+    return sort_list_;
+  }
+
+ private:
+  void MaybeFinishSort() {
+    // Sort the list of endpoints only after each Connect call has been made.
+    if (num_completed_ != num_endpoints_) {
+      return;
+    }
+    base::EraseIf(sort_list_, [](auto& element) { return element->failed; });
+    std::stable_sort(sort_list_.begin(), sort_list_.end(), CompareDestinations);
+
+    std::vector<IPEndPoint> sorted_result;
+    for (const auto& info : sort_list_)
+      sorted_result.push_back(info->endpoint);
+
+    CallbackType callback = std::move(callback_);
+    sorter_->FinishedSort(this);  // deletes this
+    std::move(callback).Run(true, std::move(sorted_result));
+  }
+
+  const size_t num_endpoints_;
+  size_t num_completed_ = 0;
+  std::vector<std::unique_ptr<DestinationInfo>> sort_list_;
+  AddressSorter::CallbackType callback_;
+
+  const AddressSorterPosix* sorter_;
+};
+
 AddressSorterPosix::AddressSorterPosix(ClientSocketFactory* socket_factory)
     : socket_factory_(socket_factory),
       precedence_table_(LoadPolicy(kDefaultPrecedenceTable,
@@ -273,8 +356,9 @@
 void AddressSorterPosix::Sort(const std::vector<IPEndPoint>& endpoints,
                               CallbackType callback) const {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  std::vector<std::unique_ptr<DestinationInfo>> sort_list;
-
+  sort_contexts_.insert(std::make_unique<SortContext>(
+      endpoints.size(), std::move(callback), this));
+  auto* sort_context = sort_contexts_.rbegin()->get();
   for (const IPEndPoint& endpoint : endpoints) {
     auto info = std::make_unique<DestinationInfo>();
     info->endpoint = endpoint;
@@ -284,53 +368,25 @@
     info->label = GetPolicyValue(label_table_, info->endpoint.address());
 
     // Each socket can only be bound once.
-    std::unique_ptr<DatagramClientSocket> socket(
-        socket_factory_->CreateDatagramClientSocket(
-            DatagramSocket::DEFAULT_BIND, nullptr /* NetLog */,
-            NetLogSource()));
-
+    info->socket = socket_factory_->CreateDatagramClientSocket(
+        DatagramSocket::DEFAULT_BIND, nullptr /* NetLog */, NetLogSource());
     IPEndPoint dest = info->endpoint;
     // Even though no packets are sent, cannot use port 0 in Connect.
-    if (dest.port() == 0)
+    if (dest.port() == 0) {
       dest = IPEndPoint(dest.address(), /*port=*/80);
-    int rv = socket->Connect(dest);
-    if (rv != OK) {
-      VLOG(1) << "Could not connect to " << dest.ToStringWithoutPort()
-              << " reason " << rv;
-      continue;
     }
-    // Filter out unusable destinations.
-    IPEndPoint src;
-    rv = socket->GetLocalAddress(&src);
-    if (rv != OK) {
-      LOG(WARNING) << "Could not get local address for "
-                   << dest.ToStringWithoutPort() << " reason " << rv;
-      continue;
+    auto* info_ptr = info.get();
+    sort_context->sort_list().push_back(std::move(info));
+    size_t info_index = sort_context->sort_list().size() - 1;
+    // Destroying a SortContext destroys the underlying socket.
+    int rv = info_ptr->socket->ConnectAsync(
+        dest,
+        base::BindOnce(&AddressSorterPosix::SortContext::DidCompleteConnect,
+                       base::Unretained(sort_context), dest, info_index));
+    if (rv != ERR_IO_PENDING) {
+      sort_context->DidCompleteConnect(dest, info_index, rv);
     }
-
-    SourceAddressInfo& src_info = source_map_[src.address()];
-    if (src_info.scope == SCOPE_UNDEFINED) {
-      // If |source_info_| is out of date, |src| might be missing, but we still
-      // want to sort, even though the HostCache will be cleared soon.
-      FillPolicy(src.address(), &src_info);
-    }
-    info->src = &src_info;
-
-    if (info->endpoint.address().size() == src.address().size()) {
-      info->common_prefix_length =
-          std::min(CommonPrefixLength(info->endpoint.address(), src.address()),
-                   info->src->prefix_length);
-    }
-    sort_list.push_back(std::move(info));
   }
-
-  std::stable_sort(sort_list.begin(), sort_list.end(), CompareDestinations);
-
-  std::vector<IPEndPoint> sorted_result;
-  for (const auto& info : sort_list)
-    sorted_result.push_back(info->endpoint);
-
-  std::move(callback).Run(true, std::move(sorted_result));
 }
 
 void AddressSorterPosix::OnIPAddressChanged() {
@@ -409,6 +465,11 @@
   info->label = GetPolicyValue(label_table_, address);
 }
 
+void AddressSorterPosix::FinishedSort(SortContext* sort_context) const {
+  auto it = sort_contexts_.find(sort_context);
+  sort_contexts_.erase(it);
+}
+
 // static
 std::unique_ptr<AddressSorter> AddressSorter::CreateAddressSorter() {
   return std::make_unique<AddressSorterPosix>(
diff --git a/net/dns/address_sorter_posix.h b/net/dns/address_sorter_posix.h
index a4a02b2..07af528f 100644
--- a/net/dns/address_sorter_posix.h
+++ b/net/dns/address_sorter_posix.h
@@ -8,12 +8,15 @@
 #include <map>
 #include <vector>
 
+#include "base/containers/unique_ptr_adapters.h"
 #include "base/memory/raw_ptr.h"
+#include "base/memory/ref_counted.h"
 #include "base/threading/thread_checker.h"
 #include "net/base/ip_address.h"
 #include "net/base/net_export.h"
 #include "net/base/network_change_notifier.h"
 #include "net/dns/address_sorter.h"
+#include "net/socket/datagram_client_socket.h"
 
 namespace net {
 
@@ -71,13 +74,15 @@
 
  private:
   friend class AddressSorterPosixTest;
+  class SortContext;
 
   // NetworkChangeNotifier::IPAddressObserver:
   void OnIPAddressChanged() override;
-
   // Fills |info| with values for |address| from policy tables.
   void FillPolicy(const IPAddress& address, SourceAddressInfo* info) const;
 
+  void FinishedSort(SortContext* sort_context) const;
+
   // Mutable to allow using default values for source addresses which were not
   // found in most recent OnIPAddressChanged.
   mutable SourceAddressMap source_map_;
@@ -87,6 +92,13 @@
   PolicyTable label_table_;
   PolicyTable ipv4_scope_table_;
 
+  // SortContext stores data for an outstanding Sort() that is completing
+  // asynchronously. Mutable to allow pushing a new SortContext when Sort is
+  // called. Since Sort can be called multiple times, a container is necessary
+  // to track different SortContexts.
+  mutable std::set<std::unique_ptr<SortContext>, base::UniquePtrComparator>
+      sort_contexts_;
+
   THREAD_CHECKER(thread_checker_);
 };
 
diff --git a/net/dns/address_sorter_posix_unittest.cc b/net/dns/address_sorter_posix_unittest.cc
index 164c931..a2e8f26 100644
--- a/net/dns/address_sorter_posix_unittest.cc
+++ b/net/dns/address_sorter_posix_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/check_op.h"
 #include "base/memory/raw_ptr.h"
 #include "base/notreached.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "net/base/ip_address.h"
 #include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
@@ -42,8 +43,10 @@
 // A mock socket which binds to source address according to AddressMapping.
 class TestUDPClientSocket : public DatagramClientSocket {
  public:
-  explicit TestUDPClientSocket(const AddressMapping* mapping)
-      : mapping_(mapping) {}
+  enum class ConnectMode { kSynchronous, kAsynchronous, kAsynchronousManual };
+  explicit TestUDPClientSocket(const AddressMapping* mapping,
+                               ConnectMode connect_mode)
+      : mapping_(mapping), connect_mode_(connect_mode) {}
 
   TestUDPClientSocket(const TestUDPClientSocket&) = delete;
   TestUDPClientSocket& operator=(const TestUDPClientSocket&) = delete;
@@ -95,8 +98,19 @@
 
   int ConnectAsync(const IPEndPoint& address,
                    CompletionOnceCallback callback) override {
-    NOTIMPLEMENTED();
-    return ERR_NOT_IMPLEMENTED;
+    DCHECK(callback);
+    int rv = Connect(address);
+    finish_connect_callback_ =
+        base::BindOnce(&TestUDPClientSocket::RunConnectCallback,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback), rv);
+    if (connect_mode_ == ConnectMode::kAsynchronous) {
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, std::move(finish_connect_callback_));
+      return ERR_IO_PENDING;
+    } else if (connect_mode_ == ConnectMode::kAsynchronousManual) {
+      return ERR_IO_PENDING;
+    }
+    return rv;
   }
 
   int ConnectUsingNetworkAsync(handles::NetworkHandle network,
@@ -132,11 +146,20 @@
 
   const NetLogWithSource& NetLog() const override { return net_log_; }
 
+  void FinishConnect() { std::move(finish_connect_callback_).Run(); }
+
  private:
+  void RunConnectCallback(CompletionOnceCallback callback, int rv) {
+    std::move(callback).Run(rv);
+  }
   NetLogWithSource net_log_;
   raw_ptr<const AddressMapping> mapping_;
   bool connected_ = false;
   IPEndPoint local_endpoint_;
+  ConnectMode connect_mode_;
+  base::OnceClosure finish_connect_callback_;
+
+  base::WeakPtrFactory<TestUDPClientSocket> weak_ptr_factory_{this};
 };
 
 // Creates TestUDPClientSockets and maintains an AddressMapping.
@@ -153,7 +176,12 @@
       DatagramSocket::BindType,
       NetLog*,
       const NetLogSource&) override {
-    return std::make_unique<TestUDPClientSocket>(&mapping_);
+    auto new_socket =
+        std::make_unique<TestUDPClientSocket>(&mapping_, connect_mode_);
+    if (socket_create_callback_) {
+      socket_create_callback_.Run(new_socket.get());
+    }
+    return new_socket;
   }
   std::unique_ptr<TransportClientSocket> CreateTransportClientSocket(
       const AddressList&,
@@ -175,16 +203,28 @@
   void AddMapping(const IPAddress& dst, const IPAddress& src) {
     mapping_[dst] = src;
   }
+  void SetConnectMode(TestUDPClientSocket::ConnectMode connect_mode) {
+    connect_mode_ = connect_mode;
+  }
+  void SetSocketCreateCallback(
+      base::RepeatingCallback<void(TestUDPClientSocket*)>
+          socket_create_callback) {
+    socket_create_callback_ = std::move(socket_create_callback);
+  }
 
  private:
   AddressMapping mapping_;
+  TestUDPClientSocket::ConnectMode connect_mode_;
+  base::RepeatingCallback<void(TestUDPClientSocket*)> socket_create_callback_;
 };
 
-void OnSortComplete(std::vector<IPEndPoint>* sorted_buf,
+void OnSortComplete(bool& completed,
+                    std::vector<IPEndPoint>* sorted_buf,
                     CompletionOnceCallback callback,
                     bool success,
                     std::vector<IPEndPoint> sorted) {
   EXPECT_TRUE(success);
+  completed = true;
   if (success)
     *sorted_buf = std::move(sorted);
   std::move(callback).Run(OK);
@@ -196,21 +236,49 @@
 // constructor of AddressSorterPosix.
 class AddressSorterPosixTest : public TestWithTaskEnvironment {
  protected:
-  AddressSorterPosixTest() : sorter_(&socket_factory_) {}
+  AddressSorterPosixTest()
+      : sorter_(std::make_unique<AddressSorterPosix>(&socket_factory_)) {}
 
   void AddMapping(const std::string& dst, const std::string& src) {
     socket_factory_.AddMapping(ParseIP(dst), ParseIP(src));
   }
 
+  void SetSocketCreateCallback(
+      base::RepeatingCallback<void(TestUDPClientSocket*)>
+          socket_create_callback) {
+    socket_factory_.SetSocketCreateCallback(std::move(socket_create_callback));
+  }
+
+  void SetConnectMode(TestUDPClientSocket::ConnectMode connect_mode) {
+    socket_factory_.SetConnectMode(connect_mode);
+  }
+
   AddressSorterPosix::SourceAddressInfo* GetSourceInfo(
       const std::string& addr) {
     IPAddress address = ParseIP(addr);
-    AddressSorterPosix::SourceAddressInfo* info = &sorter_.source_map_[address];
+    AddressSorterPosix::SourceAddressInfo* info =
+        &sorter_->source_map_[address];
     if (info->scope == AddressSorterPosix::SCOPE_UNDEFINED)
-      sorter_.FillPolicy(address, info);
+      sorter_->FillPolicy(address, info);
     return info;
   }
 
+  TestSocketFactory socket_factory_;
+  std::unique_ptr<AddressSorterPosix> sorter_;
+  bool completed_ = false;
+
+ private:
+  friend class AddressSorterPosixSyncOrAsyncTest;
+};
+
+// Parameterized subclass of AddressSorterPosixTest. Necessary because not every
+// test needs to be parameterized.
+class AddressSorterPosixSyncOrAsyncTest
+    : public AddressSorterPosixTest,
+      public testing::WithParamInterface<TestUDPClientSocket::ConnectMode> {
+ protected:
+  AddressSorterPosixSyncOrAsyncTest() { SetConnectMode(GetParam()); }
+
   // Verify that NULL-terminated |addresses| matches (-1)-terminated |order|
   // after sorting.
   void Verify(const char* const addresses[], const int order[]) {
@@ -222,8 +290,9 @@
 
     std::vector<IPEndPoint> sorted;
     TestCompletionCallback callback;
-    sorter_.Sort(endpoints,
-                 base::BindOnce(&OnSortComplete, &sorted, callback.callback()));
+    sorter_->Sort(endpoints,
+                  base::BindOnce(&OnSortComplete, std::ref(completed_), &sorted,
+                                 callback.callback()));
     callback.WaitForResult();
 
     for (size_t i = 0; (i < sorted.size()) || (order[i] >= 0); ++i) {
@@ -234,14 +303,18 @@
           << "  Actual: " << actual.ToString() << "\n"
           << "Expected: " << expected.ToString();
     }
+    EXPECT_TRUE(completed_);
   }
-
-  TestSocketFactory socket_factory_;
-  AddressSorterPosix sorter_;
 };
 
+INSTANTIATE_TEST_SUITE_P(
+    AddressSorterPosix,
+    AddressSorterPosixSyncOrAsyncTest,
+    ::testing::Values(TestUDPClientSocket::ConnectMode::kSynchronous,
+                      TestUDPClientSocket::ConnectMode::kAsynchronous));
+
 // Rule 1: Avoid unusable destinations.
-TEST_F(AddressSorterPosixTest, Rule1) {
+TEST_P(AddressSorterPosixSyncOrAsyncTest, Rule1) {
   AddMapping("10.0.0.231", "10.0.0.1");
   const char* const addresses[] = {"::1", "10.0.0.231", "127.0.0.1", nullptr};
   const int order[] = { 1, -1 };
@@ -249,7 +322,7 @@
 }
 
 // Rule 2: Prefer matching scope.
-TEST_F(AddressSorterPosixTest, Rule2) {
+TEST_P(AddressSorterPosixSyncOrAsyncTest, Rule2) {
   AddMapping("3002::1", "4000::10");      // matching global
   AddMapping("ff32::1", "fe81::10");      // matching link-local
   AddMapping("fec1::1", "fec1::10");      // matching node-local
@@ -267,7 +340,7 @@
 }
 
 // Rule 3: Avoid deprecated addresses.
-TEST_F(AddressSorterPosixTest, Rule3) {
+TEST_P(AddressSorterPosixSyncOrAsyncTest, Rule3) {
   // Matching scope.
   AddMapping("3002::1", "4000::10");
   GetSourceInfo("4000::10")->deprecated = true;
@@ -278,7 +351,7 @@
 }
 
 // Rule 4: Prefer home addresses.
-TEST_F(AddressSorterPosixTest, Rule4) {
+TEST_P(AddressSorterPosixSyncOrAsyncTest, Rule4) {
   AddMapping("3002::1", "4000::10");
   AddMapping("3002::2", "4000::20");
   GetSourceInfo("4000::20")->home = true;
@@ -288,7 +361,7 @@
 }
 
 // Rule 5: Prefer matching label.
-TEST_F(AddressSorterPosixTest, Rule5) {
+TEST_P(AddressSorterPosixSyncOrAsyncTest, Rule5) {
   AddMapping("::1", "::1");                       // matching loopback
   AddMapping("::ffff:1234:1", "::ffff:1234:10");  // matching IPv4-mapped
   AddMapping("2001::1", "::ffff:1234:10");        // Teredo vs. IPv4-mapped
@@ -305,7 +378,7 @@
 }
 
 // Rule 6: Prefer higher precedence.
-TEST_F(AddressSorterPosixTest, Rule6) {
+TEST_P(AddressSorterPosixSyncOrAsyncTest, Rule6) {
   AddMapping("::1", "::1");                       // loopback
   AddMapping("ff32::1", "fe81::10");              // multicast
   AddMapping("::ffff:1234:1", "::ffff:1234:10");  // IPv4-mapped
@@ -317,7 +390,7 @@
 }
 
 // Rule 7: Prefer native transport.
-TEST_F(AddressSorterPosixTest, Rule7) {
+TEST_P(AddressSorterPosixSyncOrAsyncTest, Rule7) {
   AddMapping("3002::1", "4000::10");
   AddMapping("3002::2", "4000::20");
   GetSourceInfo("4000::20")->native = true;
@@ -327,7 +400,7 @@
 }
 
 // Rule 8: Prefer smaller scope.
-TEST_F(AddressSorterPosixTest, Rule8) {
+TEST_P(AddressSorterPosixSyncOrAsyncTest, Rule8) {
   // Matching scope. Should precede the others by Rule 2.
   AddMapping("fe81::1", "fe81::10");  // link-local
   AddMapping("3000::1", "4000::10");  // global
@@ -342,7 +415,7 @@
 }
 
 // Rule 9: Use longest matching prefix.
-TEST_F(AddressSorterPosixTest, Rule9) {
+TEST_P(AddressSorterPosixSyncOrAsyncTest, Rule9) {
   AddMapping("3000::1", "3000:ffff::10");  // 16 bit match
   GetSourceInfo("3000:ffff::10")->prefix_length = 16;
   AddMapping("4000::1", "4000::10");       // 123 bit match, limited to 15
@@ -356,7 +429,7 @@
 }
 
 // Rule 10: Leave the order unchanged.
-TEST_F(AddressSorterPosixTest, Rule10) {
+TEST_P(AddressSorterPosixSyncOrAsyncTest, Rule10) {
   AddMapping("4000::1", "4000::10");
   AddMapping("4000::2", "4000::10");
   AddMapping("4000::3", "4000::10");
@@ -365,7 +438,7 @@
   Verify(addresses, order);
 }
 
-TEST_F(AddressSorterPosixTest, MultipleRules) {
+TEST_P(AddressSorterPosixSyncOrAsyncTest, MultipleRules) {
   AddMapping("::1", "::1");           // loopback
   AddMapping("ff32::1", "fe81::10");  // link-local multicast
   AddMapping("ff3e::1", "4000::10");  // global multicast
@@ -378,7 +451,7 @@
   Verify(addresses, order);
 }
 
-TEST_F(AddressSorterPosixTest, InputPortsAreMaintained) {
+TEST_P(AddressSorterPosixSyncOrAsyncTest, InputPortsAreMaintained) {
   AddMapping("::1", "::1");
   AddMapping("::2", "::2");
   AddMapping("::3", "::3");
@@ -390,11 +463,67 @@
   std::vector<IPEndPoint> input = {endpoint1, endpoint2, endpoint3};
   std::vector<IPEndPoint> sorted;
   TestCompletionCallback callback;
-  sorter_.Sort(input,
-               base::BindOnce(&OnSortComplete, &sorted, callback.callback()));
+  sorter_->Sort(input, base::BindOnce(&OnSortComplete, std::ref(completed_),
+                                      &sorted, callback.callback()));
   callback.WaitForResult();
 
   EXPECT_THAT(sorted, testing::ElementsAre(endpoint1, endpoint2, endpoint3));
 }
 
+TEST_P(AddressSorterPosixSyncOrAsyncTest, AddressSorterPosixDestroyed) {
+  AddMapping("::1", "::1");
+  AddMapping("::2", "::2");
+  AddMapping("::3", "::3");
+
+  IPEndPoint endpoint1(ParseIP("::1"), /*port=*/111);
+  IPEndPoint endpoint2(ParseIP("::2"), /*port=*/222);
+  IPEndPoint endpoint3(ParseIP("::3"), /*port=*/333);
+
+  std::vector<IPEndPoint> input = {endpoint1, endpoint2, endpoint3};
+  std::vector<IPEndPoint> sorted;
+  TestCompletionCallback callback;
+  sorter_->Sort(input, base::BindOnce(&OnSortComplete, std::ref(completed_),
+                                      &sorted, callback.callback()));
+  sorter_.reset();
+  base::RunLoop().RunUntilIdle();
+
+  TestUDPClientSocket::ConnectMode connect_mode = GetParam();
+  if (connect_mode == TestUDPClientSocket::ConnectMode::kAsynchronous) {
+    EXPECT_FALSE(completed_);
+  } else {
+    EXPECT_TRUE(completed_);
+  }
+}
+
+TEST_F(AddressSorterPosixTest, RandomAsyncSocketOrder) {
+  SetConnectMode(TestUDPClientSocket::ConnectMode::kAsynchronousManual);
+  std::vector<TestUDPClientSocket*> created_sockets;
+  SetSocketCreateCallback(base::BindRepeating(
+      [](std::vector<TestUDPClientSocket*>& created_sockets,
+         TestUDPClientSocket* socket) { created_sockets.push_back(socket); },
+      std::ref(created_sockets)));
+
+  AddMapping("::1", "::1");
+  AddMapping("::2", "::2");
+  AddMapping("::3", "::3");
+
+  IPEndPoint endpoint1(ParseIP("::1"), /*port=*/111);
+  IPEndPoint endpoint2(ParseIP("::2"), /*port=*/222);
+  IPEndPoint endpoint3(ParseIP("::3"), /*port=*/333);
+
+  std::vector<IPEndPoint> input = {endpoint1, endpoint2, endpoint3};
+  std::vector<IPEndPoint> sorted;
+  TestCompletionCallback callback;
+  sorter_->Sort(input, base::BindOnce(&OnSortComplete, std::ref(completed_),
+                                      &sorted, callback.callback()));
+
+  ASSERT_EQ(created_sockets.size(), 3u);
+  created_sockets[1]->FinishConnect();
+  created_sockets[2]->FinishConnect();
+  created_sockets[0]->FinishConnect();
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(completed_);
+}
+
 }  // namespace net
diff --git a/net/dns/host_resolver_manager.cc b/net/dns/host_resolver_manager.cc
index 3fb54fb..38894d0 100644
--- a/net/dns/host_resolver_manager.cc
+++ b/net/dns/host_resolver_manager.cc
@@ -4,7 +4,6 @@
 
 #include "net/dns/host_resolver_manager.h"
 
-#include <algorithm>
 #include <cmath>
 #include <iterator>
 #include <limits>
@@ -1990,8 +1989,7 @@
   // available and |fallback_only| is false, a job that is currently running an
   // insecure DnsTask will be completed with |error|.
   void AbortInsecureDnsTask(int error, bool fallback_only) {
-    bool has_system_fallback = std::find(tasks_.begin(), tasks_.end(),
-                                         TaskType::SYSTEM) != tasks_.end();
+    bool has_system_fallback = base::Contains(tasks_, TaskType::SYSTEM);
     if (has_system_fallback) {
       for (auto it = tasks_.begin(); it != tasks_.end();) {
         if (*it == TaskType::DNS)
diff --git a/net/dns/https_record_rdata.cc b/net/dns/https_record_rdata.cc
index cc52bae..58693d7ad 100644
--- a/net/dns/https_record_rdata.cc
+++ b/net/dns/https_record_rdata.cc
@@ -6,7 +6,6 @@
 
 #include <stdint.h>
 
-#include <algorithm>
 #include <map>
 #include <memory>
 #include <set>
@@ -16,6 +15,7 @@
 
 #include "base/big_endian.h"
 #include "base/check.h"
+#include "base/containers/contains.h"
 #include "base/dcheck_is_on.h"
 #include "base/immediate_crash.h"
 #include "base/memory/ptr_util.h"
@@ -447,8 +447,7 @@
 // static
 bool ServiceFormHttpsRecordRdata::IsSupportedKey(uint16_t key) {
 #if DCHECK_IS_ON()
-  return std::find(std::begin(kSupportedKeys), std::end(kSupportedKeys), key) !=
-         std::end(kSupportedKeys);
+  return base::Contains(kSupportedKeys, key);
 #else
   // Only intended for DCHECKs.
   IMMEDIATE_CRASH();
diff --git a/net/http/http_cache.cc b/net/http/http_cache.cc
index 8d5fc578..2bd3f6a 100644
--- a/net/http/http_cache.cc
+++ b/net/http/http_cache.cc
@@ -4,7 +4,6 @@
 
 #include "net/http/http_cache.h"
 
-#include <algorithm>
 #include <utility>
 
 #include "base/bind.h"
@@ -21,6 +20,7 @@
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/pickle.h"
+#include "base/ranges/algorithm.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
@@ -936,8 +936,7 @@
     entry->GetEntry()->CancelSparseIO();
 
   // Transaction is waiting in the done_headers_queue.
-  auto it = std::find(entry->done_headers_queue.begin(),
-                      entry->done_headers_queue.end(), transaction);
+  auto it = base::ranges::find(entry->done_headers_queue, transaction);
   if (it != entry->done_headers_queue.end()) {
     entry->done_headers_queue.erase(it);
 
diff --git a/net/http/http_response_headers.cc b/net/http/http_response_headers.cc
index 5fed786..17d9b46c 100644
--- a/net/http/http_response_headers.cc
+++ b/net/http/http_response_headers.cc
@@ -19,6 +19,7 @@
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/pickle.h"
+#include "base/ranges/algorithm.h"
 #include "base/strings/escape.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
@@ -460,8 +461,7 @@
 
   // ParseStatusLine adds a normalized status line to raw_headers_
   std::string::const_iterator line_begin = raw_input.begin();
-  std::string::const_iterator line_end =
-      std::find(line_begin, raw_input.end(), '\0');
+  std::string::const_iterator line_end = base::ranges::find(raw_input, '\0');
   // has_headers = true, if there is any data following the status line.
   // Used by ParseStatusLine() to decide if a HTTP/0.9 is really a HTTP/1.0.
   bool has_headers =
@@ -545,10 +545,9 @@
   // '<http_version> SP <response_code>' or
   // '<http_version> SP <response_code> SP <status_text>'.
   std::string status_text = GetStatusLine();
-  std::string::const_iterator begin = status_text.begin();
-  std::string::const_iterator end = status_text.end();
   // Seek to beginning of <response_code>.
-  begin = std::find(begin, end, ' ');
+  std::string::const_iterator begin = base::ranges::find(status_text, ' ');
+  std::string::const_iterator end = status_text.end();
   CHECK(begin != end);
   ++begin;
   CHECK(begin != end);
diff --git a/net/http/http_stream_factory_job_controller_unittest.cc b/net/http/http_stream_factory_job_controller_unittest.cc
index ca66994..c1aa49f1 100644
--- a/net/http/http_stream_factory_job_controller_unittest.cc
+++ b/net/http/http_stream_factory_job_controller_unittest.cc
@@ -4,13 +4,13 @@
 
 #include "net/http/http_stream_factory_job_controller.h"
 
-#include <algorithm>
 #include <list>
 #include <memory>
 #include <string>
 #include <utility>
 #include <vector>
 
+#include "base/containers/contains.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/scoped_refptr.h"
@@ -3698,8 +3698,7 @@
   quic::ParsedQuicVersion unsupported_version_2 =
       quic::ParsedQuicVersion::Unsupported();
   for (const quic::ParsedQuicVersion& version : quic::AllSupportedVersions()) {
-    if (std::find(supported_versions.begin(), supported_versions.end(),
-                  version) != supported_versions.end())
+    if (base::Contains(supported_versions, version))
       continue;
     if (unsupported_version_1 == quic::ParsedQuicVersion::Unsupported()) {
       unsupported_version_1 = version;
diff --git a/net/log/net_log.cc b/net/log/net_log.cc
index b46b4b6..53177340 100644
--- a/net/log/net_log.cc
+++ b/net/log/net_log.cc
@@ -8,6 +8,7 @@
 #include "base/containers/contains.h"
 #include "base/no_destructor.h"
 #include "base/notreached.h"
+#include "base/ranges/algorithm.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/values.h"
 #include "net/log/net_log_values.h"
@@ -104,7 +105,7 @@
 
   DCHECK_EQ(this, observer->net_log_);
 
-  auto it = std::find(observers_.begin(), observers_.end(), observer);
+  auto it = base::ranges::find(observers_, observer);
   DCHECK(it != observers_.end());
   observers_.erase(it);
 
@@ -132,8 +133,7 @@
   DCHECK_EQ(this, observer->net_log_);
   DCHECK(HasCaptureModeObserver(observer));
 
-  auto it = std::find(capture_mode_observers_.begin(),
-                      capture_mode_observers_.end(), observer);
+  auto it = base::ranges::find(capture_mode_observers_, observer);
   DCHECK(it != capture_mode_observers_.end());
   capture_mode_observers_.erase(it);
 
diff --git a/net/proxy_resolution/mock_proxy_resolver.cc b/net/proxy_resolution/mock_proxy_resolver.cc
index 55eeb20..13710b1 100644
--- a/net/proxy_resolution/mock_proxy_resolver.cc
+++ b/net/proxy_resolution/mock_proxy_resolver.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/check.h"
+#include "base/ranges/algorithm.h"
 
 namespace net {
 
@@ -64,7 +65,7 @@
 }
 
 void MockAsyncProxyResolver::AddCancelledJob(std::unique_ptr<Job> job) {
-  auto it = std::find(pending_jobs_.begin(), pending_jobs_.end(), job.get());
+  auto it = base::ranges::find(pending_jobs_, job.get());
   // Because this is called always when RequestImpl is destructed,
   // we need to check if it is still in pending jobs.
   if (it != pending_jobs_.end()) {
@@ -75,7 +76,7 @@
 
 void MockAsyncProxyResolver::RemovePendingJob(Job* job) {
   DCHECK(job);
-  auto it = std::find(pending_jobs_.begin(), pending_jobs_.end(), job);
+  auto it = base::ranges::find(pending_jobs_, job);
   DCHECK(it != pending_jobs_.end());
   pending_jobs_.erase(it);
 }
@@ -155,8 +156,7 @@
 }
 
 void MockAsyncProxyResolverFactory::RemovePendingRequest(Request* request) {
-  auto it =
-      std::find(pending_requests_.begin(), pending_requests_.end(), request);
+  auto it = base::ranges::find(pending_requests_, request);
   DCHECK(it != pending_requests_.end());
   pending_requests_.erase(it);
 }
diff --git a/net/quic/quic_chromium_client_session.cc b/net/quic/quic_chromium_client_session.cc
index 68beb48..1232719c 100644
--- a/net/quic/quic_chromium_client_session.cc
+++ b/net/quic/quic_chromium_client_session.cc
@@ -18,6 +18,7 @@
 #include "base/metrics/sparse_histogram.h"
 #include "base/no_destructor.h"
 #include "base/observer_list.h"
+#include "base/ranges/algorithm.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/task/single_thread_task_runner.h"
@@ -1314,8 +1315,7 @@
 void QuicChromiumClientSession::CancelRequest(StreamRequest* request) {
   // Remove |request| from the queue while preserving the order of the
   // other elements.
-  auto it =
-      std::find(stream_requests_.begin(), stream_requests_.end(), request);
+  auto it = base::ranges::find(stream_requests_, request);
   if (it != stream_requests_.end()) {
     it = stream_requests_.erase(it);
   }
diff --git a/net/quic/quic_context.cc b/net/quic/quic_context.cc
index c4921e5..481130a51 100644
--- a/net/quic/quic_context.cc
+++ b/net/quic/quic_context.cc
@@ -4,6 +4,7 @@
 
 #include "net/quic/quic_context.h"
 
+#include "base/containers/contains.h"
 #include "net/quic/platform/impl/quic_chromium_clock.h"
 #include "net/quic/quic_chromium_connection_helper.h"
 #include "net/third_party/quiche/src/quiche/quic/core/crypto/crypto_protocol.h"
@@ -53,8 +54,7 @@
       quic::QuicTime::Delta::FromMicroseconds(
           params.max_idle_time_before_crypto_handshake.InMicroseconds()));
   quic::QuicTagVector copt_to_send = params.connection_options;
-  if (std::find(copt_to_send.begin(), copt_to_send.end(), quic::kRVCM) ==
-      copt_to_send.end()) {
+  if (!base::Contains(copt_to_send, quic::kRVCM)) {
     copt_to_send.push_back(quic::kRVCM);
   }
   config.SetConnectionOptionsToSend(copt_to_send);
diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc
index ffa5be0..0bb4794 100644
--- a/net/quic/quic_stream_factory.cc
+++ b/net/quic/quic_stream_factory.cc
@@ -4,7 +4,6 @@
 
 #include "net/quic/quic_stream_factory.h"
 
-#include <algorithm>
 #include <memory>
 #include <set>
 #include <tuple>
@@ -474,11 +473,7 @@
     IPEndPoint stale_address =
         resolve_host_request_->GetAddressResults()->front();
 
-    if (std::find(endpoints.begin(), endpoints.end(), stale_address) !=
-        endpoints.end()) {
-      return true;
-    }
-    return false;
+    return base::Contains(endpoints, stale_address);
   }
 
   void LogStaleHostRacing(bool used) {
diff --git a/net/reporting/reporting_cache_unittest.cc b/net/reporting/reporting_cache_unittest.cc
index f4cdfd77..0a5afcbf 100644
--- a/net/reporting/reporting_cache_unittest.cc
+++ b/net/reporting/reporting_cache_unittest.cc
@@ -4,11 +4,11 @@
 
 #include "net/reporting/reporting_cache.h"
 
-#include <algorithm>
 #include <string>
 #include <utility>
 
 #include "base/bind.h"
+#include "base/containers/contains.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/scoped_feature_list.h"
@@ -129,7 +129,7 @@
 
     for (const ReportingReport* report : after) {
       // If report isn't in before, we've found the new instance.
-      if (std::find(before.begin(), before.end(), report) == before.end()) {
+      if (!base::Contains(before, report)) {
         EXPECT_EQ(network_isolation_key, report->network_isolation_key);
         EXPECT_EQ(url, report->url);
         EXPECT_EQ(user_agent, report->user_agent);
diff --git a/net/ssl/ssl_platform_key_win.cc b/net/ssl/ssl_platform_key_win.cc
index e974a4b0..c6fb00d0f 100644
--- a/net/ssl/ssl_platform_key_win.cc
+++ b/net/ssl/ssl_platform_key_win.cc
@@ -4,13 +4,13 @@
 
 #include "net/ssl/ssl_platform_key_win.h"
 
-#include <algorithm>
 #include <memory>
 #include <string>
 #include <utility>
 #include <vector>
 
 #include "base/logging.h"
+#include "base/ranges/algorithm.h"
 #include "base/strings/utf_string_conversions.h"
 #include "crypto/openssl_util.h"
 #include "crypto/scoped_capi_types.h"
@@ -39,7 +39,7 @@
   }
   // Per Microsoft's documentation, PP_NAME is NUL-terminated. However,
   // smartcard drivers are notoriously buggy, so check this.
-  auto nul = std::find(name.begin(), name.end(), 0);
+  auto nul = base::ranges::find(name, 0);
   if (nul != name.end()) {
     name_len = nul - name.begin();
   }
@@ -204,7 +204,7 @@
 
   // Per Microsoft's documentation, the name is NUL-terminated. However,
   // smartcard drivers are notoriously buggy, so check this.
-  auto nul = std::find(name.begin(), name.end(), 0);
+  auto nul = base::ranges::find(name, 0);
   if (nul != name.end()) {
     name.erase(nul, name.end());
   }
diff --git a/remoting/base/capabilities.cc b/remoting/base/capabilities.cc
index 218733a..89014f56 100644
--- a/remoting/base/capabilities.cc
+++ b/remoting/base/capabilities.cc
@@ -4,9 +4,9 @@
 
 #include "remoting/base/capabilities.h"
 
-#include <algorithm>
 #include <vector>
 
+#include "base/containers/contains.h"
 #include "base/stl_util.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
@@ -17,8 +17,7 @@
 bool HasCapability(const std::string& capabilities, const std::string& key) {
   std::vector<base::StringPiece> caps = base::SplitStringPiece(
       capabilities, " ", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
-  return std::find(caps.begin(), caps.end(), base::StringPiece(key)) !=
-         caps.end();
+  return base::Contains(caps, base::StringPiece(key));
 }
 
 std::string IntersectCapabilities(const std::string& client_capabilities,
diff --git a/remoting/host/client_session_unittest.cc b/remoting/host/client_session_unittest.cc
index b4d4fc4a..c040846 100644
--- a/remoting/host/client_session_unittest.cc
+++ b/remoting/host/client_session_unittest.cc
@@ -6,13 +6,13 @@
 
 #include <stdint.h>
 
-#include <algorithm>
 #include <memory>
 #include <string>
 #include <utility>
 #include <vector>
 
 #include "base/bind.h"
+#include "base/containers/contains.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/raw_ptr.h"
 #include "base/run_loop.h"
@@ -78,8 +78,7 @@
                         base::SPLIT_WANT_NONEMPTY);
 
   for (const auto& word : words_expected) {
-    if (std::find(words_args.begin(), words_args.end(), word) ==
-        words_args.end()) {
+    if (!base::Contains(words_args, word)) {
       return false;
     }
   }
diff --git a/remoting/host/linux/unicode_to_keysym_unittest.cc b/remoting/host/linux/unicode_to_keysym_unittest.cc
index f4880d97..c51bfd1 100644
--- a/remoting/host/linux/unicode_to_keysym_unittest.cc
+++ b/remoting/host/linux/unicode_to_keysym_unittest.cc
@@ -7,8 +7,7 @@
 #include <stddef.h>
 #include <stdint.h>
 
-#include <algorithm>
-
+#include "base/ranges/algorithm.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace remoting {
@@ -40,8 +39,7 @@
 
     std::vector<uint32_t> expected(
         kTests[i].expected_keysyms,
-        std::find(
-            kTests[i].expected_keysyms, kTests[i].expected_keysyms + 4, 0));
+        base::ranges::find(kTests[i].expected_keysyms, 0u));
     EXPECT_EQ(expected, keysyms);
   }
 }
diff --git a/remoting/protocol/negotiating_authenticator_unittest.cc b/remoting/protocol/negotiating_authenticator_unittest.cc
index 32f0851..5a6cd689 100644
--- a/remoting/protocol/negotiating_authenticator_unittest.cc
+++ b/remoting/protocol/negotiating_authenticator_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "base/memory/raw_ptr.h"
+#include "base/ranges/algorithm.h"
 #include "base/run_loop.h"
 #include "net/base/net_errors.h"
 #include "remoting/base/rsa_key_pair.h"
@@ -88,14 +89,14 @@
 
   void DisableMethodOnClient(NegotiatingAuthenticatorBase::Method method) {
     auto* methods = &(client_as_negotiating_authenticator_->methods_);
-    auto iter = std::find(methods->begin(), methods->end(), method);
+    auto iter = base::ranges::find(*methods, method);
     ASSERT_TRUE(iter != methods->end());
     methods->erase(iter);
   }
 
   void DisableMethodOnHost(NegotiatingAuthenticatorBase::Method method) {
     auto* methods = &(host_as_negotiating_authenticator_->methods_);
-    auto iter = std::find(methods->begin(), methods->end(), method);
+    auto iter = base::ranges::find(*methods, method);
     ASSERT_TRUE(iter != methods->end());
     methods->erase(iter);
   }
diff --git a/remoting/protocol/negotiating_client_authenticator.cc b/remoting/protocol/negotiating_client_authenticator.cc
index db996cfe..2344575 100644
--- a/remoting/protocol/negotiating_client_authenticator.cc
+++ b/remoting/protocol/negotiating_client_authenticator.cc
@@ -4,7 +4,6 @@
 
 #include "remoting/protocol/negotiating_client_authenticator.h"
 
-#include <algorithm>
 #include <memory>
 #include <sstream>
 #include <utility>
@@ -12,6 +11,7 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/check_op.h"
+#include "base/containers/contains.h"
 #include "base/memory/ptr_util.h"
 #include "base/notreached.h"
 #include "base/strings/string_split.h"
@@ -62,7 +62,7 @@
     // The host must pick a method that is valid and supported by the client,
     // and it must not change methods after it has picked one.
     if (method_set_by_host_ || method == Method::INVALID ||
-        std::find(methods_.begin(), methods_.end(), method) == methods_.end()) {
+        !base::Contains(methods_, method)) {
       state_ = REJECTED;
       rejection_reason_ = RejectionReason::PROTOCOL_ERROR;
       std::move(resume_callback).Run();
@@ -184,9 +184,7 @@
 }
 
 void NegotiatingClientAuthenticator::CreatePreferredAuthenticator() {
-  if (is_paired() &&
-      std::find(methods_.begin(), methods_.end(), Method::PAIRED_SPAKE2_P224) !=
-          methods_.end()) {
+  if (is_paired() && base::Contains(methods_, Method::PAIRED_SPAKE2_P224)) {
     PairingClientAuthenticator* pairing_authenticator =
         new PairingClientAuthenticator(
             config_, base::BindRepeating(&V2Authenticator::CreateForClient));
diff --git a/remoting/protocol/negotiating_host_authenticator.cc b/remoting/protocol/negotiating_host_authenticator.cc
index 1996b78..78b78a8 100644
--- a/remoting/protocol/negotiating_host_authenticator.cc
+++ b/remoting/protocol/negotiating_host_authenticator.cc
@@ -4,7 +4,6 @@
 
 #include "remoting/protocol/negotiating_host_authenticator.h"
 
-#include <algorithm>
 #include <memory>
 #include <sstream>
 #include <utility>
@@ -12,6 +11,7 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/check_op.h"
+#include "base/containers/contains.h"
 #include "base/notreached.h"
 #include "base/strings/string_split.h"
 #include "remoting/base/rsa_key_pair.h"
@@ -103,8 +103,7 @@
   // If the client did not specify a preferred auth method, or specified an
   // unknown or unsupported method, then select the first known method from
   // the supported-methods attribute.
-  if (method == Method::INVALID ||
-      std::find(methods_.begin(), methods_.end(), method) == methods_.end()) {
+  if (method == Method::INVALID || !base::Contains(methods_, method)) {
     method = Method::INVALID;
 
     std::string supported_methods_attr =
@@ -125,8 +124,7 @@
                            base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
       Method list_value = ParseMethodString(method_str);
       if (list_value != Method::INVALID &&
-          std::find(methods_.begin(), methods_.end(), list_value) !=
-              methods_.end()) {
+          base::Contains(methods_, list_value)) {
         // Found common method.
         method = list_value;
         break;
diff --git a/remoting/protocol/session_config.cc b/remoting/protocol/session_config.cc
index 3b04433..fc2364e0 100644
--- a/remoting/protocol/session_config.cc
+++ b/remoting/protocol/session_config.cc
@@ -4,10 +4,10 @@
 
 #include "remoting/protocol/session_config.h"
 
-#include <algorithm>
 #include <vector>
 
 #include "base/check.h"
+#include "base/containers/contains.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/notreached.h"
@@ -16,11 +16,6 @@
 
 namespace {
 
-bool IsChannelConfigSupported(const std::list<ChannelConfig>& list,
-                              const ChannelConfig& value) {
-  return std::find(list.begin(), list.end(), value) != list.end();
-}
-
 bool SelectCommonChannelConfig(const std::list<ChannelConfig>& host_configs,
                                const std::list<ChannelConfig>& client_configs,
                                ChannelConfig* config) {
@@ -28,7 +23,7 @@
   // over all of them is not a problem.
   std::list<ChannelConfig>::const_iterator it;
   for (it = client_configs.begin(); it != client_configs.end(); ++it) {
-    if (IsChannelConfigSupported(host_configs, *it)) {
+    if (base::Contains(host_configs, *it)) {
       *config = *it;
       return true;
     }
@@ -207,11 +202,10 @@
   switch (config.protocol()) {
     case SessionConfig::Protocol::ICE:
       return ice_supported() &&
-             IsChannelConfigSupported(control_configs_,
-                                      config.control_config()) &&
-             IsChannelConfigSupported(event_configs_, config.event_config()) &&
-             IsChannelConfigSupported(video_configs_, config.video_config()) &&
-             IsChannelConfigSupported(audio_configs_, config.audio_config());
+             base::Contains(control_configs_, config.control_config()) &&
+             base::Contains(event_configs_, config.event_config()) &&
+             base::Contains(video_configs_, config.video_config()) &&
+             base::Contains(audio_configs_, config.audio_config());
 
     case SessionConfig::Protocol::WEBRTC:
       return webrtc_supported();
diff --git a/sandbox/win/src/startup_information_helper.cc b/sandbox/win/src/startup_information_helper.cc
index ad58da7..cb110ef 100644
--- a/sandbox/win/src/startup_information_helper.cc
+++ b/sandbox/win/src/startup_information_helper.cc
@@ -6,11 +6,11 @@
 
 #include <Windows.h>
 
-#include <algorithm>
 #include <vector>
 
 #include "base/check.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/ranges/algorithm.h"
 #include "base/win/startup_information.h"
 #include "base/win/windows_version.h"
 #include "sandbox/win/src/app_container.h"
@@ -60,8 +60,7 @@
 
 void StartupInformationHelper::AddInheritedHandle(HANDLE handle) {
   if (handle != INVALID_HANDLE_VALUE) {
-    auto it = std::find(inherited_handle_list_.begin(),
-                        inherited_handle_list_.end(), handle);
+    auto it = base::ranges::find(inherited_handle_list_, handle);
     if (it == inherited_handle_list_.end())
       inherited_handle_list_.push_back(handle);
   }
diff --git a/sandbox/win/src/win_utils_unittest.cc b/sandbox/win/src/win_utils_unittest.cc
index 84aded0..d136f61 100644
--- a/sandbox/win/src/win_utils_unittest.cc
+++ b/sandbox/win/src/win_utils_unittest.cc
@@ -8,9 +8,9 @@
 
 #include <psapi.h>
 
-#include <algorithm>
 #include <vector>
 
+#include "base/containers/contains.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
@@ -87,9 +87,7 @@
                 const base::win::ScopedHandle& handle) {
   ProcessHandleMap::const_iterator entry = handle_map.find(type_name);
   ASSERT_NE(handle_map.end(), entry);
-  const std::vector<HANDLE>& handles = entry->second;
-  EXPECT_NE(handles.cend(),
-            std::find(handles.cbegin(), handles.cend(), handle.Get()));
+  EXPECT_TRUE(base::Contains(entry->second, handle.Get()));
 }
 
 void TestCurrentProcessHandles(absl::optional<ProcessHandleMap> (*func)()) {
diff --git a/skia/BUILD.gn b/skia/BUILD.gn
index 0a7bf4f2..edc61a9 100644
--- a/skia/BUILD.gn
+++ b/skia/BUILD.gn
@@ -10,7 +10,6 @@
 import("//gpu/vulkan/features.gni")
 import("//printing/buildflags/buildflags.gni")
 import("//testing/test.gni")
-import("//third_party/libgifcodec/libgifcodec.gni")
 import("//third_party/skia/gn/shared_sources.gni")
 import("//third_party/skia/modules/skcms/skcms.gni")
 import("//tools/grit/grit_rule.gni")
@@ -26,10 +25,6 @@
 skia_support_gpu = !is_ios
 skia_support_pdf = !is_ios && enable_basic_printing
 
-declare_args() {
-  enable_skia_wuffs_gif = true
-}
-
 buildflag_header("buildflags") {
   header = "buildflags.h"
   flags = [
@@ -54,11 +49,7 @@
   include_dirs = [ "//third_party/skia" ]
 
   if (!is_ios) {
-    if (enable_skia_wuffs_gif) {
-      include_dirs += [ "//third_party/wuffs/src/release/c" ]
-    } else {
-      include_dirs += [ "//third_party/libgifcodec" ]
-    }
+    include_dirs += [ "//third_party/wuffs/src/release/c" ]
   }
   if (enable_vulkan) {
     include_dirs += [ "//third_party/vulkan/include" ]
@@ -74,12 +65,8 @@
     defines += [
       "SK_CODEC_DECODES_JPEG",
       "SK_ENCODE_JPEG",
+      "SK_HAS_WUFFS_LIBRARY",
     ]
-    if (enable_skia_wuffs_gif) {
-      defines += [ "SK_HAS_WUFFS_LIBRARY" ]
-    } else {
-      defines += [ "SK_USE_LIBGIFCODEC" ]
-    }
   }
   if (enable_vulkan) {
     defines += [ "SK_VULKAN=1" ]
@@ -367,23 +354,15 @@
       "//third_party/skia/src/codec/SkParseEncodedOrigin.cpp",
       "//third_party/skia/src/codec/SkPngCodec.cpp",
       "//third_party/skia/src/codec/SkSampler.cpp",
-      "//third_party/skia/src/codec/SkStreamBuffer.cpp",
       "//third_party/skia/src/codec/SkSwizzler.cpp",
       "//third_party/skia/src/codec/SkWbmpCodec.cpp",
       "//third_party/skia/src/codec/SkWebpCodec.cpp",
+      "//third_party/skia/src/codec/SkWuffsCodec.cpp",
       "//third_party/skia/src/images/SkJPEGWriteUtility.cpp",
       "//third_party/skia/src/images/SkJpegEncoder.cpp",
       "//third_party/skia/src/ports/SkImageGenerator_skia.cpp",
     ]
-
-    if (enable_skia_wuffs_gif) {
-      deps += [ "//third_party/wuffs" ]
-      sources += [ "//third_party/skia/src/codec/SkWuffsCodec.cpp" ]
-    } else {
-      sources += rebase_path(libgifcodec_sources + libgifcodec_public,
-                             ".",
-                             "//third_party/libgifcodec")
-    }
+    deps += [ "//third_party/wuffs" ]
   }
 
   if (current_cpu == "arm") {
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 3e56db3..7501c7f 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -5040,14 +5040,17 @@
                           shadow_root_init_dict->slotAssignment() == "manual")
                              ? SlotAssignmentMode::kManual
                              : SlotAssignmentMode::kNamed;
+  CustomElementRegistry* registry = shadow_root_init_dict->hasRegistry()
+                                        ? shadow_root_init_dict->registry()
+                                        : nullptr;
   if (const char* error_message = ErrorMessageForAttachShadow()) {
     exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
                                       error_message);
     return nullptr;
   }
 
-  ShadowRoot& shadow_root =
-      AttachShadowRootInternal(type, focus_delegation, slot_assignment);
+  ShadowRoot& shadow_root = AttachShadowRootInternal(type, focus_delegation,
+                                                     slot_assignment, registry);
 
   // Ensure that the returned shadow root is not marked as declarative so that
   // attachShadow() calls after the first one do not succeed for a shadow host
@@ -5103,7 +5106,8 @@
 ShadowRoot& Element::AttachShadowRootInternal(
     ShadowRootType type,
     FocusDelegation focus_delegation,
-    SlotAssignmentMode slot_assignment_mode) {
+    SlotAssignmentMode slot_assignment_mode,
+    CustomElementRegistry* registry) {
   // SVG <use> is a special case for using this API to create a closed shadow
   // root.
   DCHECK(CanAttachShadowRoot() || IsA<SVGUseElement>(*this));
@@ -5131,6 +5135,8 @@
   // NEW. Set shadow’s "is declarative shadow root" property to false.
   shadow_root.SetIsDeclarativeShadowRoot(false);
 
+  shadow_root.SetRegistry(registry);
+
   // 7. If this’s custom element state is "precustomized" or "custom", then set
   // shadow’s available to element internals to true.
   shadow_root.SetAvailableToElementInternals(
diff --git a/third_party/blink/renderer/core/dom/element.h b/third_party/blink/renderer/core/dom/element.h
index 238af0a2..aab6f16d 100644
--- a/third_party/blink/renderer/core/dom/element.h
+++ b/third_party/blink/renderer/core/dom/element.h
@@ -75,6 +75,7 @@
 class CSSStyleDeclaration;
 class CSSToggleMap;
 class CustomElementDefinition;
+class CustomElementRegistry;
 class DOMRect;
 class DOMRectList;
 class DOMStringMap;
@@ -722,7 +723,8 @@
   ShadowRoot& AttachShadowRootInternal(
       ShadowRootType,
       FocusDelegation focus_delegation = FocusDelegation::kNone,
-      SlotAssignmentMode slot_assignment_mode = SlotAssignmentMode::kNamed);
+      SlotAssignmentMode slot_assignment_mode = SlotAssignmentMode::kNamed,
+      CustomElementRegistry* registry = nullptr);
 
   // Returns the shadow root attached to this element if it is a shadow host.
   ShadowRoot* GetShadowRoot() const;
diff --git a/third_party/blink/renderer/core/dom/shadow_root.cc b/third_party/blink/renderer/core/dom/shadow_root.cc
index b2743ccb..383f2ce3 100644
--- a/third_party/blink/renderer/core/dom/shadow_root.cc
+++ b/third_party/blink/renderer/core/dom/shadow_root.cc
@@ -40,6 +40,7 @@
 #include "third_party/blink/renderer/core/dom/text.h"
 #include "third_party/blink/renderer/core/dom/whitespace_attacher.h"
 #include "third_party/blink/renderer/core/editing/serializers/serialization.h"
+#include "third_party/blink/renderer/core/html/custom/custom_element_registry.h"
 #include "third_party/blink/renderer/core/html/html_slot_element.h"
 #include "third_party/blink/renderer/core/trustedtypes/trusted_types_util.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
@@ -48,7 +49,7 @@
 namespace blink {
 
 struct SameSizeAsShadowRoot : public DocumentFragment, public TreeScope {
-  Member<void*> member[2];
+  Member<void*> member[3];
   unsigned flags[1];
 };
 
@@ -250,9 +251,17 @@
   return *style_sheet_list_;
 }
 
+void ShadowRoot::SetRegistry(CustomElementRegistry* registry) {
+  DCHECK(!registry_);
+  DCHECK(!registry ||
+         RuntimeEnabledFeatures::ScopedCustomElementRegistryEnabled());
+  registry_ = registry;
+}
+
 void ShadowRoot::Trace(Visitor* visitor) const {
   visitor->Trace(style_sheet_list_);
   visitor->Trace(slot_assignment_);
+  visitor->Trace(registry_);
   TreeScope::Trace(visitor);
   DocumentFragment::Trace(visitor);
 }
diff --git a/third_party/blink/renderer/core/dom/shadow_root.h b/third_party/blink/renderer/core/dom/shadow_root.h
index e507859b..4043b58 100644
--- a/third_party/blink/renderer/core/dom/shadow_root.h
+++ b/third_party/blink/renderer/core/dom/shadow_root.h
@@ -179,6 +179,9 @@
     return has_focusgroup_attribute_on_descendant_;
   }
 
+  void SetRegistry(CustomElementRegistry*);
+  CustomElementRegistry* registry() const { return registry_; }
+
   bool ContainsShadowRoots() const { return child_shadow_root_count_; }
 
   StyleSheetList& StyleSheets();
@@ -212,6 +215,7 @@
 
   Member<StyleSheetList> style_sheet_list_;
   Member<SlotAssignment> slot_assignment_;
+  Member<CustomElementRegistry> registry_;
   unsigned child_shadow_root_count_ : 16;
   unsigned type_ : 2;
   unsigned registered_with_parent_shadow_root_ : 1;
diff --git a/third_party/blink/renderer/core/dom/shadow_root.idl b/third_party/blink/renderer/core/dom/shadow_root.idl
index e200a4ae..a1cff540 100644
--- a/third_party/blink/renderer/core/dom/shadow_root.idl
+++ b/third_party/blink/renderer/core/dom/shadow_root.idl
@@ -37,6 +37,9 @@
     readonly attribute boolean delegatesFocus;
     readonly attribute SlotAssignmentMode slotAssignment;
 
+    [RuntimeEnabled=ScopedCustomElementRegistry]
+    readonly attribute CustomElementRegistry? registry;
+
     // Declarative Shadow DOM getInnerHTML() function.
     [Affects=Nothing, MeasureAs=ElementGetInnerHTML] HTMLString getInnerHTML(optional GetInnerHTMLOptions options = {});
 };
diff --git a/third_party/blink/renderer/core/dom/shadow_root_init.idl b/third_party/blink/renderer/core/dom/shadow_root_init.idl
index cb3815df..b48d0de 100644
--- a/third_party/blink/renderer/core/dom/shadow_root_init.idl
+++ b/third_party/blink/renderer/core/dom/shadow_root_init.idl
@@ -11,4 +11,5 @@
     required ShadowRootMode mode;
     boolean delegatesFocus;
     SlotAssignmentMode slotAssignment;
+    [RuntimeEnabled=ScopedCustomElementRegistry] CustomElementRegistry registry;
 };
diff --git a/third_party/blink/renderer/core/exported/web_page_popup_impl.cc b/third_party/blink/renderer/core/exported/web_page_popup_impl.cc
index a87a5ec1..8def289 100644
--- a/third_party/blink/renderer/core/exported/web_page_popup_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_page_popup_impl.cc
@@ -180,11 +180,14 @@
     return popup_->WindowRectInScreen();
   }
 
-  gfx::Rect ViewportToScreen(const gfx::Rect& rect,
-                             const LocalFrameView*) const override {
+  gfx::Rect LocalRootToScreenDIPs(const gfx::Rect& rect_in_local_root,
+                                  const LocalFrameView* view) const override {
+    DCHECK(view);
+    DCHECK_EQ(view->GetChromeClient(), this);
+
     gfx::Rect window_rect = popup_->WindowRectInScreen();
     gfx::Rect rect_in_dips =
-        popup_->widget_base_->BlinkSpaceToEnclosedDIPs(rect);
+        popup_->widget_base_->BlinkSpaceToEnclosedDIPs(rect_in_local_root);
     rect_in_dips.Offset(window_rect.x(), window_rect.y());
     return rect_in_dips;
   }
@@ -864,8 +867,16 @@
 gfx::Rect WebPagePopupImpl::GetAnchorRectInScreen() const {
   LocalFrameView* view = popup_client_->OwnerElement().GetDocument().View();
   DCHECK(view);
-  return popup_client_->GetChromeClient().ViewportToScreen(
-      popup_client_->OwnerElement().VisibleBoundsInVisualViewport(), view);
+
+  LocalFrameView* root_view = view->GetFrame().LocalFrameRoot().View();
+  DCHECK(root_view);
+
+  // TODO(bokan): VisibleBoundsInVisualViewport will soon be turned into
+  // VisibleBoundsInLocalRoot.
+  return view->GetFrame().GetChromeClient().LocalRootToScreenDIPs(
+      root_view->ViewportToFrame(
+          popup_client_->OwnerElement().VisibleBoundsInVisualViewport()),
+      view);
 }
 
 WebInputEventResult WebPagePopupImpl::DispatchBufferedTouchEvents() {
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index f450c8f5..43428b1 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -3950,7 +3950,7 @@
 
 gfx::Rect LocalFrameView::FrameToScreen(const gfx::Rect& rect) const {
   if (auto* client = GetChromeClient())
-    return client->ViewportToScreen(FrameToViewport(rect), this);
+    return client->LocalRootToScreenDIPs(ConvertToRootFrame(rect), this);
   return gfx::Rect();
 }
 
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
index 31da7d0..cecb9d8 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
@@ -2198,7 +2198,8 @@
       .RecordImplCompositorSample(commit_compositor_frame_start_time_.value(),
                                   commit_start_time, commit_finish_time);
   commit_compositor_frame_start_time_ =
-      std::move(next_commit_compositor_frame_start_time_);
+      next_commit_compositor_frame_start_time_;
+  next_commit_compositor_frame_start_time_.reset();
 }
 
 void WebFrameWidgetImpl::ApplyViewportChanges(
diff --git a/third_party/blink/renderer/core/html/custom/custom_element_registry.cc b/third_party/blink/renderer/core/html/custom/custom_element_registry.cc
index 3e34fd3..08af50e 100644
--- a/third_party/blink/renderer/core/html/custom/custom_element_registry.cc
+++ b/third_party/blink/renderer/core/html/custom/custom_element_registry.cc
@@ -77,6 +77,7 @@
 // static
 CustomElementRegistry* CustomElementRegistry::Create(
     ScriptState* script_state) {
+  DCHECK(RuntimeEnabledFeatures::ScopedCustomElementRegistryEnabled());
   return MakeGarbageCollected<CustomElementRegistry>(
       LocalDOMWindow::From(script_state));
 }
diff --git a/third_party/blink/renderer/core/html/forms/color_chooser_client.h b/third_party/blink/renderer/core/html/forms/color_chooser_client.h
index f7bdb28..89f6eee 100644
--- a/third_party/blink/renderer/core/html/forms/color_chooser_client.h
+++ b/third_party/blink/renderer/core/html/forms/color_chooser_client.h
@@ -53,7 +53,7 @@
   // Called when ColorChooser UI was closed by the user.
   virtual void DidEndChooser() = 0;
   virtual Element& OwnerElement() const = 0;
-  virtual gfx::Rect ElementRectRelativeToViewport() const = 0;
+  virtual gfx::Rect ElementRectRelativeToLocalRoot() const = 0;
   virtual Color CurrentColor() = 0;
   virtual bool ShouldShowSuggestions() const = 0;
   virtual Vector<mojom::blink::ColorSuggestionPtr> Suggestions() const = 0;
diff --git a/third_party/blink/renderer/core/html/forms/color_chooser_popup_ui_controller.cc b/third_party/blink/renderer/core/html/forms/color_chooser_popup_ui_controller.cc
index 0410c979..3e7d4d7 100644
--- a/third_party/blink/renderer/core/html/forms/color_chooser_popup_ui_controller.cc
+++ b/third_party/blink/renderer/core/html/forms/color_chooser_popup_ui_controller.cc
@@ -92,8 +92,8 @@
 
 void ColorChooserPopupUIController::WriteColorPickerDocument(
     SharedBuffer* data) {
-  gfx::Rect anchor_rect_in_screen = chrome_client_->ViewportToScreen(
-      client_->ElementRectRelativeToViewport(), frame_->View());
+  gfx::Rect anchor_rect_in_screen = chrome_client_->LocalRootToScreenDIPs(
+      client_->ElementRectRelativeToLocalRoot(), frame_->View());
 
   PagePopupClient::AddString(
       "<!DOCTYPE html><head><meta charset='UTF-8'><meta name='color-scheme' "
@@ -162,8 +162,8 @@
     suggestion_values.push_back(
         Color::FromRGBA32(suggestion->color).SerializeAsCSSColor());
   }
-  gfx::Rect anchor_rect_in_screen = chrome_client_->ViewportToScreen(
-      client_->ElementRectRelativeToViewport(), frame_->View());
+  gfx::Rect anchor_rect_in_screen = chrome_client_->LocalRootToScreenDIPs(
+      client_->ElementRectRelativeToLocalRoot(), frame_->View());
 
   PagePopupClient::AddString(
       "<!DOCTYPE html><head><meta charset='UTF-8'><meta name='color-scheme' "
diff --git a/third_party/blink/renderer/core/html/forms/color_input_type.cc b/third_party/blink/renderer/core/html/forms/color_input_type.cc
index 20af8e44..f6a9a81 100644
--- a/third_party/blink/renderer/core/html/forms/color_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/color_input_type.cc
@@ -260,8 +260,8 @@
   return GetElement();
 }
 
-gfx::Rect ColorInputType::ElementRectRelativeToViewport() const {
-  return GetElement().GetDocument().View()->FrameToViewport(
+gfx::Rect ColorInputType::ElementRectRelativeToLocalRoot() const {
+  return GetElement().GetDocument().View()->ConvertToRootFrame(
       GetElement().PixelSnappedBoundingBox());
 }
 
diff --git a/third_party/blink/renderer/core/html/forms/color_input_type.h b/third_party/blink/renderer/core/html/forms/color_input_type.h
index e53d3aa..196e11c1 100644
--- a/third_party/blink/renderer/core/html/forms/color_input_type.h
+++ b/third_party/blink/renderer/core/html/forms/color_input_type.h
@@ -52,7 +52,7 @@
   void DidChooseColor(const Color&) override;
   void DidEndChooser() override;
   Element& OwnerElement() const override;
-  gfx::Rect ElementRectRelativeToViewport() const override;
+  gfx::Rect ElementRectRelativeToLocalRoot() const override;
   Color CurrentColor() override;
   bool ShouldShowSuggestions() const override;
   Vector<mojom::blink::ColorSuggestionPtr> Suggestions() const override;
diff --git a/third_party/blink/renderer/core/html/forms/html_input_element_test.cc b/third_party/blink/renderer/core/html/forms/html_input_element_test.cc
index 0787fc8..4c48f225 100644
--- a/third_party/blink/renderer/core/html/forms/html_input_element_test.cc
+++ b/third_party/blink/renderer/core/html/forms/html_input_element_test.cc
@@ -33,7 +33,7 @@
 
 namespace {
 
-class MockChromeClient : public EmptyChromeClient {
+class PasswordResetChromeClient : public EmptyChromeClient {
  public:
   MOCK_METHOD(void,
               PasswordFieldReset,
@@ -41,10 +41,25 @@
               (override));
 };
 
+class HTMLInputElementTestChromeClient : public EmptyChromeClient {
+ public:
+  gfx::Rect LocalRootToScreenDIPs(const gfx::Rect& local_root_rect,
+                                  const LocalFrameView* view) const override {
+    return view->GetPage()->GetVisualViewport().RootFrameToViewport(
+        local_root_rect);
+  }
+};
+
 }  // namespace
 
 class HTMLInputElementTest : public PageTestBase {
  protected:
+  void SetUp() override {
+    auto* chrome_client =
+        MakeGarbageCollected<HTMLInputElementTestChromeClient>();
+    SetupPageWithClients(chrome_client);
+  }
+
   HTMLInputElement& TestElement() {
     Element* element = GetDocument().getElementById("test");
     DCHECK(element);
@@ -307,14 +322,14 @@
       public ::testing::WithParamInterface<PasswordFieldResetParam> {
  protected:
   void SetUp() override {
-    chrome_client_ = MakeGarbageCollected<MockChromeClient>();
+    chrome_client_ = MakeGarbageCollected<PasswordResetChromeClient>();
     SetupPageWithClients(chrome_client_);
   }
 
-  MockChromeClient& chrome_client() { return *chrome_client_; }
+  PasswordResetChromeClient& chrome_client() { return *chrome_client_; }
 
  private:
-  Persistent<MockChromeClient> chrome_client_;
+  Persistent<PasswordResetChromeClient> chrome_client_;
 };
 
 // Tests that PasswordFieldReset() is (only) called for empty fields. This is
diff --git a/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc b/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc
index b247e1f..82daf66 100644
--- a/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc
+++ b/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc
@@ -278,9 +278,13 @@
   // element's items (see AddElementStyle). This requires a style-clean tree.
   // See Element::EnsureComputedStyle for further explanation.
   DCHECK(!owner_element.GetDocument().NeedsLayoutTreeUpdate());
-  gfx::Rect anchor_rect_in_screen = chrome_client_->ViewportToScreen(
-      owner_element.VisibleBoundsInVisualViewport(),
-      owner_element.GetDocument().View());
+  LocalFrameView* view = owner_element.GetDocument().View();
+  LocalFrameView* root_view = view->GetFrame().LocalFrameRoot().View();
+  // TODO(bokan): VisibleBoundsInVisualViewport will soon be
+  // VisibleBoundsInLocalRoot.
+  gfx::Rect anchor_rect_in_screen = chrome_client_->LocalRootToScreenDIPs(
+      root_view->ViewportToFrame(owner_element.VisibleBoundsInVisualViewport()),
+      view);
 
   float scale_factor = chrome_client_->WindowToViewportScalar(
       owner_element.GetDocument().GetFrame(), 1.f);
@@ -679,9 +683,14 @@
   }
   context.FinishGroupIfNecessary();
   PagePopupClient::AddString("],\n", data.get());
-  gfx::Rect anchor_rect_in_screen = chrome_client_->ViewportToScreen(
-      owner_element_->VisibleBoundsInVisualViewport(),
-      OwnerElement().GetDocument().View());
+  LocalFrameView* view = OwnerElement().GetDocument().View();
+  LocalFrameView* root_view = view->GetFrame().LocalFrameRoot().View();
+  // TODO(bokan): VisibleBoundsInVisualViewport will soon be
+  // VisibleBoundsInLocalRoot.
+  gfx::Rect anchor_rect_in_screen = chrome_client_->LocalRootToScreenDIPs(
+      root_view->ViewportToFrame(
+          OwnerElement().VisibleBoundsInVisualViewport()),
+      view);
   AddProperty("anchorRectInScreen", anchor_rect_in_screen, data.get());
   PagePopupClient::AddString("}\n", data.get());
   popup_->PostMessageToPopup(String::FromUTF8(data->Data(), data->size()));
diff --git a/third_party/blink/renderer/core/input/event_handler.cc b/third_party/blink/renderer/core/input/event_handler.cc
index c3feb8b1..70dae17 100644
--- a/third_party/blink/renderer/core/input/event_handler.cc
+++ b/third_party/blink/renderer/core/input/event_handler.cc
@@ -2194,12 +2194,10 @@
   }
 
   frame_->View()->SetCursor(PointerCursor());
-  gfx::Point location_in_viewport =
-      visual_viewport.RootFrameToViewport(location_in_root_frame);
   gfx::Point global_position =
       view->GetChromeClient()
-          ->ViewportToScreen(gfx::Rect(location_in_viewport, gfx::Size()),
-                             frame_->View())
+          ->LocalRootToScreenDIPs(
+              gfx::Rect(location_in_root_frame, gfx::Size()), frame_->View())
           .origin();
 
   // Use the focused node as the target for hover and active.
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_block.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_block.cc
index 9f062332..c4c002d4 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_block.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_block.cc
@@ -48,7 +48,7 @@
 
 void LayoutSVGBlock::WillBeDestroyed() {
   NOT_DESTROYED();
-  SVGResources::ClearClipPathFilterMask(*GetElement(), Style());
+  SVGResources::ClearEffects(*GetElement(), Style());
   LayoutBlockFlow::WillBeDestroyed();
 }
 
@@ -126,7 +126,7 @@
       SetNeedsTransformUpdate();
   }
 
-  SVGResources::UpdateClipPathFilterMask(*GetElement(), old_style, StyleRef());
+  SVGResources::UpdateEffects(*GetElement(), old_style, StyleRef());
 
   if (!Parent())
     return;
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_inline.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_inline.cc
index ad2ad57..14b4202 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_inline.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_inline.cc
@@ -182,7 +182,7 @@
 
 void LayoutSVGInline::WillBeDestroyed() {
   NOT_DESTROYED();
-  SVGResources::ClearClipPathFilterMask(To<SVGElement>(*GetNode()), Style());
+  SVGResources::ClearEffects(To<SVGElement>(*GetNode()), Style());
   SVGResources::ClearPaints(To<SVGElement>(*GetNode()), Style());
   LayoutInline::WillBeDestroyed();
 }
@@ -202,8 +202,8 @@
   if (diff.NeedsFullLayout())
     SetNeedsBoundariesUpdate();
 
-  SVGResources::UpdateClipPathFilterMask(To<SVGElement>(*GetNode()), old_style,
-                                         StyleRef());
+  SVGResources::UpdateEffects(To<SVGElement>(*GetNode()), old_style,
+                              StyleRef());
   SVGResources::UpdatePaints(To<SVGElement>(*GetNode()), old_style, StyleRef());
 
   if (!Parent())
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_model_object.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_model_object.cc
index ce27c665..20404493 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_model_object.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_model_object.cc
@@ -103,7 +103,7 @@
 
 void LayoutSVGModelObject::WillBeDestroyed() {
   NOT_DESTROYED();
-  SVGResources::ClearClipPathFilterMask(*GetElement(), Style());
+  SVGResources::ClearEffects(*GetElement(), Style());
   LayoutObject::WillBeDestroyed();
 }
 
@@ -144,7 +144,7 @@
   SetHasTransformRelatedProperty(
       StyleRef().HasTransformRelatedPropertyForSVG());
 
-  SVGResources::UpdateClipPathFilterMask(*GetElement(), old_style, StyleRef());
+  SVGResources::UpdateEffects(*GetElement(), old_style, StyleRef());
 
   if (!Parent())
     return;
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc
index f68751b..a90582f 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc
@@ -311,7 +311,7 @@
 
 void LayoutSVGRoot::WillBeDestroyed() {
   NOT_DESTROYED();
-  SVGResources::ClearClipPathFilterMask(To<SVGSVGElement>(*GetNode()), Style());
+  SVGResources::ClearEffects(To<SVGSVGElement>(*GetNode()), Style());
   LayoutReplaced::WillBeDestroyed();
 }
 
@@ -364,8 +364,8 @@
   if (old_style && StyleChangeAffectsIntrinsicSize(*old_style))
     IntrinsicSizingInfoChanged();
 
-  SVGResources::UpdateClipPathFilterMask(To<SVGSVGElement>(*GetNode()),
-                                         old_style, StyleRef());
+  SVGResources::UpdateEffects(To<SVGSVGElement>(*GetNode()), old_style,
+                              StyleRef());
 
   if (diff.TransformChanged()) {
     for (auto& svg_text : text_set_) {
diff --git a/third_party/blink/renderer/core/layout/svg/svg_resources.cc b/third_party/blink/renderer/core/layout/svg/svg_resources.cc
index 2cc93079..314de0cb 100644
--- a/third_party/blink/renderer/core/layout/svg/svg_resources.cc
+++ b/third_party/blink/renderer/core/layout/svg/svg_resources.cc
@@ -62,43 +62,56 @@
   return obb_layout_object->ObjectBoundingBox();
 }
 
-void SVGResources::UpdateClipPathFilterMask(SVGElement& element,
-                                            const ComputedStyle* old_style,
-                                            const ComputedStyle& style) {
+void SVGResources::UpdateEffects(SVGElement& element,
+                                 const ComputedStyle* old_style,
+                                 const ComputedStyle& style) {
   const bool had_client = element.GetSVGResourceClient();
   if (auto* reference_clip =
-          DynamicTo<ReferenceClipPathOperation>(style.ClipPath()))
+          DynamicTo<ReferenceClipPathOperation>(style.ClipPath())) {
     reference_clip->AddClient(element.EnsureSVGResourceClient());
+  }
   if (style.HasFilter()) {
     SVGElementResourceClient& client = element.EnsureSVGResourceClient();
     style.Filter().AddClient(client);
     LayoutObject* layout_object = element.GetLayoutObject();
     // This is called from StyleDidChange so we should have a LayoutObject.
     DCHECK(layout_object);
-    // TODO(fs): Reorganise the code so that we don't need to invalidate this
-    // again in SVGResourcesCache::ClientStyleChanged (and potentially avoid
-    // redundant invalidations).
     layout_object->SetNeedsPaintPropertyUpdate();
     client.MarkFilterDataDirty();
   }
   if (StyleSVGResource* masker_resource = style.MaskerResource())
     masker_resource->AddClient(element.EnsureSVGResourceClient());
-  if (had_client)
-    ClearClipPathFilterMask(element, old_style);
+  if (!old_style || !had_client)
+    return;
+  SVGElementResourceClient* client = element.GetSVGResourceClient();
+  if (auto* old_reference_clip =
+          DynamicTo<ReferenceClipPathOperation>(old_style->ClipPath())) {
+    old_reference_clip->RemoveClient(*client);
+  }
+  if (old_style->HasFilter()) {
+    old_style->Filter().RemoveClient(*client);
+    client->InvalidateFilterData();
+  }
+  if (StyleSVGResource* masker_resource = old_style->MaskerResource())
+    masker_resource->RemoveClient(*client);
 }
 
-void SVGResources::ClearClipPathFilterMask(SVGElement& element,
-                                           const ComputedStyle* style) {
+void SVGResources::ClearEffects(SVGElement& element,
+                                const ComputedStyle* style) {
   if (!style)
     return;
   SVGElementResourceClient* client = element.GetSVGResourceClient();
   if (!client)
     return;
   if (auto* old_reference_clip =
-          DynamicTo<ReferenceClipPathOperation>(style->ClipPath()))
+          DynamicTo<ReferenceClipPathOperation>(style->ClipPath())) {
     old_reference_clip->RemoveClient(*client);
+  }
   if (style->HasFilter()) {
     style->Filter().RemoveClient(*client);
+    // TODO(fs): We need to invalidate filter data here because the resource
+    // client is owned by the Element - thus staying alive with it even when
+    // the LayoutObject is detached. Move ownership to the LayoutObject.
     client->InvalidateFilterData();
   }
   if (StyleSVGResource* masker_resource = style->MaskerResource())
diff --git a/third_party/blink/renderer/core/layout/svg/svg_resources.h b/third_party/blink/renderer/core/layout/svg/svg_resources.h
index 83e84b5..98c3ba78 100644
--- a/third_party/blink/renderer/core/layout/svg/svg_resources.h
+++ b/third_party/blink/renderer/core/layout/svg/svg_resources.h
@@ -47,10 +47,10 @@
   static SVGElementResourceClient* GetClient(const LayoutObject&);
   static gfx::RectF ReferenceBoxForEffects(const LayoutObject&);
 
-  static void UpdateClipPathFilterMask(SVGElement&,
-                                       const ComputedStyle* old_style,
-                                       const ComputedStyle&);
-  static void ClearClipPathFilterMask(SVGElement&, const ComputedStyle*);
+  static void UpdateEffects(SVGElement&,
+                            const ComputedStyle* old_style,
+                            const ComputedStyle&);
+  static void ClearEffects(SVGElement&, const ComputedStyle*);
   static void UpdatePaints(SVGElement&,
                            const ComputedStyle* old_style,
                            const ComputedStyle&);
diff --git a/third_party/blink/renderer/core/layout/text_autosizer.cc b/third_party/blink/renderer/core/layout/text_autosizer.cc
index 2cb347e..1b016b82 100644
--- a/third_party/blink/renderer/core/layout/text_autosizer.cc
+++ b/third_party/blink/renderer/core/layout/text_autosizer.cc
@@ -803,13 +803,10 @@
 
   // 4 lines of text is considered enough to autosize.
   float minimum_text_length_to_autosize = WidthFromBlock(width_provider) * 4;
-  if (LocalFrameView* view = document_->View()) {
-    minimum_text_length_to_autosize =
-        document_->GetPage()
-            ->GetChromeClient()
-            .ViewportToScreen(
-                gfx::Rect(0, 0, minimum_text_length_to_autosize, 0), view)
-            .width();
+  if (LocalFrame* frame = document_->GetFrame()) {
+    minimum_text_length_to_autosize /=
+        document_->GetPage()->GetChromeClient().WindowToViewportScalar(frame,
+                                                                       1);
   }
 
   float length = 0;
diff --git a/third_party/blink/renderer/core/layout/text_autosizer_test.cc b/third_party/blink/renderer/core/layout/text_autosizer_test.cc
index 9f18bd1..6f6b7a38 100644
--- a/third_party/blink/renderer/core/layout/text_autosizer_test.cc
+++ b/third_party/blink/renderer/core/layout/text_autosizer_test.cc
@@ -21,8 +21,8 @@
   float WindowToViewportScalar(LocalFrame*, const float value) const override {
     return value * device_scale_factor_;
   }
-  gfx::Rect ViewportToScreen(const gfx::Rect& rect,
-                             const LocalFrameView*) const override {
+  gfx::Rect LocalRootToScreenDIPs(const gfx::Rect& rect,
+                                  const LocalFrameView*) const override {
     return gfx::ScaleToRoundedRect(rect, 1 / device_scale_factor_);
   }
 
diff --git a/third_party/blink/renderer/core/loader/empty_clients.h b/third_party/blink/renderer/core/loader/empty_clients.h
index 05f6e40..ba841a4 100644
--- a/third_party/blink/renderer/core/loader/empty_clients.h
+++ b/third_party/blink/renderer/core/loader/empty_clients.h
@@ -180,8 +180,8 @@
   void InvalidateContainer() override {}
   void ScheduleAnimation(const LocalFrameView*,
                          base::TimeDelta delay) override {}
-  gfx::Rect ViewportToScreen(const gfx::Rect& r,
-                             const LocalFrameView*) const override {
+  gfx::Rect LocalRootToScreenDIPs(const gfx::Rect& r,
+                                  const LocalFrameView*) const override {
     return r;
   }
   float WindowToViewportScalar(LocalFrame*, const float s) const override {
diff --git a/third_party/blink/renderer/core/page/chrome_client.h b/third_party/blink/renderer/core/page/chrome_client.h
index feb5feb..0fcb111 100644
--- a/third_party/blink/renderer/core/page/chrome_client.h
+++ b/third_party/blink/renderer/core/page/chrome_client.h
@@ -145,9 +145,11 @@
   // painting. This informs the client of the area that needs to be redrawn.
   virtual void InvalidateContainer() = 0;
 
-  // Converts the rect from the viewport coordinates to screen coordinates.
-  virtual gfx::Rect ViewportToScreen(const gfx::Rect&,
-                                     const LocalFrameView*) const = 0;
+  // Converts the rect from local root coordinates (using the local root of the
+  // given LocalFrameView) to screen coordinates. Performs the visual viewport
+  // transform.
+  virtual gfx::Rect LocalRootToScreenDIPs(const gfx::Rect&,
+                                          const LocalFrameView*) const = 0;
 
   void ScheduleAnimation(const LocalFrameView* view) {
     ScheduleAnimation(view, base::TimeDelta());
diff --git a/third_party/blink/renderer/core/page/chrome_client_impl.cc b/third_party/blink/renderer/core/page/chrome_client_impl.cc
index 355a209f..5a1b86b 100644
--- a/third_party/blink/renderer/core/page/chrome_client_impl.cc
+++ b/third_party/blink/renderer/core/page/chrome_client_impl.cc
@@ -514,16 +514,32 @@
   }
 }
 
-gfx::Rect ChromeClientImpl::ViewportToScreen(
-    const gfx::Rect& rect_in_viewport,
+gfx::Rect ChromeClientImpl::LocalRootToScreenDIPs(
+    const gfx::Rect& rect_in_local_root,
     const LocalFrameView* frame_view) const {
   LocalFrame& frame = frame_view->GetFrame();
 
-  gfx::Rect screen_rect =
-      frame.GetWidgetForLocalRoot()->BlinkSpaceToEnclosedDIPs(rect_in_viewport);
-  gfx::Rect view_rect = frame.GetWidgetForLocalRoot()->ViewRect();
+  WebFrameWidgetImpl* widget =
+      WebLocalFrameImpl::FromFrame(frame)->LocalRootFrameWidget();
 
+  gfx::Rect rect_in_widget;
+  if (widget->ForTopMostMainFrame()) {
+    rect_in_widget = frame.GetPage()->GetVisualViewport().RootFrameToViewport(
+        rect_in_local_root);
+  } else {
+    // TODO(bokan): This method needs to account for the visual viewport
+    // transform when in a non-top-most local frame root. Unfortunately, the
+    // widget's ViewRect doesn't include the visual viewport so this cannot be
+    // done from here yet. See: https://crbug.com/928825,
+    // https://crbug.com/840944.
+    rect_in_widget = rect_in_local_root;
+  }
+
+  gfx::Rect view_rect = widget->ViewRect();
+
+  gfx::Rect screen_rect = widget->BlinkSpaceToEnclosedDIPs(rect_in_widget);
   screen_rect.Offset(view_rect.x(), view_rect.y());
+
   return screen_rect;
 }
 
diff --git a/third_party/blink/renderer/core/page/chrome_client_impl.h b/third_party/blink/renderer/core/page/chrome_client_impl.h
index 43c397b..76433dc 100644
--- a/third_party/blink/renderer/core/page/chrome_client_impl.h
+++ b/third_party/blink/renderer/core/page/chrome_client_impl.h
@@ -147,8 +147,8 @@
   bool TabsToLinks() override;
   void InvalidateContainer() override;
   void ScheduleAnimation(const LocalFrameView*, base::TimeDelta delay) override;
-  gfx::Rect ViewportToScreen(const gfx::Rect&,
-                             const LocalFrameView*) const override;
+  gfx::Rect LocalRootToScreenDIPs(const gfx::Rect&,
+                                  const LocalFrameView*) const override;
   float WindowToViewportScalar(LocalFrame*, const float) const override;
   const display::ScreenInfo& GetScreenInfo(LocalFrame&) const override;
   const display::ScreenInfos& GetScreenInfos(LocalFrame&) const override;
diff --git a/third_party/blink/renderer/core/page/chrome_client_impl_test.cc b/third_party/blink/renderer/core/page/chrome_client_impl_test.cc
index 3c8300e..e6ccebd 100644
--- a/third_party/blink/renderer/core/page/chrome_client_impl_test.cc
+++ b/third_party/blink/renderer/core/page/chrome_client_impl_test.cc
@@ -209,7 +209,7 @@
   void DidChooseColor(const Color& color) override {}
   void DidEndChooser() override {}
   Element& OwnerElement() const override { return *owner_element_; }
-  gfx::Rect ElementRectRelativeToViewport() const override {
+  gfx::Rect ElementRectRelativeToLocalRoot() const override {
     return gfx::Rect();
   }
   Color CurrentColor() override { return Color(); }
diff --git a/third_party/blink/renderer/core/style/computed_style.cc b/third_party/blink/renderer/core/style/computed_style.cc
index 7ded70e..7f3f8fb 100644
--- a/third_party/blink/renderer/core/style/computed_style.cc
+++ b/third_party/blink/renderer/core/style/computed_style.cc
@@ -271,6 +271,11 @@
       (old_style.ScrollTimelineAxis() != new_style.ScrollTimelineAxis())) {
     return true;
   }
+  if ((old_style.ViewTimelineName() != new_style.ViewTimelineName()) ||
+      (old_style.ViewTimelineAxis() != new_style.ViewTimelineAxis()) ||
+      (old_style.ViewTimelineInset() != new_style.ViewTimelineInset())) {
+    return true;
+  }
   return false;
 }
 
@@ -278,10 +283,14 @@
 // if they reference a named timeline which appeared/disappeared.
 static bool AffectsScrollAnimations(const ComputedStyle* old_style,
                                     const ComputedStyle* new_style) {
-  if (old_style && !old_style->ScrollTimelineName().empty())
+  if (old_style && !(old_style->ScrollTimelineName().empty() &&
+                     old_style->ViewTimelineName().empty())) {
     return true;
-  if (new_style && !new_style->ScrollTimelineName().empty())
+  }
+  if (new_style && !(new_style->ScrollTimelineName().empty() &&
+                     new_style->ViewTimelineName().empty())) {
     return true;
+  }
   return false;
 }
 
diff --git a/third_party/blink/renderer/core/testing/internals.cc b/third_party/blink/renderer/core/testing/internals.cc
index 3cfd8368..ec1fdf3 100644
--- a/third_party/blink/renderer/core/testing/internals.cc
+++ b/third_party/blink/renderer/core/testing/internals.cc
@@ -3545,10 +3545,11 @@
   return GetFrame()->Selection().SelectedTextForClipboard();
 }
 
-void Internals::setVisualViewportOffset(int x, int y) {
+void Internals::setVisualViewportOffset(int css_x, int css_y) {
   if (!GetFrame())
     return;
-  gfx::PointF offset(x, y);
+  float zoom = GetFrame()->PageZoomFactor();
+  gfx::PointF offset(css_x * zoom, css_y * zoom);
   GetFrame()->GetPage()->GetVisualViewport().SetLocation(offset);
 }
 
diff --git a/third_party/blink/renderer/core/testing/internals.h b/third_party/blink/renderer/core/testing/internals.h
index bd98347..138beac0 100644
--- a/third_party/blink/renderer/core/testing/internals.h
+++ b/third_party/blink/renderer/core/testing/internals.h
@@ -517,7 +517,8 @@
   String selectedHTMLForClipboard();
   String selectedTextForClipboard();
 
-  void setVisualViewportOffset(int x, int y);
+  // Sets the visual viewport offset within the layout viewport.
+  void setVisualViewportOffset(int css_x, int css_y);
 
   // Return true if the given use counter exists for the given document.
   // |feature| must be one of the values from the WebFeature enum.
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
index 9bdecac..c0f2ae0b 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -410,6 +410,8 @@
                           action, orientation, text_direction);
   GetNode()->DispatchEvent(*keydown);
 
+  // TODO(crbug.com/1099069): add a brief pause between keydown and keyup?
+
   // The keydown handler may have caused the node to be removed.
   if (!GetNode())
     return;
@@ -417,17 +419,7 @@
   KeyboardEvent* keyup =
       CreateKeyboardEvent(local_dom_window, WebInputEvent::Type::kKeyUp, action,
                           orientation, text_direction);
-
-  // Add a 100ms delay between keydown and keyup to make events look less
-  // evidently synthesized.
-  GetDocument()
-      ->GetTaskRunner(TaskType::kUserInteraction)
-      ->PostDelayedTask(
-          FROM_HERE,
-          WTF::BindOnce(
-              [](Node* node, KeyboardEvent* evt) { node->DispatchEvent(*evt); },
-              WrapWeakPersistent(GetNode()), WrapPersistent(keyup)),
-          base::Milliseconds(100));
+  GetNode()->DispatchEvent(*keyup);
 }
 
 AXObject* AXNodeObject::ActiveDescendant() {
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope_test.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope_test.cc
index 6eb17c76..6505190 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope_test.cc
+++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope_test.cc
@@ -6,6 +6,7 @@
 
 #include "base/synchronization/waitable_event.h"
 #include "third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/inspector/worker_devtools_params.h"
 #include "third_party/blink/renderer/core/origin_trials/origin_trial_context.h"
 #include "third_party/blink/renderer/core/script/classic_script.h"
@@ -34,7 +35,8 @@
     // This test only needs the proxy client set to avoid calling
     // PaintWorkletProxyClient::Create, but it doesn't need the dispatcher/etc.
     proxy_client_ = MakeGarbageCollected<PaintWorkletProxyClient>(
-        1, nullptr, nullptr, nullptr);
+        1, nullptr, GetFrame().GetTaskRunner(TaskType::kInternalDefault),
+        nullptr, nullptr);
     reporting_proxy_ = std::make_unique<WorkerReportingProxy>();
   }
 
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.cc
index 75f69bc..90f2057 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.cc
+++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.cc
@@ -50,13 +50,15 @@
       local_frame->LocalRootFrameWidget()->EnsureCompositorPaintDispatcher(
           &compositor_host_queue);
   return MakeGarbageCollected<PaintWorkletProxyClient>(
-      worklet_id, paint_worklet, std::move(compositor_paint_dispatcher),
-      std::move(compositor_host_queue));
+      worklet_id, paint_worklet,
+      window->GetTaskRunner(TaskType::kInternalDefault),
+      std::move(compositor_paint_dispatcher), std::move(compositor_host_queue));
 }
 
 PaintWorkletProxyClient::PaintWorkletProxyClient(
     int worklet_id,
     PaintWorklet* paint_worklet,
+    scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner,
     base::WeakPtr<PaintWorkletPaintDispatcher> paint_dispatcher,
     scoped_refptr<base::SingleThreadTaskRunner> compositor_host_queue)
     : Supplement(nullptr),
@@ -64,7 +66,7 @@
       compositor_host_queue_(std::move(compositor_host_queue)),
       worklet_id_(worklet_id),
       state_(RunState::kUninitialized),
-      main_thread_runner_(Thread::MainThread()->GetDeprecatedTaskRunner()),
+      main_thread_runner_(std::move(main_thread_runner)),
       paint_worklet_(paint_worklet) {
   DCHECK(IsMainThread());
 }
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.h b/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.h
index 0f2edec3..be4371e7 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.h
+++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.h
@@ -49,6 +49,7 @@
   PaintWorkletProxyClient(
       int worklet_id,
       PaintWorklet*,
+      scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner,
       base::WeakPtr<PaintWorkletPaintDispatcher> compositor_paintee,
       scoped_refptr<base::SingleThreadTaskRunner> compositor_host_queue);
 
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client_test.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client_test.cc
index ff46e48..541efdac 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client_test.cc
+++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client_test.cc
@@ -61,8 +61,8 @@
     dispatcher_ = std::make_unique<PaintWorkletPaintDispatcher>();
     fake_compositor_thread_runner_ = base::MakeRefCounted<FakeTaskRunner>();
     proxy_client_ = MakeGarbageCollected<PaintWorkletProxyClient>(
-        1, paint_worklet_, dispatcher_->GetWeakPtr(),
-        fake_compositor_thread_runner_);
+        1, paint_worklet_, GetFrame().GetTaskRunner(TaskType::kInternalDefault),
+        dispatcher_->GetWeakPtr(), fake_compositor_thread_runner_);
     reporting_proxy_ = std::make_unique<WorkerReportingProxy>();
   }
 
@@ -137,15 +137,17 @@
 
 TEST_F(PaintWorkletProxyClientTest, PaintWorkletProxyClientConstruction) {
   PaintWorkletProxyClient* proxy_client =
-      MakeGarbageCollected<PaintWorkletProxyClient>(1, nullptr, nullptr,
-                                                    nullptr);
+      MakeGarbageCollected<PaintWorkletProxyClient>(
+          1, nullptr, GetFrame().GetTaskRunner(TaskType::kInternalDefault),
+          nullptr, nullptr);
   EXPECT_EQ(proxy_client->worklet_id_, 1);
   EXPECT_EQ(proxy_client->paint_dispatcher_, nullptr);
 
   auto dispatcher = std::make_unique<PaintWorkletPaintDispatcher>();
 
   proxy_client = MakeGarbageCollected<PaintWorkletProxyClient>(
-      1, nullptr, dispatcher->GetWeakPtr(), nullptr);
+      1, nullptr, GetFrame().GetTaskRunner(TaskType::kInternalDefault),
+      dispatcher->GetWeakPtr(), nullptr);
   EXPECT_EQ(proxy_client->worklet_id_, 1);
   EXPECT_NE(proxy_client->paint_dispatcher_, nullptr);
 }
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_test.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet_test.cc
index 61f7b73..3598f62 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_worklet_test.cc
+++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_test.cc
@@ -316,7 +316,9 @@
       std::make_unique<PaintWorkletPaintDispatcher>();
   Persistent<PaintWorkletProxyClient> proxy_client =
       MakeGarbageCollected<PaintWorkletProxyClient>(
-          1, paint_worklet_to_test, dispatcher->GetWeakPtr(), nullptr);
+          1, paint_worklet_to_test,
+          GetFrame().GetTaskRunner(TaskType::kInternalDefault),
+          dispatcher->GetWeakPtr(), nullptr);
   paint_worklet_to_test->SetProxyClientForTesting(proxy_client);
 
   while (paint_worklet_to_test->NeedsToCreateGlobalScopeForTesting()) {
diff --git a/third_party/blink/renderer/modules/webcodecs/decoder_template.cc b/third_party/blink/renderer/modules/webcodecs/decoder_template.cc
index 4601ca2..0321d65f 100644
--- a/third_party/blink/renderer/modules/webcodecs/decoder_template.cc
+++ b/third_party/blink/renderer/modules/webcodecs/decoder_template.cc
@@ -259,10 +259,12 @@
     // Skip processing for requests that are canceled by a recent reset().
     if (request->reset_generation != reset_generation_) {
       if (request->resolver) {
-        // TODO(crbug.com/1229313): We might be in a Shutdown(), in which case
-        // this may actually be due to an error or close().
         request->resolver.Release()->Reject(MakeGarbageCollected<DOMException>(
-            DOMExceptionCode::kAbortError, "Aborted due to reset()"));
+            DOMExceptionCode::kAbortError,
+            shutting_down_
+                ? (shutting_down_due_to_error_ ? "Aborted due to error"
+                                               : "Aborted due to close()")
+                : "Aborted due to reset()"));
       }
       requests_.pop_front();
       continue;
@@ -491,6 +493,9 @@
   TRACE_EVENT1(kCategory, GetTraceNames()->shutdown.c_str(), "has_exception",
                !!exception);
 
+  shutting_down_ = true;
+  shutting_down_due_to_error_ = !!exception;
+
   // Abort pending work (otherwise it will never complete)
   if (pending_request_) {
     if (pending_request_->resolver) {
diff --git a/third_party/blink/renderer/modules/webcodecs/decoder_template.h b/third_party/blink/renderer/modules/webcodecs/decoder_template.h
index 0e4db7ed0..a68e933d 100644
--- a/third_party/blink/renderer/modules/webcodecs/decoder_template.h
+++ b/third_party/blink/renderer/modules/webcodecs/decoder_template.h
@@ -213,6 +213,10 @@
   // Monotonic increasing generation counter for calls to ResetAlgorithm().
   uint32_t reset_generation_ = 0;
 
+  // Set on Shutdown(), used to generate accurate abort messages.
+  bool shutting_down_ = false;
+  bool shutting_down_due_to_error_ = false;
+
   // Which state the codec is in, determining which calls we can receive.
   V8CodecState state_;
 
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_supported_limits.cc b/third_party/blink/renderer/modules/webgpu/gpu_supported_limits.cc
index 9b16b4a..66fd678 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_supported_limits.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_supported_limits.cc
@@ -27,6 +27,7 @@
   X(minUniformBufferOffsetAlignment)           \
   X(minStorageBufferOffsetAlignment)           \
   X(maxVertexBuffers)                          \
+  X(maxBufferSize)                             \
   X(maxVertexAttributes)                       \
   X(maxVertexBufferArrayStride)                \
   X(maxInterStageShaderComponents)             \
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_supported_limits.h b/third_party/blink/renderer/modules/webgpu/gpu_supported_limits.h
index 2ad28af..116419d 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_supported_limits.h
+++ b/third_party/blink/renderer/modules/webgpu/gpu_supported_limits.h
@@ -46,6 +46,7 @@
   unsigned minUniformBufferOffsetAlignment() const;
   unsigned minStorageBufferOffsetAlignment() const;
   unsigned maxVertexBuffers() const;
+  uint64_t maxBufferSize() const;
   unsigned maxVertexAttributes() const;
   unsigned maxVertexBufferArrayStride() const;
   unsigned maxInterStageShaderComponents() const;
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_supported_limits.idl b/third_party/blink/renderer/modules/webgpu/gpu_supported_limits.idl
index 13abe957..fb6a1e6 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_supported_limits.idl
+++ b/third_party/blink/renderer/modules/webgpu/gpu_supported_limits.idl
@@ -25,6 +25,7 @@
     readonly attribute unsigned long minUniformBufferOffsetAlignment;
     readonly attribute unsigned long minStorageBufferOffsetAlignment;
     readonly attribute unsigned long maxVertexBuffers;
+    readonly attribute unsigned long long maxBufferSize;
     readonly attribute unsigned long maxVertexAttributes;
     readonly attribute unsigned long maxVertexBufferArrayStride;
     readonly attribute unsigned long maxInterStageShaderComponents;
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter.cc b/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter.cc
index 1448bf3..af4f1cb 100644
--- a/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter.cc
+++ b/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter.cc
@@ -274,7 +274,8 @@
     return *fallback_reason;
   }
 
-  if (key_frame_required_) {
+  base::AutoLock auto_lock(lock_);
+  if (status_ == Status::kNeedKeyFrame) {
     // We discarded previous frame because we have too many pending buffers (see
     // logic) below. Now we need to wait for the key frame and discard
     // everything else.
@@ -284,14 +285,12 @@
     }
     DVLOG(2) << "Key frame received, resume decoding";
     // ok, we got key frame and can continue decoding
-    key_frame_required_ = false;
+    ChangeStatus(Status::kOk);
   }
 
   // Queue for decoding.
-  base::AutoLock auto_lock(lock_);
-  if (has_error_) {
+  if (status_ == Status::kError)
     return RTCVideoDecoderFallbackReason::kPreviousErrorOnDecode;
-  }
 
   if (HasSoftwareFallback(config_.codec()) &&
       pending_buffers_.size() >= kMaxPendingBuffers) {
@@ -301,11 +300,12 @@
     pending_buffers_.clear();
     // Actually we just discarded a frame. We must wait for the key frame and
     // drop any other non-key frame.
-    key_frame_required_ = true;
     if (++consecutive_error_count_ > kMaxConsecutiveErrors) {
       decode_timestamps_.clear();
+      ChangeStatus(Status::kError);
       return RTCVideoDecoderFallbackReason::kConsecutivePendingBufferOverflow;
     }
+    ChangeStatus(Status::kNeedKeyFrame);
     return DecodeResult::kErrorRequestKeyFrame;
   }
 
@@ -381,7 +381,7 @@
                              static_cast<int>(status.code()));
 
     base::AutoLock auto_lock(lock_);
-    has_error_ = true;
+    ChangeStatus(Status::kError);
     pending_buffers_.clear();
     decode_timestamps_.clear();
     return;
@@ -534,6 +534,9 @@
   }
 
   decoder_info_.implementation_name = "ExternalDecoder (" + decoder_name + ")";
+
+  base::AutoLock auto_lock(lock_);
+  ChangeStatus(Status::kNeedKeyFrame);
   return result;
 }
 
@@ -551,13 +554,15 @@
       static_cast<int32_t>(settings.max_render_resolution().Width()) *
       settings.max_render_resolution().Height();
 
+  const bool init_success = status_ != Status::kError;
   base::UmaHistogramBoolean("Media.RTCVideoDecoderInitDecodeSuccess",
-                            !has_error_);
-  if (!has_error_) {
+                            init_success);
+
+  if (init_success) {
     UMA_HISTOGRAM_ENUMERATION("Media.RTCVideoDecoderProfile", config_.profile(),
                               media::VIDEO_CODEC_PROFILE_MAX + 1);
   }
-  return !has_error_;
+  return init_success;
 }
 
 int32_t RTCVideoDecoderAdapter::Decode(const webrtc::EncodedImage& input_image,
@@ -618,13 +623,13 @@
 
   base::AutoLock auto_lock(lock_);
   decode_complete_callback_ = callback;
-  if (has_error_) {
+  if (status_ == Status::kError) {
     RecordRTCVideoDecoderFallbackReason(
         config_.codec(),
         RTCVideoDecoderFallbackReason::kPreviousErrorOnRegisterCallback);
+    return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
   }
-  return has_error_ ? WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE
-                    : WEBRTC_VIDEO_CODEC_OK;
+  return WEBRTC_VIDEO_CODEC_OK;
 }
 
 int32_t RTCVideoDecoderAdapter::Release() {
@@ -633,8 +638,8 @@
   base::AutoLock auto_lock(lock_);
   pending_buffers_.clear();
   decode_timestamps_.clear();
-  return has_error_ ? WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE
-                    : WEBRTC_VIDEO_CODEC_OK;
+  return status_ == Status::kError ? WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE
+                                   : WEBRTC_VIDEO_CODEC_OK;
 }
 
 bool RTCVideoDecoderAdapter::ShouldReinitializeForSettingHDRColorSpace(
@@ -685,6 +690,12 @@
   return result;
 }
 
+void RTCVideoDecoderAdapter::ChangeStatus(Status new_status) {
+  // It is impossible to recover once status becomes kError.
+  if (status_ != Status::kError)
+    status_ = new_status;
+}
+
 // static
 int RTCVideoDecoderAdapter::GetCurrentDecoderCountForTesting() {
   return GetDecoderCounter()->Count();
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter.h b/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter.h
index a5c337f..b43fd1e0 100644
--- a/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter.h
+++ b/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter.h
@@ -121,6 +121,11 @@
     kOk,
     kErrorRequestKeyFrame,
   };
+  enum class Status : uint8_t {
+    kOk = 0,            // Status other than kNeedKeyFrame and kError.
+    kNeedKeyFrame = 1,  // A decoder needs a key frame.
+    kError = 2,         // A decoder will never be able to decode frames.
+  };
 
   // Called on the worker thread.
   RTCVideoDecoderAdapter(media::GpuVideoAcceleratorFactories* gpu_factories,
@@ -151,6 +156,7 @@
   bool ReinitializeSync(const media::VideoDecoderConfig& config);
   void FlushOnMediaThread(FlushDoneCB flush_success_cb,
                           FlushDoneCB flush_fail_cb);
+  void ChangeStatus(Status new_status) EXCLUSIVE_LOCKS_REQUIRED(lock_);
 
   // Construction parameters.
   const scoped_refptr<base::SequencedTaskRunner> media_task_runner_;
@@ -167,14 +173,13 @@
       GUARDED_BY_CONTEXT(media_sequence_checker_);
 
   // Decoding thread members.
-  bool key_frame_required_ = true;
   // Has anything been sent to Decode() yet?
   bool have_started_decoding_ = false;
 
   // Shared members.
   base::Lock lock_;
   int32_t consecutive_error_count_ = 0;
-  bool has_error_ = false;
+  Status status_ GUARDED_BY(lock_){Status::kNeedKeyFrame};
   webrtc::DecodedImageCallback* decode_complete_callback_ = nullptr;
   // Requests that have not been submitted to the decoder yet.
   WTF::Deque<scoped_refptr<media::DecoderBuffer>> pending_buffers_;
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 11dbc4b7..59f9df3 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1505,7 +1505,8 @@
     {
       name: "LayoutNGPrinting",
       depends_on: ["LayoutNG","LayoutNGBlockFragmentation"],
-      status: "test",
+      status: "stable",
+      base_feature: "LayoutNGPrinting",
     },
     {
       name: "LayoutNGSubgrid",
diff --git a/third_party/blink/renderer/platform/wtf/text/string_concatenate.h b/third_party/blink/renderer/platform/wtf/text/string_concatenate.h
index 1d62ece4..3351385 100644
--- a/third_party/blink/renderer/platform/wtf/text/string_concatenate.h
+++ b/third_party/blink/renderer/platform/wtf/text/string_concatenate.h
@@ -110,7 +110,7 @@
   void WriteTo(UChar* destination) const;
 
  private:
-  StringTypeAdapter<char*>(char* buffer, size_t length);
+  StringTypeAdapter(char* buffer, size_t length);
 
   const char* buffer_;
   unsigned length_;
diff --git a/third_party/blink/renderer/platform/wtf/text/string_operators.h b/third_party/blink/renderer/platform/wtf/text/string_operators.h
index 2c32c6bf..30bdc6eb 100644
--- a/third_party/blink/renderer/platform/wtf/text/string_operators.h
+++ b/third_party/blink/renderer/platform/wtf/text/string_operators.h
@@ -115,7 +115,7 @@
   STACK_ALLOCATED();
 
  public:
-  StringTypeAdapter<StringAppend<StringType1, StringType2>>(
+  explicit StringTypeAdapter(
       const StringAppend<StringType1, StringType2>& buffer)
       : buffer_(buffer) {}
 
diff --git a/third_party/blink/tools/blinkpy/tool/commands/abstract_local_server_command.py b/third_party/blink/tools/blinkpy/tool/commands/abstract_local_server_command.py
deleted file mode 100644
index 8a9e3f65..0000000
--- a/third_party/blink/tools/blinkpy/tool/commands/abstract_local_server_command.py
+++ /dev/null
@@ -1,71 +0,0 @@
-# Copyright (C) 2011 Google Inc. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# 1.  Redistributions of source code must retain the above copyright
-#     notice, this list of conditions and the following disclaimer.
-# 2.  Redistributions in binary form must reproduce the above copyright
-#     notice, this list of conditions and the following disclaimer in the
-#     documentation and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-from optparse import make_option
-import threading
-
-from blinkpy.tool.commands.command import Command
-
-
-class AbstractLocalServerCommand(Command):
-    server = None
-    launch_path = '/'
-
-    def __init__(self):
-        options = [
-            make_option(
-                '--httpd-port',
-                action='store',
-                type='int',
-                default=8127,
-                help='Port to use for the HTTP server'),
-            make_option(
-                '--no-show-results',
-                action='store_false',
-                default=True,
-                dest='show_results',
-                help="Don't launch a browser with the rebaseline server"),
-        ]
-        super(AbstractLocalServerCommand, self).__init__(options=options)
-
-    def _prepare_config(self, options, args, tool):
-        raise NotImplementedError('Subclasses should implement this method.')
-
-    def execute(self, options, args, tool):
-        config = self._prepare_config(options, args, tool)
-
-        server_url = 'http://localhost:%d%s' % (options.httpd_port,
-                                                self.launch_path)
-        print('Starting server at %s' % server_url)
-        print(
-            "Use the 'Exit' link in the UI, %squitquitquit or Ctrl-C to stop" %
-            server_url)
-
-        if options.show_results:
-            # FIXME: This seems racy.
-            threading.Timer(0.1,
-                            lambda: tool.user.open_url(server_url)).start()
-
-        httpd = self.server(httpd_port=options.httpd_port, config=config)  # pylint: disable=not-callable
-        httpd.serve_forever()
diff --git a/third_party/blink/tools/blinkpy/tool/commands/rebaseline_server.py b/third_party/blink/tools/blinkpy/tool/commands/rebaseline_server.py
deleted file mode 100644
index ea0e9a06..0000000
--- a/third_party/blink/tools/blinkpy/tool/commands/rebaseline_server.py
+++ /dev/null
@@ -1,110 +0,0 @@
-# Copyright (c) 2010 Google Inc. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-"""Starts a local HTTP server which displays web test failures (given a test
-results directory), provides comparisons of expected and actual results (both
-images and text) and allows one-click rebaselining of tests.
-"""
-
-from blinkpy.common.host import Host
-from blinkpy.common.net.web_test_results import WebTestResults
-from blinkpy.web_tests.layout_package import json_results_generator
-from blinkpy.tool.commands.abstract_local_server_command import AbstractLocalServerCommand
-# pylint: disable=no-name-in-module
-from blinkpy.tool.servers.rebaseline_server import get_test_baselines, RebaselineHTTPServer, STATE_NEEDS_REBASELINE
-
-
-class TestConfig(object):
-    def __init__(self, test_port, web_tests_directory, results_directory,
-                 platforms, host):
-        self.test_port = test_port
-        self.web_tests_directory = web_tests_directory
-        self.results_directory = results_directory
-        self.platforms = platforms
-        self.host = host
-        self.filesystem = host.filesystem
-        self.git = host.git()
-
-
-class RebaselineServer(AbstractLocalServerCommand):
-    name = 'rebaseline-server'
-    help_text = __doc__
-    show_in_main_help = True
-    argument_names = '/path/to/results/directory'
-
-    server = RebaselineHTTPServer
-
-    def __init__(self):
-        super(RebaselineServer, self).__init__()
-        self._test_config = None
-
-    def _gather_baselines(self, results_json):
-        # Rebaseline server and it's associated JavaScript expected the tests subtree to
-        # be key-value pairs instead of hierarchical.
-        # FIXME: make the rebaseline server use the hierarchical tree.
-        new_tests_subtree = {}
-
-        def gather_baselines_for_test(result):
-            if result.did_pass_or_run_as_expected():
-                return
-            result_dict = result.result_dict()
-            result_dict['state'] = STATE_NEEDS_REBASELINE
-            result_dict['baselines'] = get_test_baselines(
-                result.test_name(), self._test_config)
-            new_tests_subtree[result.test_name()] = result_dict
-
-        WebTestResults(results_json).for_each_test(gather_baselines_for_test)
-        results_json['tests'] = new_tests_subtree
-
-    def _prepare_config(self, options, args, tool):
-        results_directory = args[0]
-        host = Host()
-
-        print('Parsing full_results.json...')
-        results_json_path = host.filesystem.join(results_directory,
-                                                 'full_results.json')
-        results_json = json_results_generator.load_json(
-            host.filesystem, results_json_path)
-
-        port = tool.port_factory.get()
-        web_tests_directory = port.web_tests_dir()
-        platforms = host.filesystem.listdir(
-            host.filesystem.join(web_tests_directory, 'platform'))
-        self._test_config = TestConfig(port, web_tests_directory,
-                                       results_directory, platforms, host)
-
-        print('Gathering current baselines...')
-        self._gather_baselines(results_json)
-
-        return {
-            'test_config': self._test_config,
-            'results_json': results_json,
-            'platforms_json': {
-                'platforms': platforms,
-                'defaultPlatform': port.name(),
-            },
-        }
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
index d043da8..d632987 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
+++ b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
@@ -2126,6 +2126,7 @@
 crbug.com/1121942 printing/fixedpos-in-multicol.html [ Failure ]
 crbug.com/1121942 printing/flexbox-with-overflow-in-bottom-aligned-fixedpos.html [ Pass ]
 crbug.com/1121942 printing/multi-page-background.html [ Failure ]
+crbug.com/1121942 printing/page-break-avoid.html [ Failure ]
 crbug.com/1121942 printing/width-overflow.html [ Pass ]
 crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-001.html [ Failure ]
 crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-002.html [ Failure ]
diff --git a/third_party/blink/web_tests/FlagExpectations/force-renderer-accessibility b/third_party/blink/web_tests/FlagExpectations/force-renderer-accessibility
index b8089b92..8b9a9d90 100644
--- a/third_party/blink/web_tests/FlagExpectations/force-renderer-accessibility
+++ b/third_party/blink/web_tests/FlagExpectations/force-renderer-accessibility
@@ -21,6 +21,8 @@
 crbug.com/1350162 external/wpt/html/semantics/interactive-elements/the-dialog-element/inert-svg-hittest.html [ Crash ]
 # Should not receive unincluded child:
 external/wpt/inert/dynamic-inert-on-focused-element.html [ Crash ]
+# SVG text selection does not work correctly:
+svg/text/select-x-list-4.svg [ Crash ]
 # Unknown cause:
 crbug.com/1225856 virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-display-none-editable.html [ Crash ]
 
@@ -30,13 +32,6 @@
 fast/forms/month/month-picker-ax.html [ Failure Pass ]
 # isUseCounted() difference:
 virtual/disable-frequency-capping-for-overlay-popup-detection/http/tests/subresource_filter/overlay_popup_ad/overlay-popup-non-ad-followed-by-ad.html [ Failure Pass ]
-# ax_position.h#467 -- IsIgnored():
-external/wpt/fullscreen/crashtests/chrome-1312699.html [ Crash ]
-http/tests/devtools/console/console-format.js [ Crash ]
-virtual/plz-dedicated-worker/http/tests/devtools/console/console-format.js [ Crash ]
-media/video-prefixed-fullscreen.html [ Crash ]
-media/video-controls-attribute-fullscreen.html [ Crash ]
-media/autoplay-muted.html [ Crash ]
 # Unknown cause:
 virtual/disable-frequency-capping-for-overlay-popup-detection/http/tests/subresource_filter/overlay_popup_ad/overlay-popup-ad-fixed-position.html [ Failure Pass ]
 virtual/disable-frequency-capping-for-overlay-popup-detection/http/tests/subresource_filter/overlay_popup_ad/overlay-popup-image-ad-fixed-position.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index e99163f..1efbd6fd 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -2306,7 +2306,6 @@
 crbug.com/1121942 printing/fixed-positioned-headers-and-footers-larger-than-page.html [ Crash Failure ]
 crbug.com/1121942 printing/fixed-positioned-overflow-scroll.html [ Crash Failure ]
 crbug.com/1121942 printing/flexbox-with-overflow-in-bottom-aligned-fixedpos.html [ Crash Failure ]
-crbug.com/1121942 printing/page-break-avoid.html [ Failure ]
 crbug.com/377696 printing/setPrinting.html [ Failure ]
 crbug.com/1121942 printing/width-overflow.html [ Failure ]
 
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 7b2c806c..425d81b 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -1302,5 +1302,11 @@
     "platforms": ["Linux", "Mac", "Win"],
     "bases": ["wpt_internal/js/shared_memory"],
     "args": ["--enable-features=JavaScriptExperimentalSharedMemory"]
+  },
+  {
+    "prefix": "scoped-custom-element-registry-disabled",
+    "platforms": ["Linux", "Mac", "Win"],
+    "bases": ["external/wpt/custom-elements/scoped-registry"],
+    "args": ["--disable-blink-features=ScopedCustomElementRegistry"]
   }
 ]
diff --git a/third_party/blink/web_tests/accessibility/aria-slider-increment-decrement.html b/third_party/blink/web_tests/accessibility/aria-slider-increment-decrement.html
index f158e0e..93a3bd94 100644
--- a/third_party/blink/web_tests/accessibility/aria-slider-increment-decrement.html
+++ b/third_party/blink/web_tests/accessibility/aria-slider-increment-decrement.html
@@ -15,21 +15,9 @@
        aria-valuemin="0"
        aria-valuemax="10"
        aria-valuenow="5"></div>
-  <div id="slider-rtl"
-       class="rtl"
-       role="slider"
-       aria-valuemin="0"
-       aria-valuemax="10"
-       aria-valuenow="5"></div>
-  <div id="slider-vertical"
-       role="slider"
-       aria-orientation="vertical"
-       aria-valuemin="0"
-       aria-valuemax="10"
-       aria-valuenow="5"></div>
 
 <script>
-  function checkEvent(event, target, expected_key) {
+  function checkEvent(event, expected_key) {
     if (expected_key == "ArrowUp") {
       assert_equals(event.code, "ArrowUp", "event.code on " + event.type);
       assert_equals(event.key, "ArrowUp", "event.key on " + event.type);
@@ -54,123 +42,132 @@
 
     assert_true(event.isTrusted, "event.isTrusted on " + event.type);
     assert_equals(event.charCode, 0, "event.charCode on " + event.type);
-    assert_equals(event.target, target, "event.target on " + event.type);
-    assert_equals(event.srcElement, target, "event.srcElement on " + event.type);
+    assert_equals(event.target, slider, "event.target on " + event.type);
+    assert_equals(event.srcElement, slider, "event.srcElement on " + event.type);
     assert_true(event.bubbles, "event.bubbles on " + event.type);
     assert_false(event.defaultPrevented, "event.defaultPrevented on " + event.type);
   }
 
-  // Test horizontal, left-to-right slider
+  var slider = document.getElementById("slider");
+  var axSlider = accessibilityController.accessibleElementById("slider");
 
-  async_test(function(t) {
-    var slider = document.getElementById("slider");
-    var axSlider = accessibilityController.accessibleElementById("slider");
+  // Tests for horizontal, left-to-right slider
 
-    // Listener for keydown event after increment action.
-    slider.addEventListener("keydown", t.step_func((event) => {
-      checkEvent(event, slider, "ArrowRight");
-    }), { once: true });
+  promise_test(function(t) {
+    return new Promise(resolve => {
+      slider.addEventListener("keydown", resolve);
+      axSlider.increment();
+    }).then(event => {
+      checkEvent(event, "ArrowRight");
+    });
+  }, "check that sending an increment event to an ARIA slider generates a right arrow keydown event");
 
-    // Listener for keyup event after increment action.
-    // It sets up a new set of listeners and runs the decrement action.
-    slider.addEventListener("keyup", t.step_func((event) => {
-      checkEvent(event, slider, "ArrowRight");
+  promise_test(function(t) {
+    return new Promise(resolve => {
+      slider.addEventListener("keyup", resolve);
+      axSlider.increment();
+    }).then(event => {
+      checkEvent(event, "ArrowRight");
+    });
+  }, "check that sending an increment event to an ARIA slider generates a right arrow keyup event");
 
-      // Listener for keydown event after decrement action.
-      slider.addEventListener("keydown", t.step_func((event) => {
-        checkEvent(event, slider, "ArrowLeft");
-      }), { once: true });
+  promise_test(function(t) {
+    return new Promise(resolve => {
+      slider.addEventListener("keydown", resolve);
+      axSlider.decrement();
+    }).then(event => {
+      checkEvent(event, "ArrowLeft");
+    });
+  }, "check that sending a decrement event to an ARIA slider generates a left arrow keydown event");
 
-      // Listener for keyup event after decrement action.
-      slider.addEventListener("keyup", t.step_func_done((event) => {
-        checkEvent(event, slider, "ArrowLeft");
-      }), { once: true });
+  promise_test(function(t) {
+    return new Promise(resolve => {
+      slider.addEventListener("keyup", resolve);
+      axSlider.decrement();
+    }).then(event => {
+      checkEvent(event, "ArrowLeft");
+    });
+  }, "check that sending a decrement event to an ARIA slider generates a left arrow keyup event");
 
-      window.setTimeout(() => {
-        axSlider.decrement();
-      }, 0);
-    }), { once: true });
+  // Tests for horizontal, right-to-left slider
 
-    window.setTimeout(
-        t.unreached_func("didn't get all key events within 1000ms"), 1000);
+  promise_test(function(t) {
+    slider.classList.toggle("rtl");
 
-    axSlider.increment();
-  }, "Check that running increment and decrement actions on an ARIA slider generates the corresponding keydown and keyup events");
+    return new Promise(resolve => {
+      slider.addEventListener("keydown", resolve);
+      axSlider.increment();
+    }).then(event => {
+      checkEvent(event, "ArrowLeft");
+    });
+  }, "check that sending an increment event to a RTL ARIA slider generates a left arrow keydown event");
 
-  // Test horizontal, right-to-left slider
+  promise_test(function(t) {
+    return new Promise(resolve => {
+      slider.addEventListener("keyup", resolve);
+      axSlider.increment();
+    }).then(event => {
+      checkEvent(event, "ArrowLeft");
+    });
+  }, "check that sending an increment event to a RTL ARIA slider generates a left arrow keyup event");
 
-  async_test(function(t) {
-    var slider = document.getElementById("slider-rtl");
-    var axSlider = accessibilityController.accessibleElementById("slider-rtl");
+  promise_test(function(t) {
+    return new Promise(resolve => {
+      slider.addEventListener("keydown", resolve);
+      axSlider.decrement();
+    }).then(event => {
+      checkEvent(event, "ArrowRight");
+    });
+  }, "check that sending a decrement event to a RTL ARIA slider generates a right arrow keydown event");
 
-    // Listener for keydown event after increment action.
-    slider.addEventListener("keydown", t.step_func((event) => {
-      checkEvent(event, slider, "ArrowLeft");
-    }), { once: true });
+  promise_test(function(t) {
+    return new Promise(resolve => {
+      slider.addEventListener("keyup", resolve);
+      axSlider.decrement();
+    }).then(event => {
+      checkEvent(event, "ArrowRight");
+    });
+  }, "check that sending a decrement event to a RTL ARIA slider generates a right arrow keyup event");
 
-    // Listener for keyup event after increment action.
-    // It sets up a new set of listeners and runs the decrement action.
-    slider.addEventListener("keyup", t.step_func((event) => {
-      checkEvent(event, slider, "ArrowLeft");
+  // Tests for vertical slider
 
-      // Listener for keydown event after decrement action.
-      slider.addEventListener("keydown", t.step_func((event) => {
-        checkEvent(event, slider, "ArrowRight");
-      }), { once: true });
+  promise_test(function(t) {
+    slider.setAttribute('aria-orientation', 'vertical');
 
-      // Listener for keyup event after decrement action.
-      slider.addEventListener("keyup", t.step_func_done((event) => {
-        checkEvent(event, slider, "ArrowRight");
-        t.done();
-      }), { once: true });
+    return new Promise(resolve => {
+      slider.addEventListener("keydown", resolve);
+      axSlider.increment();
+    }).then(event => {
+      checkEvent(event, "ArrowUp");
+    });
+  }, "check that sending an increment event to a vertical ARIA slider generates an up arrow keydown event");
 
-      window.setTimeout(() => {
-        axSlider.decrement();
-      }, 0);
-    }), { once: true });
+  promise_test(function(t) {
+    return new Promise(resolve => {
+      slider.addEventListener("keyup", resolve);
+      axSlider.increment();
+    }).then(event => {
+      checkEvent(event, "ArrowUp");
+    });
+  }, "check that sending an increment event to a vertical ARIA slider generates an up arrow keyup event");
 
-    window.setTimeout(
-        t.unreached_func("didn't get all key events within 1000ms"), 1000);
+  promise_test(function(t) {
+    return new Promise(resolve => {
+      slider.addEventListener("keydown", resolve);
+      axSlider.decrement();
+    }).then(event => {
+      checkEvent(event, "ArrowDown");
+    });
+  }, "check that sending a decrement event to a vertical ARIA slider generates a down arrow keydown event");
 
-    axSlider.increment();
-  }, "Check that running increment and decrement actions on a RTL ARIA slider generates the corresponding keydown and keyup events");
-
-  // Test vertical slider
-
-  async_test(function(t) {
-    var slider = document.getElementById("slider-vertical");
-    var axSlider = accessibilityController.accessibleElementById("slider-vertical");
-
-    // Listener for keydown event after increment action.
-    slider.addEventListener("keydown", t.step_func((event) => {
-      checkEvent(event, slider, "ArrowUp");
-    }), { once: true });
-
-    // Listener for keyup event after increment action.
-    // It sets up a new set of listeners and runs the decrement action.
-    slider.addEventListener("keyup", t.step_func((event) => {
-      checkEvent(event, slider, "ArrowUp");
-
-      // Listener for keydown event after decrement action.
-      slider.addEventListener("keydown", t.step_func((event) => {
-        checkEvent(event, slider, "ArrowDown");
-      }), { once: true });
-
-      // Listener for keyup event after decrement action.
-      slider.addEventListener("keyup", t.step_func_done((event) => {
-        checkEvent(event, slider, "ArrowDown");
-      }), { once: true });
-
-      window.setTimeout(() => {
-        axSlider.decrement();
-      }, 0);
-    }), { once: true });
-
-    window.setTimeout(
-        t.unreached_func("didn't get all key events within 1000ms"), 1000);
-
-    axSlider.increment();
-  }, "Check that running increment and decrement actions on a vertical ARIA slider generates the corresponding keydown and keyup events");
+  promise_test(function(t) {
+    return new Promise(resolve => {
+      slider.addEventListener("keyup", resolve);
+      axSlider.decrement();
+    }).then(event => {
+      checkEvent(event, "ArrowDown");
+    });
+  }, "check that sending a decrement event to a vertical ARIA slider generates a down arrow keyup event");
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/accessibility/native-slider-synthesized-events-on-action.html b/third_party/blink/web_tests/accessibility/native-slider-synthesized-events-on-action.html
index ae2d215..a53bc0c 100644
--- a/third_party/blink/web_tests/accessibility/native-slider-synthesized-events-on-action.html
+++ b/third_party/blink/web_tests/accessibility/native-slider-synthesized-events-on-action.html
@@ -11,10 +11,9 @@
 </head>
 <body>
   <input id="slider" type="range" value="0" step="1"/>
-  <input id="slider-rtl" class="rtl" type="range" value="0" step="1"/>
 
 <script>
-  function checkEvent(event, target, expected_key) {
+  function checkEvent(event, expected_key) {
     if (expected_key == "ArrowUp") {
       assert_equals(event.code, "ArrowUp", "event.code on " + event.type);
       assert_equals(event.key, "ArrowUp", "event.key on " + event.type);
@@ -39,94 +38,104 @@
 
     assert_true(event.isTrusted, "event.isTrusted on " + event.type);
     assert_equals(event.charCode, 0, "event.charCode on " + event.type);
-    assert_equals(event.target, target, "event.target on " + event.type);
-    assert_equals(event.srcElement, target, "event.srcElement on " + event.type);
+    assert_equals(event.target, slider, "event.target on " + event.type);
+    assert_equals(event.srcElement, slider, "event.srcElement on " + event.type);
     assert_true(event.bubbles, "event.bubbles on " + event.type);
     assert_false(event.defaultPrevented, "event.defaultPrevented on " + event.type);
   }
 
+  var slider = document.getElementById("slider");
+  var axSlider = accessibilityController.accessibleElementById("slider");
+
   test(function(t) {
     assert_true(internals.runtimeFlags.synthesizedKeyboardEventsForAccessibilityActionsEnabled);
   }, "Make sure that keyboard event synthesis is enabled");
 
-  // Test left-to-right slider
+  // Tests for left-to-right slider
 
-  async_test(function(t) {
-    var slider = document.getElementById("slider");
-    var axSlider = accessibilityController.accessibleElementById("slider");
-
-    // Listener for keydown event after increment action.
-    slider.addEventListener("keydown", t.step_func((event) => {
-      checkEvent(event, slider, "ArrowRight");
-    }), { once: true });
-
-    // Listener for keyup event after increment action.
-    // It sets up a new set of listeners and runs the decrement action.
-    slider.addEventListener("keyup", t.step_func((event) => {
-      checkEvent(event, slider, "ArrowRight");
+  promise_test(function(t) {
+    return new Promise(resolve => {
+      slider.addEventListener("keydown", resolve);
+      axSlider.increment();
+    }).then(event => {
+      checkEvent(event, "ArrowRight");
       assert_equals(slider.value, "1", "slider value after increase");
+    });
+  }, "check that sending an increment event to a native slider generates a right arrow keydown event");
 
-      // Listener for keydown event after decrement action.
-      slider.addEventListener("keydown", t.step_func((event) => {
-        checkEvent(event, slider, "ArrowLeft");
-      }), { once: true });
+  promise_test(function(t) {
+    return new Promise(resolve => {
+      slider.addEventListener("keyup", resolve);
+      axSlider.increment();
+    }).then(event => {
+      checkEvent(event, "ArrowRight");
+      assert_equals(slider.value, "2", "slider value after increase");
+    });
+  }, "check that sending an increment event to a native slider generates a right arrow keyup event");
 
-      // Listener for keyup event after decrement action.
-      slider.addEventListener("keyup", t.step_func_done((event) => {
-        checkEvent(event, slider, "ArrowLeft");
-        assert_equals(slider.value, "0", "slider value after decrease");
-      }), { once: true });
+  promise_test(function(t) {
+    return new Promise(resolve => {
+      slider.addEventListener("keydown", resolve);
+      axSlider.decrement();
+    }).then(event => {
+      checkEvent(event, "ArrowLeft");
+      assert_equals(slider.value, "1", "slider value after decrease");
+    });
+  }, "check that sending a decrement event to a native slider generates a left arrow keydown event");
 
-      window.setTimeout(() => {
-        axSlider.decrement();
-      }, 0);
-    }), { once: true });
+  promise_test(function(t) {
+    return new Promise(resolve => {
+      slider.addEventListener("keyup", resolve);
+      axSlider.decrement();
+    }).then(event => {
+      checkEvent(event, "ArrowLeft");
+      assert_equals(slider.value, "0", "slider value after decrease");
+    });
+  }, "check that sending a decrement event to a native slider generates a left arrow keyup event");
 
-    window.setTimeout(
-        t.unreached_func("didn't get all key events within 1000ms"), 1000);
+  // Tests for right-to-left slider
 
-    axSlider.increment();
-  }, "Check that running increment and decrement actions on a native slider generates the corresponding keydown and keyup events");
+  promise_test(function(t) {
+    slider.classList.toggle("rtl");
 
-  // Test right-to-left slider
-
-  async_test(function(t) {
-    var slider = document.getElementById("slider-rtl");
-    var axSlider = accessibilityController.accessibleElementById("slider-rtl");
-
-    // Listener for keydown event after increment action.
-    slider.addEventListener("keydown", t.step_func((event) => {
-      checkEvent(event, slider, "ArrowLeft");
-    }), { once: true });
-
-    // Listener for keyup event after increment action.
-    // It sets up a new set of listeners and runs the decrement action.
-    slider.addEventListener("keyup", t.step_func((event) => {
-      checkEvent(event, slider, "ArrowLeft");
+    return new Promise(resolve => {
+      slider.addEventListener("keydown", resolve);
+      axSlider.increment();
+    }).then(event => {
+      checkEvent(event, "ArrowLeft");
       assert_equals(slider.value, "1", "slider value after increase");
+    });
+  }, "check that sending an increment event to a RTL native slider generates a left arrow keydown event");
 
-      // Listener for keydown event after decrement action.
-      slider.addEventListener("keydown", t.step_func((event) => {
-        checkEvent(event, slider, "ArrowRight");
-      }), { once: true });
+  promise_test(function(t) {
+    return new Promise(resolve => {
+      slider.addEventListener("keyup", resolve);
+      axSlider.increment();
+    }).then(event => {
+      checkEvent(event, "ArrowLeft");
+      assert_equals(slider.value, "2", "slider value after increase");
+    });
+  }, "check that sending an increment event to a RTL native slider generates a left arrow keyup event");
 
-      // Listener for keyup event after decrement action.
-      slider.addEventListener("keyup", t.step_func_done((event) => {
-        checkEvent(event, slider, "ArrowRight");
-        assert_equals(slider.value, "0", "slider value after decrease");
-        t.done();
-      }), { once: true });
+  promise_test(function(t) {
+    return new Promise(resolve => {
+      slider.addEventListener("keydown", resolve);
+      axSlider.decrement();
+    }).then(event => {
+      checkEvent(event, "ArrowRight");
+      assert_equals(slider.value, "1", "slider value after decrease");
+    });
+  }, "check that sending a decrement event to a RTL native slider generates a right arrow keydown event");
 
-      window.setTimeout(() => {
-        axSlider.decrement();
-      }, 0);
-    }), { once: true });
-
-    window.setTimeout(
-        t.unreached_func("didn't get all key events within 1000ms"), 1000);
-
-    axSlider.increment();
-  }, "Check that running increment and decrement actions on a RTL native slider generates the corresponding keydown and keyup events");
+  promise_test(function(t) {
+    return new Promise(resolve => {
+      slider.addEventListener("keyup", resolve);
+      axSlider.decrement();
+    }).then(event => {
+      checkEvent(event, "ArrowRight");
+      assert_equals(slider.value, "0", "slider value after decrease");
+    });
+  }, "check that sending a decrement event to a RTL native slider generates a right arrow keyup event");
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/clip-path-animation-svg-ref.html b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/clip-path-animation-svg-ref.html
new file mode 100644
index 0000000..6cc3b4d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/clip-path-animation-svg-ref.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<style>
+  .clipped {
+    background-color: green;
+    stroke: black;
+    stroke-width: 3;
+    fill: red;
+    clip-path: circle(35% at 50% 50%);
+  }
+
+  .svg {
+    width: 100px;
+    height: 100px;
+  }
+
+</style>
+
+<body>
+  <svg class="svg">
+    <circle class="clipped" cx="50" cy="50" r="40" />
+  </svg>
+
+</body>
+
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/clip-path-animation-svg-zoom-ref.html b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/clip-path-animation-svg-zoom-ref.html
new file mode 100644
index 0000000..8d620ceb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/clip-path-animation-svg-zoom-ref.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<style>
+  .clipped {
+    background-color: green;
+    stroke: black;
+    stroke-width: 3;
+    fill: red;
+    clip-path: circle(35% at 50% 50%);
+  }
+
+  .svg {
+    width: 100px;
+    height: 100px;
+    zoom: 1.25;
+  }
+
+</style>
+
+<body>
+  <svg class="svg">
+    <circle class="clipped" cx="40" cy="40" r="40" />
+  </svg>
+
+</body>
+
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/clip-path-animation-svg-zoom.html b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/clip-path-animation-svg-zoom.html
new file mode 100644
index 0000000..c79c098
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/clip-path-animation-svg-zoom.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel="help" href="https://drafts.csswg.org/css-shapes-1/#basic-shape-interpolation">
+<link rel="match" href="clip-path-animation-svg-zoom-ref.html">
+<style>
+  .clipped {
+    background-color: green;
+    stroke: black;
+    stroke-width: 3;
+    fill: red;
+    /* Use a long animation that start at 50% progress where the slope of the
+     selected timing function is zero. By setting up the animation in this way,
+     we accommodate lengthy delays in running the test without a potential drift
+     in the animated property value. This is important for avoiding flakes,
+     especially on debug builds. The screenshots are taken as soon as the
+     animation is ready, thus the long animation duration has no bearing on
+     the actual duration of the test. */
+    animation: clippath 1000000s cubic-bezier(0, 1, 1, 0) -500000s;
+  }
+
+  .svg {
+    width: 100px;
+    height: 100px;
+    zoom: 1.25;
+  }
+
+  @keyframes clippath {
+    0% {
+      clip-path: circle(50% at 50% 50%);
+    }
+
+    100% {
+      clip-path: circle(20% at 50% 50%);
+    }
+  }
+
+</style>
+<script src="/common/reftest-wait.js"></script>
+
+<body>
+  <svg class="svg">
+    <circle class="clipped" cx="40" cy="40" r="40" />
+  </svg>
+
+  <script>
+    document.getAnimations()[0].ready.then(takeScreenshot);
+  </script>
+</body>
+
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/clip-path-animation-svg.html b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/clip-path-animation-svg.html
new file mode 100644
index 0000000..676fe5e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/clip-path-animation-svg.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel="help" href="https://drafts.csswg.org/css-shapes-1/#basic-shape-interpolation">
+<link rel="match" href="clip-path-animation-svg-ref.html">
+<style>
+  .clipped {
+    background-color: green;
+    stroke: black;
+    stroke-width: 3;
+    fill: red;
+    /* Use a long animation that start at 50% progress where the slope of the
+     selected timing function is zero. By setting up the animation in this way,
+     we accommodate lengthy delays in running the test without a potential drift
+     in the animated property value. This is important for avoiding flakes,
+     especially on debug builds. The screenshots are taken as soon as the
+     animation is ready, thus the long animation duration has no bearing on
+     the actual duration of the test. */
+    animation: clippath 1000000s cubic-bezier(0, 1, 1, 0) -500000s;
+  }
+
+  .svg {
+    width: 100px;
+    height: 100px;
+  }
+
+  @keyframes clippath {
+    0% {
+      clip-path: circle(50% at 50% 50%);
+    }
+
+    100% {
+      clip-path: circle(20% at 50% 50%);
+    }
+  }
+
+</style>
+<script src="/common/reftest-wait.js"></script>
+
+<body>
+  <svg class="svg">
+    <circle class="clipped" cx="50" cy="50" r="40" />
+  </svg>
+
+  <script>
+    document.getAnimations()[0].ready.then(takeScreenshot());
+  </script>
+</body>
+
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/clip-path-animation-zoom-ref.html b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/clip-path-animation-zoom-ref.html
new file mode 100644
index 0000000..9912619
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/clip-path-animation-zoom-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<style>
+  .container {
+    width: 80px;
+    height: 80px;
+    zoom: 1.25;
+    background-color: green;
+    clip-path: circle(35% at 35% 35%);
+  }
+
+</style>
+
+<body>
+  <div class="container"></div>
+</body>
+
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/clip-path-animation-zoom.html b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/clip-path-animation-zoom.html
new file mode 100644
index 0000000..5573318
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-masking/clip-path/animations/clip-path-animation-zoom.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel="help" href="https://drafts.csswg.org/css-shapes-1/#basic-shape-interpolation">
+<link rel="match" href="clip-path-animation-zoom-ref.html">
+<style>
+  .container {
+    width: 80px;
+    height: 80px;
+    background-color: green;
+    zoom: 1.25;
+    /* Use a long animation that start at 50% progress where the slope of the
+     selected timing function is zero. By setting up the animation in this way,
+     we accommodate lengthy delays in running the test without a potential drift
+     in the animated property value. This is important for avoiding flakes,
+     especially on debug builds. The screenshots are taken as soon as the
+     animation is ready, thus the long animation duration has no bearing on
+     the actual duration of the test. */
+    animation: clippath 10000000s cubic-bezier(0, 1, 1, 0) -5000000s;
+  }
+
+  @keyframes clippath {
+    0% {
+      clip-path: circle(50% at 50% 50%);
+    }
+
+    100% {
+      clip-path: circle(20% at 20% 20%);
+    }
+  }
+
+</style>
+<script src="/common/reftest-wait.js"></script>
+
+<body>
+  <div class="container"></div>
+
+  <script>
+    document.getAnimations()[0].ready.then(takeScreenshot);
+  </script>
+</body>
+
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/custom-elements/CustomElementRegistry-constructor.html b/third_party/blink/web_tests/external/wpt/custom-elements/scoped-registry/CustomElementRegistry-constructor.tentative.html
similarity index 100%
rename from third_party/blink/web_tests/wpt_internal/custom-elements/CustomElementRegistry-constructor.html
rename to third_party/blink/web_tests/external/wpt/custom-elements/scoped-registry/CustomElementRegistry-constructor.tentative.html
diff --git a/third_party/blink/web_tests/external/wpt/custom-elements/scoped-registry/ShadowRoot-init-registry.tentative.html b/third_party/blink/web_tests/external/wpt/custom-elements/scoped-registry/ShadowRoot-init-registry.tentative.html
new file mode 100644
index 0000000..f9bc5b5b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/custom-elements/scoped-registry/ShadowRoot-init-registry.tentative.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<meta name="author" title="Xiaocheng Hu" href="mailto:xiaochengh@chromium.org">
+<meta name="assert" content="User code can attach CustomElementRegistry to ShadowRoot">
+<link rel="help" href="https://wicg.github.io/webcomponents/proposals/Scoped-Custom-Element-Registries">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+function createShadowHost(testObject) {
+  let element = document.createElement('div');
+  testObject.add_cleanup(() => element.remove());
+  document.body.appendChild(element);
+  return element;
+}
+
+test(function() {
+  let host = createShadowHost(this);
+  let shadow = host.attachShadow({mode: 'open'});
+  assert_equals(shadow.registry, null);
+}, 'ShadowRoot.registry is null if not explicitly specified');
+
+test(function() {
+  let host = createShadowHost(this);
+  let shadow = host.attachShadow({mode: 'open', registry: window.customElements});
+  assert_equals(shadow.registry, window.customElements);
+}, 'Attach the global registry to a shadow root');
+
+test(function() {
+  let host = createShadowHost(this);
+  let registry = new CustomElementRegistry();
+  let shadow = host.attachShadow({mode: 'open', registry});
+  assert_equals(shadow.registry, registry);
+}, 'Attach a non-global registry to a shadow root');
+
+test(function() {
+  let registry = new CustomElementRegistry();
+  let host1 = createShadowHost(this);
+  let shadow1 = host1.attachShadow({mode: 'open', registry});
+  let host2 = createShadowHost(this);
+  let shadow2 = host2.attachShadow({mode: 'open', registry});
+  assert_equals(shadow1.registry, registry);
+  assert_equals(shadow2.registry, registry);
+}, 'Attach the same registry to multiple shadow roots');
+
+test(function() {
+  let host = createShadowHost(this);
+  let shadow = host.attachShadow({mode: 'open'});
+  shadow.registry = new CustomElementRegistry();
+  assert_equals(shadow.registry, null);
+}, 'Attaching registry to shadow root can only be done during initialization');
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-dynamic.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-dynamic.html
new file mode 100644
index 0000000..483fa36
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-dynamic.html
@@ -0,0 +1,170 @@
+<!DOCTYPE html>
+<title>Changes to view-timeline are reflected in dependent elements</title>
+<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#view-timeline-shorthand">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/web-animations/testcommon.js"></script>
+<style>
+  @keyframes anim {
+    from { z-index: 0; }
+    to { z-index: 100; }
+  }
+  .scroller {
+    overflow: hidden;
+    width: 100px;
+    height: 100px;
+  }
+  .scroller > div {
+    height: 100px;
+  }
+  #target {
+    height: 0px;
+    z-index: -1;
+  }
+</style>
+<main id=main></main>
+<script>
+  function inflate(t, template) {
+    t.add_cleanup(() => main.replaceChildren());
+    main.append(template.content.cloneNode(true));
+    main.offsetTop;
+  }
+  async function scrollTop(e, value) {
+    e.scrollTop = value;
+    await waitForNextFrame();
+  }
+  async function scrollLeft(e, value) {
+    e.scrollLeft = value;
+    await waitForNextFrame();
+  }
+</script>
+
+<template id=dynamic_view_timeline_name>
+  <style>
+    .timeline {
+      view-timeline-name: t1;
+    }
+    #target {
+      animation: anim 1s linear t1;
+    }
+  </style>
+  <div id=scroller class=scroller>
+    <div id=div75></div>
+    <div id=div25></div>
+    <div id=div_before></div>
+    <div id=target></div>
+  </div>
+</template>
+<script>
+  promise_test(async (t) => {
+    inflate(t, dynamic_view_timeline_name);
+
+    await scrollTop(scroller, 50);
+
+    // scrollTop=50 is 75% for div75.
+    div75.classList.add('timeline');
+    assert_equals(getComputedStyle(target).zIndex, '75');
+
+    // scrollTop=50 is 25% for div25.
+    div25.classList.add('timeline');
+    assert_equals(getComputedStyle(target).zIndex, '25');
+
+    // scrollTop=50 is before the timeline start for div_before.
+    div_before.classList.add('timeline');
+    assert_equals(getComputedStyle(target).zIndex, '-1');
+    // Scroll to 25% (for div_before) to verify that we're linked to that
+    // timeline.
+    await scrollTop(scroller, 150);
+    assert_equals(getComputedStyle(target).zIndex, '25');
+
+    // Now we should be back to div25's timeline, although with the new
+    // scrollTop=150, it's actually at 75%.
+    div_before.classList.remove('timeline');
+    assert_equals(getComputedStyle(target).zIndex, '75');
+  }, 'Dynamically changing view-timeline-name');
+</script>
+
+<template id=dynamic_view_timeline_axis>
+  <style>
+    #timeline {
+      width: 100px;
+      height: 100px;
+      margin: 100px;
+      view-timeline-name: t1;
+    }
+    #target {
+      animation: anim 1s linear t1;
+    }
+  </style>
+  <div id=scroller class=scroller>
+    <div id=timeline style="background: red;"></div>
+    <div id=target></div>
+  </div>
+</template>
+<script>
+  promise_test(async (t) => {
+    inflate(t, dynamic_view_timeline_axis);
+
+    await scrollTop(scroller, 50); // 25% (vertical)
+    await scrollLeft(scroller, 20); // 10% (horizontal)
+
+    assert_equals(getComputedStyle(target).zIndex, '25');
+    timeline.style.viewTimelineAxis = 'horizontal';
+    assert_equals(getComputedStyle(target).zIndex, '10');
+  }, 'Dynamically changing view-timeline-axis');
+</script>
+
+<template id=dynamic_view_timeline_inset>
+  <style>
+    #timeline {
+      width: 100px;
+      height: 100px;
+      margin: 100px;
+      view-timeline-name: t1;
+    }
+    #target {
+      animation: anim 1s linear t1;
+    }
+  </style>
+  <div id=scroller class=scroller>
+    <div id=timeline style="background: red;"></div>
+    <div id=target></div>
+  </div>
+</template>
+<script>
+  promise_test(async (t) => {
+    inflate(t, dynamic_view_timeline_inset);
+
+    await scrollTop(scroller, 50); // 25% (without inset).
+
+    assert_equals(getComputedStyle(target).zIndex, '25');
+    timeline.style.viewTimelineInset = '0px 50px';
+    assert_equals(getComputedStyle(target).zIndex, '0');
+  }, 'Dynamically changing view-timeline-inset');
+</script>
+
+<template id=timeline_display_none>
+  <style>
+    #timeline {
+      view-timeline-name: t1;
+    }
+    #target {
+      animation: anim 1s linear t1;
+    }
+  </style>
+  <div id=scroller class=scroller>
+    <div></div>
+    <div id=timeline></div>
+    <div id=target></div>
+  </div>
+</template>
+<script>
+  promise_test(async (t) => {
+    inflate(t, timeline_display_none);
+
+    await scrollTop(scroller, 50);
+    assert_equals(getComputedStyle(target).zIndex, '25');
+    timeline.style.display = 'none';
+    assert_equals(getComputedStyle(target).zIndex, '-1');
+  }, 'Element with view-timeline becoming display:none');
+</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/printing/page-break-avoid-expected.txt b/third_party/blink/web_tests/printing/page-break-avoid-expected.txt
new file mode 100644
index 0000000..9e7821bc
--- /dev/null
+++ b/third_party/blink/web_tests/printing/page-break-avoid-expected.txt
@@ -0,0 +1,14 @@
+Test for combined page-break-{before,after,inside}:avoid
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS: page number of "page1-1" is 1
+PASS: page number of "page2-1" is 2
+PASS: page number of "page2-8" is 2
+All tests passed
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/third_party/blink/web_tests/virtual/percent-based-scrolling/resources/percent-based-util.js b/third_party/blink/web_tests/virtual/percent-based-scrolling/resources/percent-based-util.js
index 0f26e7b4..a22ab32 100644
--- a/third_party/blink/web_tests/virtual/percent-based-scrolling/resources/percent-based-util.js
+++ b/third_party/blink/web_tests/virtual/percent-based-scrolling/resources/percent-based-util.js
@@ -177,7 +177,6 @@
   let visual_rect = shrink(getVisualViewportRect(), 10);
   let visual_delta = offsetFromBounds(point, visual_rect);
   let visual_target = cssVisualToCssClient(visual_delta)
-  visual_target = scaleCssToBlinkPixels(visual_target);
   internals.setVisualViewportOffset(visual_target.x, visual_target.y);
 
   // Note that layout viewport = client
diff --git a/third_party/blink/web_tests/virtual/scoped-custom-element-registry-disabled/README.md b/third_party/blink/web_tests/virtual/scoped-custom-element-registry-disabled/README.md
new file mode 100644
index 0000000..4df72124
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scoped-custom-element-registry-disabled/README.md
@@ -0,0 +1,4 @@
+This suite runs tests with `--disable-blink-features=ScopedCustomElementRegistry`.
+
+It verifies that the experimental feature is not leaked to the web as the flag
+is disabled.
diff --git a/third_party/blink/web_tests/virtual/scoped-custom-element-registry-disabled/external/wpt/custom-elements/scoped-registry/CustomElementRegistry-constructor.tentative-expected.txt b/third_party/blink/web_tests/virtual/scoped-custom-element-registry-disabled/external/wpt/custom-elements/scoped-registry/CustomElementRegistry-constructor.tentative-expected.txt
new file mode 100644
index 0000000..2b7d9b2
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scoped-custom-element-registry-disabled/external/wpt/custom-elements/scoped-registry/CustomElementRegistry-constructor.tentative-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Create non-global CustomElementRegistry and add definitions Illegal constructor
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/scoped-custom-element-registry-disabled/external/wpt/custom-elements/scoped-registry/ShadowRoot-init-registry.tentative-expected.txt b/third_party/blink/web_tests/virtual/scoped-custom-element-registry-disabled/external/wpt/custom-elements/scoped-registry/ShadowRoot-init-registry.tentative-expected.txt
new file mode 100644
index 0000000..637ebbdf
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/scoped-custom-element-registry-disabled/external/wpt/custom-elements/scoped-registry/ShadowRoot-init-registry.tentative-expected.txt
@@ -0,0 +1,8 @@
+This is a testharness.js-based test.
+FAIL ShadowRoot.registry is null if not explicitly specified assert_equals: expected (object) null but got (undefined) undefined
+FAIL Attach the global registry to a shadow root assert_equals: expected (object) object "[object CustomElementRegistry]" but got (undefined) undefined
+FAIL Attach a non-global registry to a shadow root Illegal constructor
+FAIL Attach the same registry to multiple shadow roots Illegal constructor
+FAIL Attaching registry to shadow root can only be done during initialization Illegal constructor
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index ff88ca42..09a8e47 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -8658,6 +8658,7 @@
     getter onslotchange
     getter pictureInPictureElement
     getter pointerLockElement
+    getter registry
     getter slotAssignment
     getter styleSheets
     method constructor
diff --git a/third_party/ipcz/src/BUILD.gn b/third_party/ipcz/src/BUILD.gn
index 1d699b0..1884f9a4 100644
--- a/third_party/ipcz/src/BUILD.gn
+++ b/third_party/ipcz/src/BUILD.gn
@@ -228,6 +228,7 @@
     "ipcz/node_link_memory.h",
     "ipcz/node_messages.h",
     "ipcz/node_name.h",
+    "ipcz/node_type.h",
     "ipcz/parcel.h",
     "ipcz/parcel_queue.h",
     "ipcz/portal.h",
diff --git a/third_party/ipcz/src/ipcz/node.cc b/third_party/ipcz/src/ipcz/node.cc
index 7f39ef4..b9dd93d 100644
--- a/third_party/ipcz/src/ipcz/node.cc
+++ b/third_party/ipcz/src/ipcz/node.cc
@@ -80,46 +80,56 @@
   return broker_link_;
 }
 
-void Node::SetBrokerLink(Ref<NodeLink> link) {
-  std::vector<BrokerLinkCallback> callbacks;
-  {
-    absl::MutexLock lock(&mutex_);
-    ABSL_ASSERT(!broker_link_);
-    broker_link_ = link;
-    broker_link_callbacks_.swap(callbacks);
-  }
-
-  for (auto& callback : callbacks) {
-    callback(link);
-  }
-}
-
 void Node::SetAssignedName(const NodeName& name) {
   absl::MutexLock lock(&mutex_);
   ABSL_ASSERT(!assigned_name_.is_valid());
   assigned_name_ = name;
 }
 
-bool Node::AddLink(const NodeName& remote_node_name, Ref<NodeLink> link) {
+bool Node::AddConnection(const NodeName& remote_node_name,
+                         Connection connection) {
+  std::vector<BrokerLinkCallback> callbacks;
   {
-    absl::MutexLock lock(&mutex_);
-    auto [it, inserted] = node_links_.insert({remote_node_name, link});
-    if (inserted) {
-      return true;
+    absl::ReleasableMutexLock lock(&mutex_);
+    auto [it, inserted] = connections_.insert({remote_node_name, connection});
+    if (!inserted) {
+      lock.Release();
+      connection.link->Deactivate();
+      return false;
+    }
+
+    if (connection.link->remote_node_type() == Type::kBroker) {
+      // The first connection accepted by a non-broker must be a connection to
+      // its own broker.
+      ABSL_ASSERT(connections_.size() == 1);
+      ABSL_ASSERT(!broker_link_);
+      broker_link_ = connection.link;
+      broker_link_callbacks_.swap(callbacks);
     }
   }
 
-  link->Deactivate();
-  return false;
+  for (auto& callback : callbacks) {
+    callback(connection.link);
+  }
+  return true;
+}
+
+absl::optional<Node::Connection> Node::GetConnection(const NodeName& name) {
+  absl::MutexLock lock(&mutex_);
+  auto it = connections_.find(name);
+  if (it == connections_.end()) {
+    return absl::nullopt;
+  }
+  return it->second;
 }
 
 Ref<NodeLink> Node::GetLink(const NodeName& name) {
   absl::MutexLock lock(&mutex_);
-  auto it = node_links_.find(name);
-  if (it == node_links_.end()) {
+  auto it = connections_.find(name);
+  if (it == connections_.end()) {
     return nullptr;
   }
-  return it->second;
+  return it->second.link;
 }
 
 NodeName Node::GenerateRandomName() const {
@@ -152,15 +162,16 @@
 }
 
 void Node::EstablishLink(const NodeName& name, EstablishLinkCallback callback) {
-  Ref<NodeLink> broker;
-  Ref<NodeLink> link;
+  Ref<NodeLink> existing_link;
+  Ref<NodeLink> our_broker;
   {
     absl::MutexLock lock(&mutex_);
-    broker = broker_link_;
-    auto it = node_links_.find(name);
-    if (it != node_links_.end()) {
-      link = it->second;
-    } else if (type_ == Type::kNormal && broker) {
+    auto it = connections_.find(name);
+    if (it != connections_.end()) {
+      existing_link = it->second.link;
+    } else if (type_ == Type::kNormal && broker_link_) {
+      our_broker = broker_link_;
+
       auto [pending_it, inserted] = pending_introductions_.insert({name, {}});
       pending_it->second.push_back(std::move(callback));
       if (!inserted) {
@@ -171,11 +182,13 @@
     }
   }
 
-  if (broker && !link) {
-    broker->RequestIntroduction(name);
-  } else {
-    callback(link.get());
+  if (our_broker) {
+    our_broker->RequestIntroduction(name);
+    return;
   }
+
+  // NOTE: `existing_link` may be null here, implying that we have failed.
+  callback(existing_link.get());
 }
 
 void Node::HandleIntroductionRequest(NodeLink& from_node_link,
@@ -189,50 +202,19 @@
            << " received introduction request for " << for_node.ToString()
            << " from " << requestor.ToString();
 
-  // A key which uniquely identifies the pair of nodes being introduced
-  // regardless of who requested the introduction.
-  const auto key = (requestor < for_node)
-                       ? IntroductionKey(requestor, for_node)
-                       : IntroductionKey(for_node, requestor);
-
-  Ref<NodeLink> target_link;
-  {
-    absl::MutexLock lock(&mutex_);
-    auto it = node_links_.find(for_node);
-    if (it != node_links_.end()) {
-      target_link = it->second;
-
-      auto [intro_it, inserted] = in_progress_introductions_.insert(key);
-      if (!inserted) {
-        // We're already introducing the same two nodes, so drop this request.
-        return;
-      }
-    }
-  }
-
-  if (!target_link) {
+  const absl::optional<Connection> target_connection = GetConnection(for_node);
+  if (!target_connection) {
     from_node_link.RejectIntroduction(for_node);
     return;
   }
 
-  DriverMemoryWithMapping buffer = NodeLinkMemory::AllocateMemory(driver_);
-  auto [transport_for_target, transport_for_requestor] =
-      DriverTransport::CreatePair(driver_, target_link->transport().get(),
-                                  from_node_link.transport().get());
-  target_link->AcceptIntroduction(
-      requestor, LinkSide::kA, from_node_link.remote_protocol_version(),
-      std::move(transport_for_target), buffer.memory.Clone());
-  from_node_link.AcceptIntroduction(
-      for_node, LinkSide::kB, target_link->remote_protocol_version(),
-      std::move(transport_for_requestor), std::move(buffer.memory));
-
-  absl::MutexLock lock(&mutex_);
-  in_progress_introductions_.erase(key);
+  IntroduceRemoteNodes(from_node_link, *target_connection->link);
 }
 
 void Node::AcceptIntroduction(NodeLink& from_node_link,
                               const NodeName& name,
                               LinkSide side,
+                              Node::Type remote_node_type,
                               uint32_t remote_protocol_version,
                               Ref<DriverTransport> transport,
                               Ref<NodeLinkMemory> memory) {
@@ -247,14 +229,19 @@
            << from_node_link.remote_node_name().ToString();
 
   Ref<NodeLink> new_link = NodeLink::CreateInactive(
-      WrapRefCounted(this), side, local_name, name, Type::kNormal,
+      WrapRefCounted(this), side, local_name, name, remote_node_type,
       remote_protocol_version, transport, memory);
   ABSL_ASSERT(new_link);
 
   std::vector<EstablishLinkCallback> callbacks;
   {
     absl::MutexLock lock(&mutex_);
-    auto [link_it, inserted] = node_links_.insert({name, new_link});
+    auto [connection_it, inserted] =
+        connections_.insert({name,
+                             {
+                                 .link = new_link,
+                                 .broker = WrapRefCounted(&from_node_link),
+                             }});
     if (!inserted) {
       // If both nodes race to request an introduction to each other, the
       // broker may send redundant introductions. It does however take care to
@@ -324,21 +311,21 @@
   return true;
 }
 
-void Node::DropLink(const NodeName& name) {
+void Node::DropConnection(const NodeName& name) {
   Ref<NodeLink> link;
   bool lost_broker = false;
   {
     absl::MutexLock lock(&mutex_);
-    auto it = node_links_.find(name);
-    if (it == node_links_.end()) {
+    auto it = connections_.find(name);
+    if (it == connections_.end()) {
       return;
     }
-    link = std::move(it->second);
-    node_links_.erase(it);
+    link = std::move(it->second.link);
+    connections_.erase(it);
 
     const NodeName& local_name = link->local_node_name();
     DVLOG(4) << "Node " << local_name.ToString() << " dropping "
-             << " link to " << link->remote_node_name().ToString();
+             << "link to " << link->remote_node_name().ToString();
     if (link == broker_link_) {
       DVLOG(4) << "Node " << local_name.ToString() << " lost its broker link";
       broker_link_.reset();
@@ -375,16 +362,16 @@
 }
 
 void Node::ShutDown() {
-  NodeLinkMap node_links;
+  ConnectionMap connections;
   {
     absl::MutexLock lock(&mutex_);
-    std::swap(node_links_, node_links);
+    connections_.swap(connections);
     broker_link_.reset();
     allocation_delegate_link_.reset();
   }
 
-  for (const auto& entry : node_links) {
-    entry.second->Deactivate();
+  for (const auto& entry : connections) {
+    entry.second.link->Deactivate();
   }
 
   CancelAllIntroductions();
@@ -404,4 +391,36 @@
   }
 }
 
+void Node::IntroduceRemoteNodes(NodeLink& first, NodeLink& second) {
+  // Ensure that no other thread does the same introduction concurrently.
+  const NodeName& first_name = first.remote_node_name();
+  const NodeName& second_name = second.remote_node_name();
+  const auto key = (first_name < second_name)
+                       ? IntroductionKey(first_name, second_name)
+                       : IntroductionKey(second_name, first_name);
+  {
+    absl::MutexLock lock(&mutex_);
+    auto [it, inserted] = in_progress_introductions_.insert(key);
+    if (!inserted) {
+      return;
+    }
+  }
+
+  DriverMemoryWithMapping buffer = NodeLinkMemory::AllocateMemory(driver_);
+  auto [transport_for_first_node, transport_for_second_node] =
+      DriverTransport::CreatePair(driver_, first.transport().get(),
+                                  second.transport().get());
+  first.AcceptIntroduction(second_name, LinkSide::kA, second.remote_node_type(),
+                           second.remote_protocol_version(),
+                           std::move(transport_for_first_node),
+                           buffer.memory.Clone());
+  second.AcceptIntroduction(first_name, LinkSide::kB, first.remote_node_type(),
+                            first.remote_protocol_version(),
+                            std::move(transport_for_second_node),
+                            std::move(buffer.memory));
+
+  absl::MutexLock lock(&mutex_);
+  in_progress_introductions_.erase(key);
+}
+
 }  // namespace ipcz
diff --git a/third_party/ipcz/src/ipcz/node.h b/third_party/ipcz/src/ipcz/node.h
index e79704fe..c9e3e628 100644
--- a/third_party/ipcz/src/ipcz/node.h
+++ b/third_party/ipcz/src/ipcz/node.h
@@ -6,6 +6,8 @@
 #define IPCZ_SRC_IPCZ_NODE_H_
 
 #include <functional>
+#include <utility>
+#include <vector>
 
 #include "ipcz/api_object.h"
 #include "ipcz/driver_memory.h"
@@ -13,9 +15,11 @@
 #include "ipcz/link_side.h"
 #include "ipcz/node_messages.h"
 #include "ipcz/node_name.h"
+#include "ipcz/node_type.h"
 #include "third_party/abseil-cpp/absl/container/flat_hash_map.h"
 #include "third_party/abseil-cpp/absl/container/flat_hash_set.h"
 #include "third_party/abseil-cpp/absl/synchronization/mutex.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/abseil-cpp/absl/types/span.h"
 
 namespace ipcz {
@@ -29,17 +33,19 @@
 // introduced to each other exclusively through such brokers.
 class Node : public APIObjectImpl<Node, APIObject::kNode> {
  public:
-  enum class Type {
-    // A broker node assigns its own name and is able to assign names to other
-    // nodes upon connection. Brokers are trusted to introduce nodes to each
-    // other upon request, and brokers may connect to other brokers in order to
-    // share information and effectively bridge two node networks together.
-    kBroker,
+  using Type = NodeType;
 
-    // A "normal" (i.e. non-broker) node is assigned a permanent name by the
-    // first broker node it connects to, and it can only make contact with other
-    // nodes by requesting an introduction from that broker.
-    kNormal,
+  // State regarding a connection to a single remote node.
+  struct Connection {
+    // The NodeLink used to communicate with the remote node.
+    Ref<NodeLink> link;
+
+    // The NodeLink used to communicate with the broker of the remote node's
+    // network. If the remote node belongs to the same network as the local
+    // node, then this is the same link the local node's `broker_link_`. If the
+    // local node *is* the broker for the remote node on `link`, then this link
+    // is null.
+    Ref<NodeLink> broker;
   };
 
   // Constructs a new node of the given `type`, using `driver` to support IPC.
@@ -69,30 +75,23 @@
   // Gets a reference to the node's broker link, if it has one.
   Ref<NodeLink> GetBrokerLink();
 
-  // Sets this node's broker link, which is used e.g. to make introduction
-  // requests.
-  //
-  // This is called by a NodeConnector implementation after accepting a valid
-  // handshake message from a broker node, and `link` will be used as this
-  // node's permanent broker.
-  //
-  // Note that like any other NodeLink used by this Node, the same `link` must
-  // also be registered via AddLink() to associate it with its remote Node's
-  // name. This is also done by NodeConnector.
-  void SetBrokerLink(Ref<NodeLink> link);
-
   // Sets this node's assigned name as given by a broker. NodeConnector is
   // responsible for calling on non-broker Nodes this after receiving the
   // expected handshake from a broker. Must not be called on broker nodes, as
   // they assign their own name at construction time.
   void SetAssignedName(const NodeName& name);
 
-  // Registers a new NodeLink for the given `remote_node_name`.
-  bool AddLink(const NodeName& remote_node_name, Ref<NodeLink> link);
+  // Registers a new connection for the given `remote_node_name`.
+  bool AddConnection(const NodeName& remote_node_name, Connection connection);
+
+  // Returns a copy of the Connection to the remote node named by `name`, or
+  // null if this node has no connection to that node.
+  absl::optional<Node::Connection> GetConnection(const NodeName& name);
 
   // Returns a reference to the NodeLink used by this Node to communicate with
   // the remote node identified by `name`; or null if this node has no NodeLink
-  // connected to that node.
+  // connected to that node. This is shorthand for GetConnection() in the common
+  // case where the caller only wants the underlying NodeLink.
   Ref<NodeLink> GetLink(const NodeName& name);
 
   // Generates a new random NodeName using this node's driver as a source of
@@ -138,6 +137,7 @@
   void AcceptIntroduction(NodeLink& from_node_link,
                           const NodeName& name,
                           LinkSide side,
+                          Node::Type remote_node_type,
                           uint32_t remote_protocol_version,
                           Ref<DriverTransport> transport,
                           Ref<NodeLinkMemory> memory);
@@ -154,8 +154,8 @@
   // the relay source directly.
   bool AcceptRelayedMessage(msg::AcceptRelayedMessage& accept);
 
-  // Drops this node's link to the named node, if one exists.
-  void DropLink(const NodeName& name);
+  // Drops this node's connection to the named node, if one exists.
+  void DropConnection(const NodeName& name);
 
   // Asynchronously waits for this Node to acquire a broker link and then
   // invokes `callback` with it. If this node already has a broker link then the
@@ -174,6 +174,10 @@
   // failure.
   void CancelAllIntroductions();
 
+  // Creates a new transport and link memory and sends introduction messages to
+  // introduce the remote node on `first` to the remote node on `second`.
+  void IntroduceRemoteNodes(NodeLink& first, NodeLink& second);
+
   const Type type_;
   const IpczDriver& driver_;
   const IpczDriverHandle driver_node_;
@@ -199,8 +203,8 @@
   // if this is a non-broker node. If this is a broker node, these links are
   // either assigned by this node itself, or received from other brokers in the
   // system.
-  using NodeLinkMap = absl::flat_hash_map<NodeName, Ref<NodeLink>>;
-  NodeLinkMap node_links_ ABSL_GUARDED_BY(mutex_);
+  using ConnectionMap = absl::flat_hash_map<NodeName, Connection>;
+  ConnectionMap connections_ ABSL_GUARDED_BY(mutex_);
 
   // A map of other nodes to which this node is waiting for an introduction from
   // `broker_link_`. Once such an introduction is received, all callbacks for
diff --git a/third_party/ipcz/src/ipcz/node_connector.cc b/third_party/ipcz/src/ipcz/node_connector.cc
index a9f4bb3..3b12a7a 100644
--- a/third_party/ipcz/src/ipcz/node_connector.cc
+++ b/third_party/ipcz/src/ipcz/node_connector.cc
@@ -71,13 +71,12 @@
              << broker_name_.ToString() << " from new node "
              << new_remote_node_name_.ToString();
 
-    AcceptConnection(
-        NodeLink::CreateActive(
-            node_, LinkSide::kA, broker_name_, new_remote_node_name_,
-            Node::Type::kNormal, connect.params().protocol_version, transport_,
-            NodeLinkMemory::Create(node_,
-                                   std::move(link_memory_allocation_.mapping))),
-        LinkSide::kA, connect.params().num_initial_portals);
+    Ref<NodeLink> link = NodeLink::CreateActive(
+        node_, LinkSide::kA, broker_name_, new_remote_node_name_,
+        Node::Type::kNormal, connect.params().protocol_version, transport_,
+        NodeLinkMemory::Create(node_,
+                               std::move(link_memory_allocation_.mapping)));
+    AcceptConnection({.link = link}, connect.params().num_initial_portals);
     return true;
   }
 
@@ -131,12 +130,11 @@
         connect.params().protocol_version, transport_,
         NodeLinkMemory::Create(node_, buffer_memory.Map()));
     node_->SetAssignedName(connect.params().receiver_name);
-    node_->SetBrokerLink(new_link);
     if ((flags_ & IPCZ_CONNECT_NODE_TO_ALLOCATION_DELEGATE) != 0) {
       node_->SetAllocationDelegate(new_link);
     }
 
-    AcceptConnection(std::move(new_link), LinkSide::kB,
+    AcceptConnection({.link = new_link, .broker = new_link},
                      connect.params().num_initial_portals);
     return true;
   }
@@ -179,13 +177,13 @@
 
     broker_link_->ReferNonBroker(
         std::move(transport_for_broker_), checked_cast<uint32_t>(num_portals()),
-        [connector = WrapRefCounted(this)](
+        [connector = WrapRefCounted(this), broker = broker_link_](
             Ref<NodeLink> link_to_referred_node,
             uint32_t remote_num_initial_portals) {
           if (link_to_referred_node) {
-            connector->AcceptConnection(std::move(link_to_referred_node),
-                                        LinkSide::kA,
-                                        remote_num_initial_portals);
+            connector->AcceptConnection(
+                {.link = link_to_referred_node, .broker = broker},
+                remote_num_initial_portals);
           } else {
             connector->RejectConnection();
           }
@@ -255,11 +253,14 @@
         broker_protocol_version, transport_,
         NodeLinkMemory::Create(node_, broker_buffer.Map()));
     node_->SetAssignedName(connect.params().name);
-    node_->SetBrokerLink(broker_link);
     if ((flags_ & IPCZ_CONNECT_NODE_TO_ALLOCATION_DELEGATE) != 0) {
       node_->SetAllocationDelegate(broker_link);
     }
-    node_->AddLink(connect.params().broker_name, std::move(broker_link));
+    node_->AddConnection(connect.params().broker_name,
+                         {
+                             .link = broker_link,
+                             .broker = broker_link,
+                         });
 
     const uint32_t referrer_protocol_version = std::min(
         connect.params().referrer_protocol_version, msg::kProtocolVersion);
@@ -269,7 +270,7 @@
         referrer_protocol_version, std::move(referrer_transport),
         NodeLinkMemory::Create(node_, referrer_buffer.Map()));
 
-    AcceptConnection(referrer_link, LinkSide::kB,
+    AcceptConnection({.link = referrer_link, .broker = broker_link},
                      connect.params().num_initial_portals);
     referrer_link->Activate();
     return true;
@@ -324,7 +325,7 @@
         node_, LinkSide::kA, broker_name_, referred_node_name_,
         Node::Type::kNormal, protocol_version, transport_,
         NodeLinkMemory::Create(node_, std::move(link_memory_.mapping)));
-    AcceptConnection(link_to_referree, LinkSide::kA, /*num_remote_portals=*/0);
+    AcceptConnection({.link = link_to_referree}, /*num_remote_portals=*/0);
 
     // Now we can create a new link to introduce both clients -- the referrer
     // and the referree -- to each other.
@@ -511,21 +512,20 @@
 
 NodeConnector::~NodeConnector() = default;
 
-void NodeConnector::AcceptConnection(Ref<NodeLink> new_link,
-                                     LinkSide link_side,
+void NodeConnector::AcceptConnection(Node::Connection connection,
                                      uint32_t num_remote_portals) {
-  node_->AddLink(new_link->remote_node_name(), new_link);
+  node_->AddConnection(connection.link->remote_node_name(), connection);
   if (callback_) {
-    callback_(new_link);
+    callback_(connection.link);
   }
-  EstablishWaitingPortals(std::move(new_link), link_side, num_remote_portals);
+  EstablishWaitingPortals(std::move(connection.link), num_remote_portals);
 }
 
 void NodeConnector::RejectConnection() {
   if (callback_) {
     callback_(nullptr);
   }
-  EstablishWaitingPortals(nullptr, LinkSide::kA, 0);
+  EstablishWaitingPortals(nullptr, 0);
   if (transport_) {
     transport_->Deactivate();
   }
@@ -541,7 +541,6 @@
 }
 
 void NodeConnector::EstablishWaitingPortals(Ref<NodeLink> to_link,
-                                            LinkSide link_side,
                                             size_t max_valid_portals) {
   ABSL_ASSERT(to_link != nullptr || max_valid_portals == 0);
   const size_t num_valid_portals =
@@ -550,7 +549,7 @@
     const Ref<Router> router = waiting_portals_[i]->router();
     Ref<RouterLink> link = to_link->AddRemoteRouterLink(
         SublinkId(i), to_link->memory().GetInitialRouterLinkState(i),
-        LinkType::kCentral, link_side, router);
+        LinkType::kCentral, to_link->link_side(), router);
     if (link) {
       router->SetOutwardLink(std::move(link));
     } else {
diff --git a/third_party/ipcz/src/ipcz/node_connector.h b/third_party/ipcz/src/ipcz/node_connector.h
index 260ee30..96d3dc2 100644
--- a/third_party/ipcz/src/ipcz/node_connector.h
+++ b/third_party/ipcz/src/ipcz/node_connector.h
@@ -12,13 +12,13 @@
 #include "ipcz/driver_transport.h"
 #include "ipcz/ipcz.h"
 #include "ipcz/link_side.h"
+#include "ipcz/node.h"
 #include "ipcz/node_messages.h"
 #include "third_party/abseil-cpp/absl/types/span.h"
 #include "util/ref_counted.h"
 
 namespace ipcz {
 
-class Node;
 class NodeLink;
 class Portal;
 
@@ -73,11 +73,9 @@
 
   size_t num_portals() const { return waiting_portals_.size(); }
 
-  // Invoked once by the implementation when it has completed the handshake.
-  // `new_link` has already assumed ownership of the underlying transport and
-  // is listening for incoming messages on it. Destroys `this`.
-  void AcceptConnection(Ref<NodeLink> new_link,
-                        LinkSide link_side,
+  // Invoked once by the implementation when it has completed its handshake.
+  // Destroys `this`.
+  void AcceptConnection(Node::Connection connection,
                         uint32_t num_remote_portals);
 
   // Invoked if the transport observes an error before receiving the expected
@@ -95,9 +93,7 @@
 
  private:
   bool ActivateTransport();
-  void EstablishWaitingPortals(Ref<NodeLink> to_link,
-                               LinkSide link_side,
-                               size_t max_valid_portals);
+  void EstablishWaitingPortals(Ref<NodeLink> to_link, size_t max_valid_portals);
 
   const ConnectCallback callback_;
 };
diff --git a/third_party/ipcz/src/ipcz/node_link.cc b/third_party/ipcz/src/ipcz/node_link.cc
index 4a46839..7f384a7 100644
--- a/third_party/ipcz/src/ipcz/node_link.cc
+++ b/third_party/ipcz/src/ipcz/node_link.cc
@@ -191,6 +191,7 @@
 
 void NodeLink::AcceptIntroduction(const NodeName& name,
                                   LinkSide side,
+                                  Node::Type remote_node_type,
                                   uint32_t remote_protocol_version,
                                   Ref<DriverTransport> transport,
                                   DriverMemory memory) {
@@ -199,6 +200,7 @@
   msg::AcceptIntroduction accept;
   accept.params().name = name;
   accept.params().link_side = side;
+  accept.params().remote_node_type = remote_node_type;
   accept.params().remote_protocol_version = remote_protocol_version;
   accept.params().transport =
       accept.AppendDriverObject(transport->TakeDriverObject());
@@ -466,8 +468,8 @@
       accept.TakeDriverObject(accept.params().transport));
   node()->AcceptIntroduction(
       *this, accept.params().name, accept.params().link_side,
-      accept.params().remote_protocol_version, std::move(transport),
-      NodeLinkMemory::Create(node(), std::move(mapping)));
+      accept.params().remote_node_type, accept.params().remote_protocol_version,
+      std::move(transport), NodeLinkMemory::Create(node(), std::move(mapping)));
   return true;
 }
 
@@ -796,7 +798,7 @@
   }
 
   Ref<NodeLink> self = WrapRefCounted(this);
-  node_->DropLink(remote_node_name_);
+  node_->DropConnection(remote_node_name_);
 }
 
 void NodeLink::WaitForParcelFragmentToResolve(
diff --git a/third_party/ipcz/src/ipcz/node_link.h b/third_party/ipcz/src/ipcz/node_link.h
index 3bb7ee5..1b358f1 100644
--- a/third_party/ipcz/src/ipcz/node_link.h
+++ b/third_party/ipcz/src/ipcz/node_link.h
@@ -137,6 +137,7 @@
   // construct a new NodeLink to that node.
   void AcceptIntroduction(const NodeName& name,
                           LinkSide side,
+                          Node::Type remote_node_type,
                           uint32_t remote_protocol_version,
                           Ref<DriverTransport> transport,
                           DriverMemory memory);
diff --git a/third_party/ipcz/src/ipcz/node_link_memory_test.cc b/third_party/ipcz/src/ipcz/node_link_memory_test.cc
index c21a4799..44ba853 100644
--- a/third_party/ipcz/src/ipcz/node_link_memory_test.cc
+++ b/third_party/ipcz/src/ipcz/node_link_memory_test.cc
@@ -44,8 +44,9 @@
         node_b_, LinkSide::kB, kTestNonBrokerName, kTestBrokerName,
         Node::Type::kBroker, 0, transports.second,
         NodeLinkMemory::Create(node_b_, buffer.memory.Map()));
-    node_a_->AddLink(kTestNonBrokerName, link_a_);
-    node_b_->AddLink(kTestBrokerName, link_b_);
+    node_a_->AddConnection(kTestNonBrokerName, {.link = link_a_});
+    node_b_->AddConnection(kTestBrokerName,
+                           {.link = link_b_, .broker = link_a_});
     link_a_->Activate();
     link_b_->Activate();
   }
diff --git a/third_party/ipcz/src/ipcz/node_messages.h b/third_party/ipcz/src/ipcz/node_messages.h
index d27cff4..790502e 100644
--- a/third_party/ipcz/src/ipcz/node_messages.h
+++ b/third_party/ipcz/src/ipcz/node_messages.h
@@ -14,6 +14,7 @@
 #include "ipcz/link_side.h"
 #include "ipcz/message.h"
 #include "ipcz/node_name.h"
+#include "ipcz/node_type.h"
 #include "ipcz/router_descriptor.h"
 #include "ipcz/sequence_number.h"
 #include "ipcz/sublink_id.h"
diff --git a/third_party/ipcz/src/ipcz/node_messages_generator.h b/third_party/ipcz/src/ipcz/node_messages_generator.h
index d35d4040..8f0cc88 100644
--- a/third_party/ipcz/src/ipcz/node_messages_generator.h
+++ b/third_party/ipcz/src/ipcz/node_messages_generator.h
@@ -205,6 +205,9 @@
   // for the NodeLink it will establish over `transport`.
   IPCZ_MSG_PARAM(LinkSide, link_side)
 
+  // Indicates the type of the remote node being introduced.
+  IPCZ_MSG_PARAM(NodeType, remote_node_type)
+
   // Indicates the highest ipcz protocol version which the remote side of
   // `transport` able and willing to use according to the broker.
   IPCZ_MSG_PARAM(uint32_t, remote_protocol_version)
diff --git a/third_party/ipcz/src/ipcz/node_test.cc b/third_party/ipcz/src/ipcz/node_test.cc
index 08ec83d..20ebb84 100644
--- a/third_party/ipcz/src/ipcz/node_test.cc
+++ b/third_party/ipcz/src/ipcz/node_test.cc
@@ -58,9 +58,8 @@
         node, LinkSide::kB, name, broker_name, Node::Type::kBroker, 0,
         transports.second, NodeLinkMemory::Create(node, buffer.memory.Map()));
     node->SetAssignedName(name);
-    broker_->AddLink(name, broker_link);
-    node->AddLink(broker_name, node_link);
-    node->SetBrokerLink(node_link);
+    broker_->AddConnection(name, {.link = broker_link});
+    node->AddConnection(broker_name, {.link = node_link, .broker = node_link});
     broker_link->Activate();
     node_link->Activate();
   }
diff --git a/third_party/ipcz/src/ipcz/node_type.h b/third_party/ipcz/src/ipcz/node_type.h
new file mode 100644
index 0000000..8d245b84
--- /dev/null
+++ b/third_party/ipcz/src/ipcz/node_type.h
@@ -0,0 +1,30 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IPCZ_SRC_IPCZ_NODE_TYPE_H_
+#define IPCZ_SRC_IPCZ_NODE_TYPE_H_
+
+#include <cstdint>
+
+namespace ipcz {
+
+// Enumeration indicating the role a Node plays in its network of nodes. Note
+// that this is used by internal wire messages, so values must not be changed or
+// removed.
+enum class NodeType : uint8_t {
+  // A broker node assigns its own name and is able to assign names to other
+  // nodes upon connection. Brokers are trusted to introduce nodes to each
+  // other upon request, and brokers may connect to other brokers in order to
+  // share information and effectively bridge two node networks together.
+  kBroker,
+
+  // A "normal" (i.e. non-broker) node is assigned a permanent name by the
+  // first broker node it connects to, and it can only make contact with other
+  // nodes by requesting an introduction from that broker.
+  kNormal,
+};
+
+}  // namespace ipcz
+
+#endif  // IPCZ_SRC_IPCZ_NODE_TYPE_H_
diff --git a/third_party/ipcz/src/ipcz/router_link_test.cc b/third_party/ipcz/src/ipcz/router_link_test.cc
index 4e78789..d153c2fd 100644
--- a/third_party/ipcz/src/ipcz/router_link_test.cc
+++ b/third_party/ipcz/src/ipcz/router_link_test.cc
@@ -58,8 +58,9 @@
         node_b_, LinkSide::kB, kTestNonBrokerName, kTestBrokerName,
         Node::Type::kBroker, 0, transports.second,
         NodeLinkMemory::Create(node_b_, buffer.memory.Map()));
-    node_a_->AddLink(kTestNonBrokerName, node_link_a_);
-    node_b_->AddLink(kTestBrokerName, node_link_b_);
+    node_a_->AddConnection(kTestNonBrokerName, {.link = node_link_a_});
+    node_b_->AddConnection(kTestBrokerName,
+                           {.link = node_link_b_, .broker = node_link_b_});
   }
 
   ~TestNodePair() {
diff --git a/third_party/metrics_proto/README.chromium b/third_party/metrics_proto/README.chromium
index c2b8f06..9e4ce8bc 100644
--- a/third_party/metrics_proto/README.chromium
+++ b/third_party/metrics_proto/README.chromium
@@ -1,8 +1,8 @@
 Name: Metrics Protos
 Short Name: metrics_proto
 URL: This is the canonical public repository
-Version: 477470425
-Date: 2022/09/28 UTC
+Version: 477865304
+Date: 2022/09/30 UTC
 License: BSD
 Security Critical: Yes
 
diff --git a/third_party/metrics_proto/cast_logs.proto b/third_party/metrics_proto/cast_logs.proto
index 1408b92..e7d0c758 100644
--- a/third_party/metrics_proto/cast_logs.proto
+++ b/third_party/metrics_proto/cast_logs.proto
@@ -167,7 +167,7 @@
   repeated CastConnectionInfo cast_connection_info = 2;
 
   // Stores Cast-enabled device specific events with a various context data.
-  // Next tag: 28
+  // Next tag: 29
   message CastEventProto {
     // The name of the action, hashed by same logic used to hash user action
     // event and histogram.
@@ -217,6 +217,8 @@
 
     optional string aogh_agent_id = 21;
 
+    optional string aogh_standard_agent_id = 28;
+
     // Optional value associated with the event. For example, may be used for
     // error codes.
     message Metadata {
diff --git a/third_party/r8/README.chromium b/third_party/r8/README.chromium
index b7908f67..595e9ecc 100644
--- a/third_party/r8/README.chromium
+++ b/third_party/r8/README.chromium
@@ -120,6 +120,29 @@
   * The r8inputs.zip from --dump-inputs
   * Any relevant dexdump analysis
 
+Example R8 git-bisect script:
+```
+set -x
+CHROMIUM_SRC=/media/mheikal/code/clankium/src
+tools/gradle.py r8 || exit -1
+chmod +r $CHROMIUM_SRC/third_party/r8/lib/r8.jar
+rm -f $CHROMIUM_SRC/third_party/r8/lib/r8.jar
+cp build/libs/r8.jar $CHROMIUM_SRC/third_party/r8/lib/r8.jar
+
+cd $CHROMIUM_SRC
+exec tools/autotest.py --out-dir out/Release <Test Here>
+```
+
+You can run this script like so:
+```
+cd $R8_REPO
+git bisect start <known_good_rev> <known_bad_rev>
+git bisect run <path_to_script.sh>
+```
+Tip: When you are done, you can delete the r8 jar and gclient sync to get back
+     to ToT R8.
+     `rm -f $CHROMIUM_SRC/third_party/r8/lib/r8.jar && gclient sync`
+
 How to submit CLs to R8:
 * Request to be added to their allowlist in order to upload CLs.
 * After CLs are submitted, check the bots for build breakage.
diff --git a/third_party/shell-encryption/DEPS b/third_party/shell-encryption/DEPS
index bfe61b6..fde2330 100644
--- a/third_party/shell-encryption/DEPS
+++ b/third_party/shell-encryption/DEPS
@@ -8,6 +8,8 @@
   # Base test_runner includes.
   '+base/bind.h',
   '+base/test',
+  # Hooking up internal logging references.
+  '+base/logging.h',
   # The lib will be able to use abseil, but without being imported directly in
   # chromium.
   '+absl',
diff --git a/third_party/shell-encryption/glog/logging.h b/third_party/shell-encryption/glog/logging.h
index 3964b76..bc6667b0 100644
--- a/third_party/shell-encryption/glog/logging.h
+++ b/third_party/shell-encryption/glog/logging.h
@@ -1,9 +1,10 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
+// Copyright 2020 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
 
 #ifndef SHELL_ENCRYPTION_GLOG_H_
 #define SHELL_ENCRYPTION_GLOG_H_
 
-#include"base/logging.h"
+#include "base/logging.h"
 
-#endif	// SHELL_ENCRYPTION_GLOG_H_
+#endif  // SHELL_ENCRYPTION_GLOG_H_
diff --git a/third_party/tensorflow_models/BUILD.gn b/third_party/tensorflow_models/BUILD.gn
new file mode 100644
index 0000000..657709dc
--- /dev/null
+++ b/third_party/tensorflow_models/BUILD.gn
@@ -0,0 +1,56 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/sanitizers/sanitizers.gni")
+
+config("tensorflow-models-config") {
+  include_dirs = [
+    "src/research/seq_flow_lite",
+
+    # Include the "libutf" and "icu4c" subdirectories which redirects the
+    # includes for those libraries to their Chromium versions without needing
+    # patches.
+    "shims",
+  ]
+}
+
+static_library("tflite_custom_ops") {
+  sources = [
+    # Shim headers.
+    "shims/icu4c/source/common/unicode/uchar.h",
+    "shims/icu4c/source/common/unicode/utf8.h",
+    "shims/libutf/utf.h",
+
+    # When adding/removing entries from this list, also change |update.sh| in
+    # this directory.
+    "src/research/seq_flow_lite/tf_ops/projection_normalizer_util.cc",
+    "src/research/seq_flow_lite/tf_ops/projection_normalizer_util.h",
+    "src/research/seq_flow_lite/tf_ops/projection_util.cc",
+    "src/research/seq_flow_lite/tf_ops/projection_util.h",
+    "src/research/seq_flow_lite/tf_ops/skipgram_finder.cc",
+    "src/research/seq_flow_lite/tf_ops/skipgram_finder.h",
+    "src/research/seq_flow_lite/tflite_ops/denylist.cc",
+    "src/research/seq_flow_lite/tflite_ops/denylist.h",
+    "src/research/seq_flow_lite/tflite_ops/denylist_skipgram.cc",
+    "src/research/seq_flow_lite/tflite_ops/denylist_skipgram.h",
+    "src/research/seq_flow_lite/tflite_ops/quantization_util.h",
+    "src/research/seq_flow_lite/tflite_ops/sequence_string_projection.cc",
+    "src/research/seq_flow_lite/tflite_ops/sequence_string_projection.h",
+    "src/research/seq_flow_lite/tflite_ops/tflite_qrnn_pooling.cc",
+    "src/research/seq_flow_lite/tflite_ops/tflite_qrnn_pooling.h",
+  ]
+
+  deps = [
+    "//third_party/abseil-cpp:absl",
+    "//third_party/flatbuffers",
+    "//third_party/icu",
+    "//third_party/tflite",
+    "//third_party/utf",
+  ]
+
+  configs -= [ "//build/config/compiler:chromium_code" ]
+  configs += [ "//build/config/compiler:no_chromium_code" ]
+
+  public_configs = [ ":tensorflow-models-config" ]
+}
diff --git a/third_party/tensorflow_models/DEPS b/third_party/tensorflow_models/DEPS
new file mode 100644
index 0000000..a1fd34e4
--- /dev/null
+++ b/third_party/tensorflow_models/DEPS
@@ -0,0 +1,13 @@
+include_rules = [
+  "+absl",
+  "+flatbuffers",
+  "+icu4c",
+  "+libutf",
+  "+tensorflow",
+  "+third_party/icu",
+  "+third_party/utf",
+
+  # Relative includes within src/
+  "+tf_ops",
+  "+tflite_ops",
+]
diff --git a/third_party/tensorflow_models/DIR_METADATA b/third_party/tensorflow_models/DIR_METADATA
new file mode 100644
index 0000000..fb4428a
--- /dev/null
+++ b/third_party/tensorflow_models/DIR_METADATA
@@ -0,0 +1,3 @@
+monorail: {
+  component: "Internals>OptimizationGuide"
+}
diff --git a/third_party/tensorflow_models/OWNERS b/third_party/tensorflow_models/OWNERS
new file mode 100644
index 0000000..582cfc4
--- /dev/null
+++ b/third_party/tensorflow_models/OWNERS
@@ -0,0 +1,4 @@
+mcrouse@chromium.org
+robertogden@chromium.org
+sophiechang@chromium.org
+tbansal@chromium.org
diff --git a/third_party/tensorflow_models/README.chromium b/third_party/tensorflow_models/README.chromium
new file mode 100644
index 0000000..b3b205e
--- /dev/null
+++ b/third_party/tensorflow_models/README.chromium
@@ -0,0 +1,23 @@
+Name: TensorFlow Models
+Short Name: tensorflow_models
+URL: https://github.com/tensorflow/models
+Version: 45fd32c82e1bb55f8eac44d77d40902302c5aee2
+Date: 2022/09/29
+License: Apache 2.0
+License File: src/LICENSE
+Security Critical: Yes
+CPEPrefix: unknown
+
+Description:
+The TensorFlow Model Garden is a repository with a number of different
+implementations of state-of-the-art (SOTA) models and modeling solutions for
+TensorFlow users.
+
+Note: There are two shim directories, "libutf" and "icu4c", used to redirect
+includes for those directories to their Chromium versions which have slightly
+different names than those used here.
+
+Update Process/Notes:
+The owners update this repository as needed, using the `update.sh` script. If
+you need to roll this library sooner, please reach out to one of the owners so
+all the dependencies and necessary validation tests can be run.
diff --git a/third_party/tensorflow_models/shims/icu4c/source/common/unicode/uchar.h b/third_party/tensorflow_models/shims/icu4c/source/common/unicode/uchar.h
new file mode 100644
index 0000000..6afef8c
--- /dev/null
+++ b/third_party/tensorflow_models/shims/icu4c/source/common/unicode/uchar.h
@@ -0,0 +1,15 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_TENSORFLOW_MODELS_ICU4C_SOURCE_COMMON_UNICODE_UCHAR_H_
+#define THIRD_PARTY_TENSORFLOW_MODELS_ICU4C_SOURCE_COMMON_UNICODE_UCHAR_H_
+
+// This file exists so that //third_party/tensorflow_models/src can use
+// #include "icu4u/..."
+// to include Chromium's icu which lives at
+// //third_party/icu/... without needing local patches.
+
+#include "third_party/icu/source/common/unicode/uchar.h"
+
+#endif  // THIRD_PARTY_TENSORFLOW_MODELS_ICU4C_SOURCE_COMMON_UNICODE_UCHAR_H_
diff --git a/third_party/tensorflow_models/shims/icu4c/source/common/unicode/utf8.h b/third_party/tensorflow_models/shims/icu4c/source/common/unicode/utf8.h
new file mode 100644
index 0000000..1233ac8
--- /dev/null
+++ b/third_party/tensorflow_models/shims/icu4c/source/common/unicode/utf8.h
@@ -0,0 +1,15 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_TENSORFLOW_MODELS_ICU4C_SOURCE_COMMON_UNICODE_UTF8_H_
+#define THIRD_PARTY_TENSORFLOW_MODELS_ICU4C_SOURCE_COMMON_UNICODE_UTF8_H_
+
+// This file exists so that //third_party/tensorflow_models/src can use
+// #include "icu4u/..."
+// to include Chromium's icu which lives at
+// //third_party/icu/... without needing local patches.
+
+#include "third_party/icu/source/common/unicode/utf8.h"
+
+#endif  // THIRD_PARTY_TENSORFLOW_MODELS_ICU4C_SOURCE_COMMON_UNICODE_UTF8_H_
diff --git a/third_party/tensorflow_models/shims/libutf/utf.h b/third_party/tensorflow_models/shims/libutf/utf.h
new file mode 100644
index 0000000..b410bbe
--- /dev/null
+++ b/third_party/tensorflow_models/shims/libutf/utf.h
@@ -0,0 +1,15 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_TENSORFLOW_MODELS_LIBUTF_UTF_H_
+#define THIRD_PARTY_TENSORFLOW_MODELS_LIBUTF_UTF_H_
+
+// This file exists so that //third_party/tensorflow_models/src can use
+// #include "libutf/utf.h"
+// to include Chromium's libutf which lives at
+// //third_party/utf/src/include/utf.h without needing local patches.
+
+#include "third_party/utf/src/include/utf.h"
+
+#endif  // THIRD_PARTY_TENSORFLOW_MODELS_LIBUTF_UTF_H_
diff --git a/third_party/tensorflow_models/src/research/seq_flow_lite/tf_ops/projection_normalizer_util.cc b/third_party/tensorflow_models/src/research/seq_flow_lite/tf_ops/projection_normalizer_util.cc
new file mode 100644
index 0000000..bd0be7d
--- /dev/null
+++ b/third_party/tensorflow_models/src/research/seq_flow_lite/tf_ops/projection_normalizer_util.cc
@@ -0,0 +1,186 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+==============================================================================*/
+#include "tf_ops/projection_normalizer_util.h"  // seq_flow_lite
+
+#include <algorithm>
+#include <cstddef>
+#include <memory>
+#include <sstream>
+#include <utility>
+
+#include "tf_ops/projection_util.h"  // seq_flow_lite
+
+// Returns true if the given text contains a number.
+bool IsDigit(const std::string& text) {
+  Rune rune;
+  for (size_t i = 0; i < text.length();) {
+    const int bytes_read = chartorune(&rune, const_cast<char*>(text.data()));
+    if (rune == Runeerror || bytes_read == 0) break;
+    if (rune >= static_cast<Rune>('0') && rune <= static_cast<Rune>('9')) {
+      return true;
+    }
+    i += bytes_read;
+  }
+  return false;
+}
+
+// Gets the string containing |num_chars| characters from |start| position.
+std::string GetCharToken(const std::vector<std::string>& char_tokens,
+                         size_t start, size_t num_chars) {
+  std::string char_token = "";
+  if (start + num_chars <= char_tokens.size()) {
+    for (size_t i = 0; i < num_chars; ++i) {
+      char_token.append(char_tokens[start + i]);
+    }
+  }
+  return char_token;
+}
+
+// Counts how many times |pattern| appeared from |start| position.
+int GetNumPattern(const std::vector<std::string>& char_tokens, size_t start,
+                  size_t num_chars, const std::string& pattern) {
+  int count = 0;
+  for (size_t i = start; i < char_tokens.size(); i += num_chars) {
+    std::string cur_pattern = GetCharToken(char_tokens, i, num_chars);
+    if (pattern == cur_pattern) {
+      ++count;
+    } else {
+      break;
+    }
+  }
+  return count;
+}
+
+std::string ContractToken(const char* input_ptr, size_t len, size_t num_chars) {
+  // This function contracts patterns whose length is |num_chars| and appeared
+  // more than twice. So if the input is shorter than 3 * |num_chars|, do not
+  // apply any contraction.
+  if (len < 3 * num_chars) {
+    return input_ptr;
+  }
+  std::vector<std::string> char_tokens = SplitByChar(input_ptr, len, len);
+
+  std::string token;
+  token.reserve(len);
+  for (size_t i = 0; i < char_tokens.size();) {
+    std::string cur_pattern = GetCharToken(char_tokens, i, num_chars);
+
+    // Count how many times this pattern appeared.
+    int num_cur_patterns = 0;
+    if (cur_pattern.find(' ') == std::string::npos && !IsDigit(cur_pattern)) {
+      num_cur_patterns =
+          GetNumPattern(char_tokens, i + num_chars, num_chars, cur_pattern);
+    }
+
+    if (num_cur_patterns >= 2) {
+      // If this pattern is repeated, store it only twice.
+      token.append(cur_pattern);
+      token.append(cur_pattern);
+      i += (num_cur_patterns + 1) * num_chars;
+    } else {
+      token.append(char_tokens[i]);
+      ++i;
+    }
+  }
+
+  return token;
+}
+
+void NormalizeSpaces(std::string& input) {
+  // Whether to copy the next character if it's a space.
+  bool copy_space = false;
+  size_t j = 0;
+  for (size_t i = 0; i < input.length(); ++i) {
+    if (input[i] == ' ') {
+      if (!copy_space) continue;
+      copy_space = false;
+    } else {
+      copy_space = true;
+    }
+
+    if (j != i) {
+      input[j] = input[i];
+    }
+    ++j;
+  }
+  if (j > 0 && input[j - 1] == ' ') {
+    --j;
+  }
+  input.resize(j);
+}
+
+void ProjectionNormalizer::InitializeSeparators(const std::string& separators) {
+  for (size_t i = 0; i < separators.length(); ++i) {
+    if (separators[i] != ' ') {
+      separators_.insert(separators[i]);
+    }
+  }
+}
+
+std::string ProjectionNormalizer::NormalizeInternal(const char* input_ptr,
+                                                    size_t len) {
+  std::string normalized;
+  normalized.reserve(len * 2);
+  for (size_t i = 0; i < len; ++i) {
+    char c = input_ptr[i];
+    bool matched_separator = separators_.find(c) != separators_.end();
+    if (matched_separator) {
+      if (i > 0 && input_ptr[i - 1] != ' ' && normalized.back() != ' ') {
+        normalized.append(" ");
+      }
+    }
+    normalized.append(1, c);
+    if (matched_separator) {
+      if (i + 1 < len && input_ptr[i + 1] != ' ' && c != '\'') {
+        normalized.append(" ");
+      }
+    }
+  }
+  return normalized;
+}
+
+std::string ProjectionNormalizer::Normalize(const std::string& input,
+                                            size_t max_input) {
+  return Normalize(input.c_str(), input.size(), max_input);
+}
+
+std::string ProjectionNormalizer::Normalize(const char* input_ptr, size_t len,
+                                            size_t max_input) {
+  std::string normalized(input_ptr, std::min(len, max_input));
+
+  if (normalize_repetition_) {
+    // Remove repeated 1 char (e.g. soooo => soo)
+    normalized = ContractToken(normalized.data(), normalized.length(), 1);
+
+    // Remove repeated 2 chars from the beginning (e.g. hahaha =>
+    // haha, xhahaha => xhaha, xyhahaha => xyhaha).
+    normalized = ContractToken(normalized.data(), normalized.length(), 2);
+
+    // Remove repeated 3 chars from the beginning
+    // (e.g. wowwowwow => wowwow, abcdbcdbcd => abcdbcd).
+    normalized = ContractToken(normalized.data(), normalized.length(), 3);
+  }
+
+  if (normalize_spaces_) {
+    NormalizeSpaces(normalized);
+  }
+
+  if (!separators_.empty()) {
+    // Add space around separators_.
+    normalized = NormalizeInternal(normalized.data(), normalized.length());
+  }
+
+  return normalized;
+}
diff --git a/third_party/tensorflow_models/src/research/seq_flow_lite/tf_ops/projection_normalizer_util.h b/third_party/tensorflow_models/src/research/seq_flow_lite/tf_ops/projection_normalizer_util.h
new file mode 100644
index 0000000..13fec31b
--- /dev/null
+++ b/third_party/tensorflow_models/src/research/seq_flow_lite/tf_ops/projection_normalizer_util.h
@@ -0,0 +1,58 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+==============================================================================*/
+#ifndef TENSORFLOW_MODELS_SEQ_FLOW_LITE_TF_OPS_PROJECTION_NORMALIZER_UTIL_H_
+#define TENSORFLOW_MODELS_SEQ_FLOW_LITE_TF_OPS_PROJECTION_NORMALIZER_UTIL_H_
+
+#include <string>
+#include <unordered_set>
+#include <vector>
+
+#include "libutf/utf.h"
+
+// Normalizes the input with the given |separators| by adding a space before and
+// after each separator. When |normalize_repetition| is true, it removes the
+// repeated characters (except numbers) which consecutively appeared more than
+// twice in a word. When |normalize_spaces| is true, it removes spaces from
+// the beginning and ending of the input, as well as repeated spaces.
+// Examples: arwwwww -> arww, good!!!!! -> good!!, hahaha => haha.
+class ProjectionNormalizer {
+ public:
+  explicit ProjectionNormalizer(const std::string& separators,
+                                bool normalize_repetition = false,
+                                bool normalize_spaces = false)
+      : normalize_repetition_(normalize_repetition),
+        normalize_spaces_(normalize_spaces) {
+    InitializeSeparators(separators);
+  }
+
+  // Normalizes the repeated characters (except numbers) which consecutively
+  // appeared more than twice in a word.
+  std::string Normalize(const std::string& input, size_t max_input = 300);
+  std::string Normalize(const char* input_ptr, size_t len,
+                        size_t max_input = 300);
+
+ private:
+  // Parses and extracts supported separators.
+  void InitializeSeparators(const std::string& separators);
+
+  // Removes repeated chars.
+  std::string NormalizeInternal(const char* input_ptr, size_t len);
+
+  std::unordered_set<char> separators_;
+  bool normalize_repetition_;
+  bool normalize_spaces_;
+};
+
+#endif  // TENSORFLOW_MODELS_SEQ_FLOW_LITE_TF_OPS_PROJECTION_NORMALIZER_UTIL_H_
diff --git a/third_party/tensorflow_models/src/research/seq_flow_lite/tf_ops/projection_util.cc b/third_party/tensorflow_models/src/research/seq_flow_lite/tf_ops/projection_util.cc
new file mode 100644
index 0000000..c07f0cb
--- /dev/null
+++ b/third_party/tensorflow_models/src/research/seq_flow_lite/tf_ops/projection_util.cc
@@ -0,0 +1,427 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+==============================================================================*/
+#include "tf_ops/projection_util.h"  // seq_flow_lite
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <iostream>
+#include <memory>
+#include <sstream>
+#include <unordered_set>
+
+namespace {
+constexpr int kInvalid = -1;
+constexpr char kSpace = ' ';
+
+// A HashEngine that uses MurmurHash to convert text to hashcodes.
+class MurmurHash : public HashEngine {
+ public:
+  std::vector<uint64_t> GetHashCodes(const std::string& word,
+                                     int feature_size) override {
+    std::vector<uint64_t> hash_codes;
+    hash_codes.reserve(2 * (feature_size / 64 + 1));
+    uint64_t hash_low = 0;
+    uint64_t hash_high = 0;
+    for (int i = 0; i < feature_size; i += 64) {
+      if (i == 0) {
+        auto hash = MurmurHash128(word.data(), word.size());
+        hash_low = hash.first;
+        hash_high = hash.second;
+      } else {
+        GetMoreBits(hash_low, hash_high, &hash_low, &hash_high);
+      }
+      hash_codes.push_back(hash_low);
+      hash_codes.push_back(hash_high);
+    }
+    return hash_codes;
+  }
+
+ private:
+  static constexpr uint64_t kMul = 0xc6a4a7935bd1e995ULL;
+  static constexpr uint64_t kMul2 = 0x9e3779b97f4a7835ULL;
+  inline uint64_t ShiftMix(uint64_t val) { return val ^ (val >> 47); }
+  inline uint64_t MurmurStep(uint64_t hash, uint64_t data) {
+    hash ^= ShiftMix(data * kMul) * kMul;
+    hash *= kMul;
+    return hash;
+  }
+  inline uint64_t Load64VariableLength(const void* p, int len) {
+    assert(len >= 1 && len <= 8);
+    const char* buf = static_cast<const char*>(p);
+    uint64_t val = 0;
+    --len;
+    do {
+      val = (val << 8) | buf[len];
+      // (--len >= 0) is about 10 % faster than (len--) in some benchmarks.
+    } while (--len >= 0);
+    // No ToHost64(...) needed. The bytes are accessed in little-endian manner
+    // on every architecture.
+    return val;
+  }
+  void GetMoreBits(uint64_t hash, uint64_t hash2, uint64_t* rlow,
+                   uint64_t* rhigh) {
+    hash = ShiftMix(hash) * kMul;
+    hash2 ^= hash;
+    *rhigh = ShiftMix(hash);
+    *rlow = ShiftMix(hash2 * kMul2) * kMul2;
+  }
+  std::pair<uint64_t, uint64_t> MurmurHash128(const char* buf,
+                                              const size_t len) {
+    // Initialize the hashing value.
+    uint64_t hash1 = len * kMul;
+    // hash2 will be xored by hash during the hash computation iterations.
+    // In the end we use an alternative mixture multiplier for mixing
+    // the bits in hash2.
+    uint64_t hash2 = 0;
+    // Let's remove the bytes not divisible by the sizeof(uint64_t).
+    // This allows the inner loop to process the data as 64 bit integers.
+    const size_t len_aligned = len & ~0x7;
+    const char* end = buf + len_aligned;
+
+    for (const char* p = buf; p != end; p += 8) {
+      // Manually unrolling this loop 2x did not help on Intel Core 2.
+      hash1 = MurmurStep(hash1, Load64VariableLength(p, 8));
+      hash2 ^= hash1;
+    }
+    if ((len & 0x7) != 0) {
+      const uint64_t data = Load64VariableLength(end, len & 0x7);
+      hash1 ^= data;
+      hash1 *= kMul;
+      hash2 ^= hash1;
+    }
+    hash1 = ShiftMix(hash1) * kMul;
+    hash2 ^= hash1;
+    hash1 = ShiftMix(hash1);
+
+    // mul2 is a prime just above golden ratio. mul2 is used to ensure that the
+    // impact of the last few bytes is different to the upper and lower 64 bits.
+    hash2 = ShiftMix(hash2 * kMul2) * kMul2;
+
+    return {hash1, hash2};
+  }
+};
+
+// A HashEngine that uses a prefix and suffix preserving hash to convert text
+// to hashcodes.
+class XFixHash : public HashEngine {
+ public:
+  explicit XFixHash(int bits_per_char)
+      : bits_per_char_(bits_per_char), bit_mask_((1ULL << bits_per_char) - 1) {}
+
+  std::vector<uint64_t> GetHashCodes(const std::string& word,
+                                     int feature_size) override {
+    std::vector<uint64_t> hash_codes;
+    hash_codes.reserve(2 * (feature_size / 64 + 1));
+    auto token_ptr = reinterpret_cast<const uint8_t*>(word.c_str());
+    size_t token_size = word.size();
+    int token_idx = 0;
+    uint64_t hash_low = token_size * kMul;
+    uint64_t hash_high = token_size * kMul2;
+    uint64_t frhash = kMul;
+    uint64_t brhash = kMul2;
+    for (int i = 0; i < feature_size; i += 64) {
+      for (int j = i ? 0 : bits_per_char_; j < 64;
+           j += bits_per_char_, token_idx = (token_idx + 1) % token_size) {
+        frhash = ((frhash << 8) | token_ptr[token_idx]) * kMul;
+        brhash =
+            ((brhash << 8) | token_ptr[token_size - 1 - token_idx]) * kMul2;
+        hash_low = (hash_low << bits_per_char_) | (frhash & bit_mask_);
+        hash_high = (hash_high << bits_per_char_) | (brhash & bit_mask_);
+      }
+      hash_codes.push_back(hash_low);
+      hash_codes.push_back(hash_high);
+    }
+    return hash_codes;
+  }
+
+ private:
+  const uint64_t kMul = 0xc6a4a7935bd1e995ULL;
+  const uint64_t kMul2 = 0x9e3779b97f4a7835ULL;
+  const int bits_per_char_;
+  const uint64_t bit_mask_;
+};
+
+// A HashEngine that performs a position preserving unicode level hashing to
+// convert text to hashcodes.
+class UnicodeHash : public HashEngine {
+ public:
+  // bits_per_unicode should be a divisor of 64.
+  explicit UnicodeHash(int bits_per_unicode)
+      : bits_per_unicode_(bits_per_unicode),
+        bit_mask_(((1ULL << bits_per_unicode) - 1) << (64 - bits_per_unicode)) {
+  }
+
+  std::vector<uint64_t> GetHashCodes(const std::string& word,
+                                     int feature_size) override {
+    std::vector<uint64_t> hash_codes;
+    hash_codes.reserve(2 * (feature_size / 64 + 1));
+    auto word_ptr = word.c_str();
+    int utflength = utflen(const_cast<char*>(word_ptr));
+    // Both `feature_size` and `bits_per_unicode` are bit lengths.
+    const int max_usable_runes = feature_size * 2 / bits_per_unicode_;
+    if (max_usable_runes < utflength) {
+      const int unicode_skip = (utflength - max_usable_runes) / 2;
+      for (int i = 0; i < unicode_skip; ++i) {
+        Rune rune;
+        word_ptr += chartorune(&rune, const_cast<char*>(word_ptr));
+      }
+      utflength = max_usable_runes;
+    }
+
+    std::vector<uint64_t> unicode_hashes;
+    unicode_hashes.reserve(utflength);
+    for (int i = 0; i < utflength; ++i) {
+      Rune rune;
+      word_ptr += chartorune(&rune, const_cast<char*>(word_ptr));
+      unicode_hashes.push_back((rune * kMul) & bit_mask_);
+    }
+
+    uint64_t hash = 0;
+    int k = 0;
+    for (int i = 0; i < feature_size * 2; i += 64) {
+      for (int j = 0; j < 64; j += bits_per_unicode_) {
+        if (k < unicode_hashes.size()) {
+          hash = (hash >> bits_per_unicode_) | unicode_hashes[k++];
+        } else {
+          hash = hash >> bits_per_unicode_;
+        }
+      }
+      hash_codes.push_back(hash);
+    }
+    return hash_codes;
+  }
+
+ private:
+  const uint64_t kMul = 0xc6a4a7935bd1e995ULL;
+  const int bits_per_unicode_;
+  const uint64_t bit_mask_;
+};
+
+}  // namespace
+
+bool Hasher::SupportedHashType(const std::string& hash_type) {
+  std::unordered_set<std::string> supported({kMurmurHash, kUnicodeHash8,
+                                             kUnicodeHash16, kXfixHash8,
+                                             kXfixHash16, kXfixHash32});
+  return supported.find(hash_type) != supported.end();
+}
+
+Hasher* Hasher::CreateHasher(int feature_size, const std::string& hash_type) {
+  if (SupportedHashType(hash_type)) {
+    if (hash_type == kMurmurHash) {
+      return new Hasher(feature_size, new MurmurHash());
+    } else if (hash_type == kUnicodeHash8) {
+      return new Hasher(feature_size, new UnicodeHash(8));
+    } else if (hash_type == kUnicodeHash16) {
+      return new Hasher(feature_size, new UnicodeHash(16));
+    } else if (hash_type == kXfixHash8) {
+      return new Hasher(feature_size, new XFixHash(8));
+    } else if (hash_type == kXfixHash16) {
+      return new Hasher(feature_size, new XFixHash(16));
+    } else {
+      return new Hasher(feature_size, new XFixHash(32));
+    }
+  }
+  return nullptr;
+}
+
+Hasher::Hasher(int feature_size, HashEngine* hash_engine)
+    : feature_size_(feature_size), hash_engine_(hash_engine) {
+  null_hash_codes_ = hash_engine_->GetHashCodes(empty_string_, feature_size_);
+}
+
+std::string ProjectionUnicodeHandler::LowerCaseUTF8WithSupportedUnicodes(
+    const std::pair<const char*, size_t>& source, bool* first_cap,
+    bool* all_caps) const {
+  // Ideally the size of target should be less than or equal to source. But
+  // when we do to_lower the number of bytes needed to encode a unicode
+  // character could increase. To account for this 4 times the source length
+  // is allocated for target.
+  const char* csource = source.first;
+  int len = source.second;
+  auto target = std::unique_ptr<char[]>(new char[len * 4]);
+  auto target_ptr = target.get();
+  int i = 0;
+  bool first_char = true;
+  bool first_cap_value = false;
+  bool all_caps_value = false;
+  while (i < len) {
+    Rune rune;
+    const int bytes_read = chartorune(&rune, const_cast<char*>(csource + i));
+    if (bytes_read == 0 || bytes_read > len - i) {
+      break;
+    }
+    i += bytes_read;
+    if (rune != Runeerror) {
+      Rune lower = tolowerrune(rune);
+      // Skip processing the unicode if exclude_nonalphaspace_unicodes_ is
+      // true and the unicode is not alpha and not space.
+      const Rune kSpaceRune = ' ';
+      if (exclude_nonalphaspace_unicodes_ && !isalpharune(lower) &&
+          lower != kSpaceRune) {
+        continue;
+      }
+      if (IsUnrestrictedVocabulary() || IsValidUnicode(lower)) {
+        const int bytes_written = runetochar(target_ptr, &lower);
+        target_ptr += bytes_written;
+
+        const bool lower_case = (lower == rune);
+        if (first_char) {
+          first_cap_value = !lower_case;
+          all_caps_value = !lower_case;
+        } else {
+          first_cap_value &= lower_case;
+          all_caps_value &= !lower_case;
+        }
+        first_char = false;
+      }
+    }
+  }
+  if (first_cap) {
+    *first_cap = first_cap_value;
+  }
+  if (all_caps) {
+    *all_caps = all_caps_value;
+  }
+  return std::string(target.get(), target_ptr);
+}
+
+void ProjectionUnicodeHandler::InitializeVocabulary(
+    const std::string& vocabulary) {
+  for (size_t i = 0, index = 0; i < vocabulary.length();) {
+    Rune rune;
+    const int bytes_read =
+        chartorune(&rune, const_cast<char*>(vocabulary.c_str() + i));
+    if (!bytes_read || bytes_read > (vocabulary.length() - i)) {
+      break;
+    }
+    i += bytes_read;
+    // Include novel lower case unicode segments as part of valid chars.
+    if (rune == Runeerror) {
+      std::clog << "Invalid rune in vocabulary.";
+    } else if (IsValidUnicode(rune)) {
+      std::clog << "Duplicate rune " << rune << " found in vocabulary.";
+    } else if (rune != tolowerrune(rune)) {
+      std::clog << "Upper case rune " << rune << " found in vocabulary.";
+    } else {
+      valid_chars_[rune] = index++;
+    }
+  }
+}
+
+// Starting from input_ptr[from], search for the next occurrence of ' ',
+// Don't search beyond input_ptr[length](non-inclusive), return -1 if not
+// found.
+inline size_t FindNextSpace(const char* input_ptr, size_t from, size_t length) {
+  size_t space_index;
+  for (space_index = from; space_index < length; space_index++) {
+    if (input_ptr[space_index] == kSpace) {
+      break;
+    }
+  }
+  return space_index == length ? kInvalid : space_index;
+}
+
+template <typename T>
+void SplitBySpaceInternal(std::vector<T>* tokens, const char* input_ptr,
+                          size_t len, size_t max_input, size_t max_tokens) {
+  size_t last_index =
+      max_input == kEntireString ? len : (len < max_input ? len : max_input);
+  size_t start = 0;
+  // skip leading spaces
+  while (start < last_index && input_ptr[start] == kSpace) {
+    start++;
+  }
+  auto end = FindNextSpace(input_ptr, start, last_index);
+  while (end != kInvalid &&
+         (max_tokens == kAllTokens || tokens->size() < max_tokens - 1)) {
+    auto length = end - start;
+    if (length > 0) {
+      tokens->emplace_back(input_ptr + start, length);
+    }
+
+    start = end + 1;
+    end = FindNextSpace(input_ptr, start, last_index);
+  }
+  auto length = end == kInvalid ? (last_index - start) : (end - start);
+  if (length > 0) {
+    tokens->emplace_back(input_ptr + start, length);
+  }
+}
+
+std::vector<std::pair<const char*, size_t>> SplitBySpaceAsPairs(
+    const char* input_ptr, size_t len, size_t max_tokens) {
+  std::vector<std::pair<const char*, size_t>> tokens;
+  SplitBySpaceInternal(&tokens, input_ptr, len, kEntireString, max_tokens);
+  return tokens;
+}
+
+std::vector<std::string> SplitBySpace(const char* input_ptr, size_t len,
+                                      size_t max_input, size_t max_tokens) {
+  std::vector<std::string> tokens;
+  SplitBySpaceInternal(&tokens, input_ptr, len, max_input, max_tokens);
+  return tokens;
+}
+
+template <typename T>
+void SplitByCharInternal(std::vector<T>* tokens, const char* input_ptr,
+                         size_t len, size_t max_tokens) {
+  Rune rune;
+  for (size_t i = 0; i < len;) {
+    auto bytes_read = chartorune(&rune, const_cast<char*>(input_ptr + i));
+    if (bytes_read == 0 || bytes_read > (len - i)) break;
+    tokens->emplace_back(input_ptr + i, bytes_read);
+    if (max_tokens != kInvalid && tokens->size() == max_tokens) {
+      break;
+    }
+    i += bytes_read;
+  }
+}
+
+std::vector<std::pair<const char*, size_t>> SplitByCharAsPairs(
+    const char* input_ptr, size_t len, size_t max_tokens) {
+  std::vector<std::pair<const char*, size_t>> tokens;
+  SplitByCharInternal(&tokens, input_ptr, len, max_tokens);
+  return tokens;
+}
+
+std::vector<std::string> SplitByChar(const char* input_ptr, size_t len,
+                                     size_t max_tokens) {
+  std::vector<std::string> tokens;
+  SplitByCharInternal(&tokens, input_ptr, len, max_tokens);
+  return tokens;
+}
+
+std::string JoinPairsBySpace(
+    std::vector<std::pair<const char*, size_t>> words) {
+  std::stringstream ss;
+  bool first = true;
+  for (auto& str_pair : words) {
+    if (first) {
+      ss << std::string(str_pair.first, str_pair.second);
+      first = false;
+    } else {
+      ss << kSpace << std::string(str_pair.first, str_pair.second);
+    }
+  }
+  return ss.str();
+}
+
+std::vector<std::pair<const char*, size_t>> ProjectionUnicodeHandler::Tokenize(
+    const char* str, size_t len, bool by_space, int max_tokens) {
+  return by_space ? SplitBySpaceAsPairs(str, len, max_tokens)
+                  : SplitByCharAsPairs(str, len, max_tokens);
+}
diff --git a/third_party/tensorflow_models/src/research/seq_flow_lite/tf_ops/projection_util.h b/third_party/tensorflow_models/src/research/seq_flow_lite/tf_ops/projection_util.h
new file mode 100644
index 0000000..c39cc83
--- /dev/null
+++ b/third_party/tensorflow_models/src/research/seq_flow_lite/tf_ops/projection_util.h
@@ -0,0 +1,159 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+==============================================================================*/
+#ifndef TENSORFLOW_MODELS_SEQ_FLOW_LITE_TF_OPS_PROJECTION_UTIL_H_
+#define TENSORFLOW_MODELS_SEQ_FLOW_LITE_TF_OPS_PROJECTION_UTIL_H_
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "libutf/utf.h"
+
+inline constexpr int kFirstCapOffset = 3;
+inline constexpr int kAllCapsOffset = 4;
+inline constexpr int kWordNoveltyOffset = 1;
+inline constexpr int kDocSizeOffset = 2;
+
+inline constexpr char kMurmurHash[] = "murmur";
+inline constexpr char kXfixHash8[] = "xfixhash8";
+inline constexpr char kXfixHash16[] = "xfixhash16";
+inline constexpr char kXfixHash32[] = "xfixhash32";
+inline constexpr char kUnicodeHash8[] = "unicodehash8";
+inline constexpr char kUnicodeHash16[] = "unicodehash16";
+
+// A base class that specifies the interface for a hash engine used by the
+// projection operator.
+class HashEngine {
+ public:
+  // Takes a string token `word` and a `feature_size` (measured in bits) and
+  // returns hash codes that represent the token.
+  virtual std::vector<uint64_t> GetHashCodes(const std::string& word,
+                                             int feature_size) = 0;
+  virtual ~HashEngine() {}
+};
+
+// A hashing wrapper class that can hash a string and generate a hash code with
+// requested number of features (two bit values). Some of the implementations
+// are copied from murmurhash.
+class Hasher {
+ public:
+  static Hasher* CreateHasher(int feature_size,
+                              const std::string& hash_type = kMurmurHash);
+  static bool SupportedHashType(const std::string& hash_type);
+  bool GetHashCodes(const std::string& word,
+                    std::vector<uint64_t>& hash_codes) {
+    if (!hash_engine_) return false;
+    if (word.empty()) {
+      hash_codes = null_hash_codes_;
+    } else {
+      hash_codes = hash_engine_->GetHashCodes(word, feature_size_);
+    }
+    return true;
+  }
+
+ private:
+  explicit Hasher(int feature_size, HashEngine* hash_engine);
+  const std::string empty_string_ = "<null>";
+  // Size of the projection feature which represents the number of bits of
+  // hash codes that will be generated by this class.
+  const int feature_size_;
+  // The hash engine used by this class.
+  std::unique_ptr<HashEngine> hash_engine_;
+  // Hash codes for empty text is precaculated and stored below to speed
+  // up projection.
+  std::vector<uint64_t> null_hash_codes_;
+};
+
+// Unicode processor for tensorflow and tflite string projection ops.
+class ProjectionUnicodeHandler {
+ public:
+  // Takes an utf8 string which lists the unicodes that are supported and are
+  // part of the vocabulary of this instance. When the utf8 string is empty,
+  // all unicode segments are supported by this instance. The boolean
+  // flag exclude_nonalphaspace_unicodes is used to indicate if nonalpha and
+  // space unicode segments from the input should be stripped out.
+  // Another way to analyse the filtering logic is as below.
+  // Vocabulary acts as a allowlist when provided and all unicode set when
+  // empty. The flag exclude_nonalphaspace_unicodes when true acts as a
+  // allowlist on all alpha characters and space. It includes the entire unicode
+  // set when false. Valid unicode segments are the intersection of these 2
+  // sets.
+  explicit ProjectionUnicodeHandler(const std::string& vocabulary,
+                                    bool exclude_nonalphaspace_unicodes = false)
+      : exclude_nonalphaspace_unicodes_(exclude_nonalphaspace_unicodes) {
+    InitializeVocabulary(vocabulary);
+  }
+
+  // Performs language independent lower case and returns a string with
+  // supported unicode segments and two additional flags first_cap and all_caps
+  // which when true indicate the text is Firstcap or ALLCAPS.
+  std::string LowerCaseUTF8WithSupportedUnicodes(
+      const std::pair<const char*, size_t>& source, bool* first_cap = nullptr,
+      bool* all_caps = nullptr) const;
+
+  // Returns a boolean flag indicating if the unicode segment is part of the
+  // vocabulary.
+  bool IsValidUnicode(Rune rune) const {
+    return valid_chars_.find(rune) != valid_chars_.end();
+  }
+
+  // Returns an index in [0, |vocabulary|), if the unicode is part of the
+  // vocabulary and -1 if it's not.
+  int UnicodeIndex(Rune rune) const {
+    return IsValidUnicode(rune) ? valid_chars_.at(rune) : -1;
+  }
+
+  // Returns |vocabulary|.
+  size_t NumberOfValidUnicodes() const { return valid_chars_.size(); }
+
+  // Returns true if the vocabulary is empty which means all unicode segments
+  // are supported.
+  bool IsUnrestrictedVocabulary() const { return valid_chars_.empty(); }
+
+  // Tokenizes input by space or unicode point segmentation. Limit to
+  // max_tokens, when it is not -1.
+  static std::vector<std::pair<const char*, size_t>> Tokenize(
+      const std::string& input, bool by_space, int max_tokens) {
+    return Tokenize(input.c_str(), input.size(), by_space, max_tokens);
+  }
+  static std::vector<std::pair<const char*, size_t>> Tokenize(const char* str,
+                                                              size_t len,
+                                                              bool by_space,
+                                                              int max_tokens);
+
+ private:
+  // Parses and extracts supported or allowed unicode segments, also referred
+  // to as vocabulary, from a utf8 string.
+  void InitializeVocabulary(const std::string& vocabulary);
+  // A variable that maps a valid Unicode rune to its index in valid character
+  // vocabulary.
+  std::unordered_map<Rune, int> valid_chars_;
+  // Controls whether to exclude non-alphabetic, non-space characters from the
+  // output text.
+  bool exclude_nonalphaspace_unicodes_;
+};
+
+inline constexpr size_t kEntireString = SIZE_MAX;
+inline constexpr size_t kAllTokens = SIZE_MAX;
+
+std::vector<std::string> SplitBySpace(const char* input_ptr, size_t len,
+                                      size_t max_input, size_t max_tokens);
+
+std::vector<std::string> SplitByChar(const char* input_ptr, size_t len,
+                                     size_t max_tokens);
+
+std::string JoinPairsBySpace(std::vector<std::pair<const char*, size_t>> words);
+
+#endif  // TENSORFLOW_MODELS_SEQ_FLOW_LITE_TF_OPS_PROJECTION_UTIL_H_
diff --git a/third_party/tensorflow_models/src/research/seq_flow_lite/tf_ops/skipgram_finder.cc b/third_party/tensorflow_models/src/research/seq_flow_lite/tf_ops/skipgram_finder.cc
new file mode 100644
index 0000000..e711cbf
--- /dev/null
+++ b/third_party/tensorflow_models/src/research/seq_flow_lite/tf_ops/skipgram_finder.cc
@@ -0,0 +1,183 @@
+/* Copyright 2022 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+==============================================================================*/
+#include "tf_ops/skipgram_finder.h"  // seq_flow_lite
+
+#include <cctype>
+#include <deque>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "absl/container/flat_hash_map.h"
+#include "absl/container/flat_hash_set.h"
+#include "absl/strings/match.h"
+#include "absl/strings/str_split.h"
+#include "absl/strings/string_view.h"
+#include "icu4c/source/common/unicode/uchar.h"
+#include "icu4c/source/common/unicode/utf8.h"
+
+namespace seq_flow_lite {
+namespace {
+
+void PreprocessToken(std::string& token) {
+  char* s = const_cast<char*>(token.data());
+  int32_t size = token.size();
+  int32_t in = 0;
+  int32_t out = 0;
+  while (in < size) {
+    UChar32 c;
+    int32_t old_in = in;
+    U8_NEXT(s, in, size, c);
+    if (c < 0) {
+      break;
+    }
+    if (u_ispunct(c)) continue;
+    UChar32 cl = u_tolower(c);
+    // This is a hack, but there are exactly two unicode characters whose
+    // lowercase versions have longer UTF-8 encodings (0x23a to 0x2c65,
+    // 0x23e to 0x2c66).  So, to avoid sizing issues, they're not lowercased.
+    if (U8_LENGTH(cl) > (in - old_in)) {
+      cl = c;
+    }
+    U8_APPEND_UNSAFE(s, out, cl);
+  }
+
+  size_t remaining = token.size() - in;
+  if (remaining > 0) {
+    memmove(s + out, s + in, remaining);
+    out += remaining;
+  }
+  token.resize(out);
+}
+
+}  // namespace
+
+void SkipgramFinder::AddSkipgram(absl::string_view skipgram, int category) {
+  std::vector<std::string> tokens = absl::StrSplit(skipgram, ' ');
+
+  // Store the skipgram in a trie-like structure that uses tokens as the
+  // edge labels, instead of characters.  Each node represents a skipgram made
+  // from the tokens used to reach the node, and stores the categories the
+  // skipgram is associated with.
+  TrieNode* cur = &skipgram_trie_;
+  for (auto& token : tokens) {
+    if (absl::EndsWith(token, ".*")) {
+      token.resize(token.size() - 2);
+      PreprocessToken(token);
+      auto iter = cur->prefix_to_node.find(token);
+      if (iter != cur->prefix_to_node.end()) {
+        cur = &iter->second;
+      } else {
+        cur = &cur->prefix_to_node
+                   .emplace(std::piecewise_construct,
+                            std::forward_as_tuple(token), std::make_tuple<>())
+                   .first->second;
+      }
+      continue;
+    }
+
+    PreprocessToken(token);
+    auto iter = cur->token_to_node.find(token);
+    if (iter != cur->token_to_node.end()) {
+      cur = &iter->second;
+    } else {
+      cur = &cur->token_to_node
+                 .emplace(std::piecewise_construct,
+                          std::forward_as_tuple(token), std::make_tuple<>())
+                 .first->second;
+    }
+  }
+  cur->categories.insert(category);
+}
+
+absl::flat_hash_set<int> SkipgramFinder::FindSkipgrams(
+    absl::string_view input) const {
+  std::vector<std::string> tokens = absl::StrSplit(input, ' ');
+  std::vector<absl::string_view> sv_tokens;
+  sv_tokens.reserve(tokens.size());
+  for (auto& token : tokens) {
+    PreprocessToken(token);
+    sv_tokens.emplace_back(token.data(), token.size());
+  }
+  return FindSkipgrams(sv_tokens);
+}
+
+absl::flat_hash_set<int> SkipgramFinder::FindSkipgrams(
+    const std::vector<absl::string_view>& tokens) const {
+  absl::flat_hash_set<int> categories;
+
+  // Tracks skipgram prefixes and the index of their last token.
+  std::deque<std::pair<int, const TrieNode*>> indices_and_skipgrams;
+
+  for (int token_i = 0; token_i < tokens.size(); token_i++) {
+    const absl::string_view& token = tokens[token_i];
+
+    std::vector<absl::string_view> token_prefixes;
+    {
+      const char* s = token.data();
+      int32_t l = token.size();
+      int32_t n = 0;
+      while (n < l) {
+        int32_t n_old = n;
+        U8_FWD_1(s, n, l);
+        if (n == n_old) break;
+        token_prefixes.emplace_back(s, n);
+      }
+    }
+
+    // Drop any skipgrams prefixes which would skip more than `max_skip_size_`
+    // tokens between the end of the prefix and the current token.
+    while (!indices_and_skipgrams.empty()) {
+      if (indices_and_skipgrams.front().first + max_skip_size_ + 1 < token_i) {
+        indices_and_skipgrams.pop_front();
+      } else {
+        break;
+      }
+    }
+
+    // Check if we can form a valid skipgram prefix (or skipgram) by adding
+    // the current token to any of the existing skipgram prefixes, or
+    // if the current token is a valid skipgram prefix (or skipgram).
+    size_t size = indices_and_skipgrams.size();
+    for (size_t skipgram_i = 0; skipgram_i <= size; skipgram_i++) {
+      const auto& node = skipgram_i < size
+                             ? *indices_and_skipgrams[skipgram_i].second
+                             : skipgram_trie_;
+
+      auto iter = node.token_to_node.find(token);
+      if (iter != node.token_to_node.end()) {
+        categories.insert(iter->second.categories.begin(),
+                          iter->second.categories.end());
+        indices_and_skipgrams.push_back(std::make_pair(token_i, &iter->second));
+      }
+
+      for (auto token_prefix = token_prefixes.rbegin();
+           token_prefix != token_prefixes.rend(); token_prefix++) {
+        auto iter = node.prefix_to_node.find(*token_prefix);
+        if (iter != node.prefix_to_node.end()) {
+          categories.insert(iter->second.categories.begin(),
+                            iter->second.categories.end());
+          indices_and_skipgrams.push_back(
+              std::make_pair(token_i, &iter->second));
+        }
+      }
+    }
+  }
+
+  return categories;
+}
+
+}  // namespace seq_flow_lite
diff --git a/third_party/tensorflow_models/src/research/seq_flow_lite/tf_ops/skipgram_finder.h b/third_party/tensorflow_models/src/research/seq_flow_lite/tf_ops/skipgram_finder.h
new file mode 100644
index 0000000..dbbefd6
--- /dev/null
+++ b/third_party/tensorflow_models/src/research/seq_flow_lite/tf_ops/skipgram_finder.h
@@ -0,0 +1,66 @@
+/* Copyright 2022 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+==============================================================================*/
+#ifndef TENSORFLOW_MODELS_SEQ_FLOW_LITE_TF_OPS_SKIPGRAM_FINDER_H_
+#define TENSORFLOW_MODELS_SEQ_FLOW_LITE_TF_OPS_SKIPGRAM_FINDER_H_
+
+#include <string>
+#include <vector>
+
+#include "absl/container/flat_hash_map.h"
+#include "absl/container/flat_hash_set.h"
+#include "absl/strings/string_view.h"
+
+namespace seq_flow_lite {
+
+// SkipgramFinder finds skipgrams in strings.
+//
+// To use: First, add skipgrams using AddSkipgram() - each skipgram is
+// associated with some category.  Then, call FindSkipgrams() on a string,
+// which will return the set of categories of the skipgrams in the string.
+//
+// Both the skipgrams and the input strings will be tokenzied by splitting
+// on spaces.  Additionally, the tokens will be lowercased and have any
+// trailing punctuation removed.
+class SkipgramFinder {
+ public:
+  explicit SkipgramFinder(int max_skip_size) : max_skip_size_(max_skip_size) {}
+
+  // Adds a skipgram that SkipgramFinder should look for in input strings.
+  // Tokens may use the regex '.*' as a suffix.
+  void AddSkipgram(absl::string_view skipgram, int category);
+
+  // Find all of the skipgrams in `input`, and return their categories.
+  absl::flat_hash_set<int> FindSkipgrams(absl::string_view input) const;
+
+  // Find all of the skipgrams in `tokens`, and return their categories.
+  absl::flat_hash_set<int> FindSkipgrams(
+      const std::vector<absl::string_view>& tokens) const;
+
+ private:
+  struct TrieNode {
+    absl::flat_hash_set<int> categories;
+    // Maps tokens to the next node in the trie.
+    absl::flat_hash_map<std::string, TrieNode> token_to_node;
+    // Maps token prefixes (<prefix>.*) to the next node in the trie.
+    absl::flat_hash_map<std::string, TrieNode> prefix_to_node;
+  };
+
+  TrieNode skipgram_trie_;
+  int max_skip_size_;
+};
+
+}  // namespace seq_flow_lite
+
+#endif  // TENSORFLOW_MODELS_SEQ_FLOW_LITE_TF_OPS_SKIPGRAM_FINDER_H_
diff --git a/third_party/tensorflow_models/src/research/seq_flow_lite/tflite_ops/denylist.cc b/third_party/tensorflow_models/src/research/seq_flow_lite/tflite_ops/denylist.cc
new file mode 100644
index 0000000..e910444
--- /dev/null
+++ b/third_party/tensorflow_models/src/research/seq_flow_lite/tflite_ops/denylist.cc
@@ -0,0 +1,106 @@
+/* Copyright 2022 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+==============================================================================*/
+#include "tflite_ops/denylist.h"  // seq_flow_lite
+
+#include "absl/container/flat_hash_set.h"
+#include "tensorflow/lite/context.h"
+#include "tflite_ops/quantization_util.h"  // seq_flow_lite
+
+namespace seq_flow_lite {
+namespace ops {
+namespace custom {
+namespace denylist {
+
+static const int kOutputCategories = 0;
+
+void Free(TfLiteContext* context, void* buffer) {
+  delete reinterpret_cast<DenylistOp*>(buffer);
+}
+
+TfLiteStatus Resize(TfLiteContext* context, TfLiteNode* node) {
+  auto* op = reinterpret_cast<DenylistOp*>(node->user_data);
+  TF_LITE_ENSURE_STATUS(op->CheckErrors(context));
+
+  TfLiteIntArray* input_dims = op->GetInputShape(context, node);
+  TfLiteIntArray* output_dims = TfLiteIntArrayCreate(input_dims->size + 1);
+  for (int i = 0; i < input_dims->size; i++) {
+    output_dims->data[i] = input_dims->data[i];
+  }
+  output_dims->data[input_dims->size] = op->categories();
+  return context->ResizeTensor(
+      context, &context->tensors[node->outputs->data[kOutputCategories]],
+      output_dims);
+}
+
+TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
+  auto* op = reinterpret_cast<DenylistOp*>(node->user_data);
+  TF_LITE_ENSURE_STATUS(op->CheckErrors(context));
+
+  TfLiteTensor* output_categories =
+      &context->tensors[node->outputs->data[kOutputCategories]];
+
+  TfLiteIntArray* input_dims = op->GetInputShape(context, node);
+  int input_size = 1;
+  for (int i = 0; i < input_dims->size; i++) {
+    input_size *= input_dims->data[i];
+  }
+  int n_categories = op->categories();
+
+  TF_LITE_ENSURE_STATUS(op->InitializeInput(context, node));
+  if (output_categories->type == kTfLiteFloat32) {
+    for (int i = 0; i < input_size; i++) {
+      absl::flat_hash_set<int> categories;
+      TF_LITE_ENSURE_STATUS(op->GetCategories(context, i, categories));
+      if (categories.empty()) {
+        for (int j = 0; j < n_categories; j++) {
+          output_categories->data.f[i * n_categories + j] =
+              (j < op->negative_categories()) ? 1.0 : 0.0;
+        }
+      } else {
+        for (int j = 0; j < n_categories; j++) {
+          output_categories->data.f[i * n_categories + j] =
+              (categories.find(j) != categories.end()) ? 1.0 : 0.0;
+        }
+      }
+    }
+  } else if (output_categories->type == kTfLiteUInt8) {
+    const uint8_t one = PodQuantize(1.0, output_categories->params.zero_point,
+                                    1.0 / output_categories->params.scale);
+    const uint8_t zero = PodQuantize(0.0, output_categories->params.zero_point,
+                                     1.0 / output_categories->params.scale);
+    for (int i = 0; i < input_size; i++) {
+      absl::flat_hash_set<int> categories;
+      TF_LITE_ENSURE_STATUS(op->GetCategories(context, i, categories));
+      if (categories.empty()) {
+        for (int j = 0; j < n_categories; j++) {
+          output_categories->data.uint8[i * n_categories + j] =
+              (j < op->negative_categories()) ? one : zero;
+        }
+      } else {
+        for (int j = 0; j < n_categories; j++) {
+          output_categories->data.uint8[i * n_categories + j] =
+              (categories.find(j) != categories.end()) ? one : zero;
+        }
+      }
+    }
+  }
+  op->FinalizeInput();
+  return kTfLiteOk;
+}
+
+}  // namespace denylist
+}  // namespace custom
+}  // namespace ops
+}  // namespace seq_flow_lite
diff --git a/third_party/tensorflow_models/src/research/seq_flow_lite/tflite_ops/denylist.h b/third_party/tensorflow_models/src/research/seq_flow_lite/tflite_ops/denylist.h
new file mode 100644
index 0000000..9b55b15
--- /dev/null
+++ b/third_party/tensorflow_models/src/research/seq_flow_lite/tflite_ops/denylist.h
@@ -0,0 +1,123 @@
+/* Copyright 2022 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+==============================================================================*/
+#ifndef TENSORFLOW_MODELS_SEQ_FLOW_LITE_TFLITE_OPS_DENYLIST_H_
+#define TENSORFLOW_MODELS_SEQ_FLOW_LITE_TFLITE_OPS_DENYLIST_H_
+
+#include <string>
+#include <vector>
+
+#include "absl/container/flat_hash_set.h"
+#include "absl/strings/str_format.h"
+#include "absl/strings/string_view.h"
+#include "flatbuffers/flexbuffers.h"  // flatbuffer
+#include "tensorflow/lite/context.h"
+
+namespace seq_flow_lite {
+namespace ops {
+namespace custom {
+namespace denylist {
+
+/*
+ * A framework for writing ops that generate prediction vectors using a
+ * denylist.
+ *
+ * Input is defined by the specific implementation.
+ *
+ * Attributes:
+ *   denylist:           string[n]
+ *     Terms in the denylist.
+ *   denylist_category:  int[n]
+ *     Category for each term in the denylist.  Each category must be in
+ *     [0, categories).
+ *   categories:          int[]
+ *     Total number of categories.
+ *   negative_categories: int[]
+ *     Total number of negative categories.
+ *
+ * Output:
+ *   tensor[0]: Category indicators for each message, float[..., categories]
+ *
+ */
+
+class DenylistOp {
+ public:
+  explicit DenylistOp(const flexbuffers::Map& custom_options)
+      : categories_(custom_options["categories"].AsInt32()),
+        negative_categories_(custom_options["negative_categories"].AsInt32()) {
+    if (categories_ <= 0) {
+      AddError(absl::StrFormat("categories (%d) <= 0", categories_));
+    }
+
+    if (negative_categories_ <= 0) {
+      AddError(absl::StrFormat("negative_categories (%d) <= 0",
+                               negative_categories_));
+    }
+
+    if (negative_categories_ >= categories_) {
+      AddError(absl::StrFormat("negative_categories (%d) >= categories (%d)",
+                               negative_categories_, categories_));
+    }
+  }
+
+  virtual ~DenylistOp() {}
+
+  int categories() const { return categories_; }
+  int negative_categories() const { return negative_categories_; }
+
+  virtual TfLiteStatus InitializeInput(TfLiteContext* context,
+                                       TfLiteNode* node) = 0;
+  virtual TfLiteStatus GetCategories(
+      TfLiteContext* context, int i,
+      absl::flat_hash_set<int>& categories) const = 0;
+  virtual void FinalizeInput() = 0;
+
+  // Returns the input shape.  TfLiteIntArray is owned by the object.
+  virtual TfLiteIntArray* GetInputShape(TfLiteContext* context,
+                                        TfLiteNode* node) = 0;
+
+  TfLiteStatus CheckErrors(TfLiteContext* context) {
+    if (!errors_.empty()) {
+      for (const std::string& error : errors_) {
+        context->ReportError(context, "%s", error.c_str());
+      }
+      return kTfLiteError;
+    }
+    return kTfLiteOk;
+  }
+
+ protected:
+  void AddError(absl::string_view error) { errors_.emplace_back(error); }
+
+ private:
+  int categories_;
+  int negative_categories_;
+  std::vector<std::string> errors_;
+};
+
+// Individual ops should define an Init() function that returns a
+// DenylistOp.
+
+void Free(TfLiteContext* context, void* buffer);
+
+TfLiteStatus Resize(TfLiteContext* context, TfLiteNode* node);
+
+TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node);
+
+}  // namespace denylist
+}  // namespace custom
+}  // namespace ops
+}  // namespace seq_flow_lite
+
+#endif  // TENSORFLOW_MODELS_SEQ_FLOW_LITE_TFLITE_OPS_DENYLIST_H_
diff --git a/third_party/tensorflow_models/src/research/seq_flow_lite/tflite_ops/denylist_skipgram.cc b/third_party/tensorflow_models/src/research/seq_flow_lite/tflite_ops/denylist_skipgram.cc
new file mode 100644
index 0000000..9106449
--- /dev/null
+++ b/third_party/tensorflow_models/src/research/seq_flow_lite/tflite_ops/denylist_skipgram.cc
@@ -0,0 +1,113 @@
+
+/* Copyright 2022 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+==============================================================================*/
+#include "absl/container/flat_hash_set.h"
+#include "absl/strings/str_format.h"
+#include "absl/strings/string_view.h"
+#include "flatbuffers/flexbuffers.h"  // flatbuffer
+#include "tensorflow/lite/context.h"
+#include "tensorflow/lite/string_util.h"
+#include "tf_ops/skipgram_finder.h"  // seq_flow_lite
+#include "tflite_ops/denylist.h"  // seq_flow_lite
+
+namespace seq_flow_lite {
+namespace ops {
+namespace custom {
+
+namespace denylist {
+
+using ::tflite::GetString;
+using ::tflite::StringRef;
+
+// Generates prediction vectors for input strings using a skipgram denylist.
+// This uses the framework in `denylist.h`, with the implementation detail
+// that the input is a string tensor of messages and the terms are skipgrams.
+class SkipgramDenylistOp : public DenylistOp {
+ public:
+  explicit SkipgramDenylistOp(const flexbuffers::Map& custom_options)
+      : DenylistOp(custom_options),
+        skipgram_finder_(custom_options["max_skip_size"].AsInt32()),
+        input_(nullptr) {
+    auto denylist = custom_options["denylist"].AsTypedVector();
+    auto denylist_category =
+        custom_options["denylist_category"].AsTypedVector();
+    if (denylist.size() != denylist_category.size()) {
+      AddError(
+          absl::StrFormat("denylist.size (%d) != denylist_category.size (%d)",
+                          denylist.size(), denylist_category.size()));
+      return;
+    }
+
+    for (int i = 0; i < denylist.size(); i++) {
+      int category = denylist_category[i].AsInt32();
+      if (category < 0 || category >= categories()) {
+        AddError(absl::StrFormat(
+            "denylist_category[%d] (%d) is out of range: [0, %d)", i, category,
+            categories()));
+        continue;
+      }
+      flexbuffers::String s = denylist[i].AsString();
+      skipgram_finder_.AddSkipgram(absl::string_view(s.c_str(), s.length()),
+                                   category);
+    }
+  }
+
+  TfLiteStatus InitializeInput(TfLiteContext* context,
+                               TfLiteNode* node) override {
+    input_ = &context->tensors[node->inputs->data[kInputMessage]];
+    return kTfLiteOk;
+  }
+
+  TfLiteStatus GetCategories(
+      TfLiteContext* context, int i,
+      absl::flat_hash_set<int>& categories) const override {
+    StringRef input = GetString(input_, i);
+    categories =
+        skipgram_finder_.FindSkipgrams(absl::string_view(input.str, input.len));
+    return kTfLiteOk;
+  }
+
+  void FinalizeInput() override { input_ = nullptr; }
+
+  TfLiteIntArray* GetInputShape(TfLiteContext* context,
+                                TfLiteNode* node) override {
+    return context->tensors[node->inputs->data[kInputMessage]].dims;
+  }
+
+ private:
+  SkipgramFinder skipgram_finder_;
+  TfLiteTensor* input_;
+
+  static constexpr int kInputMessage = 0;
+};
+
+void* SkipgramDenylistOpInit(TfLiteContext* context, const char* buffer,
+                             size_t length) {
+  const uint8_t* buffer_t = reinterpret_cast<const uint8_t*>(buffer);
+  return new SkipgramDenylistOp(flexbuffers::GetRoot(buffer_t, length).AsMap());
+}
+
+}  // namespace denylist
+
+TfLiteRegistration* Register_SKIPGRAM_DENYLIST() {
+  static TfLiteRegistration r = {denylist::SkipgramDenylistOpInit,
+                                 denylist::Free, denylist::Resize,
+                                 denylist::Eval};
+  return &r;
+}
+
+}  // namespace custom
+}  // namespace ops
+}  // namespace seq_flow_lite
diff --git a/third_party/tensorflow_models/src/research/seq_flow_lite/tflite_ops/denylist_skipgram.h b/third_party/tensorflow_models/src/research/seq_flow_lite/tflite_ops/denylist_skipgram.h
new file mode 100644
index 0000000..b8c7591
--- /dev/null
+++ b/third_party/tensorflow_models/src/research/seq_flow_lite/tflite_ops/denylist_skipgram.h
@@ -0,0 +1,30 @@
+/* Copyright 2022 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+==============================================================================*/
+#ifndef TENSORFLOW_MODELS_SEQ_FLOW_LITE_TFLITE_OPS_DENYLIST_SKIPGRAM_H_
+#define TENSORFLOW_MODELS_SEQ_FLOW_LITE_TFLITE_OPS_DENYLIST_SKIPGRAM_H_
+
+#include "tensorflow/lite/kernels/register.h"
+
+namespace seq_flow_lite {
+namespace ops {
+namespace custom {
+
+TfLiteRegistration* Register_SKIPGRAM_DENYLIST();
+
+}  // namespace custom
+}  // namespace ops
+}  // namespace seq_flow_lite
+
+#endif  // TENSORFLOW_MODELS_SEQ_FLOW_LITE_TFLITE_OPS_DENYLIST_SKIPGRAM_H_
diff --git a/third_party/tensorflow_models/src/research/seq_flow_lite/tflite_ops/quantization_util.h b/third_party/tensorflow_models/src/research/seq_flow_lite/tflite_ops/quantization_util.h
new file mode 100644
index 0000000..99bf018
--- /dev/null
+++ b/third_party/tensorflow_models/src/research/seq_flow_lite/tflite_ops/quantization_util.h
@@ -0,0 +1,53 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+==============================================================================*/
+#ifndef TENSORFLOW_MODELS_SEQ_FLOW_LITE_TFLITE_OPS_QUANTIZATION_UTIL_H_
+#define TENSORFLOW_MODELS_SEQ_FLOW_LITE_TFLITE_OPS_QUANTIZATION_UTIL_H_
+
+#include <algorithm>
+#include <cmath>
+
+#include "tensorflow/lite/context.h"
+
+namespace seq_flow_lite {
+
+// Returns the original (dequantized) value of 8bit value.
+inline float PodDequantizeValue(const TfLiteTensor& tensor, uint8_t value) {
+  const int32_t zero_point = tensor.params.zero_point;
+  const float scale = tensor.params.scale;
+  return (static_cast<int32_t>(value) - zero_point) * scale;
+}
+
+// Returns the original (dequantized) value of the 'index'-th element of
+// 'tensor.
+inline float PodDequantize(const TfLiteTensor& tensor, int index) {
+  return PodDequantizeValue(tensor, tensor.data.uint8[index]);
+}
+
+// Quantizes 'value' to 8bit, given the quantization bias (zero_point) and
+// factor (inverse_scale).
+inline uint8_t PodQuantize(float value, int32_t zero_point,
+                           float inverse_scale) {
+  const float integer_value_in_float = value * inverse_scale;
+  const float offset = (integer_value_in_float >= 0.0) ? 0.5f : -0.5f;
+  // NOTE(sfeuz): This assumes value * inverse_scale is within [INT_MIN,
+  // INT_MAX].
+  int32_t integer_value =
+      static_cast<int32_t>(integer_value_in_float + offset) + zero_point;
+  return static_cast<uint8_t>(std::max(std::min(255, integer_value), 0));
+}
+
+}  // namespace seq_flow_lite
+
+#endif  // TENSORFLOW_MODELS_SEQ_FLOW_LITE_TFLITE_OPS_QUANTIZATION_UTIL_H_
diff --git a/third_party/tensorflow_models/src/research/seq_flow_lite/tflite_ops/sequence_string_projection.cc b/third_party/tensorflow_models/src/research/seq_flow_lite/tflite_ops/sequence_string_projection.cc
new file mode 100644
index 0000000..32ce4da
--- /dev/null
+++ b/third_party/tensorflow_models/src/research/seq_flow_lite/tflite_ops/sequence_string_projection.cc
@@ -0,0 +1,513 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+==============================================================================*/
+/**
+ * Sequence String projection op used in PRADO.
+ */
+#include "tflite_ops/sequence_string_projection.h"  // seq_flow_lite
+
+#include <algorithm>
+#include <cmath>
+#include <cstddef>
+#include <cstdint>
+#include <map>
+#include <memory>
+#include <unordered_map>
+
+#include "flatbuffers/flexbuffers.h"  // flatbuffer
+#include "tensorflow/lite/string_util.h"
+#include "tf_ops/projection_normalizer_util.h"  // seq_flow_lite
+#include "tf_ops/projection_util.h"  // seq_flow_lite
+#include "tflite_ops/quantization_util.h"  // seq_flow_lite
+
+namespace seq_flow_lite {
+namespace ops {
+namespace custom {
+
+namespace sequence_string_projection {
+/**
+ * This op referred to as Ternary Sequence String Projection op (TSP), tokenizes
+ * input text either on space or unicode boundary. Fingerprint for each token is
+ * computed using murmur hash and bit features are extracted from fingerprint
+ * that maps every 2 bits to the ternary output {-1, 0, 1}. This effectively
+ * turns a text input into a ternary rank 3 tensor (in 8bit/float format) of
+ * shape [1, max token length, requested number of features].
+ *
+ * Input:
+ *   tensor[0]: Input message, string[num_batch]
+ *   attribute[0]: feature size
+ *   attribute[1]: vocabulary, a set of allowed characters in utf8 format.
+ *   attribute[2]: split_on_space, a boolean specifying the tokenization method.
+ *   attribute[3]: max_splits, maximum number of splits allowed during
+ *                 tokenization. When max_splits is set to -1, no limit on
+ *                 number of tokens is imposed. When it is set to a positive
+ *                 integer, number of tokens is truncated beyond that integer.
+ *                 An end of input token is always added after tokenization,
+ *                 hence the number of tokens is one more than the true number
+ *                 of tokens. As a result, the number of tokens returned by this
+ *                 op is not the same as absl::StrSplit.
+ *   attribute[4]: word_novelty_bits, when set to a positive value less than 8,
+ *                 generates a word specific novelty feature in the last feature
+ *                 index.
+ *   attribute[5]: doc_size_levels, when set to a positive value less than 17,
+ *                 generates a feature proportional to the logarithm of the
+ *                 number of tokens in the second to last feature index.
+ *   attribute[6]: add_eos_tag, add an end of sequence tag to the output when
+ *                 true. Defaults to true.
+ *   attribute[7]: add_bos_tag, add a begin of sequence tag to the output when
+ *                 true. Defaults to false.
+ *   attribute[8]: add_first_cap_feature, when set to 1.0f add a feature to the
+ *                 resulting projection tensor that helps discriminate if the
+ *                 input token is Camel case. Otherwise leaves the projection
+ *                 output unmodified.
+ *   attribute[9]: add_all_caps_feature, when set to 1.0f add a feature to the
+ *                 resulting projection tensor that helps discriminate if the
+ *                 input token is ALLCAPS. Otherwise leaves the projection
+ *                 output unmodified.
+ * Output:
+ * tensor[0]: computed projections.
+ *            float32[true number of tokens][feature size]
+ *            true number of tokens is number of tokens + 1. (for end of
+ *            sequence).
+ */
+
+namespace {
+
+constexpr char kBeginToken[] = "<BOS>";
+constexpr char kEndToken[] = "<EOS>";
+constexpr int kInputMessage = 0;
+constexpr int kOutputLabel = 0;
+
+enum class BosTag { kGenerate, kNone };
+enum class EosTag { kGenerate, kNone };
+
+class ProjectionParams {
+ public:
+  ProjectionParams(int feature_size, const std::string& vocabulary,
+                   const std::string& hashtype, int max_splits,
+                   bool split_on_space, int word_novelty_bits,
+                   int doc_size_levels, BosTag add_bos_tag, EosTag add_eos_tag,
+                   bool exclude_nonalphaspace_unicodes,
+                   const std::string& token_separators,
+                   bool normalize_repetition, bool add_first_cap_feature,
+                   bool add_all_caps_feature, bool normalize_spaces)
+      : feature_size_(feature_size),
+        unicode_handler_(vocabulary, exclude_nonalphaspace_unicodes),
+        hasher_(Hasher::CreateHasher(feature_size, hashtype)),
+        max_splits_(max_splits),
+        split_on_space_(split_on_space),
+        word_novelty_bits_(word_novelty_bits),
+        doc_size_levels_(doc_size_levels),
+        add_bos_tag_(add_bos_tag == BosTag::kGenerate),
+        add_eos_tag_(add_eos_tag == EosTag::kGenerate),
+        add_first_cap_feature_(add_first_cap_feature),
+        add_all_caps_feature_(add_all_caps_feature) {
+    assert(max_splits_ == -1 || max_splits_ > 0);
+    assert(word_novelty_bits >= 0 && word_novelty_bits <= 7);
+    // hasher_ can be nullptr if the hashtype is invalid. But there is a similar
+    // check in tensorflow op when the model is created. So this failure will
+    // never happen if the model was successfully trained. Still adding a check
+    // here since you can edit the model post training, which is the only
+    // situation when this assertion will fail.
+    assert(hasher_ != nullptr);
+    if (word_novelty_bits_ != 0) {
+      assert(feature_size_ >= 1);
+    }
+    assert(doc_size_levels >= 0 && doc_size_levels <= 16);
+    if (doc_size_levels_ != 0) {
+      assert(feature_size_ >= 2);
+    }
+    word_novelty_offset_ = 2.0f / (1 << word_novelty_bits_);
+
+    if (!token_separators.empty() || normalize_repetition || normalize_spaces) {
+      projection_normalizer_ = std::make_unique<ProjectionNormalizer>(
+          token_separators, normalize_repetition, normalize_spaces);
+    }
+  }
+  virtual ~ProjectionParams() {}
+  int FeatureSize() const { return feature_size_; }
+  bool WordNoveltyEnabled() const { return word_novelty_bits_ != 0; }
+  void WordNoveltyFeature(float* data, int word_count) const {
+    *data = std::min((word_count * word_novelty_offset_) - 1.0f, 1.0f);
+  }
+  void WordNoveltyFeature(uint8_t* data, int word_count) const {
+    float word_novelty_feature;
+    WordNoveltyFeature(&word_novelty_feature, word_count);
+    *data = PodQuantize(word_novelty_feature, 127.0f, 127);
+  }
+  bool DocSizeFeatureEnabled() const { return (doc_size_levels_ != 0); }
+  bool FirstCap() const { return add_first_cap_feature_; }
+  bool AllCaps() const { return add_all_caps_feature_; }
+  int BosToken() const { return add_bos_tag_ ? 1 : 0; }
+  int EosToken() const { return add_eos_tag_ ? 1 : 0; }
+  void DocSizeFeature(float* data, int num_tokens) {
+    float doc_size_feature =
+        (doc_size_levels_ != 0)
+            ? std::log2(static_cast<float>(num_tokens)) / doc_size_levels_
+            : 0.0f;
+    *data = std::min(doc_size_feature, 1.0f) * 2.0f - 1.0f;
+  }
+  void DocSizeFeature(uint8_t* data, int num_tokens) {
+    float doc_size_feature;
+    DocSizeFeature(&doc_size_feature, num_tokens);
+    *data = PodQuantize(doc_size_feature, 127.0f, 127);
+  }
+  void Hash(const std::string& word, std::vector<uint64_t>& hash_codes) {
+    hasher_->GetHashCodes(word, hash_codes);
+  }
+  // Lower cases the input text and eliminates all unsupported
+  // unicodes in it if a vocabulary is provided.
+  std::string LowerCaseUTF8WithSupportedUnicodes(
+      std::pair<const char*, size_t> source, bool* first_cap,
+      bool* all_caps) const {
+    return unicode_handler_.LowerCaseUTF8WithSupportedUnicodes(
+        source, first_cap, all_caps);
+  }
+  // Splits the input text into a set of tokens. Uses space as the delimiter
+  // when split_on_space is True and unicode boundaries as the delimiter
+  // otherwise. When max_splits is set to -1, no limit on number of tokens is
+  // imposed. When it is set to a positive integer, number of tokens is
+  // truncated beyond that integer. An end of input token is always added after
+  // tokenization, hence the number of tokens is one more than the true number
+  // of tokens.
+  virtual TfLiteStatus PreprocessInput(TfLiteTensor* input_t,
+                                       TfLiteContext* context) {
+    if (input_t->bytes == 0) {
+      context->ReportError(context, "Empty input not supported.");
+      return kTfLiteError;
+    }
+    tflite::StringRef inputref = tflite::GetString(input_t, /*string_index=*/0);
+    if (projection_normalizer_ == nullptr) {
+      tokens_ = unicode_handler_.Tokenize(inputref.str, inputref.len,
+                                          split_on_space_, max_splits_);
+    } else {
+      normalized_input_ = projection_normalizer_->Normalize(
+          inputref.str, inputref.len, SIZE_MAX);
+      tokens_ = unicode_handler_.Tokenize(normalized_input_, split_on_space_,
+                                          max_splits_);
+    }
+    if (GetNumTokens() == 0 && !add_bos_tag_ && !add_eos_tag_) {
+      context->ReportError(context, "No tokens found.");
+      return kTfLiteError;
+    }
+    return kTfLiteOk;
+  }
+  int GetNumTokens() const { return tokens_.size(); }
+  const std::vector<std::pair<const char*, size_t>>& GetTokens() const {
+    return tokens_;
+  }
+  virtual std::string PreprocessToken(const std::string& word) { return word; }
+
+ private:
+  int feature_size_;
+  ProjectionUnicodeHandler unicode_handler_;
+  std::unique_ptr<Hasher> hasher_;
+  int max_splits_;
+  bool split_on_space_;
+  int word_novelty_bits_;
+  int doc_size_levels_;
+  bool add_bos_tag_;
+  bool add_eos_tag_;
+  bool add_first_cap_feature_;
+  bool add_all_caps_feature_;
+  float word_novelty_offset_;
+  std::string normalized_input_;
+
+ protected:
+  std::unique_ptr<ProjectionNormalizer> projection_normalizer_;
+  std::vector<std::pair<const char*, size_t>> tokens_;
+};
+
+class ProjectionParamsV2 : public ProjectionParams {
+ public:
+  ProjectionParamsV2(int feature_size, const std::string& vocabulary,
+                     const std::string& hashtype, BosTag add_bos_tag,
+                     EosTag add_eos_tag, bool normalize_repetition)
+      : ProjectionParams(feature_size, vocabulary, hashtype,
+                         /*max_splits = */ -1,
+                         /* split_on_space = */ true,
+                         /*word_novelty_bits = */ 0, /*doc_size_levels = */ 0,
+                         add_bos_tag, add_eos_tag,
+                         /*exclude_nonalphaspace_unicodes = */ false,
+                         /*token_separators = */ "", normalize_repetition,
+                         /*add_first_cap_feature = */ false,
+                         /*add_all_caps_feature = */ false,
+                         /*normalize_spaces = */ false) {}
+  ~ProjectionParamsV2() override {}
+
+  TfLiteStatus PreprocessInput(TfLiteTensor* input_t,
+                               TfLiteContext* context) override {
+    const TfLiteIntArray* const dims = input_t->dims;
+    const int num_tokens = tflite::GetStringCount(input_t);
+    if (num_tokens == 0) {
+      context->ReportError(context, "Empty input not supported.");
+      return kTfLiteError;
+    }
+    if (dims->size != 2) {
+      context->ReportError(
+          context, "Input tensor is expected to be rank 2, got rank %d.",
+          dims->size);
+      return kTfLiteError;
+    } else if (dims->data[0] != 1) {
+      context->ReportError(context,
+                           "Input tensor batch size should be 1, got %d.",
+                           dims->data[0]);
+      return kTfLiteError;
+    } else if (num_tokens != dims->data[1]) {
+      context->ReportError(context,
+                           "Inconsistent number of input tokens %d != %d.",
+                           num_tokens, dims->data[1]);
+      return kTfLiteError;
+    }
+    tokens_.clear();
+    tokens_.reserve(num_tokens);
+    for (int i = 0; i < num_tokens; ++i) {
+      const tflite::StringRef strref = tflite::GetString(input_t, i);
+      tokens_.push_back(std::pair<const char*, size_t>(strref.str, strref.len));
+    }
+    return kTfLiteOk;
+  }
+  std::string PreprocessToken(const std::string& word) override {
+    return projection_normalizer_ ? projection_normalizer_->Normalize(
+                                        word.data(), word.length(), SIZE_MAX)
+                                  : word;
+  }
+};
+
+inline void SetTensorToDynamic(TfLiteTensor* tensor) {
+  if (tensor->allocation_type != kTfLiteDynamic) {
+    tensor->allocation_type = kTfLiteDynamic;
+    tensor->data.raw = nullptr;
+  }
+}
+
+// Determines whether tensor is dynamic. Note that a tensor can be non-const and
+// not dynamic. This function specifically checks for a dynamic tensor.
+inline bool IsDynamicTensor(const TfLiteTensor* tensor) {
+  return tensor->allocation_type == kTfLiteDynamic;
+}
+
+void* Init(TfLiteContext* context, const char* buffer, size_t length) {
+  const uint8_t* buffer_t = reinterpret_cast<const uint8_t*>(buffer);
+  const flexbuffers::Map& m = flexbuffers::GetRoot(buffer_t, length).AsMap();
+  const std::string hashtype =
+      m["hashtype"].IsNull() ? kMurmurHash : m["hashtype"].AsString().str();
+  const int word_novelty_bits =
+      m["word_novelty_bits"].IsNull() ? 0 : m["word_novelty_bits"].AsInt32();
+  const int doc_size_levels =
+      m["doc_size_levels"].IsNull() ? 0 : m["doc_size_levels"].AsInt32();
+  const bool add_bos_tag =
+      m["add_bos_tag"].IsNull() ? false : m["add_bos_tag"].AsBool();
+  const bool add_eos_tag =
+      m["add_eos_tag"].IsNull() ? true : m["add_eos_tag"].AsBool();
+  float add_first_cap_feature = m["add_first_cap_feature"].IsNull()
+                                    ? 0.0f
+                                    : m["add_first_cap_feature"].AsFloat();
+  float add_all_caps_feature = m["add_all_caps_feature"].IsNull()
+                                   ? 0.0f
+                                   : m["add_all_caps_feature"].AsFloat();
+  if (add_first_cap_feature != 0.0f && add_first_cap_feature != 1.0f) {
+    context->ReportError(
+        context,
+        "add_first_cap_feature is %f, it should be 0.0 or 1.0., "
+        "resetting it to 1.0f\n",
+        add_first_cap_feature);
+    add_first_cap_feature = 1.0f;
+  }
+  if (add_all_caps_feature != 0.0f && add_all_caps_feature != 1.0f) {
+    context->ReportError(
+        context,
+        "add_all_caps_feature is %f, it should be 0.0 or 1.0., "
+        "resetting it to 1.0f\n",
+        add_all_caps_feature);
+    add_all_caps_feature = 1.0f;
+  }
+  // Old models that use the op may not have this attribute set, for those
+  // models the default value of false will be used.
+  const bool exclude_nonalphaspace_unicodes =
+      m["exclude_nonalphaspace_unicodes"].IsNull()
+          ? false
+          : m["exclude_nonalphaspace_unicodes"].AsBool();
+  const std::string token_separators =
+      m["token_separators"].IsNull() ? "" : m["token_separators"].ToString();
+  const bool normalize_repetition = m["normalize_repetition"].AsBool();
+  const bool normalize_spaces = m["normalize_spaces"].AsBool();
+  if (!Hasher::SupportedHashType(hashtype)) {
+    context->ReportError(context, "Unsupported hashtype %s\n",
+                         hashtype.c_str());
+    return nullptr;
+  }
+
+  return new ProjectionParams(
+      m["feature_size"].AsInt32(), m["vocabulary"].AsString().str(), hashtype,
+      m["max_splits"].AsInt32(), m["split_on_space"].AsBool(),
+      word_novelty_bits, doc_size_levels,
+      add_bos_tag ? BosTag::kGenerate : BosTag::kNone,
+      add_eos_tag ? EosTag::kGenerate : EosTag::kNone,
+      exclude_nonalphaspace_unicodes, token_separators, normalize_repetition,
+      add_first_cap_feature == 1.0f, add_all_caps_feature == 1.0f,
+      normalize_spaces);
+}
+
+void* InitV2(TfLiteContext* context, const char* buffer, size_t length) {
+  const uint8_t* buffer_t = reinterpret_cast<const uint8_t*>(buffer);
+  const flexbuffers::Map& m = flexbuffers::GetRoot(buffer_t, length).AsMap();
+  const std::string hashtype =
+      m["hashtype"].IsNull() ? kMurmurHash : m["hashtype"].AsString().str();
+  if (!Hasher::SupportedHashType(hashtype)) {
+    context->ReportError(context, "Unsupported hashtype %s\n",
+                         hashtype.c_str());
+    return nullptr;
+  }
+
+  return new ProjectionParamsV2(
+      m["feature_size"].AsInt32(), m["vocabulary"].AsString().str(), hashtype,
+      m["add_bos_tag"].AsBool() ? BosTag::kGenerate : BosTag::kNone,
+      m["add_eos_tag"].AsBool() ? EosTag::kGenerate : EosTag::kNone,
+      m["normalize_repetition"].AsBool());
+}
+
+void Free(TfLiteContext* context, void* buffer) {
+  delete reinterpret_cast<ProjectionParams*>(buffer);
+}
+
+TfLiteStatus Resize(TfLiteContext* context, TfLiteNode* node) {
+  TfLiteTensor* output = &context->tensors[node->outputs->data[kOutputLabel]];
+  SetTensorToDynamic(output);
+  return kTfLiteOk;
+}
+
+constexpr int kHashCodeBits = 64;
+constexpr int kMapBits = 2;
+constexpr int kIncrement = kHashCodeBits / kMapBits;
+constexpr int kMapHigh = 1;
+constexpr int kMapLow = 2;
+
+template <typename T>
+void TypedEval(const T* mapping_table, ProjectionParams* params, T* data) {
+  auto tokens = params->GetTokens();
+  std::vector<uint64_t> hash_codes;
+  std::unordered_map<uint64_t, int> word_counter;
+
+  T doc_size_feature = T{0};
+  if (params->DocSizeFeatureEnabled()) {
+    params->DocSizeFeature(&doc_size_feature, tokens.size());
+  }
+  const int num_tokens = tokens.size() + params->EosToken();
+  for (int j = -params->BosToken(), offset0 = 0; j < num_tokens; ++j) {
+    std::string word;
+    bool first_cap, all_caps;
+    if (j < 0) {
+      word = kBeginToken;
+    } else if (j < tokens.size()) {
+      word = params->LowerCaseUTF8WithSupportedUnicodes(tokens[j], &first_cap,
+                                                        &all_caps);
+      word = params->PreprocessToken(word);
+    } else {
+      word = kEndToken;
+    }
+    params->Hash(word, hash_codes);
+    for (int hindex = 0, k = 0; hindex < hash_codes.size(); hindex++) {
+      auto hash = hash_codes[hindex];
+      for (int kmax = std::min(k + kIncrement, params->FeatureSize());
+           k < kmax;) {
+        data[offset0 + k++] = mapping_table[hash & ((1 << kMapBits) - 1)];
+        hash >>= kMapBits;
+      }
+    }
+    offset0 += params->FeatureSize();
+    if (params->WordNoveltyEnabled() && !hash_codes.empty()) {
+      params->WordNoveltyFeature(&data[offset0 - kWordNoveltyOffset],
+                                 word_counter[hash_codes[0]]++);
+    }
+    if (params->DocSizeFeatureEnabled()) {
+      data[offset0 - kDocSizeOffset] = doc_size_feature;
+    }
+    if (params->FirstCap()) {
+      data[offset0 - kFirstCapOffset] =
+          mapping_table[first_cap ? kMapHigh : kMapLow];
+    }
+    if (params->AllCaps()) {
+      data[offset0 - kAllCapsOffset] =
+          mapping_table[all_caps ? kMapHigh : kMapLow];
+    }
+  }
+}
+
+TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
+  auto* params = reinterpret_cast<ProjectionParams*>(node->user_data);
+  if (params == nullptr) {
+    context->ReportError(context, "Empty user data.");
+    return kTfLiteError;
+  }
+  TF_LITE_ENSURE_OK(
+      context,
+      params->PreprocessInput(
+          &context->tensors[node->inputs->data[kInputMessage]], context));
+
+  TfLiteTensor* output = &context->tensors[node->outputs->data[kOutputLabel]];
+  if (IsDynamicTensor(output)) {
+    TfLiteIntArray* output_size = TfLiteIntArrayCreate(3);
+    output_size->data[0] = 1;
+    output_size->data[1] =
+        params->BosToken() + params->GetNumTokens() + params->EosToken();
+    output_size->data[2] = params->FeatureSize();
+    TF_LITE_ENSURE_OK(context,
+                      context->ResizeTensor(context, output, output_size));
+  } else {
+    context->ReportError(context, "Output must by dynamic.");
+    return kTfLiteError;
+  }
+
+  if (output->type == kTfLiteUInt8) {
+    const uint8_t kMappingTable[1 << kMapBits] = {127, 255, 0, 127};
+    TypedEval(kMappingTable, params, output->data.uint8);
+  } else if (output->type == kTfLiteFloat32) {
+    const float kMappingTable[1 << kMapBits] = {0.0, 1.0, -1.0, 0.0};
+    TypedEval(kMappingTable, params, output->data.f);
+  } else {
+    context->ReportError(context, "Output type must be UInt8 or Float32.");
+    return kTfLiteError;
+  }
+
+  return kTfLiteOk;
+}
+
+}  // namespace
+}  // namespace sequence_string_projection
+
+const char kSequenceStringProjection[] = "SEQUENCE_STRING_PROJECTION";
+
+// This op converts a list of strings to a sequence of features using hashing.
+TfLiteRegistration* Register_SEQUENCE_STRING_PROJECTION() {
+  static TfLiteRegistration r = {
+      sequence_string_projection::Init, sequence_string_projection::Free,
+      sequence_string_projection::Resize, sequence_string_projection::Eval};
+  return &r;
+}
+
+const char kSequenceStringProjectionV2[] = "SEQUENCE_STRING_PROJECTION_V2";
+
+// This op converts a sequence of tokens to a sequence of projected features
+// using hashing.
+TfLiteRegistration* Register_SEQUENCE_STRING_PROJECTION_V2() {
+  static TfLiteRegistration r = {
+      sequence_string_projection::InitV2, sequence_string_projection::Free,
+      sequence_string_projection::Resize, sequence_string_projection::Eval};
+  return &r;
+}
+
+}  // namespace custom
+}  // namespace ops
+}  // namespace seq_flow_lite
diff --git a/third_party/tensorflow_models/src/research/seq_flow_lite/tflite_ops/sequence_string_projection.h b/third_party/tensorflow_models/src/research/seq_flow_lite/tflite_ops/sequence_string_projection.h
new file mode 100644
index 0000000..8c99496
--- /dev/null
+++ b/third_party/tensorflow_models/src/research/seq_flow_lite/tflite_ops/sequence_string_projection.h
@@ -0,0 +1,35 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+==============================================================================*/
+#ifndef TENSORFLOW_MODELS_SEQ_FLOW_LITE_TFLITE_OPS_SEQUENCE_STRING_PROJECTION_H_
+#define TENSORFLOW_MODELS_SEQ_FLOW_LITE_TFLITE_OPS_SEQUENCE_STRING_PROJECTION_H_
+#include "tensorflow/lite/kernels/register.h"
+
+namespace seq_flow_lite {
+namespace ops {
+namespace custom {
+
+extern const char kSequenceStringProjection[];
+
+TfLiteRegistration* Register_SEQUENCE_STRING_PROJECTION();
+
+extern const char kSequenceStringProjectionV2[];
+
+TfLiteRegistration* Register_SEQUENCE_STRING_PROJECTION_V2();
+
+}  // namespace custom
+}  // namespace ops
+}  // namespace seq_flow_lite
+
+#endif  // TENSORFLOW_MODELS_SEQ_FLOW_LITE_TFLITE_OPS_SEQUENCE_STRING_PROJECTION_H_
diff --git a/third_party/tensorflow_models/src/research/seq_flow_lite/tflite_ops/tflite_qrnn_pooling.cc b/third_party/tensorflow_models/src/research/seq_flow_lite/tflite_ops/tflite_qrnn_pooling.cc
new file mode 100644
index 0000000..85b70e5
--- /dev/null
+++ b/third_party/tensorflow_models/src/research/seq_flow_lite/tflite_ops/tflite_qrnn_pooling.cc
@@ -0,0 +1,146 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+==============================================================================*/
+#include "tflite_ops/quantization_util.h"  // seq_flow_lite
+#include "tflite_ops/tflite_qrnn_pooling.h"  // seq_flow_lite
+
+namespace seq_flow_lite {
+namespace ops {
+namespace custom {
+
+namespace {
+
+const uint8_t kPoolingForward = 255;
+
+TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
+  TF_LITE_ENSURE_EQ(context, node->inputs->size, 3);
+  if (node->outputs->size < 1 || node->outputs->size > 2) {
+    return kTfLiteError;
+  }
+
+  TfLiteTensor* multiplier = &context->tensors[node->inputs->data[0]];
+  TfLiteTensor* constant = &context->tensors[node->inputs->data[1]];
+  TfLiteTensor* direction = &context->tensors[node->inputs->data[2]];
+
+  TF_LITE_ENSURE_EQ(context, multiplier->type, kTfLiteUInt8);
+  TF_LITE_ENSURE_EQ(context, constant->type, kTfLiteUInt8);
+  TF_LITE_ENSURE_EQ(context, direction->type, kTfLiteUInt8);
+
+  TF_LITE_ENSURE_EQ(context, multiplier->dims->size, 3);
+  TF_LITE_ENSURE_EQ(context, multiplier->dims->data[0], 1);
+  const int time_steps = multiplier->dims->data[1];
+  const int state_size = multiplier->dims->data[2];
+
+  TF_LITE_ENSURE_EQ(context, constant->dims->size, 3);
+  TF_LITE_ENSURE_EQ(context, constant->dims->data[0], 1);
+  TF_LITE_ENSURE_EQ(context, constant->dims->data[1], time_steps);
+  TF_LITE_ENSURE_EQ(context, constant->dims->data[2], state_size);
+
+  TF_LITE_ENSURE_EQ(context, direction->dims->size, 1);
+  TF_LITE_ENSURE_EQ(context, direction->dims->data[0], 1);
+
+  TfLiteTensor* outputs = &context->tensors[node->outputs->data[0]];
+  if (outputs) {
+    TF_LITE_ENSURE_OK(
+        context, context->ResizeTensor(context, outputs,
+                                       TfLiteIntArrayCopy(multiplier->dims)));
+  }
+
+  if (node->outputs->size == 2) {
+    TfLiteTensor* final_state = &context->tensors[node->outputs->data[1]];
+    if (final_state) {
+      TfLiteIntArray* final_state_dims = TfLiteIntArrayCreate(2);
+      final_state_dims->data[0] = 1;
+      final_state_dims->data[1] = state_size;
+      TF_LITE_ENSURE_OK(context, context->ResizeTensor(context, final_state,
+                                                       final_state_dims));
+    }
+  }
+  return kTfLiteOk;
+}
+
+TfLiteStatus QRNNPooling(TfLiteContext* context, TfLiteTensor* multiplier,
+                         TfLiteTensor* constant, TfLiteTensor* outputs,
+                         TfLiteTensor* final_state, bool forward) {
+  const int time_steps = multiplier->dims->data[1];
+  const int state_size = multiplier->dims->data[2];
+
+  auto state = std::make_unique<float[]>(state_size);
+  memset(state.get(), 0, sizeof(float) * state_size);
+
+  const int32_t out_zero_point = outputs ? outputs->params.zero_point : 0;
+  const float out_inverse_scale = outputs ? 1.0f / outputs->params.scale : 1.0f;
+  uint8_t* out_ptr = outputs ? outputs->data.uint8 : nullptr;
+  for (int i = 0; i < time_steps; ++i) {
+    for (int j = 0; j < state_size; ++j) {
+      const int time_index = forward ? i : time_steps - (i + 1);
+      const int index = time_index * state_size + j;
+      float multiplier_value = PodDequantize(*multiplier, index);
+      float constant_vale = PodDequantize(*constant, index);
+      state[j] = state[j] * multiplier_value + constant_vale;
+      if (outputs) {
+        out_ptr[index] =
+            PodQuantize(state[j], out_zero_point, out_inverse_scale);
+      }
+    }
+  }
+
+  if (final_state) {
+    uint8_t* final_state_ptr = final_state->data.uint8;
+    const int32_t zero_point = final_state->params.zero_point;
+    const float inverse_scale = 1.0f / final_state->params.scale;
+    for (int j = 0; j < state_size; ++j) {
+      final_state_ptr[j] = PodQuantize(state[j], zero_point, inverse_scale);
+    }
+  }
+
+  return kTfLiteOk;
+}
+
+TfLiteStatus Invoke(TfLiteContext* context, TfLiteNode* node) {
+  TF_LITE_ENSURE_EQ(context, node->inputs->size, 3);
+  if (node->outputs->size < 1 || node->outputs->size > 2) {
+    return kTfLiteError;
+  }
+
+  TfLiteTensor* multiplier = &context->tensors[node->inputs->data[0]];
+  TfLiteTensor* constant = &context->tensors[node->inputs->data[1]];
+  TfLiteTensor* direction = &context->tensors[node->inputs->data[2]];
+  TfLiteTensor* outputs = &context->tensors[node->outputs->data[0]];
+  TfLiteTensor* final_state = (node->outputs->size == 2)
+                                  ? &context->tensors[node->outputs->data[1]]
+                                  : nullptr;
+
+  // When pooling forward the direction parameter is expected to be
+  // kPoolingForward.
+  return QRNNPooling(context, multiplier, constant, outputs, final_state,
+                     (direction->data.uint8[0] == kPoolingForward));
+}
+
+}  // namespace
+
+const char kPoolingOp[] = "PoolingOp";
+
+void RegisterQRNNPooling(::tflite::ops::builtin::BuiltinOpResolver* resolver) {
+  resolver->AddCustom(kPoolingOp, Register_QRNN_POOLING());
+}
+
+TfLiteRegistration* Register_QRNN_POOLING() {
+  static TfLiteRegistration r = {nullptr, nullptr, Prepare, Invoke};
+  return &r;
+}
+
+}  // namespace custom
+}  // namespace ops
+}  // namespace seq_flow_lite
diff --git a/third_party/tensorflow_models/src/research/seq_flow_lite/tflite_ops/tflite_qrnn_pooling.h b/third_party/tensorflow_models/src/research/seq_flow_lite/tflite_ops/tflite_qrnn_pooling.h
new file mode 100644
index 0000000..60722df
--- /dev/null
+++ b/third_party/tensorflow_models/src/research/seq_flow_lite/tflite_ops/tflite_qrnn_pooling.h
@@ -0,0 +1,33 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+==============================================================================*/
+#ifndef TENSORFLOW_MODELS_SEQ_FLOW_LITE_TFLITE_OPS_TFLITE_QRNN_POOLING_H_
+#define TENSORFLOW_MODELS_SEQ_FLOW_LITE_TFLITE_OPS_TFLITE_QRNN_POOLING_H_
+
+#include "absl/base/macros.h"
+#include "tensorflow/lite/kernels/register.h"
+
+namespace seq_flow_lite {
+namespace ops {
+namespace custom {
+
+extern const char kPoolingOp[];
+
+TfLiteRegistration* Register_QRNN_POOLING();
+
+}  // namespace custom
+}  // namespace ops
+}  // namespace seq_flow_lite
+
+#endif  // TENSORFLOW_MODELS_SEQ_FLOW_LITE_TFLITE_OPS_TFLITE_QRNN_POOLING_H_
diff --git a/third_party/tensorflow_models/update.sh b/third_party/tensorflow_models/update.sh
new file mode 100644
index 0000000..5c0cd619
--- /dev/null
+++ b/third_party/tensorflow_models/update.sh
@@ -0,0 +1,40 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+if [ $(basename ${PWD}) != "src" ]; then
+  echo "Please set the current working directory to chromium/src first!"
+  exit 1
+fi
+
+files=(
+  "research/seq_flow_lite/tf_ops/projection_normalizer_util.cc"
+  "research/seq_flow_lite/tf_ops/projection_normalizer_util.h"
+  "research/seq_flow_lite/tf_ops/projection_util.cc"
+  "research/seq_flow_lite/tf_ops/projection_util.h"
+  "research/seq_flow_lite/tf_ops/skipgram_finder.h"
+  "research/seq_flow_lite/tf_ops/skipgram_finder.cc"
+  "research/seq_flow_lite/tflite_ops/denylist.cc"
+  "research/seq_flow_lite/tflite_ops/denylist.h"
+  "research/seq_flow_lite/tflite_ops/denylist_skipgram.cc"
+  "research/seq_flow_lite/tflite_ops/denylist_skipgram.h"
+  "research/seq_flow_lite/tflite_ops/quantization_util.h"
+  "research/seq_flow_lite/tflite_ops/sequence_string_projection.cc"
+  "research/seq_flow_lite/tflite_ops/sequence_string_projection.h"
+  "research/seq_flow_lite/tflite_ops/tflite_qrnn_pooling.cc"
+  "research/seq_flow_lite/tflite_ops/tflite_qrnn_pooling.h"
+)
+
+git clone --depth 1 https://github.com/tensorflow/models /tmp/models
+rm -rf third_party/tensorflow_models/src/*
+pushd third_party/tensorflow_models/src/
+
+for file in ${files[@]} ; do
+  if [ ! -d $(dirname ${file}) ] ; then
+    mkdir -p $(dirname ${file})
+  fi
+  cp /tmp/models/${file} ${file}
+done
+
+popd
+rm -rf /tmp/models
diff --git a/tools/cast3p/cast_core.version b/tools/cast3p/cast_core.version
index b9a6d219..44fb13d9 100644
--- a/tools/cast3p/cast_core.version
+++ b/tools/cast3p/cast_core.version
@@ -1 +1 @@
-cast_20220916_0600_RC00
+cast_20220930_0600_RC00
diff --git a/tools/git/suggest_owners.py b/tools/git/suggest_owners.py
index 49e9180e..417c331 100755
--- a/tools/git/suggest_owners.py
+++ b/tools/git/suggest_owners.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright 2018 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
@@ -8,10 +8,12 @@
 import argparse
 import subprocess
 import pickle
+import re
 import os
+from pathlib import PurePath
 from os import path
 from datetime import date, timedelta
-from collections import namedtuple, defaultdict, Counter
+from collections import namedtuple, defaultdict
 
 Commit = namedtuple('Commit', ['hash', 'author', 'commit_date', 'dirs'])
 
@@ -25,15 +27,21 @@
 # filename for pickle cache
 CACHE_FILENAME = 'suggest_owners.cache'
 
-def _RunGitCommand(options, cmd_args):
+
+def _RunGitCommand(options, cmd_args, pipe_output=False):
   repo_path = path.join(options.repo_path, '.git')
   cmd = ['git', '--git-dir', repo_path] + cmd_args
   print('>', ' '.join(cmd))
-  return subprocess.check_output(cmd)
+  if not pipe_output:
+    return subprocess.check_output(cmd, encoding='utf-8')
+  else:
+    return subprocess.Popen(cmd, encoding='utf-8',
+                            stdout=subprocess.PIPE).stdout
 
 
 def _ValidAuthor(author):
-  return author.find('@chromium.org') > -1 and author.find('roller') == -1
+  return author.endswith(
+      ('@chromium.org', '@google.com')) and 'roller' not in author
 
 
 # Returns additions/deletions by a commit to a directory (and its descendants).
@@ -53,11 +61,12 @@
 def _PropagateCommit(options, commit):
   touched_dirs = set()
   # first get all the touched dirs and their ancestors
-  for directory in commit.dirs.iterkeys():
-    while directory != '':
-      touched_dirs.add(directory)
+  for directory in commit.dirs.keys():
+    # PurePath.parent returns '.' for non absolute paths in the limit.
+    while str(directory) != '.':
+      touched_dirs.add(str(directory))
       # get the parent directory
-      directory = path.dirname(directory)
+      directory = PurePath(directory).parent
   # loop over them and calculate the edits per directory
   for directory in touched_dirs:
     author_commits, author_additions, author_deletions = \
@@ -72,9 +81,9 @@
 # Checks if child_directory is same as or below parent_directory. For some
 # reason the os.path module does not have this functionality.
 def isSubDirectory(parent_directory, child_directory):
-  parent_directory = parent_directory + '/'
-  child_directory = child_directory + '/'
-  return child_directory.startswith(parent_directory)
+  parent_directory = PurePath(parent_directory)
+  child_directory = PurePath(child_directory)
+  return child_directory.is_relative_to(parent_directory)
 
 
 def _GetGitLogCmd(options):
@@ -115,7 +124,14 @@
     deletions = 0
   else:
     deletions = int(deletions)
+  if additions == 0 and deletions == 0:
+    return True
   dir_path = path.dirname(filepath)
+  # For git renames, we count the destination directory
+  if '=>' in dir_path:
+    dir_path = re.sub(r'\{[^=]* => ([^\}]*)\}', r'\1', dir_path)
+    # remove possibly empty path parts.
+    dir_path = dir_path.replace('//', '/')
   commit_additions, commit_deletions = \
       current_commit.dirs.get(dir_path, (0,0))
   current_commit.dirs[dir_path] = (
@@ -129,9 +145,12 @@
           'subdirectory or reduce the number of days of history to low double '
           'digits to make this faster. There is no progress indicator, it is '
           'all waiting for single git log to finish.')
-  output = _RunGitCommand(options, _GetGitLogCmd(options))
+  output_pipe = _RunGitCommand(options,
+                               _GetGitLogCmd(options),
+                               pipe_output=True)
   current_commit = None
-  for line in output.splitlines():
+  for line in iter(output_pipe.readline, ''):
+    line = line.rstrip('\n')
     if current_commit is None:
       current_commit = _ParseCommitLine(line)
     else:
@@ -148,15 +167,16 @@
   # process the final commit
   if _ValidAuthor(current_commit.author):
     _PropagateCommit(options, current_commit)
+  print('Done parsing commit log.')
 
 
 def _CountCommits(directory):
   return sum(
-      [count for (count, _a, _d) in DIRECTORY_AUTHORS[directory].itervalues()])
+      [count for (count, _a, _d) in DIRECTORY_AUTHORS[directory].values()])
 
 
 def _GetOwnerLevel(options, author, directory):
-  sorted_owners = sorted(_GetOwners(options, directory), key=lambda (o,l): l)
+  sorted_owners = sorted(_GetOwners(options, directory), key=lambda e: e[1])
   for owner, level in sorted_owners:
     if author == owner:
       return level
@@ -222,8 +242,7 @@
 
 def computeSuggestions(options):
   directory_suggestions = []
-  for directory, authors in sorted(
-      DIRECTORY_AUTHORS.iteritems(), key=lambda (d, a): d):
+  for directory, authors in sorted(DIRECTORY_AUTHORS.items()):
     if _IsTrivialDirectory(options, directory):
       continue
     if _CountCommits(directory) < options.dir_commit_limit:
@@ -233,8 +252,7 @@
         and not isSubDirectory(options.subdirectory, directory)):
       continue
     # sort authors by descending number of commits
-    sorted_authors = sorted(authors.items(),
-                            key=lambda (author, details): -details[0])
+    sorted_authors = sorted(authors.items(), key=lambda entry: -entry[1][0])
     # keep only authors above the limit
     suggestions = [(a,c) for a,c in sorted_authors if \
                    a not in options.ignore_authors \
@@ -296,7 +314,7 @@
 
 def cacheProcessedCommits(options):
   metadata = _GetCacheMetadata(options)
-  with open(CACHE_FILENAME, 'w') as f:
+  with open(CACHE_FILENAME, 'wb') as f:
     pickle.dump((metadata, DIRECTORY_AUTHORS), f)
 
 
@@ -304,7 +322,7 @@
   global DIRECTORY_AUTHORS
   if not path.exists(CACHE_FILENAME):
     return False
-  with open(CACHE_FILENAME) as f:
+  with open(CACHE_FILENAME, 'rb') as f:
     stored_metadata, cached_directory_authors = pickle.load(f)
     if _IsCacheValid(options, stored_metadata):
       print('Loading from cache')
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index bea15630..7396d99 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -1274,9 +1274,6 @@
       'ios-simulator-inverse-fieldtrials-fyi': 'ios_simulator_debug_static_bot_invert_fieldtrials_xctest',
       'ios-simulator-multi-window': 'ios_simulator_debug_static_bot_xctest',
       'ios-simulator-noncq': 'ios_simulator_debug_static_bot_xctest',
-      # TODO (crbug.com/1298112): Remove once ios-simulator moves to srcless
-      # orchestrator
-      'ios-simulator-orchestrator': 'ios_simulator_code_coverage_partial_instrumentation_xctest',
       'ios-simulator-rts': 'ios_simulator_code_coverage_partial_instrumentation_xctest',
       'ios15-beta-simulator': 'ios_simulator_debug_static_bot_xctest',
       'ios15-sdk-simulator': 'ios_simulator_debug_static_bot_xctest',
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.mac.json b/tools/mb/mb_config_expectations/tryserver.chromium.mac.json
index f506219..26e6a09 100644
--- a/tools/mb/mb_config_expectations/tryserver.chromium.mac.json
+++ b/tools/mb/mb_config_expectations/tryserver.chromium.mac.json
@@ -392,21 +392,6 @@
       "use_goma": true
     }
   },
-  "ios-simulator-orchestrator": {
-    "gn_args": {
-      "coverage_instrumentation_input_file": "//.code-coverage/files_to_instrument.txt",
-      "enable_run_ios_unittests_with_xctest": true,
-      "ios_set_attributes_for_xcode_project_generation": false,
-      "is_component_build": false,
-      "is_debug": true,
-      "symbol_level": 1,
-      "target_cpu": "x64",
-      "target_environment": "simulator",
-      "target_os": "ios",
-      "use_clang_coverage": true,
-      "use_goma": true
-    }
-  },
   "ios-simulator-rts": {
     "gn_args": {
       "coverage_instrumentation_input_file": "//.code-coverage/files_to_instrument.txt",
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index a4c4a0c..53fb880 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -94825,7 +94825,11 @@
     </obsolete>
   </int>
   <int value="2" label="NOT_MY_BIRTHDAY"/>
-  <int value="3" label="THROTTLED"/>
+  <int value="3" label="THROTTLED">
+    You might want to look at Sync.ThrottledSomeModelTypes to correlate this
+    with throttling of a specific data type, or Sync.ThrottledAllModelTypes for
+    throttling of the whole client.
+  </int>
   <int value="4" label="DEPRECATED_AUTH_EXPIRED">
     <obsolete>
       Obsolete value.
@@ -94850,7 +94854,10 @@
       Obsolete value.
     </obsolete>
   </int>
-  <int value="12" label="PARTIAL_FAILURE"/>
+  <int value="12" label="PARTIAL_FAILURE">
+    You might want to look at Sync.BackedOffModelType to see the per-type effect
+    of this.
+  </int>
   <int value="13" label="CLIENT_DATA_OBSOLETE"/>
   <int value="100" label="UNKNOWN"/>
 </enum>
diff --git a/tools/metrics/histograms/metadata/sync/histograms.xml b/tools/metrics/histograms/metadata/sync/histograms.xml
index 8c91fa0e..3ad7660 100644
--- a/tools/metrics/histograms/metadata/sync/histograms.xml
+++ b/tools/metrics/histograms/metadata/sync/histograms.xml
@@ -102,6 +102,19 @@
   </token>
 </histogram>
 
+<histogram name="Sync.BackedOffModelType" enum="SyncModelTypes"
+    expires_after="2023-02-12">
+  <owner>victorvianna@google.com</owner>
+  <owner>src/components/sync/OWNERS</owner>
+  <component>Services&gt;Sync</component>
+  <summary>
+    Logged when a data type is backed off.
+
+    This histogram can help explain changes in
+    Sync.PostedClientToServerMessageError2::PARTIAL_FAILURE.
+  </summary>
+</histogram>
+
 <histogram name="Sync.BookmarkEntityReuploadNeeded.On{UpdateType}"
     enum="Boolean" expires_after="2023-10-30">
   <owner>rushans@google.com</owner>
@@ -1299,6 +1312,35 @@
   </summary>
 </histogram>
 
+<histogram name="Sync.ThrottledAllModelTypes" enum="Boolean"
+    expires_after="2023-02-12">
+  <owner>victorvianna@google.com</owner>
+  <owner>src/components/sync/OWNERS</owner>
+  <component>Services&gt;Sync</component>
+  <summary>
+    Logged when the server instructs the client to throttle all its data types.
+    See also Sync.ThrottledSomeModelTypes which records a different event.
+
+    This histogram can help explain changes in
+    Sync.PostedClientToServerMessageError2::THROTTLED.
+  </summary>
+</histogram>
+
+<histogram name="Sync.ThrottledSomeModelTypes" enum="SyncModelTypes"
+    expires_after="2023-02-12">
+  <owner>victorvianna@google.com</owner>
+  <owner>src/components/sync/OWNERS</owner>
+  <component>Services&gt;Sync</component>
+  <summary>
+    Logged when the sync server instructs this client to throttle a special list
+    of data types. See also Sync.ThrottledAllModelTypes which records a
+    different event.
+
+    This histogram can help explain changes in
+    Sync.PostedClientToServerMessageError2::THROTTLED.
+  </summary>
+</histogram>
+
 <histogram name="Sync.TrustedVaultAccessTokenFetchSuccess" enum="Boolean"
     expires_after="2023-03-19">
   <owner>mmoskvitin@google.com</owner>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 4e5b3170..f5989d1 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -15387,6 +15387,14 @@
       start to the time the parser started, for main frame documents.
     </summary>
   </metric>
+  <metric name="RefreshRateThrottled" enum="Boolean">
+    <summary>
+      Records whether the refresh rate is throttled (capped to 30Hz) by Power
+      Saver Mode for this Page Load, when the navigation starts. It is possible
+      but very unlikely for this to change mid navigation, for instance due to a
+      change by the user in settings or the battery being recharged above 20%.
+    </summary>
+  </metric>
   <metric name="SiteEngagementScore">
     <owner>mcrouse@chromium.org</owner>
     <owner>tbansal@chromium.org</owner>
diff --git a/tools/perf/benchmarks/rendering.py b/tools/perf/benchmarks/rendering.py
index fa72fb6..8385750 100644
--- a/tools/perf/benchmarks/rendering.py
+++ b/tools/perf/benchmarks/rendering.py
@@ -18,10 +18,6 @@
     'Compositing.Display.DrawToSwapUs',
     'CompositorLatency.TotalLatency',
     'CompositorLatency.Type',
-    'Event.Latency.ScrollBegin.Touch.TimeToScrollUpdateSwapBegin4',
-    'Event.Latency.ScrollUpdate.Touch.TimeToScrollUpdateSwapBegin4',
-    'Event.Latency.ScrollBegin.Wheel.TimeToScrollUpdateSwapBegin4',
-    'Event.Latency.ScrollUpdate.Wheel.TimeToScrollUpdateSwapBegin4',
     'EventLatency.FirstGestureScrollUpdate.Touchscreen.TotalLatency',
     'EventLatency.FirstGestureScrollUpdate.Wheel.TotalLatency',
     'EventLatency.GestureScrollUpdate.Touchscreen.TotalLatency',
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index b1098e9..de13c4f 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,24 +5,24 @@
             "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm64/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "win": {
-            "hash": "b8af05e4fbb9da7e0cc7339278d1db860179940e",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/be5aeea7578fb821bfcd3c8ff65aee404bb95ba8/trace_processor_shell.exe"
+            "hash": "3b56d6418913c5721efaa6ee91b0808c50e7cced",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/3b0f05a3bd85e628d15c9e6168c647237103efbd/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "58893933be305d3bfe0a72ebebcacde2ac3ca893",
             "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "mac": {
-            "hash": "b13f13b1fa6fb8caeac8337fc598aafdcf722622",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/be5aeea7578fb821bfcd3c8ff65aee404bb95ba8/trace_processor_shell"
+            "hash": "76b7448ffc6f541eb07457554864e1133433df8c",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/1d22e34af6597ada72fc7409890109de30841c8d/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "e1ad4861384b06d911a65f035317914b8cc975c6",
             "full_remote_path": "perfetto-luci-artifacts/v25.0/mac-arm64/trace_processor_shell"
         },
         "linux": {
-            "hash": "f5c3f4abd3a0e547b2be8f59391f1e587b8d011f",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/0325b52616a5c26a949b191323c5a0a11cb09ae4/trace_processor_shell"
+            "hash": "2d904901e3fb4c38a27a07689b8e73df825c2f59",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/3b0f05a3bd85e628d15c9e6168c647237103efbd/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/polymer/polymer.gni b/tools/polymer/polymer.gni
index 4a55eea..f5cec90 100644
--- a/tools/polymer/polymer.gni
+++ b/tools/polymer/polymer.gni
@@ -15,7 +15,6 @@
   "ui/webui/resources/html/cr/ui/focus_outline_manager.html|FocusOutlineManager",
   "ui/webui/resources/html/cr/ui/focus_row_behavior.html|FocusRowBehavior",
   "ui/webui/resources/html/cr/ui/focus_without_ink_js.html|focusWithoutInk",
-  "ui/webui/resources/html/list_property_update_behavior.html|ListPropertyUpdateBehavior",
   "ui/webui/resources/html/load_time_data.html|loadTimeData",
   "ui/webui/resources/html/parse_html_subset.html|parseHtmlSubset",
   "ui/webui/resources/html/polymer.html|Polymer,html",
diff --git a/tools/traffic_annotation/auditor/chromeos/safe_list.txt b/tools/traffic_annotation/auditor/chromeos/safe_list.txt
index 7f27336..1258cbd2 100644
--- a/tools/traffic_annotation/auditor/chromeos/safe_list.txt
+++ b/tools/traffic_annotation/auditor/chromeos/safe_list.txt
@@ -11,12 +11,12 @@
 all,ash/components/trial_group/trial_group_checker.cc
 all,chrome/browser/ash/net/network_portal_detector_impl.cc
 all,chrome/browser/ash/policy/uploading/system_log_uploader.cc
-all,chrome/browser/ui/ash/assistant/search_and_assistant_enabled_checker.cc
 all,chromeos/geolocation/simple_geolocation_request.cc
 all,components/quirks/quirks_client.cc
 all,ash/services/device_sync/cryptauth_client_impl.cc
 all,chrome/browser/supervised_user/child_accounts/family_info_fetcher.cc
 all,chrome/services/sharing/nearby/platform/webrtc.cc
+all,chrome/browser/nearby_sharing/client/nearby_share_client_impl.cc
 all,chrome/browser/supervised_user/child_accounts/permission_request_creator_apiary.cc
 all,chrome/browser/supervised_user/kids_chrome_management/kids_chrome_management_client.cc
 all,chrome/browser/ash/net/network_diagnostics/network_diagnostics_util.cc
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index ee07d936e..490125a 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -399,4 +399,5 @@
  <item id="nearby_share_contacts" added_in_milestone="108" type="partial" second_id="oauth2_api_call_flow" content_hash_code="0473989a" os_list="chromeos" semantics_fields="1,2,3,4,5" policy_fields="3,4" file_path="chrome/browser/nearby_sharing/client/nearby_share_client_impl.cc" />
  <item id="nearby_share_list_public_certificates" added_in_milestone="108" type="partial" second_id="oauth2_api_call_flow" content_hash_code="01706e8a" os_list="chromeos" semantics_fields="1,2,3,4,5" policy_fields="3,4" file_path="chrome/browser/nearby_sharing/client/nearby_share_client_impl.cc" />
  <item id="app_preload_service" added_in_milestone="108" content_hash_code="00720713" os_list="chromeos" file_path="chrome/browser/apps/app_preload_service/app_preload_server_connector.cc" />
+ <item id="search_and_assistant_enabled_checker" added_in_milestone="106" content_hash_code="003a5b64" os_list="chromeos" file_path="chrome/browser/ui/ash/assistant/search_and_assistant_enabled_checker.cc" />
 </annotations>
diff --git a/tools/traffic_annotation/summary/grouping.xml b/tools/traffic_annotation/summary/grouping.xml
index fef774a..f83be94 100644
--- a/tools/traffic_annotation/summary/grouping.xml
+++ b/tools/traffic_annotation/summary/grouping.xml
@@ -100,6 +100,7 @@
       <annotation id="wallpaper_google_photos_albums"/>
       <annotation id="wallpaper_google_photos_enabled"/>
       <annotation id="wallpaper_google_photos_photos"/>
+      <annotation id="search_and_assistant_enabled_checker"/>
     </sender>
     <sender name="ChromeOS Recovery">
       <annotation id="cros_recovery_image_download"/>
diff --git a/ui/accessibility/ax_assistant_structure.cc b/ui/accessibility/ax_assistant_structure.cc
index ad01a84..fb7e49d 100644
--- a/ui/accessibility/ax_assistant_structure.cc
+++ b/ui/accessibility/ax_assistant_structure.cc
@@ -275,12 +275,16 @@
   result->rect = gfx::Rect(parent_relative_rect.x(), parent_relative_rect.y(),
                            absolute_rect.width(), absolute_rect.height());
 
+  // Selection state comes from the tree data rather than
+  // GetUnignoredSelection() which uses AXPosition, as AXPosition requires a
+  // valid and registered AXTreeID, which exists only when accessibility is
+  // enabled. As an AXTreeSnapshotter does not enable accessibility, it is not
+  // able to use AXPosition.
   if (IsLeaf(node) && update.has_tree_data) {
     int start_selection = 0;
     int end_selection = 0;
-    AXSelection unignored_selection = tree->GetUnignoredSelection();
-    if (unignored_selection.anchor_object_id == node->id()) {
-      start_selection = unignored_selection.anchor_offset;
+    if (update.tree_data.sel_anchor_object_id == node->id()) {
+      start_selection = update.tree_data.sel_anchor_offset;
       config->should_select_leaf = true;
     }
 
@@ -288,8 +292,8 @@
       end_selection = static_cast<int32_t>(GetText(node).length());
     }
 
-    if (unignored_selection.focus_object_id == node->id()) {
-      end_selection = unignored_selection.focus_offset;
+    if (update.tree_data.sel_focus_object_id == node->id()) {
+      end_selection = update.tree_data.sel_focus_offset;
       config->should_select_leaf = false;
     }
     if (end_selection > 0)
diff --git a/ui/accessibility/ax_enums.mojom b/ui/accessibility/ax_enums.mojom
index 5baca3eb..caa9354 100644
--- a/ui/accessibility/ax_enums.mojom
+++ b/ui/accessibility/ax_enums.mojom
@@ -1194,16 +1194,48 @@
   kOther,
 };
 
+// The source of the accessible name. The source can influence which platform
+// accessibility API is used to expose the name (e.g. label versus title).
+// It can also impact whether or not whitespace is inserted between objects
+// (e.g. when the name of some other object should be constructed from a
+// container's children).
 enum NameFrom {
+  // No name has been provided. (See also kAttributeExplicitlyEmpty)
   kNone,
-  kAttribute,  // E.g. aria-label.
+
+  // The name comes from a flat string which is typically not displayed,
+  // such as aria-label (in the case of web content) or provided by the
+  // View.
+  kAttribute,
+
+  // The name has been removed to improve accessibility. Example: The name of
+  // this View is the same as the name of the View's parent, causing screen
+  // readers to speak the name twice. In order to prevent that, the name can
+  // be removed from one View. Note that as a general rule, the name should
+  // not be removed from the interactive/focusable View.
   kAttributeExplicitlyEmpty,
-  kCaption,  // E.g. in the case of a table, from a caption element.
+
+  // The name of this table comes from the table's caption.
+  kCaption,
+
+  // The name comes from the displayed text contents. This is appropriate for
+  // labels, links, static text nodes, etc. It is typically not appropriate for
+  // text fields.
   kContents,
-  kPlaceholder,  // E.g. from an HTML placeholder attribute on a text field.
-  kRelatedElement, // E.g. from a figcaption Element in a figure.
-  kTitle,  // E.g. <input type="text" title="title">.
-  kValue,  // E.g. <input type="button" value="Button's name">.
+
+  // The name of this text field comes from its placeholder.
+  kPlaceholder,
+
+  // The name comes from some other object in the content or UI, such as
+  // the figcaption element in a figure, or another View present in the UI.
+  kRelatedElement,
+
+  // The name comes from the title attribute (HTML), the title element (SVG),
+  // or a View's tooltip.
+  kTitle,
+
+  // The name comes from the value attribute, such as in a button element.
+  kValue,
 };
 
 // The source of the accessible description. Used by some screen readers
diff --git a/ui/accessibility/ax_node_position.cc b/ui/accessibility/ax_node_position.cc
index 9e1a220..0c84f2f 100644
--- a/ui/accessibility/ax_node_position.cc
+++ b/ui/accessibility/ax_node_position.cc
@@ -54,14 +54,66 @@
     return CreateNullPosition();
 
   AXTreeID tree_id = node.tree()->GetAXTreeID();
-  if (node.IsLeaf()) {
-    return CreateTextPosition(tree_id, node.id(), child_index_or_text_offset,
-                              affinity);
+  if (IsTextPositionAnchor(node)) {
+    // TODO(accessibility) It is a mistake for the to caller try to create a
+    // text position with BEFORE_TEXT as the text offset. Correct the callers
+    // that are doing this.
+    // DCHECK_NE(child_index_or_text_offset, BEFORE_TEXT)
+    // << "Creating a text position with BEFORE_TEXT as the offset is illegal "
+    //    "and disallowed.";
+    int text_offset = child_index_or_text_offset == BEFORE_TEXT
+                          ? 0
+                          : child_index_or_text_offset;
+    return CreateTextPosition(tree_id, node.id(), text_offset, affinity);
   }
 
+  DCHECK_LE(child_index_or_text_offset,
+            static_cast<int>(node.GetChildCountCrossingTreeBoundary()))
+      << "\n* Trying to create a tree position with a child index that is too "
+         "large. Maybe a text position should have been created instead?\n"
+      << "\n* Anchor node: " << node << "\n* IsLeaf(): " << node.IsLeaf()
+      << "\n* Child offset: " << child_index_or_text_offset
+      << "\n* IsLeafNodeForTreePosition(): " << IsLeafNodeForTreePosition(node)
+      << "\n* Tree: " << node.tree()->ToString();
+
   return CreateTreePosition(tree_id, node.id(), child_index_or_text_offset);
 }
 
+// static
+bool AXNodePosition::IsTextPositionAnchor(const AXNode& node) {
+  // TODO(accessibility) Simplify. Not actually sure if this is the correct
+  // thing for the case where IsLeaf() == false but IsLeafNodeForTreePosition()
+  // is true.
+  if (node.IsLeaf())
+    return true;
+
+  // TODO(accessibility) Try to remove this condition. Text positions for a
+  // selection operation should only be created inside selectable text.
+  // A list marker for example is not selectable text: it would either be
+  // selected as a whole or not selected, and you can't select half of it.
+  if (IsLeafNodeForTreePosition(node))
+    return true;
+
+  if (node.GetRole() == ax::mojom::Role::kSpinButton) {
+    // TODO(benjamin.beaudry) Please look into whether this code needs to
+    // remain, or can be simplified.
+    return true;
+  }
+
+  // Ignored atomic text fields and spin buttons are not considered leaves by
+  // AXNode::IsLeaf(), but should always use a text position.
+  if (node.data().IsAtomicTextField()) {
+    // Ignored atomic text fields and spin buttons are not considered leaves by
+    // AXNode::IsLeaf(), but should always use a text position.
+    // TODO(accessibility) Nobody should be creating a text position on an
+    // ignored text field.
+    DCHECK(node.IsIgnored()) << "Returned false from IsLeaf(): " << node;
+    return true;
+  }
+
+  return false;
+}
+
 AXNodePosition::AXNodePosition() = default;
 
 AXNodePosition::~AXNodePosition() = default;
diff --git a/ui/accessibility/ax_node_position.h b/ui/accessibility/ax_node_position.h
index 85a6763..b20a9ba 100644
--- a/ui/accessibility/ax_node_position.h
+++ b/ui/accessibility/ax_node_position.h
@@ -31,6 +31,11 @@
   AXNodePosition(const AXNodePosition& other);
 
   AXPositionInstance Clone() const override;
+
+ private:
+  // Return true if the provided node should always use a text position, rather
+  // than a tree position.
+  static bool IsTextPositionAnchor(const AXNode& node);
 };
 
 }  // namespace ui
diff --git a/ui/accessibility/ax_node_position_unittest.cc b/ui/accessibility/ax_node_position_unittest.cc
index 8a3303d5..e8e288eb 100644
--- a/ui/accessibility/ax_node_position_unittest.cc
+++ b/ui/accessibility/ax_node_position_unittest.cc
@@ -683,7 +683,8 @@
 
 }  // namespace
 
-TEST_F(AXPositionTest, Clone) {
+// TODO(crbug.com/1370069): Re-enable this test
+TEST_F(AXPositionTest, DISABLED_Clone) {
   TestPositionType null_position = AXNodePosition::CreateNullPosition();
   ASSERT_NE(nullptr, null_position);
   TestPositionType copy_position = null_position->Clone();
@@ -713,26 +714,13 @@
   EXPECT_EQ(1, copy_position->child_index());
   EXPECT_EQ(AXNodePosition::INVALID_OFFSET, copy_position->text_offset());
 
-  tree_position = AXNodePosition::CreateTreePosition(
-      GetTreeID(), root_.id, AXNodePosition::BEFORE_TEXT);
-  ASSERT_NE(nullptr, tree_position);
-  copy_position = tree_position->Clone();
-  ASSERT_NE(nullptr, copy_position);
-  EXPECT_TRUE(copy_position->IsNullPosition())
-      << "`AXNodePosition::BEFORE_TEXT` is invalid on non-leaves.";
-  EXPECT_EQ(kInvalidAXNodeID, copy_position->anchor_id());
-  EXPECT_EQ(AXNodePosition::INVALID_INDEX, copy_position->child_index());
-  EXPECT_EQ(AXNodePosition::INVALID_OFFSET, copy_position->text_offset());
-
-  tree_position = AXNodePosition::CreateTreePosition(
-      GetTreeID(), inline_box1_.id, AXNodePosition::BEFORE_TEXT);
-  ASSERT_NE(nullptr, tree_position);
-  copy_position = tree_position->Clone();
-  ASSERT_NE(nullptr, copy_position);
-  EXPECT_TRUE(copy_position->IsTreePosition());
-  EXPECT_EQ(inline_box1_.id, copy_position->anchor_id());
-  EXPECT_EQ(AXNodePosition::BEFORE_TEXT, copy_position->child_index());
-  EXPECT_EQ(AXNodePosition::INVALID_OFFSET, copy_position->text_offset());
+  // Expect to trigger a DCHECK for an invalid position, because the root is not
+  // a leaf. A non-leaf must use a child index >= 0 and <= AnchorChildOffset().
+  // A child index of BEFORE_TEXT can only be used with a leaf anchor.
+  EXPECT_DEATH_IF_SUPPORTED(
+      AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
+                                         AXNodePosition::BEFORE_TEXT),
+      "Creating invalid positions is disallowed.");
 
   TestPositionType text_position = AXNodePosition::CreateTextPosition(
       GetTreeID(), text_field_.id, 0 /* text_offset */,
@@ -931,7 +919,8 @@
       text_position_10->ToString());
 }
 
-TEST_F(AXPositionTest, IsIgnored) {
+// TODO(crbug.com/1370069): Re-enable this test
+TEST_F(AXPositionTest, DISABLED_IsIgnored) {
   EXPECT_FALSE(AXNodePosition::CreateNullPosition()->IsIgnored());
 
   // We now need to update the tree structure to test ignored tree and text
@@ -1071,15 +1060,13 @@
   ASSERT_TRUE(tree_position_5->IsTreePosition());
   EXPECT_TRUE(tree_position_5->IsIgnored());
 
-  // A "before text" position on an ignored node that is not a leaf, however,
-  // should be invalid. It should not be ignored because in this case the child
-  // index makes no sense: the child index can point to any one of the node's
-  // children, not only the unignored ones.
-  TestPositionType tree_position_6 = AXNodePosition::CreateTreePosition(
-      GetTreeID(), static_text_data_1.id, AXNodePosition::BEFORE_TEXT);
-  ASSERT_TRUE(tree_position_6->IsNullPosition());
-  EXPECT_FALSE(tree_position_6->IsIgnored())
-      << "Null positions are by design not ignored.";
+  // A "before text" position is created on an ignored node that is not a leaf.
+  // However, such a position is illegal. The child index must point to one
+  // node's children.
+  EXPECT_DEATH_IF_SUPPORTED(
+      AXNodePosition::CreateTreePosition(GetTreeID(), static_text_data_1.id,
+                                         AXNodePosition::BEFORE_TEXT),
+      "Creating invalid positions is disallowed.");
 }
 
 TEST_F(AXPositionTest, GetTextFromNullPosition) {
@@ -2342,8 +2329,10 @@
 
   SetTree(CreateAXTree({root, list, list_item, list_marker, static_text}));
 
+  // An anchor node that is considered a leaf for AXPosition must use a child
+  // offset of BEFORE_TEXT or AnchorChildCount().
   TestPositionType tree_position =
-      AXNodePosition::CreateTreePosition(GetTreeID(), list_marker.id, 0);
+      AXNodePosition::CreateTreePosition(GetTreeID(), list_marker.id, 1);
   ASSERT_NE(nullptr, tree_position);
   EXPECT_EQ(AXPositionKind::TREE_POSITION, tree_position->kind());
   EXPECT_TRUE(tree_position->IsLeaf());
@@ -9693,13 +9682,24 @@
   EXPECT_TRUE(text_position->IsValid());
   EXPECT_EQ(*text_position, *text_position->AsValidPosition());
 
+  // Create a tree position on the inline text box with child index 0,
+  // which means the position is after the inline text box.
+  // This is because the inline text box is a leaf, which must use one of two
+  // child offsets: BEFORE_TEXT (meaning before the anchor) or AnchorChildCount
+  // (meaning after the anchor).
   TestPositionType tree_position =
       AXNodePosition::CreateTreePosition(GetTreeID(), inline_box_4.id, 0);
+  AXNode& inline_box_4_node = *GetNode(inline_box_4.id);
+  ASSERT_TRUE(AXNodePosition::IsLeafNodeForTreePosition(inline_box_4_node));
+  EXPECT_EQ(tree_position->GetAnchor()->GetChildCount(), 0U);
+  EXPECT_EQ(tree_position->GetAnchor()->id(), inline_box_4.id);
+  EXPECT_TRUE(tree_position->IsLeaf());
   ASSERT_NE(nullptr, tree_position);
   EXPECT_TRUE(tree_position->IsTreePosition());
   EXPECT_TRUE(tree_position->IsValid());
   EXPECT_EQ(*tree_position, *tree_position->AsValidPosition());
 
+  // Mark the static text and inline box descendents of the button as ignored.
   static_text_3.AddState(ax::mojom::State::kIgnored);
   inline_box_4.AddState(ax::mojom::State::kIgnored);
   AXTreeUpdate update;
@@ -9711,10 +9711,17 @@
   EXPECT_TRUE(text_position->IsValid());
   EXPECT_EQ(1, text_position->text_offset());
 
-  EXPECT_TRUE(tree_position->IsValid());
-  tree_position = tree_position->AsValidPosition();
-  EXPECT_TRUE(tree_position->IsValid());
-  EXPECT_EQ(0, tree_position->child_index());
+  // The tree position is no longer valid. Changing it to a valid position will
+  // move the anchor to an unignored ancestor.
+  EXPECT_TRUE(tree_position->IsValid());  // TODO(nektar) Should this be false?
+  ASSERT_TRUE(tree_position->IsLeafTreePosition());
+  TestPositionType valid_tree_position = tree_position->AsValidPosition();
+  EXPECT_TRUE(valid_tree_position->IsValid());
+  EXPECT_NE(tree_position->GetAnchor(), valid_tree_position->GetAnchor());
+  EXPECT_TRUE(valid_tree_position->IsLeaf());
+  EXPECT_EQ(valid_tree_position->GetAnchor()->GetChildCount(), 1U);
+  EXPECT_EQ(valid_tree_position->GetAnchor()->id(), button_2.id);
+  EXPECT_EQ(1, valid_tree_position->child_index());
 }
 
 TEST_F(AXPositionTest, CreateNextCharacterPosition) {
@@ -10362,21 +10369,15 @@
 
   // Both child indices are invalid. It should result in equivalent null
   // positions.
-  TestPositionType tree_position1 = AXNodePosition::CreateTreePosition(
-      GetTreeID(), root_.id, 4 /* child_index */);
-  ASSERT_NE(nullptr, tree_position1);
-  TestPositionType tree_position2 = AXNodePosition::CreateTreePosition(
-      GetTreeID(), root_.id, AXNodePosition::INVALID_INDEX);
-  ASSERT_NE(nullptr, tree_position2);
-  EXPECT_EQ(*tree_position1, *tree_position2);
+  ASSERT_EQ(*AXNodePosition::CreateNullPosition(),
+            *AXNodePosition::CreateNullPosition());
 
   // An invalid position should not be equivalent to an "after children"
   // position.
-  tree_position1 = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
-                                                      3 /* child_index */);
+  TestPositionType tree_position1 = AXNodePosition::CreateTreePosition(
+      GetTreeID(), root_.id, 3 /* child_index */);
   ASSERT_NE(nullptr, tree_position1);
-  tree_position2 = AXNodePosition::CreateTreePosition(
-      GetTreeID(), root_.id, AXNodePosition::INVALID_INDEX);
+  TestPositionType tree_position2 = AXNodePosition::CreateNullPosition();
   ASSERT_NE(nullptr, tree_position2);
   EXPECT_NE(*tree_position1, *tree_position2);
 
@@ -10398,26 +10399,27 @@
   ASSERT_NE(nullptr, tree_position2);
   EXPECT_EQ(*tree_position1, *tree_position2);
 
+  // TODO(accessibility) Re-enable testing of invalid positions.
   // Both text offsets are invalid. It should result in equivalent null
   // positions.
-  TestPositionType text_position1 = AXNodePosition::CreateTextPosition(
-      GetTreeID(), inline_box1_.id, 15 /* text_offset */,
-      ax::mojom::TextAffinity::kUpstream);
-  ASSERT_NE(nullptr, text_position1);
-  ASSERT_TRUE(text_position1->IsNullPosition());
-  TestPositionType text_position2 = AXNodePosition::CreateTextPosition(
-      GetTreeID(), text_field_.id, -1 /* text_offset */,
-      ax::mojom::TextAffinity::kUpstream);
-  ASSERT_NE(nullptr, text_position2);
-  ASSERT_TRUE(text_position2->IsNullPosition());
-  EXPECT_EQ(*text_position1, *text_position2);
+  // TestPositionType text_position1 = AXNodePosition::CreateTextPosition(
+  //     GetTreeID(), inline_box1_.id, 15 /* text_offset */,
+  //     ax::mojom::TextAffinity::kUpstream);
+  // ASSERT_NE(nullptr, text_position1);
+  // ASSERT_TRUE(text_position1->IsNullPosition());
+  // TestPositionType text_position2 = AXNodePosition::CreateTextPosition(
+  //     GetTreeID(), text_field_.id, -1 /* text_offset */,
+  //     ax::mojom::TextAffinity::kUpstream);
+  // ASSERT_NE(nullptr, text_position2);
+  // ASSERT_TRUE(text_position2->IsNullPosition());
+  // EXPECT_EQ(*text_position1, *text_position2);
 
-  text_position1 = AXNodePosition::CreateTextPosition(
+  TestPositionType text_position1 = AXNodePosition::CreateTextPosition(
       GetTreeID(), inline_box1_.id, 0 /* text_offset */,
       ax::mojom::TextAffinity::kDownstream);
   ASSERT_NE(nullptr, text_position1);
   ASSERT_TRUE(text_position1->IsTextPosition());
-  text_position2 = AXNodePosition::CreateTextPosition(
+  TestPositionType text_position2 = AXNodePosition::CreateTextPosition(
       GetTreeID(), inline_box1_.id, 0 /* text_offset */,
       ax::mojom::TextAffinity::kDownstream);
   ASSERT_NE(nullptr, text_position2);
@@ -12329,12 +12331,10 @@
   AXNodeData child_1_data;
   child_1_data.id = 3;
   child_1_data.role = ax::mojom::Role::kGenericContainer;
-  child_1_data.AddState(ax::mojom::State::kIgnored);
 
   AXNodeData child_2_data;
   child_2_data.id = 4;
   child_2_data.role = ax::mojom::Role::kGenericContainer;
-  child_2_data.AddState(ax::mojom::State::kIgnored);
 
   root_data.child_ids = {parent_data.id};
   parent_data.child_ids = {child_1_data.id, child_2_data.id};
@@ -12364,17 +12364,47 @@
       AXNodePosition::CreateTreePosition(GetTreeID(), parent_data.id, 2);
   TestPositionType child_1_at_0 =
       AXNodePosition::CreateTreePosition(GetTreeID(), child_1_data.id, 0);
-  TestPositionType child_2_at_0 =
-      AXNodePosition::CreateTreePosition(GetTreeID(), child_2_data.id, 0);
+  TestPositionType child_2_at_before_text = AXNodePosition::CreateTreePosition(
+      GetTreeID(), child_2_data.id, AXNodePosition::BEFORE_TEXT);
 
-  EXPECT_EQ(*parent_at_0, *parent_at_0->AsValidPosition());
-  EXPECT_EQ(*parent_at_1, *parent_at_1->AsValidPosition());
+  // All positions are valid when created.
+  EXPECT_TRUE(parent_at_0->IsValid());
+  EXPECT_TRUE(parent_at_1->IsValid());
+  EXPECT_TRUE(parent_at_2->IsValid());
+  EXPECT_TRUE(child_1_at_0->IsValid());
+  EXPECT_TRUE(child_2_at_before_text->IsValid());
+
+  // Make some of the positions invalid.
+  child_1_data.AddState(ax::mojom::State::kIgnored);
+  child_2_data.AddState(ax::mojom::State::kIgnored);
+  AXTreeUpdate add_invalid_state_update;
+  add_invalid_state_update.nodes = {child_1_data, child_2_data};
+  ASSERT_TRUE(tree->Unserialize(add_invalid_state_update));
+  AXNode& parent_node = *GetNode(parent_data.id);
+  EXPECT_TRUE(AXNodePosition::IsLeafNodeForTreePosition(parent_node));
+  EXPECT_TRUE(parent_at_1->IsLeafTreePosition());
+
+  TestPositionType parent_at_before_text = AXNodePosition::CreateTreePosition(
+      GetTreeID(), parent_data.id, AXNodePosition::BEFORE_TEXT);
+
+  EXPECT_TRUE(parent_at_before_text->IsValid());
+  EXPECT_FALSE(parent_at_0->IsValid());
+  EXPECT_FALSE(parent_at_1->IsValid());
+  EXPECT_TRUE(parent_at_2->IsValid());
+  // TODO(accessibility) This one should be invalid because the anchor is
+  // ignored:
+  // EXPECT_FALSE(child_1_at_0->IsValid());
+  EXPECT_TRUE(child_2_at_before_text->IsValid());
+
+  EXPECT_EQ(*parent_at_before_text, *parent_at_before_text->AsValidPosition());
+  EXPECT_EQ(*parent_at_2, *parent_at_0->AsValidPosition());
+  EXPECT_EQ(*parent_at_2, *parent_at_1->AsValidPosition());
   EXPECT_EQ(*parent_at_2, *parent_at_2->AsValidPosition());
-  EXPECT_EQ(*parent_at_0, *child_1_at_0->AsValidPosition());
-  EXPECT_EQ(*parent_at_0, *child_2_at_0->AsValidPosition());
+  EXPECT_EQ(*parent_at_2, *child_1_at_0->AsValidPosition());
+  EXPECT_EQ(*parent_at_before_text, *child_2_at_before_text->AsValidPosition());
 
   for (TestPositionType::pointer position :
-       {parent_at_0.get(), child_1_at_0.get(), child_2_at_0.get()}) {
+       {parent_at_0.get(), child_1_at_0.get(), child_2_at_before_text.get()}) {
     AXNodePosition::AXPositionInstance valid = position->AsValidPosition();
     EXPECT_TRUE(position->IsLeaf());
     EXPECT_TRUE(valid->IsLeaf());
@@ -12387,10 +12417,13 @@
 
     // Should not crash.
     AXSelection s = tree->GetUnignoredSelection();
+    int expected_offset = valid->AtEndOfAnchor()
+                              ? static_cast<int>(parent_node.GetChildCount())
+                              : AXNodePosition::BEFORE_TEXT;
     EXPECT_EQ(valid->anchor_id(), s.anchor_object_id);
-    EXPECT_EQ(valid->child_index(), s.anchor_offset);
+    EXPECT_EQ(valid->child_index(), expected_offset);
     EXPECT_EQ(valid->anchor_id(), s.focus_object_id);
-    EXPECT_EQ(valid->child_index(), s.focus_offset);
+    EXPECT_EQ(valid->child_index(), expected_offset);
   }
 }
 
diff --git a/ui/accessibility/ax_position.h b/ui/accessibility/ax_position.h
index 30641ea..c137bf5 100644
--- a/ui/accessibility/ax_position.h
+++ b/ui/accessibility/ax_position.h
@@ -32,6 +32,7 @@
 #include "ui/accessibility/ax_node.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/accessibility/ax_role_properties.h"
+#include "ui/accessibility/ax_table_info.h"
 #include "ui/accessibility/ax_text_attributes.h"
 #include "ui/accessibility/ax_tree_id.h"
 #include "ui/accessibility/ax_tree_manager.h"
@@ -255,8 +256,8 @@
   static AXPositionInstance CreateTreePositionAtStartOfAnchor(
       const AXNode& anchor) {
     // Initialize the child index:
-    // For a leaf, the child index will be BEFORE_TEXT.
-    // Otherwise the child index will be 0.
+    // - For a leaf, the child index will be BEFORE_TEXT.
+    // - Otherwise the child index will be 0.
     int child_index = IsLeafNodeForTreePosition(anchor) ? BEFORE_TEXT : 0;
     AXTreeID tree_id = anchor.tree()->GetAXTreeID();
     return CreateTreePosition(tree_id, anchor.id(), child_index);
@@ -351,10 +352,12 @@
   static AXPositionInstance Unserialize(
       const SerializedPosition& serialization) {
     AXPositionInstance new_position(new AXPositionType());
-    new_position->Initialize(serialization.kind,
-                             ui::AXTreeID::FromString(serialization.tree_id),
-                             serialization.anchor_id, serialization.child_index,
-                             serialization.text_offset, serialization.affinity);
+    // Use initialize without validation because this is used by ATs that
+    // used outdated information to generated a selection request.
+    new_position->InitializeWithoutValidation(
+        serialization.kind, ui::AXTreeID::FromString(serialization.tree_id),
+        serialization.anchor_id, serialization.child_index,
+        serialization.text_offset, serialization.affinity);
     return new_position;
   }
 
@@ -402,9 +405,13 @@
     if (text_offset_ == static_cast<int>(max_text_offset)) {
       annotated_text = text + u"<>";
     } else {
-      annotated_text = text.substr(0, text_offset_) + u"<" +
-                       text[text_offset_] + u">" +
-                       text.substr(text_offset_ + 1);
+      // TODO(aleventhal) This extra casting is only necessary to satisfy a
+      // compiler error that strangely occurs only when Initialize() contains
+      // SnapToMaxTextOffsetIfBeyond().
+      size_t unsigned_text_offset = static_cast<size_t>(text_offset_);
+      annotated_text = text.substr(0, unsigned_text_offset) + u"<" +
+                       text[unsigned_text_offset] + u">" +
+                       text.substr(unsigned_text_offset + 1);
     }
 
     return str + " annotated_text=" + base::UTF16ToUTF8(annotated_text);
@@ -553,9 +560,14 @@
         // If this is a "before text" or an "after text" tree position, it's
         // pointing to the anchor itself, which we've determined to be
         // unignored.
-        DCHECK(!IsLeaf() || child_index_ == BEFORE_TEXT || child_index_ == 0)
-            << "\"Before text\" and \"after text\" tree positions are only "
-               "valid on leaf nodes.";
+        DCHECK(child_index_ == BEFORE_TEXT ||
+               child_index_ == AnchorChildCount() || !IsLeaf())
+            << "Leaf nodes can only have a position before or after, so they "
+               "must have a child index of BEFORE_TEXT or AnchorChildCount(): "
+            << ToString() << "  AnchorChildCount(): " << AnchorChildCount();
+        DCHECK(child_index_ != BEFORE_TEXT || IsLeaf())
+            << "Non-leaf nodes cannot have a position of BEFORE_TEXT: "
+            << *GetAnchor();
         if (child_index_ == BEFORE_TEXT || IsLeaf())
           return false;
 
@@ -646,8 +658,13 @@
         // `child_index_` of 0 would confusingly indicate both a "before text"
         // as well as an "after text" position. Note that some leaf positions,
         // e.g. positions in empty objects, do have children.
-        if (child_index_ == BEFORE_TEXT)
-          return IsLeaf();
+        if (IsLeaf()) {
+          // Leaf nodes can only have a position before or after, so they must
+          // have a child index of BEFORE_TEXT or AnchorChildCount().
+          return child_index_ == BEFORE_TEXT ||
+                 child_index_ == AnchorChildCount();
+        }
+
         return child_index_ >= 0 && child_index_ <= AnchorChildCount();
       case AXPositionKind::TEXT_POSITION:
         if (!GetAnchor())
@@ -687,8 +704,6 @@
         // A few positions are anchored to nodes that have children but we want
         // to treat them as leaf positions. An example is an empty text field;
         // it often has empty unignored divs coming from Blink inside it.
-        if (IsLeaf())
-          return child_index_ == 0;
         return child_index_ == AnchorChildCount();
       case AXPositionKind::TEXT_POSITION:
         return text_offset_ == MaxTextOffset();
@@ -1350,7 +1365,14 @@
         if (!position->GetAnchor())
           return CreateNullPosition();
 
-        if (const AXNode* empty_object_node = GetEmptyObjectAncestorNode()) {
+        const AXNode* leaf_node = GetEmptyObjectAncestorNode();
+        if (!leaf_node && position->IsLeaf()) {
+          // If there is no empty object ancestor, but the current position's
+          // anchor is a leaf, then use the same anchor, as it will be valid
+          // as long as a valid offset is used.
+          leaf_node = position->GetAnchor();
+        }
+        if (leaf_node) {
           // In this class, we define the empty node as a leaf node (see
           // `AXNode::IsLeaf()`) that doesn't have any content. On certain
           // platforms, and so that such nodes will act as a character and a
@@ -1369,18 +1391,23 @@
           // valid we move the position from the descendant to the empty leaf
           // node itself. Otherwise, character and word navigation won't work
           // properly.
-          return CreateTreePosition(
-              position->tree_id(), empty_object_node->id(),
-              position->child_index() == BEFORE_TEXT ? BEFORE_TEXT
-                                                     : AnchorChildCount());
+          AXPositionInstance new_position =
+              position->child_index() == BEFORE_TEXT
+                  ? CreateTreePositionAtStartOfAnchor(*leaf_node)
+                  : CreateTreePositionAtEndOfAnchor(*leaf_node);
+          DCHECK(new_position->IsLeaf());
+          return new_position;
         }
 
-        if (position->child_index_ == BEFORE_TEXT)
-          return position;
-
-        if (position->child_index_ < 0)
+        DCHECK(!position->IsLeaf());
+        // Not a leaf: use a child index from 0 to AnchorChilkdCount().
+        if (position->child_index_ == BEFORE_TEXT) {
           position->child_index_ = 0;
-        else if (position->child_index_ > position->AnchorChildCount())
+          return position;
+        }
+
+        DCHECK_GE(position->child_index_, 0);
+        if (position->child_index_ > position->AnchorChildCount())
           position->child_index_ = position->AnchorChildCount();
         break;
       }
@@ -1421,7 +1448,7 @@
         break;
       }
     }
-    DCHECK(position->IsValid());
+    DCHECK(position->IsValid()) << *position;
     return position;
   }
 
@@ -1443,11 +1470,13 @@
         copy->child_index_ = BEFORE_TEXT;
       } else {
         const int max_text_offset = copy->MaxTextOffset();
-        copy->child_index_ =
-            copy->text_offset_ != max_text_offset ? BEFORE_TEXT : 0;
+        copy->child_index_ = copy->text_offset_ != max_text_offset
+                                 ? BEFORE_TEXT
+                                 : AnchorChildCount();
       }
 
       copy->kind_ = AXPositionKind::TREE_POSITION;
+      DCHECK(copy->IsValid());
       return copy;
     }
 
@@ -2257,10 +2286,10 @@
     switch (kind_) {
       case AXPositionKind::NULL_POSITION:
         return CreateNullPosition();
-      case AXPositionKind::TREE_POSITION:
-        if (IsLeaf())
-          return CreateTreePosition(tree_id_, anchor_id_, /* child_index */ 0);
-        return CreateTreePosition(tree_id_, anchor_id_, AnchorChildCount());
+      case AXPositionKind::TREE_POSITION: {
+        DCHECK(GetAnchor());
+        return CreateTreePositionAtEndOfAnchor(*GetAnchor());
+      }
       case AXPositionKind::TEXT_POSITION:
         return CreateTextPosition(tree_id_, anchor_id_, MaxTextOffset(),
                                   ax::mojom::TextAffinity::kDownstream);
@@ -2420,11 +2449,25 @@
         return CreateNullPosition();
 
       case AXPositionKind::TREE_POSITION: {
-        int child_index = AnchorIndexInParent();
+        if (IsLeafNodeForTreePosition(*parent_anchor)) {
+          if (AtEndOfAnchor() ||
+              move_direction == ax::mojom::MoveDirection::kForward) {
+            // If this position is an "after children" or an "after text"
+            // position inside of a leaf, or we are seeking a parent position
+            // for a forward movement operation with a parent leaf anchor,
+            // return a position at the end of the parent anchor.
+            return CreateTreePositionAtEndOfAnchor(*parent_anchor);
+          }
+          // If we are seeking a parent position for a backward movement
+          // operation, return a position at the start of the parent anchor.
+          return CreateTreePositionAtStartOfAnchor(*parent_anchor);
+        }
+
         // If this position is an "after children" or an "after text" position,
         // return either an "after children" position on the parent anchor, or a
         // position anchored at the next child, depending on whether this is the
         // last child in its parent anchor.
+        int child_index = AnchorIndexInParent();
         if (AtEndOfAnchor())
           return CreateTreePosition(parent_tree_id, parent_anchor_id,
                                     (child_index + 1));
@@ -3487,10 +3530,11 @@
   //   <0: if this position is logically less than the other position
   //   >0: if this position is logically greater than the other position
   absl::optional<int> CompareTo(const AXPosition& other) const {
-    if (IsNullPosition() && other.IsNullPosition())
-      return 0;
-    if (IsNullPosition() || other.IsNullPosition())
+    if (IsNullPosition() || other.IsNullPosition()) {
+      if (IsNullPosition() && other.IsNullPosition())
+        return 0;
       return absl::nullopt;
+    }
 
     if (GetAnchor() == other.GetAnchor())
       return SlowCompareTo(other);  // No optimization is necessary.
@@ -4229,12 +4273,12 @@
     return grapheme_iterator;
   }
 
-  void Initialize(AXPositionKind kind,
-                  AXTreeID tree_id,
-                  AXNodeID anchor_id,
-                  int child_index,
-                  int text_offset,
-                  ax::mojom::TextAffinity affinity) {
+  void InitializeWithoutValidation(AXPositionKind kind,
+                                   AXTreeID tree_id,
+                                   AXNodeID anchor_id,
+                                   int child_index,
+                                   int text_offset,
+                                   ax::mojom::TextAffinity affinity) {
     kind_ = kind;
     tree_id_ = tree_id;
     anchor_id_ = anchor_id;
@@ -4253,6 +4297,54 @@
     }
   }
 
+  void Initialize(AXPositionKind kind,
+                  AXTreeID tree_id,
+                  AXNodeID anchor_id,
+                  int child_index,
+                  int text_offset,
+                  ax::mojom::TextAffinity affinity) {
+    kind_ = kind;
+    tree_id_ = tree_id;
+    anchor_id_ = anchor_id;
+    child_index_ = child_index;
+    text_offset_ = text_offset;
+    affinity_ = affinity;
+
+    // TODO(accessibility) Consider using WeakPtr<AXTree> instead of an
+    // AXTreeID, which would be both faster and easier to use in combination
+    // with AXTreeSnapshotter, which does not use AXTreeManager to cache
+    // AXTreeIDs in a map.
+    SANITIZER_CHECK(GetManager() || kind_ == AXPositionKind::NULL_POSITION)
+        << "Tree manager required, tree_id = " << tree_id.ToString()
+        << "  is unknown = " << (tree_id == AXTreeIDUnknown());
+    SANITIZER_CHECK(GetAnchor() || kind_ == AXPositionKind::NULL_POSITION)
+        << "Creating a position without an anchor is disallowed:\n"
+        << ToDebugString();
+
+    // TODO(accessibility) Remove this line and let the below IsValid()
+    // assertion get triggered instead. We shouldn't be creating test positions
+    // with offsets that are too large. This seems to occur when the anchor node
+    // is ignored, and leads to a number of failing tests.
+    SnapToMaxTextOffsetIfBeyond();
+
+#if defined(AX_EXTRA_MAC_NODES)
+    // Temporary hack to constrain child index when extra mac nodes are present.
+    // TODO(accessibility) Remove this hack that works around the fact that Mac
+    // can set a selection on extra mac nodes, which looks invalid because the
+    // child index is larger than AnchorChildCount(), which does not account
+    // for them. We need to get a child count that includes extra mac nodes,
+    // similar to how BrowserAccessibility::PlatformChildCount() does.
+    if (!IsValid() && IsTreePosition() &&
+        ui::IsTableLike(GetAnchor()->GetRole()) &&
+        child_index > AnchorChildCount()) {
+      child_index_ = AnchorChildCount();
+    }
+#endif
+
+    SANITIZER_CHECK(IsValid()) << "Creating invalid positions is disallowed:\n"
+                               << ToDebugString();
+  }
+
   int AnchorChildCount() const {
     if (!GetAnchor())
       return 0;
@@ -5279,6 +5371,9 @@
   }
 
   AXPositionKind kind_;
+  // TODO(crbug.com/1362839): use weak pointers for the AXTree, so that
+  // AXPosition can be used without AXTreeManager support (and also faster than
+  // the slow AXTreeID).
   AXTreeID tree_id_;
   AXNodeID anchor_id_;
 
diff --git a/ui/accessibility/ax_range.h b/ui/accessibility/ax_range.h
index ccffc36..0562fe0 100644
--- a/ui/accessibility/ax_range.h
+++ b/ui/accessibility/ax_range.h
@@ -130,6 +130,8 @@
   //   nullopt - If positions are not comparable (see AXPosition::CompareTo).
   static absl::optional<int> CompareEndpoints(const AXPositionType* first,
                                               const AXPositionType* second) {
+    DCHECK(first->IsValid());
+    DCHECK(second->IsValid());
     absl::optional<int> tree_position_comparison =
         first->AsTreePosition()->CompareTo(*second->AsTreePosition());
 
diff --git a/ui/accessibility/ax_tree.cc b/ui/accessibility/ax_tree.cc
index baf91f0f..fced115 100644
--- a/ui/accessibility/ax_tree.cc
+++ b/ui/accessibility/ax_tree.cc
@@ -2649,7 +2649,7 @@
     node = tree.GetFromId(node_id);
   if (!node) {
     node_id = kInvalidAXNodeID;
-    offset = -1;
+    offset = AXNodePosition::INVALID_OFFSET;
     affinity = ax::mojom::TextAffinity::kDownstream;
     return false;
   }
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc b/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc
index 2f43b31..91e65a7 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc
@@ -2161,6 +2161,8 @@
   update.nodes.push_back(item_2);
   update.nodes.push_back(item_3);
   update.nodes.push_back(item_4);
+  update.has_tree_data = true;
+  update.tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
   Init(update);
 
   AtkObject* root_atk_object(GetRootAtkObject());
diff --git a/ui/accessibility/platform/ax_platform_node_unittest.cc b/ui/accessibility/platform/ax_platform_node_unittest.cc
index 5193910..562a5a2 100644
--- a/ui/accessibility/platform/ax_platform_node_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_unittest.cc
@@ -41,6 +41,9 @@
   AXTreeUpdate update;
   update.root_id = text_field_node.id;
   update.nodes.push_back(text_field_node);
+  update.has_tree_data = true;
+  // An AXPosition will be created, and requires an AXTreeID.
+  update.tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
   return update;
 }
 
@@ -64,6 +67,8 @@
   AXTreeUpdate update;
   update.root_id = text_field_node.id;
   update.nodes.push_back(text_field_node);
+  update.has_tree_data = true;
+  update.tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
   return update;
 }
 
@@ -80,6 +85,8 @@
   AXTreeUpdate update;
   update.root_id = content_editable_node.id;
   update.nodes.push_back(content_editable_node);
+  update.has_tree_data = true;
+  update.tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
   return update;
 }
 
@@ -102,6 +109,7 @@
   update.nodes.push_back(content_editable_node);
 
   update.has_tree_data = true;
+  update.tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
   update.tree_data.sel_anchor_object_id = content_editable_node.id;
   update.tree_data.sel_focus_object_id = content_editable_node.id;
   update.tree_data.sel_anchor_offset = start;
@@ -263,6 +271,8 @@
   update.nodes.push_back(table_cell_3);        // 11
   update.nodes.push_back(table_cell_4);        // 12
 
+  update.has_tree_data = true;
+  update.tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
   return update;
 }
 
@@ -326,6 +336,9 @@
   update.nodes.push_back(rowcolindex_cell);
   update.nodes.push_back(rowcolcount_grid);
   update.nodes.push_back(unknown_grid);
+
+  update.has_tree_data = true;
+  update.tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
   return update;
 }
 
@@ -371,6 +384,9 @@
   update.nodes.push_back(option_1);
   update.nodes.push_back(option_2);
   update.nodes.push_back(option_3);
+
+  update.has_tree_data = true;
+  update.tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
   return update;
 }
 
diff --git a/ui/ozone/platform/wayland/ozone_platform_wayland.cc b/ui/ozone/platform/wayland/ozone_platform_wayland.cc
index 02fba51d..6a6ab20 100644
--- a/ui/ozone/platform/wayland/ozone_platform_wayland.cc
+++ b/ui/ozone/platform/wayland/ozone_platform_wayland.cc
@@ -314,11 +314,9 @@
       // These properties are set when GetPlatformRuntimeProperties is called on
       // the browser process side.
       properties.supports_server_side_window_decorations =
-          override_supports_ssd_for_test == SupportsSsdForTest::kNotSet
-              ? (connection_->xdg_decoration_manager_v1() != nullptr)
-              : (override_supports_ssd_for_test == SupportsSsdForTest::kNo
-                     ? false
-                     : true);
+          (connection_->xdg_decoration_manager_v1() != nullptr &&
+          override_supports_ssd_for_test == SupportsSsdForTest::kNotSet) ||
+          override_supports_ssd_for_test == SupportsSsdForTest::kYes;
       properties.supports_overlays =
           ui::IsWaylandOverlayDelegationEnabled() && connection_->viewporter();
       properties.supports_non_backed_solid_color_buffers =
diff --git a/ui/views/controls/textfield/textfield.cc b/ui/views/controls/textfield/textfield.cc
index ac05150..d7a9be21 100644
--- a/ui/views/controls/textfield/textfield.cc
+++ b/ui/views/controls/textfield/textfield.cc
@@ -949,7 +949,7 @@
   node_data->role = ax::mojom::Role::kTextField;
 
   node_data->SetName(accessible_name_);
-  node_data->SetNameFrom(ax::mojom::NameFrom::kContents);
+  node_data->SetNameFrom(ax::mojom::NameFrom::kAttribute);
 
   // Editable state indicates support of editable interface, and is always set
   // for a textfield, even if disabled or readonly.
diff --git a/ui/views/controls/textfield/textfield_unittest.cc b/ui/views/controls/textfield/textfield_unittest.cc
index a81f8d0..541ea02 100644
--- a/ui/views/controls/textfield/textfield_unittest.cc
+++ b/ui/views/controls/textfield/textfield_unittest.cc
@@ -3523,6 +3523,12 @@
   const std::string& name =
       data.GetStringAttribute(ax::mojom::StringAttribute::kName);
   EXPECT_EQ(test_tooltip_text, base::ASCIIToUTF16(name));
+
+  // `NameFrom::kAttribute` is appropriate when the name is explicitly set to
+  // a developer-provided string (rather than a label, tooltip, or placeholder
+  // for which there are other `NameFrom` values). `NameFrom::kContents` is
+  // typically not an appropriate value.
+  EXPECT_EQ(data.GetNameFrom(), ax::mojom::NameFrom::kAttribute);
 }
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/ui/views/widget/ax_native_widget_mac_unittest.mm b/ui/views/widget/ax_native_widget_mac_unittest.mm
index 422e85b..106f6b1 100644
--- a/ui/views/widget/ax_native_widget_mac_unittest.mm
+++ b/ui/views/widget/ax_native_widget_mac_unittest.mm
@@ -377,13 +377,24 @@
   textfield->RequestFocus();
   EXPECT_EQ(YES, ax_obj.isAccessibilityFocused);
 
-  // NSAccessibilityTitleAttribute.
-  EXPECT_NSEQ(NSAccessibilityTextFieldRole, ax_obj.accessibilityRole);
-  EXPECT_NSEQ(kTestTitle, ax_obj.accessibilityTitle);
+  // NSAccessibilityTitleAttribute, NSAccessibilityLabelAttribute.
+  // https://developer.apple.com/documentation/appkit/nsaccessibilityprotocol
+  // * accessibilityTitle() returns the title of the accessibility element,
+  //   for example a button's visible text.
+  // * accessibilityLabel() returns a short description of the accessibility
+  //   element.
+  // Textfield::SetAssociatedLabel() is what should be used if the textfield
+  // has a visible label. Because AddChildTextfield() uses SetAccessibleName()
+  // to set the accessible name to a flat string, the title should be exposed
+  // via accessibilityLabel() instead of accessibilityTitle();
+  EXPECT_NSEQ(@"", ax_obj.accessibilityTitle);
+  EXPECT_NSEQ(kTestTitle, ax_obj.accessibilityLabel);
   EXPECT_NSEQ(kTestStringValue, ax_obj.accessibilityValue);
 
+  // NSAccessibilityRoleAttribute,
   // NSAccessibilitySubroleAttribute and
   // NSAccessibilityRoleDescriptionAttribute.
+  EXPECT_NSEQ(NSAccessibilityTextFieldRole, ax_obj.accessibilityRole);
   EXPECT_NSEQ(nil, ax_obj.accessibilitySubrole);
   NSString* role_description =
       NSAccessibilityRoleDescription(NSAccessibilityTextFieldRole, nil);
diff --git a/ui/webui/resources/BUILD.gn b/ui/webui/resources/BUILD.gn
index 06d9d97b..7ff8dc5 100644
--- a/ui/webui/resources/BUILD.gn
+++ b/ui/webui/resources/BUILD.gn
@@ -158,10 +158,7 @@
 ]
 
 if (is_chromeos_ash) {
-  checked_in_dts_files += [
-    "cr_elements/cr_container_shadow_behavior.d.ts",
-    "js/list_property_update_behavior.d.ts",
-  ]
+  checked_in_dts_files += [ "cr_elements/cr_container_shadow_behavior.d.ts" ]
 }
 
 # Copies checked-in .d.ts files to the preprocess folder so that they are
diff --git a/ui/webui/resources/js/BUILD.gn b/ui/webui/resources/js/BUILD.gn
index 4f727867..c3a2f488 100644
--- a/ui/webui/resources/js/BUILD.gn
+++ b/ui/webui/resources/js/BUILD.gn
@@ -78,11 +78,6 @@
     in_files += [ "os_about.js" ]
   }
 
-  # TODO(crbug.com/1184053): Fully remove once no longer used by CrOS.
-  if (is_chromeos_ash) {
-    in_files += [ "list_property_update_behavior.js" ]
-  }
-
   if (is_ios) {
     in_files += [
       "ios/mojo_api.js",
@@ -139,7 +134,6 @@
     ":cr.m",
     ":event_tracker",
     ":icon",
-    ":list_property_update_behavior",
     ":load_time_data.m",
     ":parse_html_subset",
     ":plural_string_proxy",
@@ -167,12 +161,6 @@
   deps = [ ":cr.m" ]
 }
 
-js_library("list_property_update_behavior") {
-  deps = [
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-  ]
-}
-
 js_library("load_time_data.m") {
 }