diff --git a/AUTHORS b/AUTHORS
index f45b094..38bed14 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -686,6 +686,7 @@
 Jihoon Chung <jihoon@gmail.com>
 Jihun Brent Kim <devgrapher@gmail.com>
 Jihwan Marc Kim <bluewhale.marc@gmail.com>
+Jihye Hyun <jijinny26@gmail.com>
 Jihyeon Lee <wlgus7464@gmail.com>
 Jin Yang <jin.a.yang@intel.com>
 Jincheol Jo <jincheol.jo@navercorp.com>
@@ -702,6 +703,7 @@
 Jinwoo Song <jinwoo7.song@samsung.com>
 Jinyoung Hur <hur.ims@navercorp.com>
 Jinyoung Hur <hurims@gmail.com>
+Jisu Kim <hellojs242@gmail.com>
 Jitendra Kumar Sahoo <jitendra.ks@samsung.com>
 Jitesh Pareek <j1.pareek@samsung.com>
 Joachim Bauch <jbauch@webrtc.org>
@@ -1036,6 +1038,7 @@
 Minjeong Lee <apenr1234@gmail.com>
 Minseok Koo <kei98301@gmail.com>
 Minsoo Max Koo <msu.koo@samsung.com>
+Minsung Jin <m4ushold@gmail.com>
 Miran Karic <miran.karic@imgtec.com>
 Mirela Budaes <mbudaes@adobe.com>
 Mirela Budaes <mbudaes@gmail.com>
diff --git a/DEPS b/DEPS
index 39e6884..38f105b 100644
--- a/DEPS
+++ b/DEPS
@@ -295,19 +295,19 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'src_internal_revision': '5de18f0aacc3865198ffcb45372a83f6b0b9b27b',
+  'src_internal_revision': '7a2d2c5a328e293e0a9849b8d1e17e59d56a3b14',
   # 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': 'ac2d5ceecb588e3be2a798896b92d27c91ad6461',
+  'skia_revision': '0d16b74f74a5018c20fdaa753b41103bb08d08fd',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'a1ba74a85fdfad2d8eac3857059676bc6e0fb686',
+  'v8_revision': 'a46966388bbd892ee59d5d2688287704d1844302',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '00845fd649a450a00e61215f44bb4fc95d515c97',
+  'angle_revision': 'b729fb74e68bb2b3db6c95067f4ffc1e0c25bc07',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -319,7 +319,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
-  'boringssl_revision': '154bb5860a47c160c3be09003908c64c1de11bff',
+  'boringssl_revision': 'f371c85a8d58bf911cba6073bcca6d497af81dc9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Fuchsia sdk
   # and whatever else without interference from each other.
@@ -371,7 +371,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '64c31ffa4d735add4a7e7520a52e0e3160216132',
+  'catapult_revision': '74f545d009ceef464f840938ae079ffbc112a102',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling CrossBench
   # and whatever else without interference from each other.
@@ -391,7 +391,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': '6e4e34acb617713aa7b51311d6ea87c58c609b32',
+  'devtools_frontend_revision': '6d3155a3c0b50687e4f417e19d4491e66d56ef3c',
   # 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.
@@ -745,7 +745,7 @@
       'packages': [
           {
               'package': 'chrome_internal/third_party/google3/data_sharing_sdk',
-              'version': 'BmL2YCZyicXbUzlB_Nv7oAsX_cphB8D9DeDeEnMX8fkC',
+              'version': 'OINoFC4BjUT2ky7xBnUMwlAKhv4EYTLC-XJZ59t2udUC',
           },
       ],
       'condition': 'checkout_src_internal and non_git_source',
@@ -1486,7 +1486,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    '1881fd29e10d5d32e8b8f6ed892c860e43dded76',
+    '86901d8e9a33f78ccf07acfa57b63439e7cfa5f8',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -1645,7 +1645,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': '0kpSWsOkTy_kz_7BrmXaMj0RWlcTCIC9WOKWl3eSM4cC',
+          'version': '3f6bb_ds1Ab_INPp6lc8i778IRbWZSwVm2QBzfQFTMoC',
       },
     ],
     'condition': 'checkout_android and non_git_source',
@@ -1738,7 +1738,7 @@
       'packages': [
           {
                'package': 'chromium/third_party/android_build_tools/lint',
-               'version': '2fKjMen4v_UU1CDYJqhwJCr6uh-L5w25n9lAKx6SlP0C',
+               'version': 'QltgzNfGb4l4ekv1_GjIWtuew1hrBof1WVy2B8zrkYwC',
           },
       ],
       'condition': 'checkout_android and non_git_source',
@@ -1749,7 +1749,7 @@
       'packages': [
           {
                'package': 'chromium/third_party/android_build_tools/manifest_merger',
-               'version': 'GnPGgBK8kiq32GuJB3iI20yJbFjgt2dqHsmwoPShJ-4C',
+               'version': 'hVPp69VTDru05sisN9HcrOf67Tk_Mhu6P6bsR24gOrEC',
           },
       ],
       'condition': 'checkout_android and non_git_source',
@@ -1978,7 +1978,7 @@
 
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'fab0a4296b8830c569a4dc82285bfb58a5c2fca8',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'a7571b1596305d6a6fe26c788f24e40c579fcda8',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -2526,7 +2526,7 @@
     Var('pdfium_git') + '/pdfium.git' + '@' +  Var('pdfium_revision'),
 
   'src/third_party/perfetto':
-    Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + 'e2d7750acd15d57c6c88e3c8fb8c5a45f424ba96',
+    Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + '993d41d0c39335e35fe577808e8f704e58d9dbfb',
 
   'src/base/tracing/test/data': {
     'bucket': 'perfetto',
@@ -2950,7 +2950,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/windows-amd64',
-          'version': 'zasiUhQIHGZ2e60_M6g41oz8mYgPta5JmOWV5LwFjGMC',
+          'version': 'LLNTOh1PJ9ZXVrdmwgWAf0KYOgZgQhfgaldCTjmg1c0C',
         },
       ],
       'dep_type': 'cipd',
@@ -2961,7 +2961,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/mac-amd64',
-          'version': 'tZvHxy8AcDJhO9A-5UuZKL_-5vfE_wC-RBkElqWZpM4C',
+          'version': 'xo66yxZ-zTgqRnY5597wSJpk-3BX2OpElqzYg032slAC',
         },
       ],
       'dep_type': 'cipd',
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index c9b9c0df..570a0b1 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -1284,7 +1284,8 @@
             'common_range',
             'viewable_range',
             'constant_range',
-            # Banned: Views
+            # Views
+            'subrange',
             # Banned: Range factories
             # Banned: Range adaptors
             # Incidentally listed on
@@ -2335,6 +2336,7 @@
     'build/android/resource_sizes.pydeps',
     'build/android/test_runner.pydeps',
     'build/android/test_wrapper/logdog_wrapper.pydeps',
+    'build/fuchsia/test/component_storage_test.pydeps',
     'build/protoc_java.pydeps',
     'chrome/android/monochrome/scripts/monochrome_python_tests.pydeps',
     'chrome/test/chromedriver/log_replay/client_replay_unittest.pydeps',
diff --git a/PRESUBMIT_test.py b/PRESUBMIT_test.py
index a2ff946..65b2b67c 100755
--- a/PRESUBMIT_test.py
+++ b/PRESUBMIT_test.py
@@ -3033,6 +3033,7 @@
                 'allowed_ranges_usage.cc',
                 [
                     'std::ranges::begin(vec);',
+                    'std::ranges::subrange(first, last);',
                     # std::ranges::view is a concept and allowed, but the views
                     # library itself is not (see below)
                     'static_assert(std::ranges::view<SomeType>);'
@@ -3040,7 +3041,7 @@
             MockFile(
                 'banned_ranges_usage.cc',
                 [
-                    'std::ranges::subrange(first, last);',
+                    'std::ranges::borrowed_subrange_t(subrange);',
                     # Edge case: make sure std::ranges::views is disallowed,
                     # even though std::ranges::view is allowed.
                     'std::ranges::views::take(first, count);'
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index ab2903e..a6326088 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -860,9 +860,18 @@
     renaming_sources = []
     renaming_destinations = []
     foreach(_locale, platform_pak_locales) {
-      renaming_sources +=
-          [ "$root_out_dir/android_webview/locales/$_locale.pak" ]
-      renaming_destinations += [ "stored-locales/$_locale.pak" ]
+      if (translate_genders) {
+        renaming_sources += process_file_template(
+                all_chrome_genders,
+                "$root_out_dir/android_webview/locales/${_locale}_{{source_name_part}}.pak")
+        renaming_destinations += process_file_template(
+                all_chrome_genders,
+                "stored-locales/${_locale}_{{source_name_part}}.pak")
+      } else {
+        renaming_sources +=
+            [ "$root_out_dir/android_webview/locales/${_locale}.pak" ]
+        renaming_destinations += [ "stored-locales/${_locale}.pak" ]
+      }
     }
     treat_as_locale_paks = true
     deps = [ ":repack_locales" ]
@@ -1112,7 +1121,7 @@
   }
 }
 
-grit("generate_components_strings") {
+grit_strings("generate_components_strings") {
   source = "../components/components_strings.grd"
 
   defines = [
@@ -1135,16 +1144,10 @@
     "-w",
     _allowlist,
   ]
-  outputs =
-      [
-        "grit/components_strings.h",
-        "java/res/values/components_strings.xml",
-      ] +
-      process_file_template(
-          android_bundle_locales_as_resources,
-          [ "java/res/values-{{source_name_part}}/components_strings.xml" ]) +
-      process_file_template(all_chrome_locales,
-                            [ "components_strings_{{source_name_part}}.pak" ])
+  outputs = [ "grit/components_strings.h" ]
+
+  create_android_resources = true
+  output_prefix = "components_strings_"
 }
 
 generate_jni("common_jni") {
diff --git a/android_webview/browser/aw_browser_context.cc b/android_webview/browser/aw_browser_context.cc
index 60aea01..dd45ce4 100644
--- a/android_webview/browser/aw_browser_context.cc
+++ b/android_webview/browser/aw_browser_context.cc
@@ -160,7 +160,6 @@
   migrate_context_storage_data("QuotaManager-journal");
   migrate_context_storage_data("Service Worker");
   migrate_context_storage_data("VideoDecodeStats");
-  migrate_context_storage_data("databases");
   migrate_context_storage_data("shared_proto_db");
   migrate_context_storage_data("webrtc_event_logs");
 }
diff --git a/android_webview/browser/aw_content_browser_client.cc b/android_webview/browser/aw_content_browser_client.cc
index b749005..bdb962f7 100644
--- a/android_webview/browser/aw_content_browser_client.cc
+++ b/android_webview/browser/aw_content_browser_client.cc
@@ -176,8 +176,8 @@
 // navigation, and forwards it to the proxying loader factory.
 class XrwNavigationThrottle : public content::NavigationThrottle {
  public:
-  explicit XrwNavigationThrottle(content::NavigationHandle* handle)
-      : NavigationThrottle(handle) {}
+  explicit XrwNavigationThrottle(content::NavigationThrottleRegistry& registry)
+      : NavigationThrottle(registry) {}
   ~XrwNavigationThrottle() override {
     AwProxyingURLLoaderFactory::ClearXrwResultForNavigation(
         navigation_handle()->GetNavigationId());
@@ -683,22 +683,16 @@
   // doesn't actually call into an arbitrary client, it just posts a task to
   // call onPageStarted. shouldOverrideUrlLoading happens earlier (see
   // ContentBrowserClient::ShouldOverrideUrlLoading).
-  registry.MaybeAddThrottle(
-      navigation_interception::InterceptNavigationDelegate::
-          MaybeCreateThrottleFor(
-              &navigation_handle,
-              navigation_interception::SynchronyMode::kSync));
+  navigation_interception::InterceptNavigationDelegate::MaybeCreateAndAdd(
+      registry, navigation_interception::SynchronyMode::kSync);
 
   registry.AddThrottle(std::make_unique<PolicyBlocklistNavigationThrottle>(
-      &navigation_handle,
+      registry,
       AwBrowserContext::FromWebContents(navigation_handle.GetWebContents())));
 
-  registry.MaybeAddThrottle(
-      AwSafeBrowsingNavigationThrottle::MaybeCreateThrottleFor(
-          &navigation_handle));
+  AwSafeBrowsingNavigationThrottle::MaybeCreateAndAdd(registry);
   if (base::FeatureList::IsEnabled(kWebViewOptimizeXrwNavigationFlow)) {
-    registry.AddThrottle(
-        std::make_unique<XrwNavigationThrottle>(&navigation_handle));
+    registry.AddThrottle(std::make_unique<XrwNavigationThrottle>(registry));
   }
 
   if ((navigation_handle.GetNavigatingFrameType() ==
@@ -708,8 +702,8 @@
     AwSupervisedUserUrlClassifier* urlClassifier =
         AwSupervisedUserUrlClassifier::GetInstance();
     if (urlClassifier->ShouldCreateThrottle()) {
-      registry.AddThrottle(std::make_unique<AwSupervisedUserThrottle>(
-          &navigation_handle, urlClassifier));
+      registry.AddThrottle(
+          std::make_unique<AwSupervisedUserThrottle>(registry, urlClassifier));
     }
   }
 }
diff --git a/android_webview/browser/safe_browsing/aw_safe_browsing_navigation_throttle.cc b/android_webview/browser/safe_browsing/aw_safe_browsing_navigation_throttle.cc
index a4389b3..df97e29 100644
--- a/android_webview/browser/safe_browsing/aw_safe_browsing_navigation_throttle.cc
+++ b/android_webview/browser/safe_browsing/aw_safe_browsing_navigation_throttle.cc
@@ -14,27 +14,30 @@
 #include "components/security_interstitials/content/security_interstitial_tab_helper.h"
 #include "components/security_interstitials/core/unsafe_resource.h"
 #include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/navigation_throttle_registry.h"
 
 namespace android_webview {
 
 using safe_browsing::ThreatSeverity;
 
 // static
-std::unique_ptr<AwSafeBrowsingNavigationThrottle>
-AwSafeBrowsingNavigationThrottle::MaybeCreateThrottleFor(
-    content::NavigationHandle* handle) {
+void AwSafeBrowsingNavigationThrottle::MaybeCreateAndAdd(
+    content::NavigationThrottleRegistry& registry) {
   // Only outer-most main frames show the interstitial through the navigation
   // throttle. In other cases, the interstitial is shown via
   // BaseUIManager::DisplayBlockingPage.
-  if (!handle->IsInPrimaryMainFrame() && !handle->IsInPrerenderedMainFrame())
-    return nullptr;
+  if (!registry.GetNavigationHandle().IsInPrimaryMainFrame() &&
+      !registry.GetNavigationHandle().IsInPrerenderedMainFrame()) {
+    return;
+  }
 
-  return base::WrapUnique(new AwSafeBrowsingNavigationThrottle(handle));
+  registry.AddThrottle(
+      base::WrapUnique(new AwSafeBrowsingNavigationThrottle(registry)));
 }
 
 AwSafeBrowsingNavigationThrottle::AwSafeBrowsingNavigationThrottle(
-    content::NavigationHandle* handle)
-    : content::NavigationThrottle(handle) {}
+    content::NavigationThrottleRegistry& registry)
+    : content::NavigationThrottle(registry) {}
 
 const char* AwSafeBrowsingNavigationThrottle::GetNameForLogging() {
   return "AwSafeBrowsingNavigationThrottle";
diff --git a/android_webview/browser/safe_browsing/aw_safe_browsing_navigation_throttle.h b/android_webview/browser/safe_browsing/aw_safe_browsing_navigation_throttle.h
index d4a77f5..3682d5e 100644
--- a/android_webview/browser/safe_browsing/aw_safe_browsing_navigation_throttle.h
+++ b/android_webview/browser/safe_browsing/aw_safe_browsing_navigation_throttle.h
@@ -7,10 +7,8 @@
 
 #include "content/public/browser/navigation_throttle.h"
 
-#include <memory>
-
 namespace content {
-class NavigationHandle;
+class NavigationThrottleRegistry;
 }  // namespace content
 
 namespace android_webview {
@@ -29,15 +27,15 @@
 // Lifetime: Temporary
 class AwSafeBrowsingNavigationThrottle : public content::NavigationThrottle {
  public:
-  static std::unique_ptr<AwSafeBrowsingNavigationThrottle>
-  MaybeCreateThrottleFor(content::NavigationHandle* handle);
+  static void MaybeCreateAndAdd(content::NavigationThrottleRegistry& registry);
   ~AwSafeBrowsingNavigationThrottle() override {}
   const char* GetNameForLogging() override;
 
   content::NavigationThrottle::ThrottleCheckResult WillFailRequest() override;
 
  private:
-  explicit AwSafeBrowsingNavigationThrottle(content::NavigationHandle* handle);
+  explicit AwSafeBrowsingNavigationThrottle(
+      content::NavigationThrottleRegistry& registry);
 };
 }  // namespace android_webview
 
diff --git a/android_webview/browser/supervised_user/aw_supervised_user_throttle.cc b/android_webview/browser/supervised_user/aw_supervised_user_throttle.cc
index 95297ef..bce359f 100644
--- a/android_webview/browser/supervised_user/aw_supervised_user_throttle.cc
+++ b/android_webview/browser/supervised_user/aw_supervised_user_throttle.cc
@@ -11,17 +11,17 @@
 namespace android_webview {
 
 // static
-std::unique_ptr<AwSupervisedUserThrottle> AwSupervisedUserThrottle::Create(
-    content::NavigationHandle* navigation_handle,
+void AwSupervisedUserThrottle::CreateAndAdd(
+    content::NavigationThrottleRegistry& registry,
     AwSupervisedUserUrlClassifier* url_classifier) {
-  return base::WrapUnique<AwSupervisedUserThrottle>(
-      new AwSupervisedUserThrottle(navigation_handle, url_classifier));
+  registry.AddThrottle(base::WrapUnique<AwSupervisedUserThrottle>(
+      new AwSupervisedUserThrottle(registry, url_classifier)));
 }
 
 AwSupervisedUserThrottle::AwSupervisedUserThrottle(
-    content::NavigationHandle* navigation_handle,
+    content::NavigationThrottleRegistry& registry,
     AwSupervisedUserUrlClassifier* url_classifier)
-    : NavigationThrottle(navigation_handle), url_classifier_(url_classifier) {
+    : NavigationThrottle(registry), url_classifier_(url_classifier) {
   DCHECK(url_classifier_);
   DETACH_FROM_SEQUENCE(sequence_checker_);
 }
diff --git a/android_webview/browser/supervised_user/aw_supervised_user_throttle.h b/android_webview/browser/supervised_user/aw_supervised_user_throttle.h
index 0589480..551ae72 100644
--- a/android_webview/browser/supervised_user/aw_supervised_user_throttle.h
+++ b/android_webview/browser/supervised_user/aw_supervised_user_throttle.h
@@ -10,6 +10,10 @@
 #include "content/public/browser/navigation_throttle.h"
 #include "net/http/http_request_headers.h"
 
+namespace content {
+class NavigationThrottleRegistry;
+}  // namespace content
+
 namespace android_webview {
 
 // This throttle is used to check if a given url (http and https only)
@@ -28,13 +32,11 @@
 // lives from navigation start until the navigation has been committed.
 class AwSupervisedUserThrottle : public content::NavigationThrottle {
  public:
-  static std::unique_ptr<AwSupervisedUserThrottle> Create(
-      content::NavigationHandle* navigation_handle,
-      AwSupervisedUserUrlClassifier* bridge);
+  static void CreateAndAdd(content::NavigationThrottleRegistry& registry,
+                           AwSupervisedUserUrlClassifier* bridge);
 
-  explicit AwSupervisedUserThrottle(
-      content::NavigationHandle* navigation_handle,
-      AwSupervisedUserUrlClassifier* url_classifier);
+  AwSupervisedUserThrottle(content::NavigationThrottleRegistry& registry,
+                           AwSupervisedUserUrlClassifier* url_classifier);
   AwSupervisedUserThrottle(const AwSupervisedUserThrottle&) = delete;
   AwSupervisedUserThrottle& operator=(const AwSupervisedUserThrottle&) = delete;
   ~AwSupervisedUserThrottle() override;
diff --git a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
index 81dc59d..087b36c 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
@@ -217,6 +217,7 @@
         Flag.baseFeature(
                 BlinkFeatures.PRELOAD_LINK_REL_DATA_URLS,
                 "Allow preloading data: URLs with link rel=preload"),
+        Flag.baseFeature(BlinkFeatures.OPTIMIZE_HTML_ELEMENT_URLS, "Optimize HTML Element URLs"),
         Flag.baseFeature(
                 BlinkFeatures.DOCUMENT_POLICY_EXPECT_NO_LINKED_RESOURCES,
                 "Enables the ability to use Document Policy header to control feature"
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 4b4af5a..d39db4a 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -6938,6 +6938,12 @@
       <message name="IDS_ASH_SUNFISH_EDUCATE_TOAST_MESSAGE" desc="The message shown in a toast window to educate the user about the Sunfish feature.">
         Select anything on your screen to search, copy text, and more
       </message>
+      <message name="IDS_ASH_SUNFISH_RESULTS_LOADING_ACCESSIBLE_NAME" desc="The accessible name of the loading image in the Sunfish search results panel while waiting for the results web contents to load.">
+        Results loading
+      </message>
+      <message name="IDS_ASH_SUNFISH_RESULTS_LOADED_ACCESSIBLE_NAME" desc="Text for the accessible name that is announced when the results for the Sunfish search results panel are loaded.">
+        Results loaded
+      </message>
 
       <!-- Scanner -->
       <message name="IDS_ASH_SCANNER_DISCLAIMER_TITLE" desc="Text shown as the title of the suggested actions when searching your screen disclaimer view.">
diff --git a/ash/ash_strings_grd/IDS_ASH_SUNFISH_RESULTS_LOADED_ACCESSIBLE_NAME.png.sha1 b/ash/ash_strings_grd/IDS_ASH_SUNFISH_RESULTS_LOADED_ACCESSIBLE_NAME.png.sha1
new file mode 100644
index 0000000..f19df497
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_SUNFISH_RESULTS_LOADED_ACCESSIBLE_NAME.png.sha1
@@ -0,0 +1 @@
+3b7a6e4979a9f968c705a59e4d743d60fae85c45
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_SUNFISH_RESULTS_LOADING_ACCESSIBLE_NAME.png.sha1 b/ash/ash_strings_grd/IDS_ASH_SUNFISH_RESULTS_LOADING_ACCESSIBLE_NAME.png.sha1
new file mode 100644
index 0000000..fe20041
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_SUNFISH_RESULTS_LOADING_ACCESSIBLE_NAME.png.sha1
@@ -0,0 +1 @@
+9b6a3702a6e518712aa38664c470f403cb346d98
\ No newline at end of file
diff --git a/ash/capture_mode/capture_mode_constants.h b/ash/capture_mode/capture_mode_constants.h
index 0f8bf925..956f962 100644
--- a/ash/capture_mode/capture_mode_constants.h
+++ b/ash/capture_mode/capture_mode_constants.h
@@ -177,6 +177,9 @@
 inline constexpr int kRegionGlowMinOutsetDp = 0;
 inline constexpr int kRegionGlowMaxOutsetDp = 6;
 
+// The view ID for the search results panel loading animation.
+inline constexpr int kLoadingAnimationViewId = 1;
+
 }  // namespace ash::capture_mode
 
 #endif  // ASH_CAPTURE_MODE_CAPTURE_MODE_CONSTANTS_H_
diff --git a/ash/capture_mode/capture_mode_controller.cc b/ash/capture_mode/capture_mode_controller.cc
index 87c69641..2ae8f4c8 100644
--- a/ash/capture_mode/capture_mode_controller.cc
+++ b/ash/capture_mode/capture_mode_controller.cc
@@ -14,7 +14,6 @@
 #include "ash/capture_mode/capture_mode_ash_notification_view.h"
 #include "ash/capture_mode/capture_mode_behavior.h"
 #include "ash/capture_mode/capture_mode_camera_controller.h"
-#include "ash/capture_mode/capture_mode_constants.h"
 #include "ash/capture_mode/capture_mode_education_controller.h"
 #include "ash/capture_mode/capture_mode_metrics.h"
 #include "ash/capture_mode/capture_mode_observer.h"
@@ -712,8 +711,8 @@
              : nullptr;
 }
 
-void CaptureModeController::ShowSearchResultsPanel(const gfx::ImageSkia& image,
-                                                   GURL url) {
+void CaptureModeController::ShowSearchResultsPanel(
+    const gfx::ImageSkia& image) {
   // We should not use `CanShowSunfishUi` here, as that could change between
   // sending the region and receiving a URL (for example, if the Sunfish policy
   // changes).
@@ -746,6 +745,9 @@
   // If the panel was not visible beforehand (either the panel was not created
   // yet or the panel was hidden from making a new selection), emit a metric.
   if (!search_results_panel_widget_->IsVisible()) {
+    // Each time we make a new request, we should show the loading animation.
+    GetSearchResultsPanel()->ShowLoadingAnimation();
+
     search_results_panel_widget_->Show();
     RecordSearchResultsPanelShown();
     // Setting or updating the bounds here only accounts for newly selected
@@ -763,7 +765,7 @@
   if (!features::IsSunfishLensWebEnabled()) {
     search_results_panel->SetSearchBoxImage(image);
   }
-  search_results_panel->Navigate(url);
+
   if (should_end_session) {
     Stop();
   }
@@ -2068,6 +2070,7 @@
     return;
   }
 
+  gfx::ImageSkia image_skia = gfx::ImageSkia();
   if (features::IsSunfishLensWebEnabled()) {
     const gfx::Image image = gfx::Image::CreateFrom1xBitmap(bitmap);
     const bool is_standalone_session =
@@ -2083,19 +2086,24 @@
         base::BindRepeating(&CaptureModeController::OnLensWebError,
                             weak_ptr_factory_.GetWeakPtr(),
                             image_search_token));
-    return;
+  } else {
+    image_skia = gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
+    // `OnSearchUrlFetched()` will be invoked with `image` when the server
+    // response is fetched.
+    delegate_->SendRegionSearch(
+        bitmap, user_capture_region_,
+        base::BindRepeating(&CaptureModeController::OnSearchUrlFetched,
+                            weak_ptr_factory_.GetWeakPtr(),
+                            user_capture_region_, image_skia),
+        base::BindRepeating(&CaptureModeController::OnLensTextDetectionComplete,
+                            weak_ptr_factory_.GetWeakPtr(),
+                            image_search_token));
   }
 
-  const gfx::ImageSkia image = gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
-  // `OnSearchUrlFetched()` will be invoked with `image` when the server
-  // response is fetched.
-  delegate_->SendRegionSearch(
-      bitmap, user_capture_region_,
-      base::BindRepeating(&CaptureModeController::OnSearchUrlFetched,
-                          weak_ptr_factory_.GetWeakPtr(), user_capture_region_,
-                          image),
-      base::BindRepeating(&CaptureModeController::OnLensTextDetectionComplete,
-                          weak_ptr_factory_.GetWeakPtr(), image_search_token));
+  // Immediately show the search results panel, with a loading animation in
+  // place of the web contents. We will replace it once we receive the URL from
+  // the server.
+  ShowSearchResultsPanel(image_skia);
 }
 
 void CaptureModeController::OnTextDetectionComplete(
@@ -2208,12 +2216,14 @@
                                                const gfx::ImageSkia& image,
                                                GURL url) {
   if (captured_region == user_capture_region_) {
-    ShowSearchResultsPanel(image, url);
+    NavigateSearchResultsPanel(url);
   }
 }
 
 void CaptureModeController::OnLensWebError(
     base::WeakPtr<BaseCaptureModeSession> image_search_token) {
+  CloseSearchResultsPanel();
+
   // TODO: crbug.com/406072681 - Show an error message if the session is no
   // longer active, such as in the case of clicking the Search with Lens button
   // in a regular session.
@@ -3013,4 +3023,12 @@
       base::BindOnce(std::move(callback), path));
 }
 
+void CaptureModeController::NavigateSearchResultsPanel(const GURL& url) {
+  if (auto* panel = GetSearchResultsPanel()) {
+    capture_mode_util::TriggerAccessibilityAlert(
+        IDS_ASH_SUNFISH_RESULTS_LOADED_ACCESSIBLE_NAME);
+    panel->Navigate(url);
+  }
+}
+
 }  // namespace ash
diff --git a/ash/capture_mode/capture_mode_controller.h b/ash/capture_mode/capture_mode_controller.h
index 2adb4e6f..06af9617 100644
--- a/ash/capture_mode/capture_mode_controller.h
+++ b/ash/capture_mode/capture_mode_controller.h
@@ -42,6 +42,7 @@
 #include "services/viz/privileged/mojom/compositing/frame_sink_video_capture.mojom-forward.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/image/image.h"
+#include "ui/gfx/image/image_skia.h"
 
 class PrefRegistrySimple;
 
@@ -150,9 +151,13 @@
   // Returns the search results panel, or nullptr if none exists.
   SearchResultsPanel* GetSearchResultsPanel() const;
 
-  // Shows the results panel with the captured region as `image` and the search
-  // results `url`.
-  void ShowSearchResultsPanel(const gfx::ImageSkia& image, GURL url);
+  // Shows the results panel. `image` is only needed for the thumbnail if the
+  // Lens Web feature flag is disabled.
+  void ShowSearchResultsPanel(const gfx::ImageSkia& image);
+
+  // Navigates the Sunfish search results panel to the given URL, if the panel
+  // is available.
+  void NavigateSearchResultsPanel(const GURL& url);
 
   // Closes the search results panel, or does nothing if it doesn't exist.
   void CloseSearchResultsPanel();
diff --git a/ash/capture_mode/capture_mode_session_focus_cycler.cc b/ash/capture_mode/capture_mode_session_focus_cycler.cc
index 00431fc..be9228e 100644
--- a/ash/capture_mode/capture_mode_session_focus_cycler.cc
+++ b/ash/capture_mode/capture_mode_session_focus_cycler.cc
@@ -633,10 +633,17 @@
     SearchResultsPanel* panel =
         CaptureModeController::Get()->GetSearchResultsPanel();
     if (panel) {
-      views::View* web_view = panel->GetWebViewForFocus();
-      CHECK(web_view);
-      web_view->AboutToRequestFocusFromTabTraversal(reverse);
-      web_view->RequestFocus();
+      // The web contents might not be available when we try to focus them, but
+      // we can focus the loading animation instead.
+      if (panel->search_results_view()) {
+        views::View* web_view = panel->GetWebViewForFocus();
+        CHECK(web_view);
+        web_view->AboutToRequestFocusFromTabTraversal(reverse);
+        web_view->RequestFocus();
+      } else {
+        focus_index_ = 0u;
+        panel->GetHighlightableLoadingAnimation()->PseudoFocus();
+      }
     }
   } else if (!current_views.empty()) {
     DCHECK_LT(focus_index_, current_views.size());
@@ -1116,9 +1123,6 @@
     case FocusGroup::kSelection:
     case FocusGroup::kPendingSettings:
     case FocusGroup::kPendingRecordingType:
-    // The web contents does not contain any highlightable views, as we want its
-    // `FocusManager` to handle tab traversal and focus.
-    case FocusGroup::kSearchResultsPanelWebContents:
       break;
     case FocusGroup::kTypeSource: {
       CaptureModeBarView* bar_view = session_->capture_mode_bar_view_;
@@ -1217,16 +1221,30 @@
       break;
     }
     case FocusGroup::kSearchResultsPanel: {
+      auto* controller = CaptureModeController::Get();
       auto* search_results_panel_widget =
-          CaptureModeController::Get()->search_results_panel_widget();
+          controller->search_results_panel_widget();
       if (search_results_panel_widget &&
           search_results_panel_widget->IsVisible()) {
-        items = CaptureModeController::Get()
-                    ->GetSearchResultsPanel()
-                    ->GetHighlightableItems();
+        items = controller->GetSearchResultsPanel()->GetHighlightableItems();
       }
       break;
     }
+    // The web contents are not highlightable, only the animation view. We'll
+    // let `AdvanceFocus` handle the web contents separately.
+    case FocusGroup::kSearchResultsPanelWebContents: {
+      auto* controller = CaptureModeController::Get();
+      auto* search_results_panel_widget =
+          controller->search_results_panel_widget();
+      if (search_results_panel_widget &&
+          search_results_panel_widget->IsVisible()) {
+        auto* animation_view = controller->GetSearchResultsPanel()
+                                   ->GetHighlightableLoadingAnimation();
+        if (animation_view) {
+          items.push_back(animation_view);
+        }
+      }
+    }
   }
   return items;
 }
diff --git a/ash/capture_mode/search_results_panel.cc b/ash/capture_mode/search_results_panel.cc
index e6c8ca95..2d71f681 100644
--- a/ash/capture_mode/search_results_panel.cc
+++ b/ash/capture_mode/search_results_panel.cc
@@ -18,6 +18,8 @@
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/icon_button.h"
 #include "ash/style/typography.h"
+#include "ash/system/mahi/mahi_animation_utils.h"
+#include "ash/system/mahi/resources/grit/mahi_resources.h"
 #include "components/vector_icons/vector_icons.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
@@ -28,6 +30,7 @@
 #include "ui/display/tablet_state.h"
 #include "ui/views/background.h"
 #include "ui/views/border.h"
+#include "ui/views/controls/animated_image_view.h"
 #include "ui/views/controls/focus_ring.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/label.h"
@@ -226,20 +229,6 @@
     search_box_view_->SetProperty(views::kMarginsKey, kSearchBoxViewSpacing);
   }
 
-  search_results_view_ =
-      AddChildView(CaptureModeController::Get()->CreateSearchResultsView());
-  search_results_view_->SetProperty(
-      views::kFlexBehaviorKey,
-      views::FlexSpecification(views::MinimumFlexSizeRule::kPreferred,
-                               views::MaximumFlexSizeRule::kUnbounded));
-
-  // Without the native search box, there needs to be padding between the header
-  // and the web view.
-  if (features::IsSunfishLensWebEnabled()) {
-    search_results_view_->SetProperty(views::kMarginsKey,
-                                      kSearchResultsViewSpacing);
-  }
-
   SetBackground(views::CreateRoundedRectBackground(
       cros_tokens::kCrosSysSystemBaseElevated, kPanelCornerRadius));
   SetPaintToLayer();
@@ -249,6 +238,8 @@
   layer()->SetBackgroundBlur(ColorProvider::kBackgroundBlurSigma);
   layer()->SetBackdropFilterQuality(ColorProvider::kBackgroundBlurQuality);
 
+  ShowLoadingAnimation();
+
   // Install highlightable views for when a Sunfish session is active and the
   // `CaptureModeSessionFocusCycler` is handling focus. Set up the focus
   // predicate for the focusable views now, so they will have the correct
@@ -258,6 +249,12 @@
   CaptureModeSessionFocusCycler::HighlightHelper::Get(close_button_)
       ->SetUpFocusPredicate();
 
+  auto* animation_view = GetViewByID(capture_mode::kLoadingAnimationViewId);
+  CHECK(animation_view);
+  CaptureModeSessionFocusCycler::HighlightHelper::Install(animation_view);
+  CaptureModeSessionFocusCycler::HighlightHelper::Get(animation_view)
+      ->SetUpFocusPredicate();
+
   if (!features::IsSunfishLensWebEnabled()) {
     CaptureModeSessionFocusCycler::HighlightHelper::Install(
         search_box_view_->textfield_);
@@ -308,11 +305,35 @@
   return highlightable_items;
 }
 
+CaptureModeSessionFocusCycler::HighlightableView*
+SearchResultsPanel::GetHighlightableLoadingAnimation() {
+  auto* animation_view = GetViewByID(capture_mode::kLoadingAnimationViewId);
+  return animation_view ? CaptureModeSessionFocusCycler::HighlightHelper::Get(
+                              animation_view)
+                        : nullptr;
+}
+
 views::View* SearchResultsPanel::GetWebViewForFocus() {
+  CHECK(search_results_view_);
   return search_results_view_->GetInitiallyFocusedView();
 }
 
 void SearchResultsPanel::Navigate(const GURL& url) {
+  if (!search_results_view_) {
+    // Remove the loading animation.
+    auto* animation_view = GetViewByID(capture_mode::kLoadingAnimationViewId);
+    CHECK(animation_view);
+    RemoveChildViewT(animation_view);
+
+    search_results_view_ =
+        AddChildView(CaptureModeController::Get()->CreateSearchResultsView());
+    search_results_view_->SetProperty(
+        views::kFlexBehaviorKey,
+        views::FlexSpecification(views::MinimumFlexSizeRule::kPreferred,
+                                 views::MaximumFlexSizeRule::kUnbounded));
+    search_results_view_->SetProperty(views::kMarginsKey,
+                                      kSearchResultsViewSpacing);
+  }
   search_results_view_->Navigate(url);
 }
 
@@ -346,6 +367,40 @@
       ->has_focus();
 }
 
+void SearchResultsPanel::ShowLoadingAnimation() {
+  if (GetViewByID(capture_mode::kLoadingAnimationViewId)) {
+    return;
+  }
+
+  // Remove the search results view if present.
+  if (search_results_view_) {
+    auto* search_results_view = search_results_view_.get();
+    search_results_view_ = nullptr;
+    RemoveChildViewT(search_results_view);
+  }
+
+  CHECK(!search_results_view_);
+
+  // Add the animation view and play it.
+  auto* animation_view = AddChildView(
+      views::Builder<views::AnimatedImageView>()
+          // Use an ID instead of saving a `raw_ptr` to avoid a dangling pointer
+          // when we remove this child later.
+          .SetID(capture_mode::kLoadingAnimationViewId)
+          .SetAccessibleName(l10n_util::GetStringUTF16(
+              IDS_ASH_SUNFISH_RESULTS_LOADING_ACCESSIBLE_NAME))
+          .SetAnimatedImage(mahi_animation_utils::GetLottieAnimationData(
+              IDR_MAHI_LOADING_SUMMARY_ANIMATION))
+          .Build());
+  animation_view->SetProperty(
+      views::kFlexBehaviorKey,
+      views::FlexSpecification(views::MinimumFlexSizeRule::kPreferred,
+                               views::MaximumFlexSizeRule::kUnbounded));
+  animation_view->Play(mahi_animation_utils::GetLottiePlaybackConfig(
+      *animation_view->animated_image()->skottie(),
+      IDR_MAHI_LOADING_SUMMARY_ANIMATION));
+}
+
 void SearchResultsPanel::AddedToWidget() {
   GetFocusManager()->AddFocusChangeListener(this);
 }
diff --git a/ash/capture_mode/search_results_panel.h b/ash/capture_mode/search_results_panel.h
index 3c6a31a7..30aa235 100644
--- a/ash/capture_mode/search_results_panel.h
+++ b/ash/capture_mode/search_results_panel.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "ash/ash_export.h"
+#include "ash/capture_mode/capture_mode_constants.h"
 #include "ash/capture_mode/capture_mode_session_focus_cycler.h"
 #include "ash/wm/system_panel_view.h"
 #include "ui/base/metadata/metadata_header_macros.h"
@@ -44,12 +45,24 @@
 
   AshWebView* search_results_view() const { return search_results_view_; }
   views::Button* close_button() const { return close_button_; }
+  views::View* animation_view_for_test() {
+    return GetViewByID(capture_mode::kLoadingAnimationViewId);
+  }
 
   views::Textfield* GetSearchBoxTextfield() const;
 
+  // Gets the highlightable views for the search results panel, which may
+  // include the close button and the search box textfield. Does not include
+  // the web contents or animation as they need to be handled separately.
   std::vector<CaptureModeSessionFocusCycler::HighlightableView*>
   GetHighlightableItems() const;
 
+  // Gets the highlightable view for the loading animation view. Returns
+  // `nullptr` if the loading animation is not available (i.e., the web contents
+  // are available).
+  CaptureModeSessionFocusCycler::HighlightableView*
+  GetHighlightableLoadingAnimation();
+
   // Gets the inner `WebView` to receive focus events.
   views::View* GetWebViewForFocus();
 
@@ -67,6 +80,9 @@
   // this view has focus, otherwise returns false.
   bool IsTextfieldPseudoFocused() const;
 
+  // Shows and plays a loading animation in place of the web contents.
+  void ShowLoadingAnimation();
+
   // views::View:
   void AddedToWidget() override;
   void RemovedFromWidget() override;
@@ -91,7 +107,7 @@
 
   // Owned by the views hierarchy.
   raw_ptr<SunfishSearchBoxView> search_box_view_ = nullptr;
-  raw_ptr<AshWebView> search_results_view_;
+  raw_ptr<AshWebView> search_results_view_ = nullptr;
   raw_ptr<views::Button> close_button_;
 
   // Observes display and metrics changes.
diff --git a/ash/capture_mode/sunfish_unittest.cc b/ash/capture_mode/sunfish_unittest.cc
index 6683c2e..709f8d7b 100644
--- a/ash/capture_mode/sunfish_unittest.cc
+++ b/ash/capture_mode/sunfish_unittest.cc
@@ -915,10 +915,11 @@
 
   // The results panel can be dragged by points outside the search results view
   // and searchbox textfield.
-  const gfx::Point draggable_point(search_results_panel->search_results_view()
-                                       ->GetBoundsInScreen()
-                                       .origin() +
-                                   gfx::Vector2d(0, -3));
+  const gfx::Point draggable_point(
+      search_results_panel->animation_view_for_test()
+          ->GetBoundsInScreen()
+          .origin() +
+      gfx::Vector2d(0, -3));
   event_generator->MoveMouseTo(draggable_point);
 
   // Test that dragging the panel to arbitrary points repositions the panel.
@@ -945,7 +946,7 @@
 
   // Start dragging the panel.
   const gfx::Point draggable_point(
-      panel->search_results_view()->GetBoundsInScreen().origin() +
+      panel->animation_view_for_test()->GetBoundsInScreen().origin() +
       gfx::Vector2d(0, -3));
   event_generator->MoveMouseTo(draggable_point);
   event_generator->PressLeftButton();
@@ -1663,7 +1664,8 @@
   // Mock getting a new response from the server. Test the panel is updated.
   EXPECT_CALL(*search_results_panel, Navigate(testing::_));
 
-  controller->ShowSearchResultsPanel(gfx::ImageSkia(), GURL("kTestUrl2"));
+  controller->ShowSearchResultsPanel(gfx::ImageSkia());
+  controller->NavigateSearchResultsPanel(GURL("kTestUrl2"));
 }
 
 // Tests that the search results panel is closed when starting a new session.
diff --git a/ash/shelf/shelf_tooltip_manager.cc b/ash/shelf/shelf_tooltip_manager.cc
index 90672ce..1bfbebe 100644
--- a/ash/shelf/shelf_tooltip_manager.cc
+++ b/ash/shelf/shelf_tooltip_manager.cc
@@ -23,6 +23,7 @@
 #include "ui/events/types/event_type.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/views/controls/button/button.h"
+#include "ui/views/view_tracker.h"
 #include "ui/wm/core/window_animations.h"
 
 namespace ash {
@@ -115,8 +116,15 @@
 void ShelfTooltipManager::ShowTooltipWithDelay(views::View* view) {
   if (ShouldShowTooltipForView(view)) {
     timer_.Start(FROM_HERE, base::Milliseconds(timer_delay_),
-                 base::BindOnce(&ShelfTooltipManager::ShowTooltip,
-                                weak_factory_.GetWeakPtr(), view));
+                 base::BindOnce(
+                     [](const base::WeakPtr<ShelfTooltipManager>& self,
+                        views::ViewTracker* view_tracker) {
+                       if (self && view_tracker->view()) {
+                         self->ShowTooltip(view_tracker->view());
+                       }
+                     },
+                     weak_factory_.GetWeakPtr(),
+                     base::Owned(std::make_unique<views::ViewTracker>(view))));
   }
 }
 
diff --git a/ash/shelf/shelf_tooltip_manager_unittest.cc b/ash/shelf/shelf_tooltip_manager_unittest.cc
index e9cc9f27..a3d9dde 100644
--- a/ash/shelf/shelf_tooltip_manager_unittest.cc
+++ b/ash/shelf/shelf_tooltip_manager_unittest.cc
@@ -25,6 +25,7 @@
 #include "ui/events/event_constants.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
+#include "ui/views/view_tracker.h"
 #include "ui/views/widget/widget.h"
 
 namespace ash {
@@ -52,6 +53,14 @@
   bool IsTimerRunning() { return tooltip_manager_->timer_.IsRunning(); }
   views::Widget* GetTooltip() { return tooltip_manager_->bubble_->GetWidget(); }
 
+  void FireTimerNow() { tooltip_manager_->timer_.FireNow(); }
+
+  void RemoveItemAt(int index) {
+    test_api_->SetAnimationDuration(base::Milliseconds(0));
+    test_api_->RemoveItemAt(index);
+    test_api_->SetAnimationDuration(base::Milliseconds(1));
+  }
+
   void ShowTooltipForFirstAppIcon() {
     EXPECT_GE(shelf_view_->number_of_visible_apps(), 1u);
     tooltip_manager_->ShowTooltip(
@@ -79,6 +88,25 @@
   // TODO: Test that the delayed tooltip is shown, without flaky failures.
 }
 
+TEST_F(ShelfTooltipManagerTest, ShowTooltipWithDelayAndAsyncViewDestruction) {
+  views::ViewTracker view_tracker(
+      shelf_view_->first_visible_button_for_testing());
+
+  // Show tooltip for view with delay.
+  tooltip_manager_->ShowTooltipWithDelay(view_tracker.view());
+  EXPECT_FALSE(tooltip_manager_->IsVisible());
+  EXPECT_TRUE(IsTimerRunning());
+
+  // Destroy view before delay completes.
+  RemoveItemAt(0);
+  EXPECT_FALSE(view_tracker.view());
+
+  // Verify that `tooltip_manager_` no-ops gracefully.
+  FireTimerNow();
+  EXPECT_FALSE(IsTimerRunning());
+  EXPECT_FALSE(tooltip_manager_->IsVisible());
+}
+
 TEST_F(ShelfTooltipManagerTest, DoNotShowForInvalidView) {
   // The manager should not show or start the timer for a null view.
   tooltip_manager_->ShowTooltip(nullptr);
diff --git a/ash/shelf/shelf_view_test_api.cc b/ash/shelf/shelf_view_test_api.cc
index c4000d3e..101c519c 100644
--- a/ash/shelf/shelf_view_test_api.cc
+++ b/ash/shelf/shelf_view_test_api.cc
@@ -69,6 +69,10 @@
   return item.id;
 }
 
+void ShelfViewTestAPI::RemoveItemAt(int index) {
+  shelf_view_->model_->RemoveItemAt(index);
+}
+
 views::View* ShelfViewTestAPI::GetViewAt(int index) {
   return shelf_view_->view_model_->view_at(index);
 }
diff --git a/ash/shelf/shelf_view_test_api.h b/ash/shelf/shelf_view_test_api.h
index 1b110c7..a066433 100644
--- a/ash/shelf/shelf_view_test_api.h
+++ b/ash/shelf/shelf_view_test_api.h
@@ -55,6 +55,9 @@
   // Adds a new item of the given type to the view.
   ShelfID AddItem(ShelfItemType type);
 
+  // Removes the item at the specified |index| from the view.
+  void RemoveItemAt(int index);
+
   // Retrieve the view at |index|.
   views::View* GetViewAt(int index);
 
diff --git a/ash/strings/BUILD.gn b/ash/strings/BUILD.gn
index 7e1ec08b..ef1c8ea 100644
--- a/ash/strings/BUILD.gn
+++ b/ash/strings/BUILD.gn
@@ -9,12 +9,11 @@
 
 assert(is_chromeos)
 
-grit("strings") {
+grit_strings("strings") {
   source = "../ash_strings.grd"
   defines = [ "is_chrome_branded=$is_chrome_branded" ]
-  outputs = [ "grit/ash_strings.h" ] +
-            process_file_template(all_chrome_locales,
-                                  [ "ash_strings_{{source_name_part}}.pak" ])
+  outputs = [ "grit/ash_strings.h" ]
+  output_prefix = "ash_strings_"
 }
 
 # Creates locale-specific pak files with strings needed for ash_unittests, etc.
diff --git a/ash/webui/camera_app_ui/resources/strings/BUILD.gn b/ash/webui/camera_app_ui/resources/strings/BUILD.gn
index d98ede1e..8e9bd48 100644
--- a/ash/webui/camera_app_ui/resources/strings/BUILD.gn
+++ b/ash/webui/camera_app_ui/resources/strings/BUILD.gn
@@ -8,12 +8,10 @@
 
 assert(is_chromeos)
 
-grit("strings") {
+grit_strings("strings") {
   source = "camera_strings.grd"
   defines = chrome_grit_defines
 
   outputs = [ "grit/ash_camera_app_strings.h" ]
-  foreach(locale, all_chrome_locales) {
-    outputs += [ "ash_camera_app_strings_$locale.pak" ]
-  }
+  output_prefix = "ash_camera_app_strings_"
 }
diff --git a/ash/webui/common/resources/sea_pen/sea_pen_input_query_element.ts b/ash/webui/common/resources/sea_pen/sea_pen_input_query_element.ts
index f47c11d..6587e803 100644
--- a/ash/webui/common/resources/sea_pen/sea_pen_input_query_element.ts
+++ b/ash/webui/common/resources/sea_pen/sea_pen_input_query_element.ts
@@ -230,7 +230,7 @@
 
   private onClickInspire_() {
     const index = Math.floor(Math.random() * SEA_PEN_SAMPLES.length);
-    this.textValue_ = SEA_PEN_SAMPLES[index].prompt;
+    this.textValue_ = this.i18n(SEA_PEN_SAMPLES[index].prompt);
     this.showCreateButton_();
     this.searchInputQuery_();
   }
diff --git a/base/test/android/javatests/src/org/chromium/base/test/transit/CarryOn.java b/base/test/android/javatests/src/org/chromium/base/test/transit/CarryOn.java
index eb0be79..d1bbe061 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/transit/CarryOn.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/transit/CarryOn.java
@@ -14,7 +14,7 @@
 
 /** CarryOn is a lightweight, stand-alone ConditionalState not tied to any Station. */
 @NullMarked
-public abstract class CarryOn extends ConditionalState {
+public class CarryOn extends ConditionalState {
 
     private final int mId;
     private final String mName;
@@ -60,14 +60,11 @@
 
     /** Convenience method to create a CarryOn from one or more Conditions. */
     public static CarryOn fromConditions(Condition... conditions) {
-        return new CarryOn() {
-            @Override
-            public void declareElements(Elements.Builder elements) {
-                for (Condition condition : conditions) {
-                    elements.declareEnterCondition(condition);
-                }
-            }
-        };
+        CarryOn carryOn = new CarryOn();
+        for (Condition condition : conditions) {
+            carryOn.declareEnterCondition(condition);
+        }
+        return carryOn;
     }
 
     private static class Drop extends Transition {
diff --git a/base/test/android/javatests/src/org/chromium/base/test/transit/ConditionalState.java b/base/test/android/javatests/src/org/chromium/base/test/transit/ConditionalState.java
index 1186c1d4..2a00b5b 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/transit/ConditionalState.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/transit/ConditionalState.java
@@ -77,12 +77,24 @@
      * constructor and/or override this method.
      *
      * @param elements use the #declare___() methods to describe the Elements that define the state.
+     * @deprecated Declare elements in the constructor or in{@link #declareExtraElements()}.
      */
+    @Deprecated
     public void declareElements(Elements.Builder elements) {}
 
+    /**
+     * Declare extra {@link Element}s that define this ConditionalState, such as Views.
+     *
+     * <p>Transit-layer {@link Station}s and {@link Facility}s can declare Elements in their
+     * constructor and/or override this method. This method is called after binding a Facility to a
+     * Station, so some elements are easier to declare here.
+     */
+    public void declareExtraElements() {}
+
     Elements getElements() {
         if (!mDeclareElementsCalled) {
             declareElements(mElements);
+            declareExtraElements();
             mElements.consolidate();
             mDeclareElementsCalled = true;
         }
diff --git a/base/test/android/javatests/src/org/chromium/base/test/transit/Elements.java b/base/test/android/javatests/src/org/chromium/base/test/transit/Elements.java
index a5bac17e..2cec22b 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/transit/Elements.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/transit/Elements.java
@@ -43,8 +43,7 @@
     /**
      * Builder for {@link Elements}.
      *
-     * <p>Passed to {@link ConditionalState#declareElements(Elements.Builder)}}, which must declare
-     * the ConditionalState's elements by calling the declare___() methods.
+     * <p>Passed to |delayedDeclarations| to add elements with declareElementFactory().
      */
     public static class Builder {
         private @Nullable Elements mOwner;
diff --git a/base/test/android/javatests/src/org/chromium/base/test/transit/ScrollableFacility.java b/base/test/android/javatests/src/org/chromium/base/test/transit/ScrollableFacility.java
index 8da47fc..636dc6a 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/transit/ScrollableFacility.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/transit/ScrollableFacility.java
@@ -59,7 +59,7 @@
 
     @CallSuper
     @Override
-    public void declareElements(Elements.Builder elements) {
+    public void declareExtraElements() {
         mItems = new ArrayList<>();
         declareItems(new ItemsBuilder(mItems));
 
@@ -72,13 +72,13 @@
                 switch (item.mPresence) {
                     case Presence.ABSENT:
                         assert item.mViewSpec != null;
-                        elements.declareNoView(item.mViewSpec.getViewMatcher());
+                        declareNoView(item.mViewSpec.getViewMatcher());
                         break;
                     case Presence.PRESENT_AND_ENABLED:
                     case Presence.PRESENT_AND_DISABLED:
                         assert item.mViewSpec != null;
                         assert item.mViewElementOptions != null;
-                        elements.declareView(item.mViewSpec, item.mViewElementOptions);
+                        declareView(item.mViewSpec, item.mViewElementOptions);
                         break;
                     case Presence.MAYBE_PRESENT:
                     case Presence.MAYBE_PRESENT_STUB:
@@ -449,8 +449,8 @@
         }
 
         @Override
-        public void declareElements(Elements.Builder elements) {
-            viewElement = elements.declareView(mItem.getViewSpec(), mItem.getViewElementOptions());
+        public void declareExtraElements() {
+            viewElement = declareView(mItem.getViewSpec(), mItem.getViewElementOptions());
         }
 
         /** Select the item and trigger its |selectHandler|. */
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 708c204..25099af9 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -512,9 +512,10 @@
   # Linux-specific compiler flags setup.
   # ------------------------------------
   if (use_icf && (!is_apple || use_lld)) {
-    if (is_fuchsia) {
+    if (is_fuchsia || is_android) {
       # TODO(crbug.com/415810137): A temporary workaround to avoid crashing
       # blink on fuchsia.
+      # TODO(crbug.com/415842701): blink also crashing on android.
       ldflags += [ "-Wl,--icf=safe" ]
     } else {
       ldflags += [ "-Wl,--icf=all" ]
diff --git a/build/config/locales.gni b/build/config/locales.gni
index e1f23cb53..e9396dc 100644
--- a/build/config/locales.gni
+++ b/build/config/locales.gni
@@ -109,6 +109,13 @@
       "zu",
     ] + pseudolocales
 
+all_chrome_genders = [
+  "OTHER",
+  "MASCULINE",
+  "FEMININE",
+  "NEUTER",
+]
+
 if (is_ios) {
   # Chrome on iOS uses "es-MX" and "pt" for "es-419" and "pt-BR".
   all_chrome_locales -= [
diff --git a/build/config/mac/rules.gni b/build/config/mac/rules.gni
index b4ee8b8..c32a3e6 100644
--- a/build/config/mac/rules.gni
+++ b/build/config/mac/rules.gni
@@ -314,6 +314,7 @@
     visibility = [ ":$_framework_target+link" ]
     framework_dirs = [ root_out_dir ]
     frameworks = [ _framework_name ]
+    inputs = [ "$root_out_dir/$_framework_name" ]
   }
 
   create_bundle(_framework_target) {
diff --git a/build/fuchsia/test/BUILD.gn b/build/fuchsia/test/BUILD.gn
index 333dc48..72e48c0 100644
--- a/build/fuchsia/test/BUILD.gn
+++ b/build/fuchsia/test/BUILD.gn
@@ -18,3 +18,9 @@
     "gs_util_wrapper.py",
   ]
 }
+
+python_library("component_storage_test") {
+  testonly = true
+  pydeps_file = "component_storage_test.pydeps"
+  data_deps = [ "//build/config/fuchsia:deployment_resources" ]
+}
diff --git a/build/fuchsia/test/component_storage_test.py b/build/fuchsia/test/component_storage_test.py
new file mode 100755
index 0000000..346333b23
--- /dev/null
+++ b/build/fuchsia/test/component_storage_test.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env vpython3
+# Copyright 2025 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+""" Runs component_storage tests on a fuchsia emulator. """
+
+import logging
+import os
+import subprocess
+import sys
+
+from test_env_setup import wait_for_env_setup
+
+LOG_DIR = os.environ.get('ISOLATED_OUTDIR', '/tmp')
+
+
+def main() -> int:
+    """Entry of the test."""
+    proc = subprocess.Popen([
+        os.path.join(os.path.dirname(__file__), 'test_env_setup.py'),
+        '--logs-dir', LOG_DIR
+    ])
+    assert wait_for_env_setup(proc, LOG_DIR)
+    logging.warning('test_env_setup.py is running on process %s', proc.pid)
+    proc.terminate()
+    return proc.wait()
+
+
+if __name__ == '__main__':
+    sys.exit(main())
diff --git a/build/fuchsia/test/component_storage_test.pydeps b/build/fuchsia/test/component_storage_test.pydeps
new file mode 100644
index 0000000..c639c259
--- /dev/null
+++ b/build/fuchsia/test/component_storage_test.pydeps
@@ -0,0 +1,35 @@
+# Generated by running:
+#   build/print_python_deps.py --root build/fuchsia/test --output build/fuchsia/test/component_storage_test.pydeps build/fuchsia/test/component_storage_test.py
+../../util/lib/proto/average.py
+../../util/lib/proto/count.py
+../../util/lib/proto/data_points.py
+../../util/lib/proto/measure.py
+../../util/lib/proto/measures.py
+../../util/lib/proto/metric.py
+../../util/lib/proto/test_script_metrics_pb2.py
+../../util/lib/proto/time_consumption.py
+boot_device.py
+browser_runner.py
+common.py
+compatible_utils.py
+component_storage_test.py
+ffx_emulator.py
+ffx_integration.py
+flash_device.py
+isolate_daemon.py
+lockfile.py
+log_manager.py
+modification_waiter.py
+monitors.py
+publish_package.py
+repeating_log.py
+run_blink_test.py
+run_executable_test.py
+run_telemetry_test.py
+run_test.py
+run_webpage_test.py
+serve_repo.py
+start_emulator.py
+test_connection.py
+test_env_setup.py
+test_runner.py
diff --git a/build/fuchsia/test/coveragetest.py b/build/fuchsia/test/coveragetest.py
index 88f6c522..7ee986d 100755
--- a/build/fuchsia/test/coveragetest.py
+++ b/build/fuchsia/test/coveragetest.py
@@ -22,7 +22,7 @@
 TESTED_FILES = [
     'bundled_test_runner.py', 'common.py', 'ffx_emulator.py',
     'modification_waiter.py', 'monitors.py', 'serial_boot_device.py',
-    'test_env_setup.py', 'test_server.py', 'version.py'
+    'test_server.py', 'version.py'
 ]
 
 
diff --git a/build/fuchsia/test/test_env_setup.py b/build/fuchsia/test/test_env_setup.py
index bc46c79..11380b1 100755
--- a/build/fuchsia/test/test_env_setup.py
+++ b/build/fuchsia/test/test_env_setup.py
@@ -21,8 +21,9 @@
 import argparse
 import os
 import sys
+import time
 
-from subprocess import CompletedProcess
+from subprocess import CompletedProcess, Popen
 from typing import List
 
 import run_test
@@ -77,5 +78,18 @@
     return run_test.main()
 
 
+def wait_for_env_setup(proc: Popen, logs_dir: str) -> bool:
+    """ Waits for the test_env_setup.py process to be ready, returns false if
+        the process terminated unexpected. """
+    pid_file = os.path.join(logs_dir,
+                            'test_env_setup.' + str(proc.pid) + '.pid')
+    while not os.path.isfile(pid_file):
+        proc.poll()
+        if proc.returncode:
+            return False
+        time.sleep(1)
+    return True
+
+
 if __name__ == '__main__':
     sys.exit(setup_env())
diff --git a/build/fuchsia/test/test_env_setup_unittests.py b/build/fuchsia/test/test_env_setup_unittests.py
deleted file mode 100755
index 8dff717..0000000
--- a/build/fuchsia/test/test_env_setup_unittests.py
+++ /dev/null
@@ -1,97 +0,0 @@
-#!/usr/bin/env vpython3
-# Copyright 2024 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-"""File for testing test_env_setup.py."""
-
-import os
-import subprocess
-import time
-import unittest
-
-from compatible_utils import running_unattended
-
-# TODO(crbug.com/352409265): Try to run the following tests on trybot.
-
-
-# Test names should be self-explained, no point of adding function docstring.
-# pylint: disable=missing-function-docstring
-
-
-class TestEnvSetupTests(unittest.TestCase):
-    """Test class."""
-
-    @staticmethod
-    def _merge_env(env: dict, env2: dict) -> dict:
-        if env and env2:
-            return {**env, **env2}
-        # Always copy to avoid changing os.environ.
-        if env:
-            return {**env}
-        if env2:
-            return {**env2}
-        return {}
-
-    def _start_test_env(self, env: dict = None) -> subprocess.Popen:
-        proc = subprocess.Popen([
-            os.path.join(os.path.dirname(__file__), 'test_env_setup.py'),
-            '--logs-dir', '/tmp/test_env_setup'
-        ],
-                                env=TestEnvSetupTests._merge_env(
-                                    os.environ, env))
-        while not os.path.isfile('/tmp/test_env_setup/test_env_setup.' +
-                                 str(proc.pid) + '.pid'):
-            proc.poll()
-            self.assertIsNone(proc.returncode)
-            time.sleep(1)
-        return proc
-
-    def _run_without_packages(self, env: dict = None):
-        proc = self._start_test_env(env)
-        proc.terminate()
-        proc.wait()
-
-    def test_run_without_packages(self):
-        if running_unattended():
-            # The test needs sdk and images to run and it's not designed to work
-            # on platforms other than linux.
-            return
-        self._run_without_packages()
-
-    def test_run_without_packages_unattended(self):
-        if running_unattended():
-            return
-        # Do not impact the environment of the current process.
-        self._run_without_packages({'CHROME_HEADLESS': '1'})
-
-    def _run_with_base_tests(self, env: dict = None):
-        env = TestEnvSetupTests._merge_env(
-            {'FFX_ISOLATE_DIR': '/tmp/test_env_setup/daemon'}, env)
-        proc = self._start_test_env(env)
-        try:
-            subprocess.run([
-                os.path.join(os.path.dirname(__file__),
-                             '../../out/fuchsia/bin/run_base_unittests'),
-                '--logs-dir', '/tmp/test_env_setup', '--device'
-            ],
-                           env=TestEnvSetupTests._merge_env(os.environ, env),
-                           check=True,
-                           stdout=subprocess.DEVNULL,
-                           stderr=subprocess.DEVNULL)
-        finally:
-            proc.terminate()
-            proc.wait()
-
-    def test_run_with_base_tests(self):
-        if running_unattended():
-            return
-        self._run_with_base_tests()
-
-    def test_run_with_base_tests_unattended(self):
-        if running_unattended():
-            return
-        self._run_with_base_tests({'CHROME_HEADLESS': '1'})
-
-
-if __name__ == '__main__':
-    unittest.main()
diff --git a/cc/base/features.cc b/cc/base/features.cc
index fb35620..188d451 100644
--- a/cc/base/features.cc
+++ b/cc/base/features.cc
@@ -121,10 +121,6 @@
 const base::FeatureParam<double> kWaitForLateScrollEventsDeadlineRatio{
     &kWaitForLateScrollEvents, "deadline_ratio", 0.333};
 
-BASE_FEATURE(kNonBatchedCopySharedImage,
-             "NonBatchedCopySharedImage",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 BASE_FEATURE(kDontAlwaysPushPictureLayerImpls,
              "DontAlwaysPushPictureLayerImpls",
              base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/cc/base/features.h b/cc/base/features.h
index 4e96f70..6a3f3f6 100644
--- a/cc/base/features.h
+++ b/cc/base/features.h
@@ -83,10 +83,6 @@
 // to when tracing is enabled.
 CC_BASE_EXPORT BASE_DECLARE_FEATURE(kMetricsTracingCalculationReduction);
 
-// When enabled we will submit the 'CopySharedImage' in one call and not batch
-// it up into 4MiB increments.
-CC_BASE_EXPORT BASE_DECLARE_FEATURE(kNonBatchedCopySharedImage);
-
 // Currently there is a race between OnBeginFrames from the GPU process and
 // input arriving from the Browser process. Due to this we can start to produce
 // a frame while scrolling without any input events. Late arriving events are
diff --git a/cc/layers/layer.h b/cc/layers/layer.h
index bd49110..e6de243 100644
--- a/cc/layers/layer.h
+++ b/cc/layers/layer.h
@@ -663,19 +663,6 @@
   void SetHasClipNode(bool val) { SetBitFlag(val, kHasClipNodeFlagMask); }
   bool has_clip_node() const { return GetBitFlag(kHasClipNodeFlagMask); }
 
-  // Sets that the content shown in this layer may be a video. This may be used
-  // by the system compositor to distinguish between animations updating the
-  // screen and video, which the user would be watching. This allows
-  // optimizations like turning off the display when video is not playing,
-  // without interfering with video playback.
-  void SetMayContainVideo(bool value) {
-    SetBitFlag(value, kMayContainVideoFlagMask, /*invalidate=*/false,
-               /*needs_push=*/true);
-  }
-  bool may_contain_video() const {
-    return GetBitFlag(kMayContainVideoFlagMask);
-  }
-
   // Stable identifier for clients. See comment in cc/paint/element_id.h.
   void SetElementId(ElementId id);
   ElementId element_id() const { return inputs_.Read(*this).element_id; }
@@ -921,6 +908,19 @@
   // generated or committed.
   bool IsPropertyChangeAllowed() const;
 
+  // Sets that the content shown in this layer may be a video. This may be used
+  // by the system compositor to distinguish between animations updating the
+  // screen and video, which the user would be watching. This allows
+  // optimizations like turning off the display when video is not playing,
+  // without interfering with video playback.
+  void SetMayContainVideo(bool value) {
+    SetBitFlag(value, kMayContainVideoFlagMask, /*invalidate=*/false,
+               /*needs_push=*/true);
+  }
+  bool may_contain_video() const {
+    return GetBitFlag(kMayContainVideoFlagMask);
+  }
+
   void IncreasePaintCount() {
     if (debug_info_.Read(*this))
       ++debug_info_.Write(*this)->paint_count;
diff --git a/cc/layers/surface_layer.cc b/cc/layers/surface_layer.cc
index cd2b306..779c014e 100644
--- a/cc/layers/surface_layer.cc
+++ b/cc/layers/surface_layer.cc
@@ -25,8 +25,7 @@
 }
 
 SurfaceLayer::SurfaceLayer()
-    : may_contain_video_(false),
-      deadline_in_frames_(0u),
+    : deadline_in_frames_(0u),
       stretch_content_to_fill_bounds_(false),
       surface_hit_testable_(false),
       has_pointer_events_none_(false),
@@ -37,7 +36,6 @@
     UpdateSubmissionStateCB update_submission_state_callback)
     : update_submission_state_callback_(
           std::move(update_submission_state_callback)),
-      may_contain_video_(false),
       deadline_in_frames_(0u),
       stretch_content_to_fill_bounds_(false),
       surface_hit_testable_(false),
@@ -140,11 +138,6 @@
   override_child_paint_flags_.Write(*this) = true;
 }
 
-void SurfaceLayer::SetMayContainVideo(bool may_contain_video) {
-  may_contain_video_.Write(*this) = may_contain_video;
-  SetNeedsCommit();
-}
-
 std::unique_ptr<LayerImpl> SurfaceLayer::CreateLayerImpl(
     LayerTreeImpl* tree_impl) const {
   auto layer_impl = SurfaceLayerImpl::Create(
diff --git a/cc/layers/surface_layer.h b/cc/layers/surface_layer.h
index b19abae..c678142 100644
--- a/cc/layers/surface_layer.h
+++ b/cc/layers/surface_layer.h
@@ -61,7 +61,7 @@
 
   void SetOverrideChildPaintFlags(bool override_child_paint_flags);
 
-  void SetMayContainVideo(bool may_contain_video);
+  using Layer::SetMayContainVideo;
 
   // Layer overrides.
   std::unique_ptr<LayerImpl> CreateLayerImpl(
@@ -98,7 +98,6 @@
   ProtectedSequenceWritable<UpdateSubmissionStateCB>
       update_submission_state_callback_;
 
-  ProtectedSequenceReadable<bool> may_contain_video_;
   ProtectedSequenceReadable<viz::SurfaceRange> surface_range_;
   ProtectedSequenceWritable<std::optional<uint32_t>> deadline_in_frames_;
 
diff --git a/cc/raster/one_copy_raster_buffer_provider.cc b/cc/raster/one_copy_raster_buffer_provider.cc
index 71b418c..0d747f6 100644
--- a/cc/raster/one_copy_raster_buffer_provider.cc
+++ b/cc/raster/one_copy_raster_buffer_provider.cc
@@ -138,7 +138,6 @@
                          max_copy_texture_chromium_size)
               : kMaxBytesPerCopyOperation),
       use_partial_raster_(use_partial_raster),
-      bytes_scheduled_since_last_flush_(0),
       tile_overlay_candidate_(is_overlay_candidate),
       staging_pool_(std::move(task_runner),
                     worker_context_provider,
@@ -397,39 +396,9 @@
     }
   }
 
-  if (base::FeatureList::IsEnabled(features::kNonBatchedCopySharedImage)) {
-    ri->CopySharedImage(staging_buffer->client_shared_image->mailbox(),
-                        backing->shared_image()->mailbox(), 0, 0, 0, 0,
-                        rect_to_copy.width(), rect_to_copy.height());
-  } else {
-    int bytes_per_row = viz::ResourceSizes::UncheckedWidthInBytes<int>(
-        rect_to_copy.width(), staging_buffer->format);
-    int chunk_size_in_rows =
-        std::max(1, max_bytes_per_copy_operation_ / bytes_per_row);
-    // Align chunk size to 4. Required to support compressed texture formats.
-    chunk_size_in_rows = MathUtil::UncheckedRoundUp(chunk_size_in_rows, 4);
-    int y = 0;
-    int height = rect_to_copy.height();
-    while (y < height) {
-      // Copy at most |chunk_size_in_rows|.
-      int rows_to_copy = std::min(chunk_size_in_rows, height - y);
-      DCHECK_GT(rows_to_copy, 0);
-
-      ri->CopySharedImage(staging_buffer->client_shared_image->mailbox(),
-                          backing->shared_image()->mailbox(), 0, y, 0, y,
-                          rect_to_copy.width(), rows_to_copy);
-      y += rows_to_copy;
-
-      // Increment |bytes_scheduled_since_last_flush_| by the amount of memory
-      // used for this copy operation.
-      bytes_scheduled_since_last_flush_ += rows_to_copy * bytes_per_row;
-
-      if (bytes_scheduled_since_last_flush_ >= max_bytes_per_copy_operation_) {
-        ri->ShallowFlushCHROMIUM();
-        bytes_scheduled_since_last_flush_ = 0;
-      }
-    }
-  }
+  ri->CopySharedImage(staging_buffer->client_shared_image->mailbox(),
+                      backing->shared_image()->mailbox(), 0, 0, 0, 0,
+                      rect_to_copy.width(), rect_to_copy.height());
 
   if (query_target != GL_NONE)
     ri->EndQueryEXT(query_target);
diff --git a/cc/raster/one_copy_raster_buffer_provider.h b/cc/raster/one_copy_raster_buffer_provider.h
index 057728e6..e32730d 100644
--- a/cc/raster/one_copy_raster_buffer_provider.h
+++ b/cc/raster/one_copy_raster_buffer_provider.h
@@ -139,10 +139,6 @@
   const raw_ptr<viz::RasterContextProvider> worker_context_provider_;
   const int max_bytes_per_copy_operation_;
   const bool use_partial_raster_;
-
-  // Context lock must be acquired when accessing this member.
-  int bytes_scheduled_since_last_flush_;
-
   const bool tile_overlay_candidate_;
 
   StagingBufferPool staging_pool_;
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index ce68883..9d076c8 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -1823,9 +1823,18 @@
       renaming_destinations = []
 
       foreach(_locale, platform_pak_locales) {
-        renaming_sources +=
-            [ "$target_gen_dir/${_variant}_paks/locales/$_locale.pak" ]
-        renaming_destinations += [ "locales/$_locale.pak" ]
+        if (translate_genders) {
+          renaming_sources += process_file_template(
+                  all_chrome_genders,
+                  "$target_gen_dir/${_variant}_paks/locales/${_locale}_{{source_name_part}}.pak")
+          renaming_destinations += process_file_template(
+                  all_chrome_genders,
+                  "locales/${_locale}_{{source_name_part}}.pak")
+        } else {
+          renaming_sources +=
+              [ "$target_gen_dir/${_variant}_paks/locales/${_locale}.pak" ]
+          renaming_destinations += [ "locales/${_locale}.pak" ]
+        }
       }
       treat_as_locale_paks = true
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinator.java
index 7c8cbc07..ab08bd9 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinator.java
@@ -18,6 +18,7 @@
 import android.text.style.ForegroundColorSpan;
 import android.util.Size;
 import android.view.LayoutInflater;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
@@ -183,8 +184,8 @@
      * dialog when no archived tabs remain.
      */
     private final Callback<Integer> mTabCountObserver =
-            (count) -> {
-                if (count == 0 && !ArchivedTabsDialogCoordinator.this.mIsOpeningLastTab) {
+            (tabCount) -> {
+                if (tabCount == 0 && !ArchivedTabsDialogCoordinator.this.mIsOpeningLastItem) {
                     // Post task to allow the last tab to be unregistered.
                     PostTask.postTask(
                             TaskTraits.UI_DEFAULT,
@@ -195,7 +196,7 @@
             };
 
     /** Used to override the default tab click behavior to restore/open the tab. */
-    private final GridCardOnClickListenerProvider mGridCardOnCLickListenerProvider =
+    private final GridCardOnClickListenerProvider mGridCardOnClickListenerProvider =
             new GridCardOnClickListenerProvider() {
                 @Nullable
                 @Override
@@ -208,12 +209,20 @@
                 public TabActionListener openTabGridDialog(@NonNull String syncId) {
                     return new TabActionListener() {
                         @Override
-                        public void run(View view, int tabId) {
+                        public void run(
+                                View view, int tabId, @Nullable MotionEvent triggeringMotionEvent) {
                             // Intentional no-op.
                         }
 
                         @Override
-                        public void run(View view, String syncId) {
+                        public void run(
+                                View view,
+                                String syncId,
+                                @Nullable MotionEvent triggeringMotionEvent) {
+                            SavedTabGroup savedTabGroup = mTabGroupSyncService.getGroup(syncId);
+                            mIsOpeningLastItem =
+                                    getArchivedTabCount() == savedTabGroup.savedTabs.size();
+
                             TabSwitcherPaneBase tabSwitcherPaneBase =
                                     (TabSwitcherPaneBase)
                                             mPaneManagerSupplier
@@ -242,7 +251,7 @@
 
                 @Override
                 public void onTabSelecting(int tabId, boolean fromActionButton) {
-                    mIsOpeningLastTab = mArchivedTabModel.getCount() == 1;
+                    mIsOpeningLastItem = getArchivedTabCount() == 1;
                     Tab tab = mArchivedTabModel.getTabById(tabId);
                     mArchivedTabModelOrchestrator
                             .getTabArchiver()
@@ -352,7 +361,7 @@
     private OnTabSelectingListener mOnTabSelectingListener;
     private PropertyModel mIphMessagePropertyModel;
     private int mSnackbarOverrideToken;
-    private boolean mIsOpeningLastTab;
+    private boolean mIsOpeningLastItem;
     private boolean mIsShowing;
 
     /**
@@ -506,7 +515,7 @@
         }
 
         mOnTabSelectingListener = onTabSelectingListener;
-        mArchivedTabModel.getTabCountSupplier().addObserver(mTabCountObserver);
+        mArchivedTabModelOrchestrator.getTabCountSupplier().addObserver(mTabCountObserver);
         mUndoBarController.initialize();
 
         TabListEditorController controller = mTabListEditorCoordinator.getController();
@@ -641,7 +650,7 @@
         controller.setLifecycleObserver(null);
         mBackPressManager.removeHandler(mTabListEditorCoordinator.getController());
         mTabArchiveSettings.removeObserver(mTabArchiveSettingsObserver);
-        mArchivedTabModel.getTabCountSupplier().removeObserver(mTabCountObserver);
+        mArchivedTabModelOrchestrator.getTabCountSupplier().removeObserver(mTabCountObserver);
         mSnackbarOverrideToken = TokenHolder.INVALID_TOKEN;
         mIsShowing = false;
         mTabSwitcherRecyclerView.get().setBlockTouchInput(false);
@@ -668,7 +677,7 @@
 
     @VisibleForTesting
     void updateTitle() {
-        int numInactiveTabs = mArchivedTabModel.getCount();
+        int numInactiveTabs = getArchivedTabCount();
         String title =
                 mActivity
                         .getResources()
@@ -697,7 +706,7 @@
                         mSnackbarManager,
                         /* bottomSheetController= */ null,
                         TabProperties.TabActionState.CLOSABLE,
-                        mGridCardOnCLickListenerProvider,
+                        mGridCardOnClickListenerProvider,
                         mModalDialogManager,
                         mDesktopWindowStateManager,
                         /* edgeToEdgeSupplier= */ null,
@@ -758,6 +767,10 @@
                 });
     }
 
+    private int getArchivedTabCount() {
+        return mArchivedTabModelOrchestrator.getTabCountSupplier().get();
+    }
+
     private void restoreArchivedTabs(List<Tab> tabs) {
         mArchivedTabModelOrchestrator
                 .getTabArchiver()
@@ -865,7 +878,7 @@
     }
 
     GridCardOnClickListenerProvider getGridCardOnClickListenerProviderForTesting() {
-        return mGridCardOnCLickListenerProvider;
+        return mGridCardOnClickListenerProvider;
     }
 
     /** Returns the Edge to edge pad adjuster. */
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsMessageService.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsMessageService.java
index 1e9ad786..cc066e9 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsMessageService.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsMessageService.java
@@ -12,6 +12,7 @@
 import static org.chromium.chrome.browser.tasks.tab_management.ArchivedTabsCardViewProperties.WIDTH;
 
 import android.app.Activity;
+import android.graphics.drawable.GradientDrawable;
 import android.util.Size;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -45,6 +46,7 @@
 import org.chromium.chrome.browser.tasks.tab_management.TabListCoordinator.TabListMode;
 import org.chromium.chrome.browser.tasks.tab_management.TabListModel.CardProperties.ModelType;
 import org.chromium.chrome.browser.tasks.tab_management.TabSwitcherMessageManager.MessageUpdateObserver;
+import org.chromium.chrome.browser.theme.SurfaceColorUpdateUtils;
 import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeController;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
 import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateManager;
@@ -107,6 +109,11 @@
                                 });
                     }
                     mEndIconView = mCustomCardView.findViewById(R.id.end_image);
+                    GradientDrawable cardViewBg =
+                            (GradientDrawable)
+                                    mCustomCardView.findViewById(R.id.card).getBackground();
+                    cardViewBg.setColor(
+                            SurfaceColorUpdateUtils.getMessageCardBackgroundColor(mActivity));
                     PropertyModelChangeProcessor.create(
                             mCustomCardModel, mCustomCardView, ArchivedTabsCardViewBinder::bind);
                 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MessageCardView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MessageCardView.java
index 545d19b..df18aed 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MessageCardView.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MessageCardView.java
@@ -15,8 +15,8 @@
 import android.widget.LinearLayout;
 
 import org.chromium.base.Callback;
+import org.chromium.chrome.browser.theme.SurfaceColorUpdateUtils;
 import org.chromium.chrome.tab_ui.R;
-import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.ui.widget.ButtonCompat;
 import org.chromium.ui.widget.ChromeImageView;
 import org.chromium.ui.widget.TextViewWithLeading;
@@ -171,7 +171,8 @@
         }
         // Set dynamic color.
         GradientDrawable gradientDrawable = (GradientDrawable) getBackground();
-        gradientDrawable.setColor(SemanticColorUtils.getCardBackgroundColor(getContext()));
+        gradientDrawable.setColor(
+                SurfaceColorUpdateUtils.getMessageCardBackgroundColor(getContext()));
     }
 
     /**
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
index c85db0f..86328b2 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
@@ -1088,6 +1088,7 @@
             TabUiUtils.closeTabGroup(
                     mCurrentTabGroupModelFilterSupplier.get(),
                     tabId,
+                    /* allowUndo= */ true,
                     hideTabGroups,
                     /* didCloseCallback= */ null);
         } else if (menuId == R.id.delete_shared_group) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallback.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallback.java
index 273ef77..461251e 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallback.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallback.java
@@ -270,7 +270,9 @@
 
         if (simpleViewHolder.model.get(CARD_TYPE) == TAB) {
             mTabClosedListener.run(
-                    viewHolder.itemView, simpleViewHolder.model.get(TabProperties.TAB_ID));
+                    viewHolder.itemView,
+                    simpleViewHolder.model.get(TabProperties.TAB_ID),
+                    /* triggeringMotionEvent= */ null);
 
             RecordUserAction.record("MobileStackViewSwipeCloseTab." + mComponentName);
         } else if (simpleViewHolder.model.get(CARD_TYPE) == MESSAGE) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
index 61bbfd6..fcdb81dd 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
@@ -6,12 +6,16 @@
 
 import static org.chromium.chrome.browser.tasks.tab_management.TabListModel.CardProperties.CARD_ALPHA;
 
+import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.util.Size;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
 import android.view.View;
+import android.view.View.OnTouchListener;
 import android.view.ViewGroup;
 import android.view.ViewStub;
 import android.widget.FrameLayout;
@@ -21,6 +25,7 @@
 import androidx.annotation.ColorInt;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
 import androidx.core.graphics.drawable.DrawableCompat;
 import androidx.core.view.ViewCompat;
 import androidx.core.widget.ImageViewCompat;
@@ -41,6 +46,7 @@
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.util.MotionEventUtils;
 import org.chromium.ui.widget.ChromeImageView;
 import org.chromium.ui.widget.ViewLookupCachingFrameLayout;
 
@@ -194,6 +200,8 @@
             TabActionListener tabActionListener = data == null ? null : data.tabActionListener;
             setNullableClickListener(
                     tabActionListener, view.fastFindViewById(R.id.action_button), model);
+            setNullablePeripheralClickListener(
+                    tabActionListener, view.fastFindViewById(R.id.action_button), model);
 
             boolean showOverflowButton =
                     data == null ? false : data.type == TabActionButtonType.OVERFLOW;
@@ -288,16 +296,44 @@
             view.setOnClickListener(null);
         } else {
             view.setOnClickListener(
-                    v -> {
-                        if (propertyModel.containsKey(TabProperties.TAB_GROUP_SYNC_ID)) {
-                            listener.run(v, propertyModel.get(TabProperties.TAB_GROUP_SYNC_ID));
-                        } else {
-                            listener.run(v, propertyModel.get(TabProperties.TAB_ID));
-                        }
-                    });
+                    v ->
+                            runTabActionListener(
+                                    listener, v, propertyModel, /* triggeringMotionEvent= */ null));
         }
     }
 
+    /**
+     * Sets an {@link OnPeripheralClickListener} on the given view to intercept clicks from
+     * peripherals.
+     *
+     * <p>Note that this method cannot replace {@link #setNullableClickListener(TabActionListener,
+     * View, PropertyModel)} as an {@link android.view.View.OnClickListener} is needed to handle
+     * clicks not from peripherals, accessibility actions, and the "confirm" key event.
+     *
+     * @param tabActionListener the {@link TabActionListener} to run when a click is detected.
+     * @param view the View to receive clicks.
+     * @param propertyModel contains data to determine how to run the {@link TabActionListener}.
+     */
+    static void setNullablePeripheralClickListener(
+            @Nullable TabActionListener tabActionListener,
+            @NonNull View view,
+            @NonNull PropertyModel propertyModel) {
+        if (tabActionListener == null) {
+            view.setOnTouchListener(null);
+            return;
+        }
+
+        view.setOnTouchListener(
+                new OnPeripheralClickListener(
+                        view,
+                        triggeringMotionEvent ->
+                                runTabActionListener(
+                                        tabActionListener,
+                                        view,
+                                        propertyModel,
+                                        triggeringMotionEvent)));
+    }
+
     static void setNullableLongClickListener(
             @Nullable TabActionListener listener,
             @NonNull View view,
@@ -307,16 +343,29 @@
         } else {
             view.setOnLongClickListener(
                     v -> {
-                        if (propertyModel.containsKey(TabProperties.TAB_GROUP_SYNC_ID)) {
-                            listener.run(v, propertyModel.get(TabProperties.TAB_GROUP_SYNC_ID));
-                        } else {
-                            listener.run(v, propertyModel.get(TabProperties.TAB_ID));
-                        }
+                        runTabActionListener(
+                                listener, v, propertyModel, /* triggeringMotionEvent= */ null);
                         return true;
                     });
         }
     }
 
+    private static void runTabActionListener(
+            @NonNull TabActionListener tabActionListener,
+            @NonNull View view,
+            @NonNull PropertyModel propertyModel,
+            @Nullable MotionEvent triggeringMotionEvent) {
+        if (propertyModel.containsKey(TabProperties.TAB_GROUP_SYNC_ID)) {
+            tabActionListener.run(
+                    view,
+                    propertyModel.get(TabProperties.TAB_GROUP_SYNC_ID),
+                    triggeringMotionEvent);
+        } else {
+            tabActionListener.run(
+                    view, propertyModel.get(TabProperties.TAB_ID), triggeringMotionEvent);
+        }
+    }
+
     private static void fetchPriceDrop(
             PropertyModel model,
             Callback<ShoppingPersistedTabData.PriceDrop> callback,
@@ -540,4 +589,55 @@
         sThumbnailFetcherForTesting = fetcher;
         ResettersForTesting.register(() -> sThumbnailFetcherForTesting = null);
     }
+
+    /**
+     * An {@link OnTouchListener} to detect a click from peripherals.
+     *
+     * <p>If a {@link MotionEvent} comes from a peripheral, this listener will consume it. Then, if
+     * the event completes a click action, the {@link OnPeripheralClickRunnable} will be run.
+     *
+     * <p>If a {@link MotionEvent} doesn't come from a peripheral, this listener is a no-op. It
+     * won't consume or interpret the event.
+     */
+    @VisibleForTesting
+    static final class OnPeripheralClickListener implements OnTouchListener {
+
+        @NonNull private final GestureDetector mGestureDetector;
+
+        OnPeripheralClickListener(
+                @NonNull View view, @NonNull OnPeripheralClickRunnable onPeripheralClickRunnable) {
+            mGestureDetector =
+                    new GestureDetector(
+                            view.getContext(),
+                            new GestureDetector.SimpleOnGestureListener() {
+
+                                @Override
+                                public boolean onSingleTapUp(@NonNull MotionEvent e) {
+                                    onPeripheralClickRunnable.run(e);
+                                    return true;
+                                }
+                            });
+        }
+
+        @SuppressLint("ClickableViewAccessibility")
+        @Override
+        public boolean onTouch(View v, MotionEvent event) {
+            if (!MotionEventUtils.isMouseEvent(event)) {
+                return false;
+            }
+
+            mGestureDetector.onTouchEvent(event);
+            return true;
+        }
+
+        interface OnPeripheralClickRunnable {
+
+            /**
+             * Called when a peripheral click is detected.
+             *
+             * @param triggeringMotionEvent {@link MotionEvent} that triggered the click.
+             */
+            void run(@NonNull MotionEvent triggeringMotionEvent);
+        }
+    }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListGroupMenuCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListGroupMenuCoordinator.java
index 970da51..12d0c4ae 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListGroupMenuCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListGroupMenuCoordinator.java
@@ -7,6 +7,7 @@
 import android.app.Activity;
 import android.content.Context;
 import android.content.res.Resources;
+import android.view.MotionEvent;
 import android.view.View;
 
 import androidx.annotation.NonNull;
@@ -66,7 +67,7 @@
     TabListMediator.TabActionListener getTabActionListener() {
         return new TabListMediator.TabActionListener() {
             @Override
-            public void run(View view, int tabId) {
+            public void run(View view, int tabId, @Nullable MotionEvent triggeringMotionEvent) {
                 @Nullable TabModel tabModel = getTabModel();
                 if (tabModel == null) return;
 
@@ -86,7 +87,7 @@
             }
 
             @Override
-            public void run(View view, String syncId) {
+            public void run(View view, String syncId, @Nullable MotionEvent triggeringMotionEvent) {
                 // Intentional no-op.
             }
         };
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListGroupMenuCoordinatorUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListGroupMenuCoordinatorUnitTest.java
index 427c373..b4fcd8e 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListGroupMenuCoordinatorUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListGroupMenuCoordinatorUnitTest.java
@@ -246,7 +246,9 @@
         when(mCollaborationService.getCurrentUserRoleForGroup(COLLABORATION_ID1))
                 .thenReturn(MemberRole.MEMBER);
 
-        mMenuCoordinator.getTabActionListener().run(mView, TAB_ID);
+        mMenuCoordinator
+                .getTabActionListener()
+                .run(mView, TAB_ID, /* triggeringMotionEvent= */ null);
 
         verify(mMenuCoordinator).buildMenuActionItems(any(), eq(TAB_GROUP_TOKEN));
         verify(mMenuCoordinator)
@@ -269,7 +271,9 @@
         when(mCollaborationService.getCurrentUserRoleForGroup(COLLABORATION_ID1))
                 .thenReturn(MemberRole.OWNER);
 
-        mMenuCoordinator.getTabActionListener().run(mView, TAB_ID);
+        mMenuCoordinator
+                .getTabActionListener()
+                .run(mView, TAB_ID, /* triggeringMotionEvent= */ null);
 
         verify(mMenuCoordinator).buildMenuActionItems(any(), eq(TAB_GROUP_TOKEN));
         verify(mMenuCoordinator)
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
index 695e571..6ec95983 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
@@ -28,6 +28,7 @@
 import android.text.TextUtils;
 import android.util.Pair;
 import android.util.Size;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
@@ -114,6 +115,7 @@
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
 import org.chromium.ui.modelutil.SimpleRecyclerViewAdapter;
+import org.chromium.ui.util.MotionEventUtils;
 import org.chromium.ui.util.XrUtils;
 import org.chromium.url.GURL;
 
@@ -185,13 +187,27 @@
         boolean isReorderAction(int action);
     }
 
-    /** Interface for implementing a {@link Runnable} that takes a tabId for a generic action. */
+    /** Interface for implementing a generic tab action. */
     public interface TabActionListener {
-        /** Run the action for the given view and tabId. */
-        void run(View view, int tabId);
+        /**
+         * Runs the action for the given {@code view} and {@code tabId}.
+         *
+         * @param view {@link View} for the tab.
+         * @param tabId ID of the tab.
+         * @param triggeringMotionEvent {@link MotionEvent} that triggered the action; it will be
+         *     {@code null} if the action is not a result of direct interpretation of {@link
+         *     MotionEvent}s. For example, this parameter will be {@code null} if the action is run
+         *     by a {@link android.view.View.OnClickListener} where {@link MotionEvent} is not
+         *     available.
+         */
+        void run(View view, int tabId, @Nullable MotionEvent triggeringMotionEvent);
 
-        /** Run the action for the given view and tab group sync id. */
-        void run(View view, String syncId);
+        /**
+         * Runs the action for the given {@code view} and tab group {@code syncId}.
+         *
+         * @see #run(View, int, MotionEvent)
+         */
+        void run(View view, String syncId, @Nullable MotionEvent triggeringMotionEvent);
     }
 
     /**
@@ -392,7 +408,7 @@
     private final TabActionListener mTabSelectedListener =
             new TabActionListener() {
                 @Override
-                public void run(View view, int tabId) {
+                public void run(View view, int tabId, @Nullable MotionEvent triggeringMotionEvent) {
                     if (mModelList.indexFromTabId(tabId) == TabModel.INVALID_TAB_INDEX) return;
 
                     mNextTabId = tabId;
@@ -429,7 +445,8 @@
                 }
 
                 @Override
-                public void run(View view, String syncId) {
+                public void run(
+                        View view, String syncId, @Nullable MotionEvent triggeringMotionEvent) {
                     // Intentional no-op.
                 }
 
@@ -462,7 +479,7 @@
     private final TabActionListener mSelectableTabOnClickListener =
             new TabActionListener() {
                 @Override
-                public void run(View view, int tabId) {
+                public void run(View view, int tabId, @Nullable MotionEvent triggeringMotionEvent) {
                     SelectionDelegate<TabListEditorItemSelectionId> selectionDelegate =
                             getTabSelectionDelegate();
                     assert selectionDelegate != null;
@@ -490,7 +507,8 @@
                 }
 
                 @Override
-                public void run(View view, String syncId) {
+                public void run(
+                        View view, String syncId, @Nullable MotionEvent triggeringMotionEvent) {
                     SelectionDelegate<TabListEditorItemSelectionId> selectionDelegate =
                             getTabSelectionDelegate();
                     assert selectionDelegate != null;
@@ -1153,7 +1171,8 @@
         mTabClosedListener =
                 new TabActionListener() {
                     @Override
-                    public void run(View view, int tabId) {
+                    public void run(
+                            View view, int tabId, @Nullable MotionEvent triggeringMotionEvent) {
                         // TODO(crbug.com/40638921): Consider disabling all touch events during
                         // animation.
                         if (mModelList.indexFromTabId(tabId) == TabModel.INVALID_TAB_INDEX) return;
@@ -1166,9 +1185,13 @@
                         setUseShrinkCloseAnimation(tabId, /* useShrinkCloseAnimation= */ true);
                         if (mActionsOnAllRelatedTabs && filter.isTabInTabGroup(closingTab)) {
                             onGroupClosedFrom(tabId);
+
+                            // TODO(crbug.com/375468032): use "triggeringMotionEvent" to determine
+                            //  if the "undo" snackbar should be shown when closing a tab group.
                             TabUiUtils.closeTabGroup(
                                     mCurrentTabGroupModelFilterSupplier.get(),
                                     tabId,
+                                    /* allowUndo= */ true,
                                     /* hideTabGroups= */ true,
                                     getOnMaybeTabClosedCallback(tabId));
                             return;
@@ -1177,10 +1200,13 @@
                         onTabClosedFrom(tabId, mComponentName);
                         Tab currentTab = TabModelUtils.getCurrentTab(tabModel);
                         Tab nextTab = currentTab == closingTab ? getNextTab(tabId) : null;
+                        boolean allowUndo =
+                                triggeringMotionEvent == null
+                                        || !MotionEventUtils.isMouseEvent(triggeringMotionEvent);
                         TabClosureParams closureParams =
                                 TabClosureParams.closeTab(closingTab)
                                         .recommendedNextTab(nextTab)
-                                        .allowUndo(true)
+                                        .allowUndo(allowUndo)
                                         .build();
 
                         @Nullable
@@ -1192,7 +1218,8 @@
                     }
 
                     @Override
-                    public void run(View view, String syncId) {
+                    public void run(
+                            View view, String syncId, @Nullable MotionEvent triggeringMotionEvent) {
                         int index = mModelList.indexFromSyncId(syncId);
                         if (index == TabModel.INVALID_TAB_INDEX) return;
 
@@ -1244,7 +1271,8 @@
         TabActionListener swipeSafeTabActionListener =
                 new TabActionListener() {
                     @Override
-                    public void run(View view, int tabId) {
+                    public void run(
+                            View view, int tabId, @Nullable MotionEvent triggeringMotionEvent) {
                         // The DefaultItemAnimator is prone to crashing in combination with the
                         // swipe animation when closing the last tab. Avoid this issue by disabling
                         // the default item animation for the duration of the removal of the last
@@ -1260,7 +1288,7 @@
                             mRecyclerViewItemAnimationToggle.setDisableItemAnimations(true);
                         }
 
-                        mTabClosedListener.run(view, tabId);
+                        mTabClosedListener.run(view, tabId, /* triggeringMotionEvent= */ null);
 
                         // It is necessary to post the restoration as otherwise any animation
                         // triggered by removing the tab will still use the animator as they are
@@ -1276,7 +1304,8 @@
                     }
 
                     @Override
-                    public void run(View view, String syncId) {
+                    public void run(
+                            View view, String syncId, @Nullable MotionEvent triggeringMotionEvent) {
                         // Swipe is disabled in the {@link ArchivedTabsDialogCoordinator}
                         // implementation of the TabListMediator. Intentional no-op.
                     }
@@ -1454,8 +1483,14 @@
                 }
             }
         }
+
+        // The current implementation of TAB_GROUP card types places all groups at the beginning of
+        // the model list. As a result, if any tab group cards exist, adjust the index for tab
+        // insertion to start after the allotted count of tab groups in the model list.
+        tabIndex += mModelList.getTabGroupCardCount();
+
         // Get the position of the nth tab card ignoring any other CARD_TYPE entries present in the
-        // model list.
+        // model list outside of TAB and TAB_GROUP.
         return mModelList.indexOfNthTabCard(tabIndex);
     }
 
@@ -2796,7 +2831,11 @@
             setUseShrinkCloseAnimation(tabId, /* useShrinkCloseAnimation= */ true);
             onGroupClosedFrom(tabId);
             TabUiUtils.closeTabGroup(
-                    filter, tabId, hideTabGroups, getOnMaybeTabClosedCallback(tabId));
+                    filter,
+                    tabId,
+                    /* allowUndo= */ true,
+                    hideTabGroups,
+                    getOnMaybeTabClosedCallback(tabId));
         } else if (menuId == R.id.edit_group_name) {
             RecordUserAction.record("TabGroupItemMenu.Rename");
             renameTabGroup(tabId);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListModel.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListModel.java
index 17d21d2..a5421ca 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListModel.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListModel.java
@@ -158,7 +158,7 @@
         int lastTabIndex = TabModel.INVALID_TAB_INDEX;
         for (int i = 0; i < size(); i++) {
             PropertyModel model = get(i).model;
-            if (model.get(CARD_TYPE) == TAB) {
+            if (model.get(CARD_TYPE) == TAB || model.get(CARD_TYPE) == TAB_GROUP) {
                 if (tabCount++ == n) return i;
                 lastTabIndex = i;
             }
@@ -177,6 +177,18 @@
         return getTabCardCountsBefore(index);
     }
 
+    /** Get the number of TAB_GROUP cards in the TabListModel. */
+    public int getTabGroupCardCount() {
+        int tabGroupCardCount = 0;
+        for (int i = 0; i < size(); i++) {
+            PropertyModel model = get(i).model;
+            if (model.get(CARD_TYPE) == TAB_GROUP) {
+                tabGroupCardCount++;
+            }
+        }
+        return tabGroupCardCount;
+    }
+
     /**
      * Get the number of TAB cards before the given index in TabListModel.
      *
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewBinder.java
index 98ed678..2173dfa 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewBinder.java
@@ -63,13 +63,19 @@
                             TabActionButtonData data =
                                     model.get(TabProperties.TAB_ACTION_BUTTON_DATA);
                             assert data.type != TabActionButtonData.TabActionButtonType.OVERFLOW;
-                            data.tabActionListener.run(v, model.get(TabProperties.TAB_ID));
+                            data.tabActionListener.run(
+                                    v,
+                                    model.get(TabProperties.TAB_ID),
+                                    /* triggeringMotionEvent= */ null);
                         });
             } else {
                 button.setOnClickListener(
                         v -> {
                             model.get(TabProperties.TAB_CLICK_LISTENER)
-                                    .run(v, model.get(TabProperties.TAB_ID));
+                                    .run(
+                                            v,
+                                            model.get(TabProperties.TAB_ID),
+                                            /* triggeringMotionEvent= */ null);
                         });
             }
             setContentDescription(view, model);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneMediator.java
index 38c1b60..91e0eac 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneMediator.java
@@ -8,6 +8,7 @@
 import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.FOCUS_TAB_INDEX_FOR_ACCESSIBILITY;
 import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.INITIAL_SCROLL_INDEX;
 
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -50,13 +51,14 @@
     private final TabActionListener mTabGridDialogOpener =
             new TabActionListener() {
                 @Override
-                public void run(View view, int tabId) {
+                public void run(View view, int tabId, @Nullable MotionEvent triggeringMotionEvent) {
                     openTabGroupDialog(tabId);
                     RecordUserAction.record("TabGridDialog.ExpandedFromSwitcher");
                 }
 
                 @Override
-                public void run(View view, String syncId) {
+                public void run(
+                        View view, String syncId, @Nullable MotionEvent triggeringMotionEvent) {
                     // Intentional no-op.
                 }
             };
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneMediatorUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneMediatorUnitTest.java
index ed924d49..07414e9 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneMediatorUnitTest.java
@@ -352,7 +352,7 @@
     public void testOpenTabGridDialog() {
         TabActionListener listener = mMediator.openTabGridDialog(mGroupedTab1);
         assertNotNull(listener);
-        listener.run(mCustomView, mGroupedTab1.getId());
+        listener.run(mCustomView, mGroupedTab1.getId(), /* triggeringMotionEvent= */ null);
 
         verify(mTabGridDialogController).resetWithListOfTabs(List.of(mGroupedTab1, mGroupedTab2));
     }
@@ -368,7 +368,7 @@
 
         TabActionListener listener = mMediator.openTabGridDialog(mUngroupedTab);
         assertNotNull(listener);
-        listener.run(mCustomView, mUngroupedTab.getId());
+        listener.run(mCustomView, mUngroupedTab.getId(), /* triggeringMotionEvent= */ null);
         verify(mTabGridDialogController).resetWithListOfTabs(List.of(mUngroupedTab));
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUtils.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUtils.java
index c5a5ee16..09e060d 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUtils.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUtils.java
@@ -62,12 +62,14 @@
      *
      * @param filter The {@link TabGroupModelFilter} to act on.
      * @param tabId The ID of one of the tabs in the tab group.
+     * @param allowUndo Whether to allow undo of the tab group closure.
      * @param hideTabGroups Whether to hide or delete the tab group.
      * @param didCloseCallback Run after the close confirmation to indicate if a close happened.
      */
     public static void closeTabGroup(
             TabGroupModelFilter filter,
             int tabId,
+            boolean allowUndo,
             boolean hideTabGroups,
             @Nullable Callback<Boolean> didCloseCallback) {
         TabModel tabModel = filter.getTabModel();
@@ -79,7 +81,7 @@
         TabClosureParams closureParams =
                 TabClosureParams.forCloseTabGroup(filter, tab.getTabGroupId())
                         .hideTabGroups(hideTabGroups)
-                        .allowUndo(true)
+                        .allowUndo(allowUndo)
                         .build();
 
         @Nullable TabModelActionListener listener = buildMaybeDidCloseTabListener(didCloseCallback);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUtilsUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUtilsUnitTest.java
index 80185f44..ebbbf66 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUtilsUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUtilsUnitTest.java
@@ -4,9 +4,12 @@
 
 package org.chromium.chrome.browser.tasks.tab_management;
 
+import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -134,15 +137,50 @@
     @Test
     public void testCloseTabGroup_NoTab() {
         TabUiUtils.closeTabGroup(
-                mFilter, Tab.INVALID_TAB_ID, /* hideTabGroups= */ false, mDidCloseTabsCallback);
+                mFilter,
+                Tab.INVALID_TAB_ID,
+                /* allowUndo= */ true,
+                /* hideTabGroups= */ false,
+                mDidCloseTabsCallback);
         verify(mDidCloseTabsCallback).onResult(false);
     }
 
     @Test
+    public void testCloseTabGroup_AllowUndo() {
+        testCloseTabGroupForAllowUndoParam(/* shouldAllowUndo= */ true);
+    }
+
+    @Test
+    public void testCloseTabGroup_DisallowUndo() {
+        testCloseTabGroupForAllowUndoParam(/* shouldAllowUndo= */ false);
+    }
+
+    private void testCloseTabGroupForAllowUndoParam(boolean shouldAllowUndo) {
+        // Act
+        TabUiUtils.closeTabGroup(
+                mFilter,
+                TAB_ID,
+                shouldAllowUndo,
+                /* hideTabGroups= */ false,
+                /* didCloseCallback= */ null);
+
+        // Assert
+        ArgumentCaptor<TabClosureParams> tabClosureParamsCaptor =
+                ArgumentCaptor.forClass(TabClosureParams.class);
+        verify(mTabRemover)
+                .closeTabs(
+                        tabClosureParamsCaptor.capture(),
+                        /* allowDialog= */ anyBoolean(),
+                        /* listener= */ nullable(TabModelActionListener.class));
+        assertEquals(shouldAllowUndo, tabClosureParamsCaptor.getValue().allowUndo);
+    }
+
+    @Test
     public void testCloseTabGroup_NoHide() {
         boolean hideTabGroups = false;
 
-        TabUiUtils.closeTabGroup(mFilter, TAB_ID, hideTabGroups, mDidCloseTabsCallback);
+        TabUiUtils.closeTabGroup(
+                mFilter, TAB_ID, /* allowUndo= */ true, hideTabGroups, mDidCloseTabsCallback);
 
         verify(mTabRemover)
                 .closeTabs(
@@ -186,7 +224,8 @@
     public void testCloseTabGroup_Hide() {
         boolean hideTabGroups = true;
 
-        TabUiUtils.closeTabGroup(mFilter, TAB_ID, hideTabGroups, mDidCloseTabsCallback);
+        TabUiUtils.closeTabGroup(
+                mFilter, TAB_ID, /* allowUndo= */ true, hideTabGroups, mDidCloseTabsCallback);
 
         verify(mTabRemover)
                 .closeTabs(
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java
index 5c1da73..9c78c146 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java
@@ -30,10 +30,13 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.VectorDrawable;
+import android.os.SystemClock;
 import android.util.Size;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewStub;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.FrameLayout;
 import android.widget.ImageButton;
 import android.widget.ImageView;
@@ -41,6 +44,7 @@
 import android.widget.TextView;
 
 import androidx.annotation.IdRes;
+import androidx.annotation.Nullable;
 import androidx.core.widget.ImageViewCompat;
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.filters.MediumTest;
@@ -210,13 +214,14 @@
     private TabListMediator.TabActionListener mMockCloseListener =
             new TabListMediator.TabActionListener() {
                 @Override
-                public void run(View view, int tabId) {
+                public void run(View view, int tabId, @Nullable MotionEvent triggeringMotionEvent) {
                     mCloseClicked.set(true);
                     mCloseTabId.set(tabId);
                 }
 
                 @Override
-                public void run(View view, String syncId) {}
+                public void run(
+                        View view, String syncId, @Nullable MotionEvent triggeringMotionEvent) {}
             };
     private AtomicBoolean mCloseClicked = new AtomicBoolean();
     private AtomicInteger mCloseTabId = new AtomicInteger();
@@ -224,13 +229,14 @@
     private TabListMediator.TabActionListener mMockSelectedListener =
             new TabListMediator.TabActionListener() {
                 @Override
-                public void run(View view, int tabId) {
+                public void run(View view, int tabId, @Nullable MotionEvent triggeringMotionEvent) {
                     mSelectClicked.set(true);
                     mSelectTabId.set(tabId);
                 }
 
                 @Override
-                public void run(View view, String syncId) {}
+                public void run(
+                        View view, String syncId, @Nullable MotionEvent triggeringMotionEvent) {}
             };
     private AtomicBoolean mSelectClicked = new AtomicBoolean();
     private AtomicInteger mSelectTabId = new AtomicInteger();
@@ -238,13 +244,14 @@
     private TabListMediator.TabActionListener mMockCreateGroupButtonListener =
             new TabListMediator.TabActionListener() {
                 @Override
-                public void run(View view, int tabId) {
+                public void run(View view, int tabId, @Nullable MotionEvent triggeringMotionEvent) {
                     mCreateGroupButtonClicked.set(true);
                     mCreateGroupTabId.set(tabId);
                 }
 
                 @Override
-                public void run(View view, String syncId) {}
+                public void run(
+                        View view, String syncId, @Nullable MotionEvent triggeringMotionEvent) {}
             };
     private AtomicBoolean mCreateGroupButtonClicked = new AtomicBoolean();
     private AtomicInteger mCreateGroupTabId = new AtomicInteger();
@@ -644,6 +651,64 @@
     @Test
     @MediumTest
     @UiThreadTest
+    public void testCloseButtonClick() {
+        ImageView gridActionButton = mTabGridView.findViewById(R.id.action_button);
+        Assert.assertFalse(mCloseClicked.get());
+
+        gridActionButton.performClick();
+
+        Assert.assertTrue(mCloseClicked.get());
+        Assert.assertEquals(TAB1_ID, mCloseTabId.get());
+    }
+
+    @Test
+    @MediumTest
+    @UiThreadTest
+    public void testCloseButtonPeripheralClick() {
+        // Setup
+        ImageView gridActionButton = mTabGridView.findViewById(R.id.action_button);
+        Assert.assertFalse(mCloseClicked.get());
+
+        // Act: peripheral click is intercepted by an OnTouchListener, so we should dispatch
+        // MotionEvents to simulate a click.
+        long motionDownTime = SystemClock.uptimeMillis();
+        gridActionButton.dispatchTouchEvent(
+                TabUiTestHelper.createMouseMotionEvent(
+                        motionDownTime,
+                        /* eventTime= */ motionDownTime,
+                        MotionEvent.ACTION_DOWN,
+                        /* x= */ 0.0f,
+                        /* y= */ 0.0f));
+        gridActionButton.dispatchTouchEvent(
+                TabUiTestHelper.createMouseMotionEvent(
+                        motionDownTime,
+                        /* eventTime= */ motionDownTime + 200,
+                        MotionEvent.ACTION_UP,
+                        /* x= */ 0.0f,
+                        /* y= */ 0.0f));
+
+        // Assert
+        Assert.assertTrue(mCloseClicked.get());
+        Assert.assertEquals(TAB1_ID, mCloseTabId.get());
+    }
+
+    @Test
+    @MediumTest
+    @UiThreadTest
+    public void testCloseButtonA11yClick() {
+        ImageView gridActionButton = mTabGridView.findViewById(R.id.action_button);
+        Assert.assertFalse(mCloseClicked.get());
+
+        gridActionButton.performAccessibilityAction(
+                AccessibilityNodeInfo.ACTION_CLICK, /* arguments= */ null);
+
+        Assert.assertTrue(mCloseClicked.get());
+        Assert.assertEquals(TAB1_ID, mCloseTabId.get());
+    }
+
+    @Test
+    @MediumTest
+    @UiThreadTest
     public void testCloseButtonDescription() {
         ImageView listActionButton = mTabListView.findViewById(R.id.end_button);
         ImageView gridActionButton = mTabGridView.findViewById(R.id.action_button);
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherLayoutTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherLayoutTest.java
index 085e73e..bfebffe 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherLayoutTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherLayoutTest.java
@@ -27,6 +27,7 @@
 
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.closeFirstTabGroupInTabSwitcher;
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.closeFirstTabInTabSwitcher;
+import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.closeNthTabInTabSwitcher;
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.createTabGroup;
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.createTabs;
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.enterTabSwitcher;
@@ -271,6 +272,25 @@
 
     @Test
     @MediumTest
+    public void testCloseTabViaCloseButton_peripheralClick_noUndoSnackbar() {
+        // Setup
+        ChromeTabbedActivity activity = mActivityTestRule.getActivity();
+        SnackbarManager snackbarManager = activity.getSnackbarManager();
+        createTabs(activity, /* isIncognito= */ false, /* tabsCount= */ 3);
+        enterTabSwitcher(activity);
+        verifyTabSwitcherCardCount(activity, /* count= */ 3);
+        assertNull(snackbarManager.getCurrentSnackbarForTesting());
+
+        // Act
+        closeNthTabInTabSwitcher(activity, /* index= */ 0, /* performMouseClick= */ true);
+
+        // Assert: no undo snackbar & tab closed
+        assertNull(snackbarManager.getCurrentSnackbarForTesting());
+        verifyTabSwitcherCardCount(activity, /* count= */ 2);
+    }
+
+    @Test
+    @MediumTest
     @DisabledTest(message = "Flaky - https://crbug.com/1124041, crbug.com/1061178")
     public void testSwipeToDismiss_Gts() {
         ChromeTabbedActivity cta = mActivityTestRule.getActivity();
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabUiTestHelper.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabUiTestHelper.java
index 68097e1b..60c85f5b 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabUiTestHelper.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabUiTestHelper.java
@@ -32,7 +32,12 @@
 import android.app.Activity;
 import android.content.Context;
 import android.graphics.drawable.BitmapDrawable;
+import android.os.SystemClock;
 import android.provider.Settings;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.MotionEvent.PointerCoords;
+import android.view.MotionEvent.PointerProperties;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -284,12 +289,23 @@
     }
 
     /**
-     * Close the Nth tab in grid tab switcher.
+     * Closes the Nth tab in grid tab switcher.
      *
-     * @param context The activity context.
-     * @param index The index of the target tab to close.
+     * @see #closeNthTabInTabSwitcher(Context, int, boolean)
      */
     static void closeNthTabInTabSwitcher(Context context, int index) {
+        closeNthTabInTabSwitcher(context, index, /* performMouseClick= */ false);
+    }
+
+    /**
+     * Closes the Nth tab in grid tab switcher.
+     *
+     * @param context the Activity context.
+     * @param index the index of the tab to close.
+     * @param performMouseClick whether to click the close button by simulating {@link MotionEvent}s
+     *     from a mouse.
+     */
+    static void closeNthTabInTabSwitcher(Context context, int index, boolean performMouseClick) {
         onView(
                         allOf(
                                 isDescendantOfA(withId(getTabSwitcherAncestorId(context))),
@@ -312,12 +328,66 @@
                                 RecyclerView.ViewHolder viewHolder =
                                         recyclerView.findViewHolderForAdapterPosition(index);
                                 assert viewHolder != null;
-                                viewHolder.itemView.findViewById(R.id.action_button).performClick();
+
+                                View actionButton =
+                                        viewHolder.itemView.findViewById(R.id.action_button);
+                                if (!performMouseClick) {
+                                    actionButton.performClick();
+                                    return;
+                                }
+
+                                long motionDownTime = SystemClock.uptimeMillis();
+                                actionButton.dispatchTouchEvent(
+                                        createMouseMotionEvent(
+                                                motionDownTime,
+                                                /* eventTime= */ motionDownTime,
+                                                MotionEvent.ACTION_DOWN,
+                                                /* x= */ 0.0f,
+                                                /* y= */ 0.0f));
+                                actionButton.dispatchTouchEvent(
+                                        createMouseMotionEvent(
+                                                motionDownTime,
+                                                /* eventTime= */ motionDownTime + 200,
+                                                MotionEvent.ACTION_UP,
+                                                /* x= */ 0.0f,
+                                                /* y= */ 0.0f));
                             }
                         });
     }
 
     /**
+     * Creates a {@link MotionEvent} that matches one from a mouse.
+     *
+     * <p>All parameters are for {@link MotionEvent#obtain}.
+     */
+    static MotionEvent createMouseMotionEvent(
+            long downTime, long eventTime, int action, float x, float y) {
+        PointerProperties pointerProperties = new MotionEvent.PointerProperties();
+        pointerProperties.id = 0;
+        pointerProperties.toolType = MotionEvent.TOOL_TYPE_MOUSE;
+
+        PointerCoords pointerCoords = new PointerCoords();
+        pointerCoords.x = x;
+        pointerCoords.y = y;
+
+        return MotionEvent.obtain(
+                downTime,
+                eventTime,
+                action,
+                /* pointerCount= */ 1,
+                new PointerProperties[] {pointerProperties},
+                new PointerCoords[] {pointerCoords},
+                /* metaState= */ 0,
+                /* buttonState= */ 0,
+                /* xPrecision= */ 1.0f,
+                /* yPrecision= */ 1.0f,
+                /* deviceId= */ 0,
+                /* edgeFlags= */ 0,
+                InputDevice.SOURCE_MOUSE,
+                /* flags= */ 0);
+    }
+
+    /**
      * Check whether the tab list in {@link android.widget.PopupWindow} is completely showing. This
      * can be used for tab grid dialog and tab group popup UI.
      *
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinatorUnitTest.java
index c61dd6f3..504678d 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinatorUnitTest.java
@@ -12,7 +12,6 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -129,7 +128,7 @@
 
     private Activity mActivity;
     private ArchivedTabsDialogCoordinator mCoordinator;
-    private ObservableSupplierImpl<Integer> mTabCountSupplier = new ObservableSupplierImpl<>();
+    private ObservableSupplierImpl<Integer> mTabCountSupplier = new ObservableSupplierImpl<>(1);
     private ObservableSupplierImpl<EdgeToEdgeController> mEdgeToEdgeSupplier =
             new ObservableSupplierImpl<>();
     private OneshotSupplierImpl<PaneManager> mPaneManagerSupplier = new OneshotSupplierImpl<>();
@@ -201,13 +200,12 @@
                     }
                 });
 
-        doReturn(mArchivedTabModelSelector)
-                .when(mArchivedTabModelOrchestrator)
-                .getTabModelSelector();
-        doReturn(mArchivedTabModel).when(mArchivedTabModelSelector).getModel(false);
-        doReturn(mTabCountSupplier).when(mArchivedTabModel).getTabCountSupplier();
+        when(mArchivedTabModelOrchestrator.getTabModelSelector())
+                .thenReturn(mArchivedTabModelSelector);
+        when(mArchivedTabModelSelector.getModel(false)).thenReturn(mArchivedTabModel);
+        when(mArchivedTabModelOrchestrator.getTabCountSupplier()).thenReturn(mTabCountSupplier);
 
-        doReturn(mTabListEditorController).when(mTabListEditorCoordinator).getController();
+        when(mTabListEditorCoordinator.getController()).thenReturn(mTabListEditorController);
         doAnswer(
                         invocationOnMock -> {
                             mCoordinator.getTabListEditorLifecycleObserver().willHide();
@@ -224,15 +222,10 @@
         verify(mRootView).addView(any());
         verify(mTabListEditorController).show(any(), eq(Collections.emptyList()), eq(null));
         verify(mTabListEditorController).setNavigationProvider(any());
-        verify(mTabListEditorController).setToolbarTitle("0 inactive tabs");
+        verify(mTabListEditorController, times(2)).setToolbarTitle("1 inactive tab");
         verify(mBackPressManager).addHandler(any(), eq(BackPressHandler.Type.ARCHIVED_TABS_DIALOG));
 
-        doReturn(1).when(mArchivedTabModel).getCount();
-        mCoordinator.updateTitle();
-        verify(mTabListEditorController).setToolbarTitle("1 inactive tab");
-
-        doReturn(2).when(mArchivedTabModel).getCount();
-        mCoordinator.updateTitle();
+        mTabCountSupplier.set(2);
         verify(mTabListEditorController).setToolbarTitle("2 inactive tabs");
     }
 
@@ -255,22 +248,17 @@
     public void testAddRemoveTab() {
         mCoordinator.show(mOnTabSelectingListener);
 
-        // First add a tab
-        doReturn(1).when(mArchivedTabModel).getCount();
-        mTabCountSupplier.set(1);
-        verify(mTabListEditorController).setToolbarTitle("1 inactive tab");
+        // First verify a tab exists as the base condition for showing.
+        verify(mTabListEditorController, times(2)).setToolbarTitle("1 inactive tab");
 
-        // Then a second
-        doReturn(2).when(mArchivedTabModel).getCount();
+        // Then add a second tab.
         mTabCountSupplier.set(2);
         verify(mTabListEditorController).setToolbarTitle("2 inactive tabs");
 
-        // Then close bloth
-        doReturn(1).when(mArchivedTabModel).getCount();
+        // Then close both tabs.
         mTabCountSupplier.set(1);
-        verify(mTabListEditorController, times(2)).setToolbarTitle("1 inactive tab");
+        verify(mTabListEditorController, times(3)).setToolbarTitle("1 inactive tab");
 
-        doReturn(0).when(mArchivedTabModel).getCount();
         mTabCountSupplier.set(0);
 
         // Allow animations to finish.
@@ -294,7 +282,7 @@
 
     @Test
     public void testDestroyHidesDialog() {
-        doReturn(true).when(mTabListEditorController).isVisible();
+        when(mTabListEditorController.isVisible()).thenReturn(true);
         mCoordinator.show(mOnTabSelectingListener);
         mCoordinator.destroy();
 
@@ -353,9 +341,10 @@
         when(mPaneManager.getPaneForId(PaneId.TAB_SWITCHER)).thenReturn(mTabSwitcherPaneBase);
         when(mTabGroupSyncService.getGroup(TAB_GROUP_ID_STRING))
                 .thenReturn(savedTabGroupBefore)
+                .thenReturn(savedTabGroupBefore)
                 .thenReturn(savedTabGroupAfter);
         when(mCurrentTabGroupModelFilter.getRootIdFromTabGroupId(TAB_GROUP_ID)).thenReturn(TAB1_ID);
-        doReturn(true).when(mTabListEditorController).isVisible();
+        when(mTabListEditorController.isVisible()).thenReturn(true);
 
         // Show the dialog.
         mCoordinator.show(mOnTabSelectingListener);
@@ -364,7 +353,7 @@
         GridCardOnClickListenerProvider provider =
                 mCoordinator.getGridCardOnClickListenerProviderForTesting();
         TabActionListener listener = provider.openTabGridDialog(TAB_GROUP_ID_STRING);
-        listener.run(mItemView1, TAB_GROUP_ID_STRING);
+        listener.run(mItemView1, TAB_GROUP_ID_STRING, /* triggeringMotionEvent= */ null);
 
         verify(mTabGroupUiActionHandler).openTabGroup(TAB_GROUP_ID_STRING);
 
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallbackUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallbackUnitTest.java
index 73cc895..a9e68401e 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallbackUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallbackUnitTest.java
@@ -243,7 +243,7 @@
     public void onSwipeTab_Delete() {
         mItemTouchHelperCallback.onSwiped(mMockViewHolder1, POSITION1);
 
-        verify(mTabClosedListener).run(mItemView1, TAB1_ID);
+        verify(mTabClosedListener).run(mItemView1, TAB1_ID, /* triggeringMotionEvent= */ null);
     }
 
     @Test
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinderUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinderUnitTest.java
index 9055510d0..85eb598 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinderUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinderUnitTest.java
@@ -12,6 +12,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isA;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
@@ -31,6 +32,7 @@
 import android.graphics.drawable.Drawable;
 import android.util.Size;
 import android.view.ContextThemeWrapper;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup.LayoutParams;
 import android.view.ViewStub;
@@ -38,6 +40,8 @@
 import android.widget.ImageView;
 import android.widget.ImageView.ScaleType;
 
+import androidx.annotation.Nullable;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -59,7 +63,11 @@
 import org.chromium.chrome.browser.tab_ui.TabListFaviconProvider.TabFavicon;
 import org.chromium.chrome.browser.tab_ui.TabListFaviconProvider.TabFaviconFetcher;
 import org.chromium.chrome.browser.tab_ui.TabThumbnailView;
+import org.chromium.chrome.browser.tasks.tab_management.TabGridViewBinder.OnPeripheralClickListener;
 import org.chromium.chrome.browser.tasks.tab_management.TabListMediator.ShoppingPersistedTabDataFetcher;
+import org.chromium.chrome.browser.tasks.tab_management.TabListMediator.TabActionButtonData;
+import org.chromium.chrome.browser.tasks.tab_management.TabListMediator.TabActionButtonData.TabActionButtonType;
+import org.chromium.chrome.browser.tasks.tab_management.TabListMediator.TabActionListener;
 import org.chromium.chrome.browser.tasks.tab_management.TabProperties.TabActionState;
 import org.chromium.ui.modelutil.PropertyModel;
 
@@ -468,6 +476,32 @@
         verify(mViewGroup).setTabActionButtonTint(any());
     }
 
+    @Test
+    public void bindTabWithActionButtonData_setOnClickListenerAndOnPeripheralClickListener() {
+        TabActionButtonData tabActionButtonData =
+                new TabActionButtonData(
+                        TabActionButtonType.CLOSE,
+                        new TabActionListener() {
+                            @Override
+                            public void run(
+                                    View view,
+                                    int tabId,
+                                    @Nullable MotionEvent triggeringMotionEvent) {}
+
+                            @Override
+                            public void run(
+                                    View view,
+                                    String syncId,
+                                    @Nullable MotionEvent triggeringMotionEvent) {}
+                        });
+        mModel.set(TabProperties.TAB_ACTION_BUTTON_DATA, tabActionButtonData);
+
+        TabGridViewBinder.bindTab(mModel, mViewGroup, TabProperties.TAB_ACTION_BUTTON_DATA);
+
+        verify(mActionButton).setOnClickListener(any());
+        verify(mActionButton).setOnTouchListener(isA(OnPeripheralClickListener.class));
+    }
+
     private void assertImageMatrix(
             ArgumentCaptor<Matrix> matrixCaptor, float expectedScale, float expectedTrans) {
         float[] matValues = new float[9];
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
index f087f5f..6d97ae2 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
@@ -53,8 +53,10 @@
 import android.graphics.Bitmap;
 import android.graphics.Rect;
 import android.os.Bundle;
+import android.os.SystemClock;
 import android.util.Pair;
 import android.util.Size;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
@@ -984,7 +986,10 @@
                 .get(1)
                 .model
                 .get(TabProperties.TAB_CLICK_LISTENER)
-                .run(mItemView2, mModelList.get(1).model.get(TabProperties.TAB_ID));
+                .run(
+                        mItemView2,
+                        mModelList.get(1).model.get(TabProperties.TAB_ID),
+                        /* triggeringMotionEvent= */ null);
 
         verify(mGridCardOnClickListenerProvider)
                 .onTabSelecting(mModelList.get(1).model.get(TabProperties.TAB_ID), true);
@@ -999,9 +1004,13 @@
                 .get(0)
                 .model
                 .get(TabProperties.TAB_CLICK_LISTENER)
-                .run(mItemView1, mModelList.get(0).model.get(TabProperties.TAB_ID));
+                .run(
+                        mItemView1,
+                        mModelList.get(0).model.get(TabProperties.TAB_ID),
+                        /* triggeringMotionEvent= */ null);
 
-        verify(mOpenGroupActionListener).run(mItemView1, TAB1_ID);
+        verify(mOpenGroupActionListener)
+                .run(mItemView1, TAB1_ID, /* triggeringMotionEvent= */ null);
     }
 
     @Test
@@ -1013,9 +1022,13 @@
                 .get(0)
                 .model
                 .get(TabProperties.TAB_CLICK_LISTENER)
-                .run(mItemView1, mModelList.get(0).model.get(TabProperties.TAB_ID));
+                .run(
+                        mItemView1,
+                        mModelList.get(0).model.get(TabProperties.TAB_ID),
+                        /* triggeringMotionEvent= */ null);
 
-        verify(mOpenGroupActionListener).run(mItemView1, TAB1_ID);
+        verify(mOpenGroupActionListener)
+                .run(mItemView1, TAB1_ID, /* triggeringMotionEvent= */ null);
     }
 
     @Test
@@ -1026,7 +1039,10 @@
                 .model
                 .get(TabProperties.TAB_ACTION_BUTTON_DATA)
                 .tabActionListener
-                .run(mItemView2, mModelList.get(1).model.get(TabProperties.TAB_ID));
+                .run(
+                        mItemView2,
+                        mModelList.get(1).model.get(TabProperties.TAB_ID),
+                        /* triggeringMotionEvent= */ null);
 
         TabClosureParams params = TabClosureParams.closeTab(mTab2).allowUndo(true).build();
         verify(mTabRemover)
@@ -1048,6 +1064,31 @@
     }
 
     @Test
+    public void sendsCloseSignalCorrectly_TriggeringMotionEventFromMouse_DisallowUndo() {
+        mMediator.setActionOnAllRelatedTabsForTesting(false);
+        mModelList
+                .get(1)
+                .model
+                .get(TabProperties.TAB_ACTION_BUTTON_DATA)
+                .tabActionListener
+                .run(
+                        mItemView2,
+                        mModelList.get(1).model.get(TabProperties.TAB_ID),
+                        TabUiTestHelper.createMouseMotionEvent(
+                                /* downTime= */ SystemClock.uptimeMillis(),
+                                /* eventTime= */ SystemClock.uptimeMillis() + 200,
+                                MotionEvent.ACTION_UP,
+                                /* x= */ 0,
+                                /* y= */ 0));
+
+        verify(mTabRemover)
+                .closeTabs(
+                        eq(TabClosureParams.closeTab(mTab2).allowUndo(false).build()),
+                        /* allowDialog= */ eq(true),
+                        /* listener= */ any());
+    }
+
+    @Test
     public void sendsCloseSignalCorrectly_ActionOnAllRelatedTabs() {
         mMediator.setActionOnAllRelatedTabsForTesting(true);
         mModelList
@@ -1055,7 +1096,10 @@
                 .model
                 .get(TabProperties.TAB_ACTION_BUTTON_DATA)
                 .tabActionListener
-                .run(mItemView2, mModelList.get(1).model.get(TabProperties.TAB_ID));
+                .run(
+                        mItemView2,
+                        mModelList.get(1).model.get(TabProperties.TAB_ID),
+                        /* triggeringMotionEvent= */ null);
 
         verify(mTabRemover)
                 .closeTabs(
@@ -1073,7 +1117,10 @@
                 .model
                 .get(TabProperties.TAB_ACTION_BUTTON_DATA)
                 .tabActionListener
-                .run(mItemView2, mModelList.get(1).model.get(TabProperties.TAB_ID));
+                .run(
+                        mItemView2,
+                        mModelList.get(1).model.get(TabProperties.TAB_ID),
+                        /* triggeringMotionEvent= */ null);
 
         verify(mTabRemover)
                 .closeTabs(
@@ -3986,7 +4033,11 @@
 
         when(mTabGroupModelFilter.isTabInTabGroup(mTab2)).thenReturn(false);
         ThumbnailFetcher fetcher2 = mModelList.get(1).model.get(TabProperties.THUMBNAIL_FETCHER);
-        mModelList.get(1).model.get(TabProperties.TAB_CLICK_LISTENER).run(mItemView2, TAB2_ID);
+        mModelList
+                .get(1)
+                .model
+                .get(TabProperties.TAB_CLICK_LISTENER)
+                .run(mItemView2, TAB2_ID, /* triggeringMotionEvent= */ null);
         assertThat(mModelList.get(1).model.get(TabProperties.IS_SELECTED), equalTo(true));
         assertEquals(fetcher2, mModelList.get(1).model.get(TabProperties.THUMBNAIL_FETCHER));
     }
@@ -4029,7 +4080,11 @@
 
         when(mTabGroupModelFilter.isTabInTabGroup(mTab2)).thenReturn(true);
         ThumbnailFetcher fetcher2 = mModelList.get(1).model.get(TabProperties.THUMBNAIL_FETCHER);
-        mModelList.get(1).model.get(TabProperties.TAB_CLICK_LISTENER).run(mItemView2, TAB2_ID);
+        mModelList
+                .get(1)
+                .model
+                .get(TabProperties.TAB_CLICK_LISTENER)
+                .run(mItemView2, TAB2_ID, /* triggeringMotionEvent= */ null);
         assertThat(mModelList.get(1).model.get(TabProperties.IS_SELECTED), equalTo(true));
         assertNotEquals(fetcher2, mModelList.get(1).model.get(TabProperties.THUMBNAIL_FETCHER));
     }
@@ -4800,7 +4855,10 @@
                 .model
                 .get(TabProperties.TAB_ACTION_BUTTON_DATA)
                 .tabActionListener
-                .run(mItemView1, mModelList.get(0).model.get(TabProperties.TAB_GROUP_SYNC_ID));
+                .run(
+                        mItemView1,
+                        mModelList.get(0).model.get(TabProperties.TAB_GROUP_SYNC_ID),
+                        /* triggeringMotionEvent= */ null);
 
         // Assert that the tab group has been removed from the model list and archive status reset.
         assertEquals(TAB, mModelList.get(0).model.get(CARD_TYPE));
@@ -4818,9 +4876,13 @@
                 .get(0)
                 .model
                 .get(TabProperties.TAB_CLICK_LISTENER)
-                .run(mItemView1, mModelList.get(0).model.get(TabProperties.TAB_GROUP_SYNC_ID));
+                .run(
+                        mItemView1,
+                        mModelList.get(0).model.get(TabProperties.TAB_GROUP_SYNC_ID),
+                        /* triggeringMotionEvent= */ null);
 
-        verify(mOpenGroupActionListener).run(mItemView1, SYNC_GROUP_ID1);
+        verify(mOpenGroupActionListener)
+                .run(mItemView1, SYNC_GROUP_ID1, /* triggeringMotionEvent= */ null);
     }
 
     @Test
@@ -4869,7 +4931,10 @@
                 .get(0)
                 .model
                 .get(TabProperties.TAB_CLICK_LISTENER)
-                .run(mItemView1, mModelList.get(0).model.get(TabProperties.TAB_GROUP_SYNC_ID));
+                .run(
+                        mItemView1,
+                        mModelList.get(0).model.get(TabProperties.TAB_GROUP_SYNC_ID),
+                        /* triggeringMotionEvent= */ null);
         assertThat(mModelList.get(0).model.get(TabProperties.IS_SELECTED), equalTo(true));
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ai/PageSummaryButtonController.java b/chrome/android/java/src/org/chromium/chrome/browser/ai/PageSummaryButtonController.java
index c8d71cf..cdcce42 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ai/PageSummaryButtonController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ai/PageSummaryButtonController.java
@@ -56,8 +56,7 @@
                 /* supportsTinting= */ true,
                 /* iphCommandBuilder= */ null,
                 AdaptiveToolbarButtonVariant.PAGE_SUMMARY,
-                /* tooltipTextResId= */ R.string.menu_summarize_with_ai,
-                /* showBackgroundHighlight= */ true);
+                /* tooltipTextResId= */ R.string.menu_summarize_with_ai);
         mContext = context;
         mAiAssistantService = aiAssistantService;
         mTrackerSupplier = tracker;
@@ -75,7 +74,6 @@
                         AdaptiveToolbarButtonVariant.PAGE_SUMMARY,
                         /* actionChipLabelResId= */ Resources.ID_NULL,
                         /* tooltipTextResId= */ R.string.menu_review_pdf_with_ai,
-                        /* showBackgroundHighlight= */ true,
                         /* hasErrorBadge= */ false);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java
index 8aaabb1..e261c2d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java
@@ -189,6 +189,7 @@
                 TabUiUtils.closeTabGroup(
                         tabGroupModelFilter,
                         tabId,
+                        /* allowUndo= */ true,
                         /* hideTabGroups= */ true,
                         /* didCloseCallback= */ null);
                 recordUserAction("CloseGroup");
@@ -196,6 +197,7 @@
                 TabUiUtils.closeTabGroup(
                         tabGroupModelFilter,
                         tabId,
+                        /* allowUndo= */ true,
                         /* hideTabGroups= */ false,
                         /* didCloseCallback= */ null);
                 recordUserAction("DeleteGroup");
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeToolbarButtonController.java b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeToolbarButtonController.java
index 1815cb6e4..5b62084b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeToolbarButtonController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeToolbarButtonController.java
@@ -43,8 +43,7 @@
                 /* supportsTinting= */ true,
                 /* iphCommandBuilder= */ null,
                 AdaptiveToolbarButtonVariant.READER_MODE,
-                /* tooltipTextResId= */ Resources.ID_NULL,
-                /* showBackgroundHighlight= */ false);
+                /* tooltipTextResId= */ Resources.ID_NULL);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/identity_disc/IdentityDiscController.java b/chrome/android/java/src/org/chromium/chrome/browser/identity_disc/IdentityDiscController.java
index 09014ff..8239c8d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/identity_disc/IdentityDiscController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/identity_disc/IdentityDiscController.java
@@ -121,8 +121,7 @@
                                 R.string.iph_identity_disc_accessibility_text),
                         /* isEnabled= */ true,
                         AdaptiveToolbarButtonVariant.UNKNOWN,
-                        /* tooltipTextResId= */ Resources.ID_NULL,
-                        /* showBackgroundHighlight= */ true);
+                        /* tooltipTextResId= */ Resources.ID_NULL);
     }
 
     /** Registers itself to observe sign-in and sync status events. */
@@ -192,7 +191,6 @@
                 AdaptiveToolbarButtonVariant.UNKNOWN,
                 buttonSpec.getActionChipLabelResId(),
                 buttonSpec.getHoverTooltipTextId(),
-                buttonSpec.shouldShowBackgroundHighlight(),
                 /* hasErrorBadge= */ mIdentityError != SyncError.NO_ERROR);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ChromePaymentRequestService.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ChromePaymentRequestService.java
index 9193520..8df86f7b4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ChromePaymentRequestService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ChromePaymentRequestService.java
@@ -13,6 +13,7 @@
 import androidx.appcompat.app.AlertDialog;
 
 import org.chromium.base.Callback;
+import org.chromium.base.Log;
 import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.autofill.PersonalDataManagerFactory;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
@@ -42,6 +43,7 @@
 import org.chromium.components.payments.PaymentResponseHelper;
 import org.chromium.components.payments.PaymentResponseHelperInterface;
 import org.chromium.components.payments.secure_payment_confirmation.SecurePaymentConfirmationAuthnController;
+import org.chromium.components.payments.secure_payment_confirmation.SecurePaymentConfirmationAuthnController.SpcResponseStatus;
 import org.chromium.components.payments.secure_payment_confirmation.SecurePaymentConfirmationNoMatchingCredController;
 import org.chromium.content_public.browser.RenderFrameHost;
 import org.chromium.content_public.browser.WebContents;
@@ -68,6 +70,8 @@
  */
 public class ChromePaymentRequestService
         implements BrowserPaymentRequest, PaymentUiService.Delegate {
+    private static final String TAG = "ChromePaymentReqServ";
+
     // Null-check is necessary because retainers of ChromePaymentRequestService could still
     // reference ChromePaymentRequestService after mPaymentRequestService is set null, e.g.,
     // crbug.com/1122148.
@@ -356,20 +360,28 @@
             assert mSpcAuthnUiController == null;
             mSpcAuthnUiController = SecurePaymentConfirmationAuthnController.create(mWebContents);
 
-            Callback<Boolean> responseCallback =
-                    (response) -> {
-                        mJourneyLogger.setAborted(AbortReason.ABORTED_BY_USER);
-
-                        // User wishes to proceed with payment but not through SPC.
-                        if (response) {
-                            disconnectFromClientWithDebugMessage(
-                                    ErrorStrings.WEB_AUTHN_OPERATION_TIMED_OUT_OR_NOT_ALLOWED,
-                                    PaymentErrorReason.NOT_ALLOWED_ERROR);
-                        } else {
-                            disconnectFromClientWithDebugMessage(
-                                    ErrorStrings.USER_CANCELLED, PaymentErrorReason.USER_CANCEL);
+            Callback<Integer> responseCallback =
+                    (responseStatus) -> {
+                        switch (responseStatus) {
+                            case SpcResponseStatus.ANOTHER_WAY:
+                                mJourneyLogger.setAborted(AbortReason.ABORTED_BY_USER);
+                                disconnectFromClientWithDebugMessage(
+                                        ErrorStrings.WEB_AUTHN_OPERATION_TIMED_OUT_OR_NOT_ALLOWED,
+                                        PaymentErrorReason.NOT_ALLOWED_ERROR);
+                                break;
+                            case SpcResponseStatus.CANCEL:
+                                mJourneyLogger.setAborted(AbortReason.ABORTED_BY_USER);
+                                disconnectFromClientWithDebugMessage(
+                                        ErrorStrings.USER_CANCELLED,
+                                        PaymentErrorReason.USER_CANCEL);
+                                break;
+                            default:
+                                Log.e(TAG, "Unexpected SPC response status: %d", responseStatus);
+                                mJourneyLogger.setAborted(AbortReason.ABORTED_BY_USER);
+                                disconnectFromClientWithDebugMessage(
+                                        ErrorStrings.WEB_AUTHN_OPERATION_TIMED_OUT_OR_NOT_ALLOWED,
+                                        PaymentErrorReason.NOT_ALLOWED_ERROR);
                         }
-
                         mSpcAuthnUiController = null;
                     };
 
@@ -422,18 +434,34 @@
             PaymentMethodData spcMethodData =
                     mSpec.getMethodData().get(MethodStrings.SECURE_PAYMENT_CONFIRMATION);
             assert spcMethodData != null;
-            Callback<Boolean> responseCallback =
-                    (response) -> {
-                        if (response) {
-                            onSecurePaymentConfirmationUiAccepted(getSelectedPaymentApp());
-                        } else {
-                            mJourneyLogger.setAborted(AbortReason.ABORTED_BY_USER);
-                            disconnectFromClientWithDebugMessage(
-                                    ErrorStrings.WEB_AUTHN_OPERATION_TIMED_OUT_OR_NOT_ALLOWED,
-                                    PaymentErrorReason.NOT_ALLOWED_ERROR);
+            Callback<Integer> responseCallback =
+                    (responseStatus) -> {
+                        switch (responseStatus) {
+                            case SpcResponseStatus.ACCEPT:
+                                onSecurePaymentConfirmationUiAccepted(getSelectedPaymentApp());
+                                break;
+                            case SpcResponseStatus.ANOTHER_WAY:
+                                mJourneyLogger.setAborted(AbortReason.ABORTED_BY_USER);
+                                disconnectFromClientWithDebugMessage(
+                                        ErrorStrings.WEB_AUTHN_OPERATION_TIMED_OUT_OR_NOT_ALLOWED,
+                                        PaymentErrorReason.NOT_ALLOWED_ERROR);
+                                break;
+                            case SpcResponseStatus.CANCEL:
+                                mJourneyLogger.setAborted(AbortReason.ABORTED_BY_USER);
+                                disconnectFromClientWithDebugMessage(
+                                        ErrorStrings.USER_CANCELLED,
+                                        PaymentErrorReason.USER_CANCEL);
+                                break;
+                            default:
+                                Log.e(TAG, "Unexpected SPC response status: %d", responseStatus);
+                                mJourneyLogger.setAborted(AbortReason.ABORTED_BY_USER);
+                                disconnectFromClientWithDebugMessage(
+                                        ErrorStrings.WEB_AUTHN_OPERATION_TIMED_OUT_OR_NOT_ALLOWED,
+                                        PaymentErrorReason.NOT_ALLOWED_ERROR);
                         }
                         mSpcAuthnUiController = null;
                     };
+
             Runnable optOutCallback =
                     () -> {
                         mJourneyLogger.setAborted(AbortReason.USER_OPTED_OUT);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/share/ShareButtonController.java b/chrome/android/java/src/org/chromium/chrome/browser/share/ShareButtonController.java
index 1c0a647..c5db914 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/share/ShareButtonController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/share/ShareButtonController.java
@@ -66,8 +66,7 @@
                 /* supportsTinting= */ true,
                 /* iphCommandBuilder= */ null,
                 AdaptiveToolbarButtonVariant.SHARE,
-                /* tooltipTextResId= */ R.string.adaptive_toolbar_button_preference_share,
-                /* showBackgroundHighlight= */ true);
+                /* tooltipTextResId= */ R.string.adaptive_toolbar_button_preference_share);
 
         mShareDelegateSupplier = shareDelegateSupplier;
         mTrackerSupplier = trackerSupplier;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBrowserControlsConstraintsHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBrowserControlsConstraintsHelper.java
index f7b532c..0798fbd2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBrowserControlsConstraintsHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabBrowserControlsConstraintsHelper.java
@@ -178,7 +178,6 @@
     public void destroy() {
         if (mVisibilityDelegate != null) {
             mVisibilityDelegate.removeObserver(mConstraintsChangedCallback);
-            mVisibilityDelegate.destroy();
             mVisibilityDelegate = null;
         }
 
@@ -193,7 +192,6 @@
     private void updateVisibilityDelegate() {
         if (mVisibilityDelegate != null) {
             mVisibilityDelegate.removeObserver(mConstraintsChangedCallback);
-            mVisibilityDelegate.destroy();
         }
         mVisibilityDelegate =
                 mTab.getDelegateFactory().createBrowserControlsVisibilityDelegate(mTab);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java
index 1a0d276..68a99aa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java
@@ -49,7 +49,6 @@
     private boolean mIsFocusedNodeEditable;
 
     private final Set<Long> mOutstandingNavigations = new HashSet<>();
-    private final TabObserver mTabObserver;
 
     /**
      * Basic constructor.
@@ -60,7 +59,8 @@
         super(BrowserControlsState.BOTH);
 
         mTab = (TabImpl) tab;
-        mTabObserver =
+
+        mTab.addObserver(
                 new EmptyTabObserver() {
                     @SuppressLint("HandlerLeak")
                     private Handler mHandler =
@@ -218,18 +218,11 @@
                         // Remove pending handler actions to prevent memory leaks.
                         mHandler.removeCallbacksAndMessages(null);
                     }
-                };
-        mTab.addObserver(mTabObserver);
-
+                });
         onWebContentsUpdated(mTab.getWebContents());
         updateVisibilityConstraints();
     }
 
-    @Override
-    public void destroy() {
-        mTab.removeObserver(mTabObserver);
-    }
-
     private void onWebContentsUpdated(WebContents contents) {
         if (mWebContents == contents) return;
         mWebContents = contents;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeServerHelper.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeServerHelper.java
index 5a2b018..0a1efca 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeServerHelper.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeServerHelper.java
@@ -357,7 +357,7 @@
                                 .addCollaboration(mNativeFakeServer, collaborationId));
     }
 
-    /** Adds collaboration from fake sync server. */
+    /** Removes collaboration from fake sync server. */
     public void removeCollaboration(String collaborationId) {
         ThreadUtils.runOnUiThreadBlocking(
                 () ->
@@ -365,6 +365,15 @@
                                 .removeCollaboration(mNativeFakeServer, collaborationId));
     }
 
+    /** Adds collaboration group to fake sync server. */
+    public void addCollaborationGroupToFakeServer(String collaborationId) {
+        ThreadUtils.runOnUiThreadBlocking(
+                () ->
+                        FakeServerHelperJni.get()
+                                .addCollaborationGroupToFakeServer(
+                                        mNativeFakeServer, collaborationId));
+    }
+
     @NativeMethods
     interface Natives {
         long createFakeServer();
@@ -445,5 +454,8 @@
         void addCollaboration(long fakeServer, @JniType("std::string") String collaborationId);
 
         void removeCollaboration(long fakeServer, @JniType("std::string") String collaborationId);
+
+        void addCollaborationGroupToFakeServer(
+                long fakeServer, @JniType("std::string") String collaborationId);
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhoneTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhoneTest.java
index 5b31cb72..76c14cf 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhoneTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhoneTest.java
@@ -239,8 +239,7 @@
                                     null,
                                     false,
                                     AdaptiveToolbarButtonVariant.UNKNOWN,
-                                    0,
-                                    false));
+                                    0));
                     // Make sure the button is visible in the beginning of the test.
                     assertEquals(true, realMenuButtonCoordinator.isVisible());
 
@@ -349,8 +348,7 @@
                         null,
                         true,
                         AdaptiveToolbarButtonVariant.UNKNOWN,
-                        0,
-                        false);
+                        0);
 
         // Show a button, this will inflate the optional button view and create its coordinator.
         ThreadUtils.runOnUiThreadBlocking(() -> mToolbar.updateOptionalButton(buttonData));
@@ -394,8 +392,7 @@
                         null,
                         true,
                         AdaptiveToolbarButtonVariant.UNKNOWN,
-                        0,
-                        false);
+                        0);
 
         // Show a button, this will inflate the optional button view and create its coordinator.
         ThreadUtils.runOnUiThreadBlocking(() -> mToolbar.updateOptionalButton(buttonData));
@@ -439,8 +436,7 @@
                         null,
                         true,
                         AdaptiveToolbarButtonVariant.UNKNOWN,
-                        0,
-                        false);
+                        0);
 
         // Show a button, this will inflate the optional button view and create its coordinator.
         ThreadUtils.runOnUiThreadBlocking(() -> mToolbar.updateOptionalButton(buttonData));
@@ -589,8 +585,7 @@
                         null,
                         true,
                         AdaptiveToolbarButtonVariant.UNKNOWN,
-                        0,
-                        false);
+                        0);
         ThreadUtils.runOnUiThreadBlocking(() -> mToolbar.updateOptionalButton(buttonData));
         verify(mOptionalButtonCoordinator).updateButton(buttonData, false);
 
diff --git a/chrome/android/junit/BUILD.gn b/chrome/android/junit/BUILD.gn
index 123582c..236338d 100644
--- a/chrome/android/junit/BUILD.gn
+++ b/chrome/android/junit/BUILD.gn
@@ -67,6 +67,7 @@
       "//chrome/android:usage_stats_proto_java",
       "//chrome/android/features/keyboard_accessory:internal_java",
       "//chrome/android/features/tab_ui:java_resources",
+      "//chrome/android/features/tab_ui:test_support_javalib",
       "//chrome/android/features/tab_ui/public:java",
       "//chrome/android/features/tab_ui/public:ui_java_resources",
       "//chrome/android/webapk/libs/client:client_java",
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegateTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegateTest.java
index 0ca8809..83aa649e 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegateTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegateTest.java
@@ -5,7 +5,6 @@
 package org.chromium.chrome.browser.tab;
 
 import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -198,13 +197,4 @@
                 BrowserControlsState.BOTH,
                 controlsVisibilityDelegate.calculateVisibilityConstraints());
     }
-
-    @Test
-    public void testDestroy() {
-        TabStateBrowserControlsVisibilityDelegate controlsVisibilityDelegate =
-                new TabStateBrowserControlsVisibilityDelegate(mTabImpl);
-        verify(mTabImpl).addObserver(any());
-        controlsVisibilityDelegate.destroy();
-        verify(mTabImpl).removeObserver(any());
-    }
 }
diff --git a/chrome/app/BUILD.gn b/chrome/app/BUILD.gn
index 476d661..9b4b553d 100644
--- a/chrome/app/BUILD.gn
+++ b/chrome/app/BUILD.gn
@@ -82,14 +82,12 @@
   }
 }
 
-grit("generated_resources") {
+grit_strings("generated_resources") {
   source = "generated_resources.grd"
   defines = chrome_grit_defines + [ "is_cfm=${is_cfm}" ]
   output_dir = "$root_gen_dir/chrome"
-  outputs =
-      [ "grit/generated_resources.h" ] +
-      process_file_template(all_chrome_locales,
-                            [ "generated_resources_{{source_name_part}}.pak" ])
+  outputs = [ "grit/generated_resources.h" ]
+
   if (is_android) {
     create_android_resources = true
   }
@@ -110,14 +108,11 @@
   }
 }
 
-grit("branded_strings") {
+grit_strings("branded_strings") {
   source = "${branding_path_product}_strings.grd"
   defines = chrome_grit_defines
   output_dir = "$root_gen_dir/chrome"
-  outputs =
-      [ "grit/branded_strings.h" ] +
-      process_file_template(all_chrome_locales,
-                            [ "branded_strings_{{source_name_part}}.pak" ])
+  outputs = [ "grit/branded_strings.h" ]
 
   if (is_android) {
     create_android_resources = true
diff --git a/chrome/app/chrome_command_ids.h b/chrome/app/chrome_command_ids.h
index 7dbc937b..44f3323 100644
--- a/chrome/app/chrome_command_ids.h
+++ b/chrome/app/chrome_command_ids.h
@@ -351,6 +351,7 @@
 #define IDC_CONTENT_CONTEXT_OPENLINKINPROFILE 50108
 #define IDC_CONTENT_CONTEXT_OPENLINKBOOKMARKAPP 50109
 #define IDC_CONTENT_CONTEXT_OPENLINKPREVIEW 50110
+#define IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW 50111
 // Image items.
 #define IDC_CONTENT_CONTEXT_SAVEIMAGEAS 50120
 #define IDC_CONTENT_CONTEXT_COPYIMAGELOCATION 50121
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index b6aaaba..8c721e6 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -630,6 +630,9 @@
           <message name="IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD" desc="The name of the open a link in Incognito window command">
             Open link in inco&amp;gnito window
           </message>
+          <message name="IDS_CONTENT_CONTEXT_OPENLINKSPLITVIEW" translateable="false" desc="The name of the open a link in split view command">
+            Open link in split view
+          </message>
           <message name="IDS_CONTENT_CONTEXT_OPENLINKINPROFILES" desc="The name of the open a link as a different user context menu">
             Open link as
           </message>
@@ -939,6 +942,9 @@
           <message name="IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD" desc="In Title Case: The name of the open a link in Incognito window command">
             Open Link in Inco&amp;gnito Window
           </message>
+          <message name="IDS_CONTENT_CONTEXT_OPENLINKSPLITVIEW" translateable="false" desc="The name of the open a link in split view command">
+            Open Link in Split View
+          </message>
           <message name="IDS_CONTENT_CONTEXT_OPENLINKINPROFILES" desc="In Title Case: The name of the open a link as a different user context menu">
             Open Link as
           </message>
diff --git a/chrome/app/resources/BUILD.gn b/chrome/app/resources/BUILD.gn
index d896c75f..3b967e8f 100644
--- a/chrome/app/resources/BUILD.gn
+++ b/chrome/app/resources/BUILD.gn
@@ -11,17 +11,14 @@
 assert(!is_ios, "Chromium/iOS shouldn't use anything in //chrome")
 assert(!is_fuchsia, "Fuchsia shouldn't use anything in //chrome")
 
-grit("locale_settings") {
+grit_strings("locale_settings") {
   source = "locale_settings.grd"
   defines = chrome_grit_defines
   output_dir = "$root_gen_dir/chrome"
-  outputs =
-      [ "grit/locale_settings.h" ] +
-      process_file_template(all_chrome_locales,
-                            [ "locale_settings_{{source_name_part}}.pak" ])
+  outputs = [ "grit/locale_settings.h" ]
 }
 
-grit("platform_locale_settings") {
+grit_strings("platform_locale_settings") {
   if (is_win) {
     source = "locale_settings_win.grd"
   } else if (is_mac) {
@@ -38,9 +35,7 @@
   }
 
   defines = chrome_grit_defines
-  outputs = [ "grit/platform_locale_settings.h" ] +
-            process_file_template(
-                locales_with_pseudolocales,
-                [ "platform_locale_settings_{{source_name_part}}.pak" ])
+  outputs = [ "grit/platform_locale_settings.h" ]
+  locales = locales_with_pseudolocales
   output_dir = "$root_gen_dir/chrome"
 }
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 56cab97..be7189c 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -295,6 +295,8 @@
     "chrome_content_browser_client.h",
     "chrome_content_browser_client_binder_policies.cc",
     "chrome_content_browser_client_binder_policies.h",
+    "chrome_content_browser_client_navigation_throttles.cc",
+    "chrome_content_browser_client_navigation_throttles.h",
     "chrome_content_browser_client_parts.h",
     "chrome_content_browser_client_receiver_bindings.cc",
     "chrome_resource_bundle_helper.cc",
diff --git a/chrome/browser/OWNERS b/chrome/browser/OWNERS
index dcd151a..d0d86445 100644
--- a/chrome/browser/OWNERS
+++ b/chrome/browser/OWNERS
@@ -34,6 +34,8 @@
 per-file chrome_content_browser_client.cc=*
 per-file chrome_content_browser_client.h=*
 per-file chrome_content_browser_client_browsertest.cc=*
+per-file chrome_content_browser_client_navigation_throttles.cc=*
+per-file chrome_content_browser_client_navigation_throttles.h=*
 per-file chrome_content_browser_client_unittest.cc=*
 
 per-file chrome_browser_field_trials*=file://components/variations/OWNERS
diff --git a/chrome/browser/accessibility/accessibility_labels_service.cc b/chrome/browser/accessibility/accessibility_labels_service.cc
index 9a6b3a383..5e7a26c 100644
--- a/chrome/browser/accessibility/accessibility_labels_service.cc
+++ b/chrome/browser/accessibility/accessibility_labels_service.cc
@@ -63,7 +63,7 @@
     const PrefService* pref_service = profile_->GetPrefs();
     std::string accept_languages_pref =
         pref_service->GetString(language::prefs::kAcceptLanguages);
-    for (std::string lang :
+    for (const std::string& lang :
          base::SplitString(accept_languages_pref, ",", base::TRIM_WHITESPACE,
                            base::SPLIT_WANT_NONEMPTY)) {
       accept_languages.push_back(lang);
diff --git a/chrome/browser/accessibility/pdf_ocr_controller.cc b/chrome/browser/accessibility/pdf_ocr_controller.cc
index 76be195..f7f0b0c 100644
--- a/chrome/browser/accessibility/pdf_ocr_controller.cc
+++ b/chrome/browser/accessibility/pdf_ocr_controller.cc
@@ -134,7 +134,7 @@
 }
 
 void RecordAcceptLanguages(const std::string& accept_languages) {
-  for (std::string language :
+  for (const std::string& language :
        base::SplitString(accept_languages, ",", base::TRIM_WHITESPACE,
                          base::SPLIT_WANT_NONEMPTY)) {
     // Convert to a Chrome language code synonym. This language synonym is then
diff --git a/chrome/browser/apps/icon_standardizer.cc b/chrome/browser/apps/icon_standardizer.cc
index b2dd1f2..14f6f03 100644
--- a/chrome/browser/apps/icon_standardizer.cc
+++ b/chrome/browser/apps/icon_standardizer.cc
@@ -221,7 +221,7 @@
   TRACE_EVENT0("ui", "apps::StandardizeSize");
   gfx::ImageSkia final_image;
 
-  for (gfx::ImageSkiaRep rep : image.image_reps()) {
+  for (const gfx::ImageSkiaRep& rep : image.image_reps()) {
     std::optional<gfx::ImageSkiaRep> new_rep =
         StandardizeSizeOfImageRep(rep, rep.scale());
     if (!new_rep) {
@@ -344,7 +344,7 @@
   gfx::ImageSkia final_image;
   gfx::ImageSkia standard_size_image = StandardizeSize(image);
 
-  for (gfx::ImageSkiaRep rep : standard_size_image.image_reps()) {
+  for (const gfx::ImageSkiaRep& rep : standard_size_image.image_reps()) {
     std::optional<gfx::ImageSkiaRep> standard_rep =
         CreateStandardIconImageRep(rep, rep.scale());
     final_image.AddRepresentation(standard_rep.value_or(rep));
diff --git a/chrome/browser/apps/platform_apps/platform_app_navigation_redirector.cc b/chrome/browser/apps/platform_apps/platform_app_navigation_redirector.cc
index cda6618..3599e48e 100644
--- a/chrome/browser/apps/platform_apps/platform_app_navigation_redirector.cc
+++ b/chrome/browser/apps/platform_apps/platform_app_navigation_redirector.cc
@@ -84,30 +84,30 @@
 }  // namespace
 
 // static
-std::unique_ptr<content::NavigationThrottle>
-PlatformAppNavigationRedirector::MaybeCreateThrottleFor(
-    content::NavigationHandle* handle) {
+void PlatformAppNavigationRedirector::MaybeCreateAndAdd(
+    content::NavigationThrottleRegistry& registry) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DVLOG(1) << "Considering URL for redirection: " << handle->GetURL().spec();
+  content::NavigationHandle& handle = registry.GetNavigationHandle();
+  DVLOG(1) << "Considering URL for redirection: " << handle.GetURL().spec();
 
   content::BrowserContext* browser_context =
-      handle->GetWebContents()->GetBrowserContext();
+      handle.GetWebContents()->GetBrowserContext();
   DCHECK(browser_context);
 
-  if (!handle->IsInOutermostMainFrame()) {
+  if (!handle.IsInOutermostMainFrame()) {
     DVLOG(1) << "Skip redirection: navigation is from an iframe or inner page";
-    return nullptr;
+    return;
   }
 
   // Support only GET for now.
-  if (handle->IsPost()) {
+  if (handle.IsPost()) {
     DVLOG(1) << "Skip redirection: method is not GET";
-    return nullptr;
+    return;
   }
 
-  if (!handle->GetURL().SchemeIsHTTPOrHTTPS()) {
+  if (!handle.GetURL().SchemeIsHTTPOrHTTPS()) {
     DVLOG(1) << "Skip redirection: scheme is not HTTP or HTTPS";
-    return nullptr;
+    return;
   }
 
   // Redirect URLs to apps only in regular mode. Technically, apps are not
@@ -116,7 +116,7 @@
   Profile* profile = Profile::FromBrowserContext(browser_context);
   if (profile->IsOffTheRecord()) {
     DVLOG(1) << "Skip redirection: unsupported in incognito and guest modes";
-    return nullptr;
+    return;
   }
 
   for (const auto& extension_ref :
@@ -129,19 +129,20 @@
 
     const UrlHandlerInfo* handler =
         UrlHandlers::GetMatchingPlatformAppUrlHandler(extension_ref.get(),
-                                                      handle->GetURL());
+                                                      handle.GetURL());
     if (handler) {
       DVLOG(1) << "Found matching app handler for redirection: "
                << extension_ref->name() << "(" << extension_ref->id()
                << "):" << handler->id;
-      return std::make_unique<
-          navigation_interception::InterceptNavigationThrottle>(
-          handle,
-          base::BindRepeating(&LaunchAppWithUrl, extension_ref, handler->id),
-          navigation_interception::SynchronyMode::kSync, std::nullopt);
+      registry.AddThrottle(
+          std::make_unique<
+              navigation_interception::InterceptNavigationThrottle>(
+              registry,
+              base::BindRepeating(&LaunchAppWithUrl, extension_ref,
+                                  handler->id),
+              navigation_interception::SynchronyMode::kSync, std::nullopt));
     }
   }
 
   DVLOG(1) << "Skipping redirection: no matching app handler found";
-  return nullptr;
 }
diff --git a/chrome/browser/apps/platform_apps/platform_app_navigation_redirector.h b/chrome/browser/apps/platform_apps/platform_app_navigation_redirector.h
index 6ae67f8..e845bb7cb 100644
--- a/chrome/browser/apps/platform_apps/platform_app_navigation_redirector.h
+++ b/chrome/browser/apps/platform_apps/platform_app_navigation_redirector.h
@@ -5,13 +5,8 @@
 #ifndef CHROME_BROWSER_APPS_PLATFORM_APPS_PLATFORM_APP_NAVIGATION_REDIRECTOR_H_
 #define CHROME_BROWSER_APPS_PLATFORM_APPS_PLATFORM_APP_NAVIGATION_REDIRECTOR_H_
 
-#include <memory>
-
-#include "content/public/browser/navigation_throttle.h"
-
 namespace content {
-class NavigationHandle;
-class NavigationThrottle;
+class NavigationThrottleRegistry;
 }  // namespace content
 
 // This class creates navigation throttles that redirect navigations to URLs for
@@ -19,8 +14,7 @@
 // manifest key. Note that this is a UI thread class.
 class PlatformAppNavigationRedirector {
  public:
-  static std::unique_ptr<content::NavigationThrottle> MaybeCreateThrottleFor(
-      content::NavigationHandle* handle);
+  static void MaybeCreateAndAdd(content::NavigationThrottleRegistry& registry);
   PlatformAppNavigationRedirector(const PlatformAppNavigationRedirector&) =
       delete;
   PlatformAppNavigationRedirector& operator=(
diff --git a/chrome/browser/autocomplete/keyword_extensions_delegate_impl.h b/chrome/browser/autocomplete/keyword_extensions_delegate_impl.h
index 0369dfa..762a0ed 100644
--- a/chrome/browser/autocomplete/keyword_extensions_delegate_impl.h
+++ b/chrome/browser/autocomplete/keyword_extensions_delegate_impl.h
@@ -65,9 +65,7 @@
   void OnOmniboxDefaultSuggestionChanged() override;
 
   ACMatches* matches() { return &provider_->matches_; }
-  void set_done(bool done) {
-    provider_->done_ = done;
-  }
+  void set_done(bool done) { provider_->done_ = done; }
 
   // Notifies the KeywordProvider about asynchronous updates from the extension.
   void OnProviderUpdate(bool updated_matches);
diff --git a/chrome/browser/autocomplete/search_provider_unittest.cc b/chrome/browser/autocomplete/search_provider_unittest.cc
index 22e2af55..79c8b7d3 100644
--- a/chrome/browser/autocomplete/search_provider_unittest.cc
+++ b/chrome/browser/autocomplete/search_provider_unittest.cc
@@ -187,24 +187,24 @@
 // BaseSearchProviderTest -----------------------------------------------------
 
 // Base class that configures following environment:
-// . The TemplateURL default_t_url_ is set as the default provider.
-// . The TemplateURL keyword_t_url_ is added to the TemplateURLService.
-//   TemplateURL values are set by subclasses. Most tests use SearchProviderTest
-//   with valid ones.
-// . The URL created by using the search term term1_ with default_t_url_ is
+// - The `TemplateURL` `default_t_url_` is set as the default provider.
+// - The `TemplateURL` `keyword_t_url_` is added to the `TemplateURLService`.
+//   `TemplateURL` values are set by subclasses. Most tests use
+//   `SearchProviderTest` with valid ones.
+// - The URL created by using the search term `term1_` with `default_t_url_` is
 //   added to history.
-// . The URL created by using the search term keyword_term_ with keyword_t_url_
-//   is added to history.
-// . test_url_loader_factory_ is set as the URLLoaderFactory.
+// - The URL created by using the search term `keyword_term_` with
+//   `keyword_t_url_` is added to history.
+// - `test_url_loader_factory_` is set as the `URLLoaderFactory`.
 //
-// Most tests use SearchProviderTest subclass, see below.
+// Most tests use `SearchProviderTest` subclass, see below.
 class BaseSearchProviderTest : public testing::Test,
                                public AutocompleteProviderListener {
  public:
   struct ResultInfo {
-    ResultInfo() : result_type(AutocompleteMatchType::NUM_TYPES),
-                   allowed_to_be_default_match(false) {
-    }
+    ResultInfo()
+        : result_type(AutocompleteMatchType::NUM_TYPES),
+          allowed_to_be_default_match(false) {}
     ResultInfo(GURL gurl,
                AutocompleteMatch::Type result_type,
                bool allowed_to_be_default_match,
@@ -442,8 +442,7 @@
       for (size_t j = 0; j < cases[i].num_results; ++j) {
         EXPECT_EQ(cases[i].output[j].gurl, matches[j].destination_url);
         EXPECT_EQ(cases[i].output[j].result_type, matches[j].type);
-        EXPECT_EQ(cases[i].output[j].fill_into_edit,
-                  matches[j].fill_into_edit);
+        EXPECT_EQ(cases[i].output[j].fill_into_edit, matches[j].fill_into_edit);
         EXPECT_EQ(cases[i].output[j].allowed_to_be_default_match,
                   matches[j].allowed_to_be_default_match);
       }
@@ -1333,213 +1332,345 @@
     const ExpectedMatch matches[6];
     const std::string inline_autocompletion;
   } cases[] = {
-    // Ensure that suggestrelevance scores reorder matches.
-    { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
-      { { "a", true }, { "c", false }, { "b", false }, kEmptyExpectedMatch,
-        kEmptyExpectedMatch, kEmptyExpectedMatch },
-      std::string() },
-    { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
+      // Ensure that suggestrelevance scores reorder matches.
+      {"[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
+       {{"a", true},
+        {"c", false},
+        {"b", false},
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch},
+       std::string()},
+      {"[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
-        "\"google:suggestrelevance\":[1, 2]}]",
-      { { "a", true }, { "c.com", false }, { "b.com", false },
-        kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
-      std::string() },
+       "\"google:suggestrelevance\":[1, 2]}]",
+       {{"a", true},
+        {"c.com", false},
+        {"b.com", false},
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch},
+       std::string()},
 
-    // Without suggested relevance scores, we should only allow one
-    // navsuggest result to be be displayed.
-    { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
+      // Without suggested relevance scores, we should only allow one
+      // navsuggest result to be be displayed.
+      {"[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]",
-      { { "a", true }, { "b.com", false }, kEmptyExpectedMatch,
-        kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
-      std::string() },
+       {{"a", true},
+        {"b.com", false},
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch},
+       std::string()},
 
-    // Ensure that verbatimrelevance scores reorder or suppress verbatim.
-    // Negative values will have no effect; the calculated value will be used.
-    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999,"
-                             "\"google:suggestrelevance\":[9998]}]",
-      { { "a", true}, { "a1", false }, kEmptyExpectedMatch,
-        kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
-      std::string() },
-    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998,"
-                             "\"google:suggestrelevance\":[9999]}]",
-      { { "a1", true }, { "a", true }, kEmptyExpectedMatch,
-        kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
-      "1" },
-    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0,"
-                             "\"google:suggestrelevance\":[9999]}]",
-      { { "a1", true }, kEmptyExpectedMatch, kEmptyExpectedMatch,
-        kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
-      "1" },
-    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1,"
-                             "\"google:suggestrelevance\":[9999]}]",
-      { { "a1", true }, { "a", true }, kEmptyExpectedMatch,
-        kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
-      "1" },
-    { "[\"a\",[\"http://a.com\"],[],[],"
+      // Ensure that verbatimrelevance scores reorder or suppress verbatim.
+      // Negative values will have no effect; the calculated value will be used.
+      {"[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999,"
+       "\"google:suggestrelevance\":[9998]}]",
+       {{"a", true},
+        {"a1", false},
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch},
+       std::string()},
+      {"[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998,"
+       "\"google:suggestrelevance\":[9999]}]",
+       {{"a1", true},
+        {"a", true},
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch},
+       "1"},
+      {"[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0,"
+       "\"google:suggestrelevance\":[9999]}]",
+       {{"a1", true},
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch},
+       "1"},
+      {"[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1,"
+       "\"google:suggestrelevance\":[9999]}]",
+       {{"a1", true},
+        {"a", true},
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch},
+       "1"},
+      {"[\"a\",[\"http://a.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\"],"
-        "\"google:verbatimrelevance\":9999,"
-        "\"google:suggestrelevance\":[9998]}]",
-      { { "a", true }, { "a.com", false }, kEmptyExpectedMatch,
-        kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
-      std::string() },
-    { "[\"a\",[\"http://a.com\"],[],[],"
+       "\"google:verbatimrelevance\":9999,"
+       "\"google:suggestrelevance\":[9998]}]",
+       {{"a", true},
+        {"a.com", false},
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch},
+       std::string()},
+      {"[\"a\",[\"http://a.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\"],"
-        "\"google:verbatimrelevance\":9998,"
-        "\"google:suggestrelevance\":[9999]}]",
-      { { "a.com", true }, { "a", true }, kEmptyExpectedMatch,
-        kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
-      ".com" },
-    { "[\"a\",[\"http://a.com\"],[],[],"
+       "\"google:verbatimrelevance\":9998,"
+       "\"google:suggestrelevance\":[9999]}]",
+       {{"a.com", true},
+        {"a", true},
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch},
+       ".com"},
+      {"[\"a\",[\"http://a.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\"],"
-        "\"google:verbatimrelevance\":0,"
-        "\"google:suggestrelevance\":[9999]}]",
-      { { "a.com", true }, kEmptyExpectedMatch, kEmptyExpectedMatch,
-        kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
-      ".com" },
-    { "[\"a\",[\"http://a.com\"],[],[],"
+       "\"google:verbatimrelevance\":0,"
+       "\"google:suggestrelevance\":[9999]}]",
+       {{"a.com", true},
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch},
+       ".com"},
+      {"[\"a\",[\"http://a.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\"],"
-        "\"google:verbatimrelevance\":-1,"
-        "\"google:suggestrelevance\":[9999]}]",
-      { { "a.com", true }, { "a", true }, kEmptyExpectedMatch,
-        kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
-      ".com" },
+       "\"google:verbatimrelevance\":-1,"
+       "\"google:suggestrelevance\":[9999]}]",
+       {{"a.com", true},
+        {"a", true},
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch},
+       ".com"},
 
-    // Ensure that both types of relevance scores reorder matches together.
-    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997],"
-                                     "\"google:verbatimrelevance\":9998}]",
-      { { "a1", true }, { "a", true }, { "a2", false }, kEmptyExpectedMatch,
-        kEmptyExpectedMatch, kEmptyExpectedMatch },
-      "1" },
+      // Ensure that both types of relevance scores reorder matches together.
+      {"[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, "
+       "9997],\"google:verbatimrelevance\":9998}]",
+       {{"a1", true},
+        {"a", true},
+        {"a2", false},
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch},
+       "1"},
 
-    // Check that an inlineable result appears first regardless of its score.
-    // Also, if the result set lacks a single inlineable result, abandon the
-    // request to suppress verbatim (verbatim_relevance=0), which will then
-    // cause verbatim to appear (first).
-    { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]",
-      { { "a", true }, { "b", false }, kEmptyExpectedMatch,
-        kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
-      std::string() },
-    { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999],"
-                            "\"google:verbatimrelevance\":0}]",
-      { { "a", true }, { "b", false }, kEmptyExpectedMatch,
-        kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
-      std::string() },
-    { "[\"a\",[\"http://b.com\"],[],[],"
+      // Check that an inlineable result appears first regardless of its score.
+      // Also, if the result set lacks a single inlineable result, abandon the
+      // request to suppress verbatim (verbatim_relevance=0), which will then
+      // cause verbatim to appear (first).
+      {"[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]",
+       {{"a", true},
+        {"b", false},
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch},
+       std::string()},
+      {"[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999],"
+       "\"google:verbatimrelevance\":0}]",
+       {{"a", true},
+        {"b", false},
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch},
+       std::string()},
+      {"[\"a\",[\"http://b.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\"],"
-        "\"google:suggestrelevance\":[9999]}]",
-      { { "a", true }, { "b.com", false }, kEmptyExpectedMatch,
-        kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
-      std::string() },
-    { "[\"a\",[\"http://b.com\"],[],[],"
+       "\"google:suggestrelevance\":[9999]}]",
+       {{"a", true},
+        {"b.com", false},
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch},
+       std::string()},
+      {"[\"a\",[\"http://b.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\"],"
-        "\"google:suggestrelevance\":[9999],"
-        "\"google:verbatimrelevance\":0}]",
-      { { "a", true }, { "b.com", false }, kEmptyExpectedMatch,
-        kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
-      std::string() },
+       "\"google:suggestrelevance\":[9999],"
+       "\"google:verbatimrelevance\":0}]",
+       {{"a", true},
+        {"b.com", false},
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch},
+       std::string()},
 
-    // Allow low-scoring matches.
-    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]",
-      { { "a1", true }, kEmptyExpectedMatch, kEmptyExpectedMatch,
-        kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
-      "1" },
-    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":10}]",
-      { { "a1", true }, { "a", true }, kEmptyExpectedMatch,
-        kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
-      "1" },
-    { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[10],"
-                             "\"google:verbatimrelevance\":0}]",
-      { { "a1", true }, kEmptyExpectedMatch, kEmptyExpectedMatch,
-        kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
-      "1" },
-    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[10, 20],"
-                                     "\"google:verbatimrelevance\":0}]",
-      { { "a2", true }, { "a1", false }, kEmptyExpectedMatch,
-        kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
-      "2" },
-    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[10, 30],"
-      "\"google:verbatimrelevance\":20}]",
-      { { "a2", true }, { "a", true }, { "a1", false }, kEmptyExpectedMatch,
-        kEmptyExpectedMatch, kEmptyExpectedMatch },
-      "2" },
-    { "[\"a\",[\"http://a.com\"],[],[],"
+      // Allow low-scoring matches.
+      {"[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]",
+       {{"a1", true},
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch},
+       "1"},
+      {"[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":10}]",
+       {{"a1", true},
+        {"a", true},
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch},
+       "1"},
+      {"[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[10],"
+       "\"google:verbatimrelevance\":0}]",
+       {{"a1", true},
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch},
+       "1"},
+      {"[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[10, 20],"
+       "\"google:verbatimrelevance\":0}]",
+       {{"a2", true},
+        {"a1", false},
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch},
+       "2"},
+      {"[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[10, 30],"
+       "\"google:verbatimrelevance\":20}]",
+       {{"a2", true},
+        {"a", true},
+        {"a1", false},
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch},
+       "2"},
+      {"[\"a\",[\"http://a.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\"],"
-        "\"google:suggestrelevance\":[10],"
-        "\"google:verbatimrelevance\":0}]",
-      { { "a.com", true }, kEmptyExpectedMatch, kEmptyExpectedMatch,
-        kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
-      ".com" },
-    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
+       "\"google:suggestrelevance\":[10],"
+       "\"google:verbatimrelevance\":0}]",
+       {{"a.com", true},
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch},
+       ".com"},
+      {"[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
-        "\"google:suggestrelevance\":[10, 20],"
-        "\"google:verbatimrelevance\":0}]",
-      { { "a2.com", true }, { "a1.com", false }, kEmptyExpectedMatch,
-        kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
-      "2.com" },
+       "\"google:suggestrelevance\":[10, 20],"
+       "\"google:verbatimrelevance\":0}]",
+       {{"a2.com", true},
+        {"a1.com", false},
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch},
+       "2.com"},
 
-    // Ensure that all suggestions are considered, regardless of order.
-    { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[],"
+      // Ensure that all suggestions are considered, regardless of order.
+      {"[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[],"
        "{\"google:suggestrelevance\":[10, 20, 30, 40, 50, 60, 70]}]",
-      { { "a", true }, { "h", false }, { "g", false }, { "f", false },
-        { "e", false }, { "d", false } },
-      std::string() },
-    { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\","
-              "\"http://e.com\", \"http://f.com\", \"http://g.com\","
-              "\"http://h.com\"],[],[],"
+       {{"a", true},
+        {"h", false},
+        {"g", false},
+        {"f", false},
+        {"e", false},
+        {"d", false}},
+       std::string()},
+      {"[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\","
+       "\"http://e.com\", \"http://f.com\", \"http://g.com\","
+       "\"http://h.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\","
-                                "\"NAVIGATION\", \"NAVIGATION\","
-                                "\"NAVIGATION\", \"NAVIGATION\","
-                                "\"NAVIGATION\"],"
-        "\"google:suggestrelevance\":[10, 20, 30, 40, 50, 60, 70]}]",
-      { { "a", true }, { "h.com", false }, { "g.com", false },
-        { "f.com", false }, { "e.com", false }, { "d.com", false } },
-      std::string() },
+       "\"NAVIGATION\", \"NAVIGATION\","
+       "\"NAVIGATION\", \"NAVIGATION\","
+       "\"NAVIGATION\"],"
+       "\"google:suggestrelevance\":[10, 20, 30, 40, 50, 60, 70]}]",
+       {{"a", true},
+        {"h.com", false},
+        {"g.com", false},
+        {"f.com", false},
+        {"e.com", false},
+        {"d.com", false}},
+       std::string()},
 
-    // Ensure that incorrectly sized suggestion relevance lists are ignored.
-    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[10]}]",
-      { { "a", true }, { "a1", false }, { "a2", false }, kEmptyExpectedMatch,
-        kEmptyExpectedMatch, kEmptyExpectedMatch },
-      std::string() },
-    { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 10]}]",
-      { { "a", true }, { "a1", false }, kEmptyExpectedMatch,
-        kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
-      std::string() },
-    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
+      // Ensure that incorrectly sized suggestion relevance lists are ignored.
+      {"[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[10]}]",
+       {{"a", true},
+        {"a1", false},
+        {"a2", false},
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch},
+       std::string()},
+      {"[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 10]}]",
+       {{"a", true},
+        {"a1", false},
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch},
+       std::string()},
+      {"[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
-        "\"google:suggestrelevance\":[10]}]",
-      { { "a", true }, { "a1.com", false }, kEmptyExpectedMatch,
-        kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
-      std::string() },
-    { "[\"a\",[\"http://a1.com\"],[],[],"
+       "\"google:suggestrelevance\":[10]}]",
+       {{"a", true},
+        {"a1.com", false},
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch},
+       std::string()},
+      {"[\"a\",[\"http://a1.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\"],"
        "\"google:suggestrelevance\":[9999, 10]}]",
-      { { "a", true }, { "a1.com", false }, kEmptyExpectedMatch,
-        kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
-      std::string() },
+       {{"a", true},
+        {"a1.com", false},
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch},
+       std::string()},
 
-    // Ensure that all 'verbatim' results are merged with their maximum score.
-    { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
+      // Ensure that all 'verbatim' results are merged with their maximum score.
+      {"[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
        "{\"google:suggestrelevance\":[9998, 9997, 9999]}]",
-      { { "a2", true }, { "a", true }, { "a1", false }, kEmptyExpectedMatch,
-        kEmptyExpectedMatch, kEmptyExpectedMatch },
-      "2" },
-    { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
+       {{"a2", true},
+        {"a", true},
+        {"a1", false},
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch},
+       "2"},
+      {"[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
        "{\"google:suggestrelevance\":[9998, 9997, 9999],"
-        "\"google:verbatimrelevance\":0}]",
-      { { "a2", true }, { "a", true }, { "a1", false }, kEmptyExpectedMatch,
-        kEmptyExpectedMatch, kEmptyExpectedMatch },
-      "2" },
+       "\"google:verbatimrelevance\":0}]",
+       {{"a2", true},
+        {"a", true},
+        {"a1", false},
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch},
+       "2"},
 
-    // Ensure that verbatim is always generated without other suggestions.
-    // TODO(msw): Ensure verbatimrelevance is respected (except suppression).
-    { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]",
-      { { "a", true }, kEmptyExpectedMatch, kEmptyExpectedMatch,
-        kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
-      std::string() },
-    { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]",
-      { { "a", true }, kEmptyExpectedMatch, kEmptyExpectedMatch,
-        kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
-      std::string() },
+      // Ensure that verbatim is always generated without other suggestions.
+      // TODO(msw): Ensure verbatimrelevance is respected (except suppression).
+      {"[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]",
+       {{"a", true},
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch},
+       std::string()},
+      {"[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]",
+       {{"a", true},
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch},
+       std::string()},
   };
 
   for (auto& test_case : cases) {
@@ -1580,7 +1711,7 @@
     bool from_keyword;
     bool allowed_to_be_default_match;
   };
-  const KeywordFetcherMatch kEmptyMatch = { kNotApplicable, false, false };
+  const KeywordFetcherMatch kEmptyMatch = {kNotApplicable, false, false};
   struct Cases {
     const std::string json;
     const KeywordFetcherMatch matches[6];
@@ -2009,186 +2140,183 @@
     const std::string second_json;
     const ExpectedMatch second_async_matches[4];
   } cases[] = {
-    // A simple test that verifies we don't inline autocomplete after the
-    // first asynchronous response, but we do at the next keystroke if the
-    // response's results were good enough.  Furthermore, we should continue
-    // inline autocompleting after the second asynchronous response if the new
-    // top suggestion is the same as the old inline autocompleted suggestion.
-    { "[\"a\",[\"ab1\", \"ab2\"],[],[],"
+      // A simple test that verifies we don't inline autocomplete after the
+      // first asynchronous response, but we do at the next keystroke if the
+      // response's results were good enough.  Furthermore, we should continue
+      // inline autocompleting after the second asynchronous response if the new
+      // top suggestion is the same as the old inline autocompleted suggestion.
+      {"[\"a\",[\"ab1\", \"ab2\"],[],[],"
        "{\"google:verbatimrelevance\":9000,"
-        "\"google:suggestrelevance\":[9002, 9001]}]",
-      { { "a", true }, { "ab1", false }, { "ab2", false },
-        kEmptyExpectedMatch },
-      { { "ab1", true }, { "ab2", true }, { "ab", true },
-        kEmptyExpectedMatch },
-      "[\"ab\",[\"ab1\", \"ab2\"],[],[],"
+       "\"google:suggestrelevance\":[9002, 9001]}]",
+       {{"a", true}, {"ab1", false}, {"ab2", false}, kEmptyExpectedMatch},
+       {{"ab1", true}, {"ab2", true}, {"ab", true}, kEmptyExpectedMatch},
+       "[\"ab\",[\"ab1\", \"ab2\"],[],[],"
        "{\"google:verbatimrelevance\":9000,"
-        "\"google:suggestrelevance\":[9002, 9001]}]",
-      { { "ab1", true }, { "ab2", false }, { "ab", true },
-        kEmptyExpectedMatch } },
-    // Ditto, just for a navigation suggestion.
-    { "[\"a\",[\"ab1.com\", \"ab2.com\"],[],[],"
+       "\"google:suggestrelevance\":[9002, 9001]}]",
+       {{"ab1", true}, {"ab2", false}, {"ab", true}, kEmptyExpectedMatch}},
+      // Ditto, just for a navigation suggestion.
+      {"[\"a\",[\"ab1.com\", \"ab2.com\"],[],[],"
        "{\"google:verbatimrelevance\":9000,"
-        "\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
-        "\"google:suggestrelevance\":[9002, 9001]}]",
-      { { "a", true }, { "ab1.com", false }, { "ab2.com", false },
-        kEmptyExpectedMatch },
-      { { "ab1.com", true }, { "ab2.com", true }, { "ab", true },
-        kEmptyExpectedMatch },
-      "[\"ab\",[\"ab1.com\", \"ab2.com\"],[],[],"
+       "\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
+       "\"google:suggestrelevance\":[9002, 9001]}]",
+       {{"a", true},
+        {"ab1.com", false},
+        {"ab2.com", false},
+        kEmptyExpectedMatch},
+       {{"ab1.com", true},
+        {"ab2.com", true},
+        {"ab", true},
+        kEmptyExpectedMatch},
+       "[\"ab\",[\"ab1.com\", \"ab2.com\"],[],[],"
        "{\"google:verbatimrelevance\":9000,"
-        "\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
-        "\"google:suggestrelevance\":[9002, 9001]}]",
-      { { "ab1.com", true }, { "ab2.com", false }, { "ab", true },
-        kEmptyExpectedMatch } },
-    // A more realistic test of the same situation.
-    { "[\"a\",[\"abcdef\", \"abcdef.com\", \"abc\"],[],[],"
+       "\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
+       "\"google:suggestrelevance\":[9002, 9001]}]",
+       {{"ab1.com", true},
+        {"ab2.com", false},
+        {"ab", true},
+        kEmptyExpectedMatch}},
+      // A more realistic test of the same situation.
+      {"[\"a\",[\"abcdef\", \"abcdef.com\", \"abc\"],[],[],"
        "{\"google:verbatimrelevance\":900,"
-        "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\", \"QUERY\"],"
-        "\"google:suggestrelevance\":[1250, 1200, 1000]}]",
-      { { "a", true }, { "abcdef", false }, { "abcdef.com", false },
-        { "abc", false } },
-      { { "abcdef", true }, { "abcdef.com", true }, { "abc", true },
-        { "ab", true } },
-      "[\"ab\",[\"abcdef\", \"abcdef.com\", \"abc\"],[],[],"
+       "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\", \"QUERY\"],"
+       "\"google:suggestrelevance\":[1250, 1200, 1000]}]",
+       {{"a", true}, {"abcdef", false}, {"abcdef.com", false}, {"abc", false}},
+       {{"abcdef", true}, {"abcdef.com", true}, {"abc", true}, {"ab", true}},
+       "[\"ab\",[\"abcdef\", \"abcdef.com\", \"abc\"],[],[],"
        "{\"google:verbatimrelevance\":900,"
-        "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\", \"QUERY\"],"
-        "\"google:suggestrelevance\":[1250, 1200, 1000]}]",
-      { { "abcdef", true }, { "abcdef.com", false }, { "abc", false },
-        { "ab", true } } },
+       "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\", \"QUERY\"],"
+       "\"google:suggestrelevance\":[1250, 1200, 1000]}]",
+       {{"abcdef", true}, {"abcdef.com", false}, {"abc", false}, {"ab", true}}},
 
-    // Without an original inline autcompletion, a new inline autcompletion
-    // should be rejected.
-    { "[\"a\",[\"ab1\", \"ab2\"],[],[],"
+      // Without an original inline autcompletion, a new inline autcompletion
+      // should be rejected.
+      {"[\"a\",[\"ab1\", \"ab2\"],[],[],"
        "{\"google:verbatimrelevance\":9000,"
-        "\"google:suggestrelevance\":[8000, 7000]}]",
-      { { "a", true }, { "ab1", false }, { "ab2", false },
-        kEmptyExpectedMatch },
-      { { "ab", true }, { "ab1", true }, { "ab2", true },
-        kEmptyExpectedMatch },
-      "[\"ab\",[\"ab1\", \"ab2\"],[],[],"
+       "\"google:suggestrelevance\":[8000, 7000]}]",
+       {{"a", true}, {"ab1", false}, {"ab2", false}, kEmptyExpectedMatch},
+       {{"ab", true}, {"ab1", true}, {"ab2", true}, kEmptyExpectedMatch},
+       "[\"ab\",[\"ab1\", \"ab2\"],[],[],"
        "{\"google:verbatimrelevance\":9000,"
-        "\"google:suggestrelevance\":[9002, 9001]}]",
-      { { "ab", true }, { "ab1", false }, { "ab2", false },
-        kEmptyExpectedMatch } },
-    // For the same test except with the queries scored in the opposite order
-    // on the second JSON response, the queries should be ordered by the second
-    // response's scores, not the first.
-    { "[\"a\",[\"ab1\", \"ab2\"],[],[],"
+       "\"google:suggestrelevance\":[9002, 9001]}]",
+       {{"ab", true}, {"ab1", false}, {"ab2", false}, kEmptyExpectedMatch}},
+      // For the same test except with the queries scored in the opposite order
+      // on the second JSON response, the queries should be ordered by the
+      // second response's scores, not the first.
+      {"[\"a\",[\"ab1\", \"ab2\"],[],[],"
        "{\"google:verbatimrelevance\":9000,"
-        "\"google:suggestrelevance\":[8000, 7000]}]",
-      { { "a", true }, { "ab1", false }, { "ab2", false },
-        kEmptyExpectedMatch },
-      { { "ab", true }, { "ab1", true }, { "ab2", true },
-        kEmptyExpectedMatch },
-      "[\"ab\",[\"ab1\", \"ab2\"],[],[],"
+       "\"google:suggestrelevance\":[8000, 7000]}]",
+       {{"a", true}, {"ab1", false}, {"ab2", false}, kEmptyExpectedMatch},
+       {{"ab", true}, {"ab1", true}, {"ab2", true}, kEmptyExpectedMatch},
+       "[\"ab\",[\"ab1\", \"ab2\"],[],[],"
        "{\"google:verbatimrelevance\":9000,"
-        "\"google:suggestrelevance\":[9001, 9002]}]",
-      { { "ab", true }, { "ab2", false }, { "ab1", false },
-        kEmptyExpectedMatch } },
-    // Now, the same verifications but with the new inline autocompletion as a
-    // navsuggestion.  The new autocompletion should still be rejected.
-    { "[\"a\",[\"ab1.com\", \"ab2.com\"],[],[],"
+       "\"google:suggestrelevance\":[9001, 9002]}]",
+       {{"ab", true}, {"ab2", false}, {"ab1", false}, kEmptyExpectedMatch}},
+      // Now, the same verifications but with the new inline autocompletion as a
+      // navsuggestion.  The new autocompletion should still be rejected.
+      {"[\"a\",[\"ab1.com\", \"ab2.com\"],[],[],"
        "{\"google:verbatimrelevance\":9000,"
-        "\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
-        "\"google:suggestrelevance\":[8000, 7000]}]",
-      { { "a", true }, { "ab1.com", false }, { "ab2.com", false },
-        kEmptyExpectedMatch },
-      { { "ab", true }, { "ab1.com", true }, { "ab2.com", true },
-        kEmptyExpectedMatch },
-      "[\"ab\",[\"ab1.com\", \"ab2.com\"],[],[],"
+       "\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
+       "\"google:suggestrelevance\":[8000, 7000]}]",
+       {{"a", true},
+        {"ab1.com", false},
+        {"ab2.com", false},
+        kEmptyExpectedMatch},
+       {{"ab", true},
+        {"ab1.com", true},
+        {"ab2.com", true},
+        kEmptyExpectedMatch},
+       "[\"ab\",[\"ab1.com\", \"ab2.com\"],[],[],"
        "{\"google:verbatimrelevance\":9000,"
-        "\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
-        "\"google:suggestrelevance\":[9002, 9001]}]",
-      { { "ab", true }, { "ab1.com", false }, { "ab2.com", false },
-        kEmptyExpectedMatch } },
-    { "[\"a\",[\"ab1.com\", \"ab2.com\"],[],[],"
+       "\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
+       "\"google:suggestrelevance\":[9002, 9001]}]",
+       {{"ab", true},
+        {"ab1.com", false},
+        {"ab2.com", false},
+        kEmptyExpectedMatch}},
+      {"[\"a\",[\"ab1.com\", \"ab2.com\"],[],[],"
        "{\"google:verbatimrelevance\":9000,"
-        "\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
-        "\"google:suggestrelevance\":[8000, 7000]}]",
-      { { "a", true }, { "ab1.com", false }, { "ab2.com", false },
-        kEmptyExpectedMatch },
-      { { "ab", true }, { "ab1.com", true }, { "ab2.com", true },
-        kEmptyExpectedMatch },
-      "[\"ab\",[\"ab1.com\", \"ab2.com\"],[],[],"
+       "\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
+       "\"google:suggestrelevance\":[8000, 7000]}]",
+       {{"a", true},
+        {"ab1.com", false},
+        {"ab2.com", false},
+        kEmptyExpectedMatch},
+       {{"ab", true},
+        {"ab1.com", true},
+        {"ab2.com", true},
+        kEmptyExpectedMatch},
+       "[\"ab\",[\"ab1.com\", \"ab2.com\"],[],[],"
        "{\"google:verbatimrelevance\":9000,"
-        "\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
-        "\"google:suggestrelevance\":[9001, 9002]}]",
-      { { "ab", true }, { "ab2.com", false }, { "ab1.com", false },
-        kEmptyExpectedMatch } },
+       "\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
+       "\"google:suggestrelevance\":[9001, 9002]}]",
+       {{"ab", true},
+        {"ab2.com", false},
+        {"ab1.com", false},
+        kEmptyExpectedMatch}},
 
-    // It's okay to abandon an inline autocompletion asynchronously.
-    { "[\"a\",[\"ab1\", \"ab2\"],[],[],"
+      // It's okay to abandon an inline autocompletion asynchronously.
+      {"[\"a\",[\"ab1\", \"ab2\"],[],[],"
        "{\"google:verbatimrelevance\":9000,"
-        "\"google:suggestrelevance\":[9002, 9001]}]",
-      { { "a", true }, { "ab1", false }, { "ab2", false },
-        kEmptyExpectedMatch },
-      { { "ab1", true }, { "ab2", true }, { "ab", true },
-        kEmptyExpectedMatch },
-      "[\"ab\",[\"ab1\", \"ab2\"],[],[],"
+       "\"google:suggestrelevance\":[9002, 9001]}]",
+       {{"a", true}, {"ab1", false}, {"ab2", false}, kEmptyExpectedMatch},
+       {{"ab1", true}, {"ab2", true}, {"ab", true}, kEmptyExpectedMatch},
+       "[\"ab\",[\"ab1\", \"ab2\"],[],[],"
        "{\"google:verbatimrelevance\":9000,"
-        "\"google:suggestrelevance\":[8000, 7000]}]",
-      { { "ab", true }, { "ab1", true }, { "ab2", false },
-        kEmptyExpectedMatch } },
+       "\"google:suggestrelevance\":[8000, 7000]}]",
+       {{"ab", true}, {"ab1", true}, {"ab2", false}, kEmptyExpectedMatch}},
 
-    // If a suggestion is equivalent to the verbatim suggestion, it should be
-    // collapsed into one.  Furthermore, it should be allowed to be the default
-    // match even if it was not previously displayed inlined.  This test is
-    // mainly for checking the first_async_matches.
-    { "[\"a\",[\"A\"],[],[],"
+      // If a suggestion is equivalent to the verbatim suggestion, it should be
+      // collapsed into one.  Furthermore, it should be allowed to be the
+      // default match even if it was not previously displayed inlined.  This
+      // test is mainly for checking the first_async_matches.
+      {"[\"a\",[\"A\"],[],[],"
        "{\"google:verbatimrelevance\":9000, "
-        "\"google:suggestrelevance\":[9001]}]",
-      { { "A", true }, kEmptyExpectedMatch, kEmptyExpectedMatch,
-        kEmptyExpectedMatch },
-      { { "ab", true }, { "A", false }, kEmptyExpectedMatch,
-        kEmptyExpectedMatch },
-      std::string(),
-      { { "ab", true }, { "A", false }, kEmptyExpectedMatch,
-        kEmptyExpectedMatch } },
+       "\"google:suggestrelevance\":[9001]}]",
+       {{"A", true},
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch,
+        kEmptyExpectedMatch},
+       {{"ab", true}, {"A", false}, kEmptyExpectedMatch, kEmptyExpectedMatch},
+       std::string(),
+       {{"ab", true}, {"A", false}, kEmptyExpectedMatch, kEmptyExpectedMatch}},
 
-    // Note: it's possible that the suggest server returns a suggestion with
-    // an inline autocompletion (that as usual we delay in allowing it to
-    // be displayed as an inline autocompletion until the next keystroke),
-    // then, in response to the next keystroke, the server returns a different
-    // suggestion as an inline autocompletion.  This is not likely to happen.
-    // Regardless, if it does, one could imagine three different behaviors:
-    // - keep the original inline autocompletion until the next keystroke
-    //   (i.e., don't abandon an inline autocompletion asynchronously), then
-    //   use the new suggestion
-    // - abandon all inline autocompletions upon the server response, then use
-    //   the new suggestion on the next keystroke
-    // - ignore the new inline autocompletion provided by the server, yet
-    //   possibly keep the original if it scores well in the most recent
-    //   response, then use the new suggestion on the next keystroke
-    // All of these behaviors are reasonable.  The main thing we want to
-    // ensure is that the second asynchronous response shouldn't cause *a new*
-    // inline autocompletion to be displayed.  We test that here.
-    // The current implementation does the third bullet, but all of these
-    // behaviors seem reasonable.
-    { "[\"a\",[\"ab1\", \"ab2\"],[],[],"
+      // Note: it's possible that the suggest server returns a suggestion with
+      // an inline autocompletion (that as usual we delay in allowing it to
+      // be displayed as an inline autocompletion until the next keystroke),
+      // then, in response to the next keystroke, the server returns a different
+      // suggestion as an inline autocompletion.  This is not likely to happen.
+      // Regardless, if it does, one could imagine three different behaviors:
+      // - keep the original inline autocompletion until the next keystroke
+      //   (i.e., don't abandon an inline autocompletion asynchronously), then
+      //   use the new suggestion
+      // - abandon all inline autocompletions upon the server response, then use
+      //   the new suggestion on the next keystroke
+      // - ignore the new inline autocompletion provided by the server, yet
+      //   possibly keep the original if it scores well in the most recent
+      //   response, then use the new suggestion on the next keystroke
+      // All of these behaviors are reasonable.  The main thing we want to
+      // ensure is that the second asynchronous response shouldn't cause *a new*
+      // inline autocompletion to be displayed.  We test that here.
+      // The current implementation does the third bullet, but all of these
+      // behaviors seem reasonable.
+      {"[\"a\",[\"ab1\", \"ab2\"],[],[],"
        "{\"google:verbatimrelevance\":9000,"
-        "\"google:suggestrelevance\":[9002, 9001]}]",
-      { { "a", true }, { "ab1", false }, { "ab2", false },
-        kEmptyExpectedMatch },
-      { { "ab1", true }, { "ab2", true }, { "ab", true },
-        kEmptyExpectedMatch },
-      "[\"ab\",[\"ab1\", \"ab3\"],[],[],"
+       "\"google:suggestrelevance\":[9002, 9001]}]",
+       {{"a", true}, {"ab1", false}, {"ab2", false}, kEmptyExpectedMatch},
+       {{"ab1", true}, {"ab2", true}, {"ab", true}, kEmptyExpectedMatch},
+       "[\"ab\",[\"ab1\", \"ab3\"],[],[],"
        "{\"google:verbatimrelevance\":9000,"
-        "\"google:suggestrelevance\":[9002, 9900]}]",
-      { { "ab1", true }, { "ab3", false }, { "ab", true },
-        kEmptyExpectedMatch } },
-    { "[\"a\",[\"ab1\", \"ab2\"],[],[],"
+       "\"google:suggestrelevance\":[9002, 9900]}]",
+       {{"ab1", true}, {"ab3", false}, {"ab", true}, kEmptyExpectedMatch}},
+      {"[\"a\",[\"ab1\", \"ab2\"],[],[],"
        "{\"google:verbatimrelevance\":9000,"
-        "\"google:suggestrelevance\":[9002, 9001]}]",
-      { { "a", true }, { "ab1", false }, { "ab2", false },
-        kEmptyExpectedMatch },
-      { { "ab1", true }, { "ab2", true }, { "ab", true },
-        kEmptyExpectedMatch },
-      "[\"ab\",[\"ab1\", \"ab3\"],[],[],"
+       "\"google:suggestrelevance\":[9002, 9001]}]",
+       {{"a", true}, {"ab1", false}, {"ab2", false}, kEmptyExpectedMatch},
+       {{"ab1", true}, {"ab2", true}, {"ab", true}, kEmptyExpectedMatch},
+       "[\"ab\",[\"ab1\", \"ab3\"],[],[],"
        "{\"google:verbatimrelevance\":9000,"
-        "\"google:suggestrelevance\":[8000, 9500]}]",
-      { { "ab", true }, { "ab3", false }, { "ab1", true },
-        kEmptyExpectedMatch } },
+       "\"google:suggestrelevance\":[8000, 9500]}]",
+       {{"ab", true}, {"ab3", false}, {"ab1", true}, kEmptyExpectedMatch}},
   };
 
   for (auto& test_case : cases) {
@@ -2364,8 +2492,8 @@
   });
 
   for (size_t i = 0; i < std::size(cases); ++i) {
-    QueryForInputAndWaitForFetcherResponses(
-        cases[i].input, false, cases[i].json, std::string());
+    QueryForInputAndWaitForFetcherResponses(cases[i].input, false,
+                                            cases[i].json, std::string());
 
     const std::string description = "for input with json=" + cases[i].json;
     const ACMatches& matches = provider_->matches();
@@ -2376,12 +2504,12 @@
     size_t j = 0;
     // Ensure that the returned matches equal the expectations.
     for (; j < matches.size(); ++j)
-      EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j]),
-                matches[j].contents) << description;
+      EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j]), matches[j].contents)
+          << description;
     // Ensure that no expected matches are missing.
     for (; j < std::size(cases[i].matches); ++j)
-      EXPECT_EQ(kNotApplicable, cases[i].matches[j]) <<
-          "Case # " << i << " " << description;
+      EXPECT_EQ(kNotApplicable, cases[i].matches[j])
+          << "Case # " << i << " " << description;
   }
 }
 
@@ -2392,14 +2520,14 @@
     AutocompleteMatch::Type match_type;
     bool allowed_to_be_default_match;
   };
-  const DefaultFetcherUrlInputMatch kEmptyMatch =
-      { kNotApplicable, AutocompleteMatchType::NUM_TYPES, false };
+  const DefaultFetcherUrlInputMatch kEmptyMatch = {
+      kNotApplicable, AutocompleteMatchType::NUM_TYPES, false};
   struct {
     const std::string input;
     const std::string json;
     const DefaultFetcherUrlInputMatch output[4];
   } cases[] = {
-    // clang-format off
+      // clang-format off
     // Ensure NAVIGATION matches are allowed to be listed first for URL input.
     // Non-inlineable matches should not be allowed to be the default match.
     // Note that the top-scoring inlineable match is moved to the top
@@ -2480,7 +2608,7 @@
         { "http://a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
                                                                  true  },
         kEmptyMatch } },
-    // clang-format on
+      // clang-format on
   };
 
   for (auto& test_case : cases) {
@@ -2695,132 +2823,111 @@
     const bool allowed_to_be_default_match_in_regular_mode;
     const bool allowed_to_be_default_match_in_prevent_inline_mode;
   } cases[] = {
-    // Do not inline matches that do not contain the input; trim http as needed.
-    { "x",                 "http://www.abc.com",
-                                  "www.abc.com",  std::string(), false, false },
-    { "https:",            "http://www.abc.com",
-                                  "www.abc.com",  std::string(), false, false },
-    { "http://www.abc.com/a", "http://www.abc.com",
-                              "http://www.abc.com",  std::string(), false,
-                                                                    false },
+      // Do not inline matches that do not contain the input; trim http as
+      // needed.
+      {"x", "http://www.abc.com", "www.abc.com", std::string(), false, false},
+      {"https:", "http://www.abc.com", "www.abc.com", std::string(), false,
+       false},
+      {"http://www.abc.com/a", "http://www.abc.com", "http://www.abc.com",
+       std::string(), false, false},
 
-    // Do not inline matches with invalid input prefixes; trim http as needed.
-    { "ttp",              "http://www.abc.com",
-                                 "www.abc.com", std::string(), false, false },
-    { "://w",             "http://www.abc.com",
-                                 "www.abc.com", std::string(), false, false },
-    { "ww.",              "http://www.abc.com",
-                                 "www.abc.com", std::string(), false, false },
-    { ".ab",              "http://www.abc.com",
-                                 "www.abc.com", std::string(), false, false },
-    { "bc",               "http://www.abc.com",
-                                 "www.abc.com", std::string(), false, false },
-    { ".com",             "http://www.abc.com",
-                                 "www.abc.com", std::string(), false, false },
+      // Do not inline matches with invalid input prefixes; trim http as needed.
+      {"ttp", "http://www.abc.com", "www.abc.com", std::string(), false, false},
+      {"://w", "http://www.abc.com", "www.abc.com", std::string(), false,
+       false},
+      {"ww.", "http://www.abc.com", "www.abc.com", std::string(), false, false},
+      {".ab", "http://www.abc.com", "www.abc.com", std::string(), false, false},
+      {"bc", "http://www.abc.com", "www.abc.com", std::string(), false, false},
+      {".com", "http://www.abc.com", "www.abc.com", std::string(), false,
+       false},
 
-    // Do not inline matches that omit input domain labels; trim http as needed.
-    { "www.a",            "http://a.com",
-                                 "a.com",       std::string(), false, false },
-    { "http://www.a",     "http://a.com",
-                          "http://a.com",       std::string(), false, false },
-    { "www.a",            "ftp://a.com",
-                          "ftp://a.com",        std::string(), false, false },
-    { "ftp://www.a",      "ftp://a.com",
-                          "ftp://a.com",        std::string(), false, false },
+      // Do not inline matches that omit input domain labels; trim http as
+      // needed.
+      {"www.a", "http://a.com", "a.com", std::string(), false, false},
+      {"http://www.a", "http://a.com", "http://a.com", std::string(), false,
+       false},
+      {"www.a", "ftp://a.com", "ftp://a.com", std::string(), false, false},
+      {"ftp://www.a", "ftp://a.com", "ftp://a.com", std::string(), false,
+       false},
 
-    // Input matching but with nothing to inline will not yield an offset, but
-    // will be allowed to be default.
-    { "abc.com",             "http://www.abc.com",
-                                    "www.abc.com", std::string(), true, true },
-    { "http://www.abc.com",  "http://www.abc.com",
-                             "http://www.abc.com", std::string(), true, true },
+      // Input matching but with nothing to inline will not yield an offset, but
+      // will be allowed to be default.
+      {"abc.com", "http://www.abc.com", "www.abc.com", std::string(), true,
+       true},
+      {"http://www.abc.com", "http://www.abc.com", "http://www.abc.com",
+       std::string(), true, true},
 
-    // Inputs with trailing whitespace should inline when possible.
-    { "abc.com ",      "http://www.abc.com",
-                              "www.abc.com",      std::string(), true,  true },
-    { "abc.com ",      "http://www.abc.com/bar",
-                              "www.abc.com/bar",  "/bar",        false, false },
+      // Inputs with trailing whitespace should inline when possible.
+      {"abc.com ", "http://www.abc.com", "www.abc.com", std::string(), true,
+       true},
+      {"abc.com ", "http://www.abc.com/bar", "www.abc.com/bar", "/bar", false,
+       false},
 
-    // Inline matches when the input is a leading substring of the scheme.
-    { "h",             "http://www.abc.com",
-                       "http://www.abc.com", "ttp://www.abc.com", true, false },
-    { "http",          "http://www.abc.com",
-                       "http://www.abc.com", "://www.abc.com",    true, false },
+      // Inline matches when the input is a leading substring of the scheme.
+      {"h", "http://www.abc.com", "http://www.abc.com", "ttp://www.abc.com",
+       true, false},
+      {"http", "http://www.abc.com", "http://www.abc.com", "://www.abc.com",
+       true, false},
 
-    // Inline matches when the input is a leading substring of the full URL.
-    { "http:",             "http://www.abc.com",
-                           "http://www.abc.com", "//www.abc.com", true, false },
-    { "http://w",          "http://www.abc.com",
-                           "http://www.abc.com", "ww.abc.com",    true, false },
-    { "http://www.",       "http://www.abc.com",
-                           "http://www.abc.com", "abc.com",       true, false },
-    { "http://www.ab",     "http://www.abc.com",
-                           "http://www.abc.com", "c.com",         true, false },
-    { "http://www.abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo",
-                              "http://www.abc.com/path/file.htm?q=x#foo",
-                                                  "ath/file.htm?q=x#foo",
-                                                                  true, false },
-    { "http://abc.com/p",     "http://abc.com/path/file.htm?q=x#foo",
-                              "http://abc.com/path/file.htm?q=x#foo",
-                                              "ath/file.htm?q=x#foo",
-                                                                  true, false},
+      // Inline matches when the input is a leading substring of the full URL.
+      {"http:", "http://www.abc.com", "http://www.abc.com", "//www.abc.com",
+       true, false},
+      {"http://w", "http://www.abc.com", "http://www.abc.com", "ww.abc.com",
+       true, false},
+      {"http://www.", "http://www.abc.com", "http://www.abc.com", "abc.com",
+       true, false},
+      {"http://www.ab", "http://www.abc.com", "http://www.abc.com", "c.com",
+       true, false},
+      {"http://www.abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo",
+       "http://www.abc.com/path/file.htm?q=x#foo", "ath/file.htm?q=x#foo", true,
+       false},
+      {"http://abc.com/p", "http://abc.com/path/file.htm?q=x#foo",
+       "http://abc.com/path/file.htm?q=x#foo", "ath/file.htm?q=x#foo", true,
+       false},
 
-    // Inline matches with valid URLPrefixes; only trim "http://".
-    { "w",               "http://www.abc.com",
-                                "www.abc.com", "ww.abc.com", true, false },
-    { "www.a",           "http://www.abc.com",
-                                "www.abc.com", "bc.com",     true, false },
-    { "abc",             "http://www.abc.com",
-                                "www.abc.com", ".com",       true, false },
-    { "abc.c",           "http://www.abc.com",
-                                "www.abc.com", "om",         true, false },
-    { "abc.com/p",       "http://www.abc.com/path/file.htm?q=x#foo",
-                                "www.abc.com/path/file.htm?q=x#foo",
-                                             "ath/file.htm?q=x#foo",
-                                                             true, false },
-    { "abc.com/p",       "http://abc.com/path/file.htm?q=x#foo",
-                                "abc.com/path/file.htm?q=x#foo",
-                                         "ath/file.htm?q=x#foo",
-                                                             true, false },
+      // Inline matches with valid URLPrefixes; only trim "http://".
+      {"w", "http://www.abc.com", "www.abc.com", "ww.abc.com", true, false},
+      {"www.a", "http://www.abc.com", "www.abc.com", "bc.com", true, false},
+      {"abc", "http://www.abc.com", "www.abc.com", ".com", true, false},
+      {"abc.c", "http://www.abc.com", "www.abc.com", "om", true, false},
+      {"abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo",
+       "www.abc.com/path/file.htm?q=x#foo", "ath/file.htm?q=x#foo", true,
+       false},
+      {"abc.com/p", "http://abc.com/path/file.htm?q=x#foo",
+       "abc.com/path/file.htm?q=x#foo", "ath/file.htm?q=x#foo", true, false},
 
-    // Inline matches using the maximal URLPrefix components.
-    { "h",               "http://help.com",
-                                "help.com", "elp.com",     true, false },
-    { "http",            "http://http.com",
-                                "http.com", ".com",        true, false },
-    { "h",               "http://www.help.com",
-                                "www.help.com", "elp.com", true, false },
-    { "http",            "http://www.http.com",
-                                "www.http.com", ".com",    true, false },
-    { "w",               "http://www.www.com",
-                                "www.www.com",  "ww.com",  true, false },
+      // Inline matches using the maximal URLPrefix components.
+      {"h", "http://help.com", "help.com", "elp.com", true, false},
+      {"http", "http://http.com", "http.com", ".com", true, false},
+      {"h", "http://www.help.com", "www.help.com", "elp.com", true, false},
+      {"http", "http://www.http.com", "www.http.com", ".com", true, false},
+      {"w", "http://www.www.com", "www.www.com", "ww.com", true, false},
 
-    // Test similar behavior for the ftp and https schemes.
-    { "ftp://www.ab",  "ftp://www.abc.com/path/file.htm?q=x#foo",
-                       "ftp://www.abc.com/path/file.htm?q=x#foo",
-                                  "c.com/path/file.htm?q=x#foo",  true, false },
-    { "www.ab",        "ftp://www.abc.com/path/file.htm?q=x#foo",
-                       "ftp://www.abc.com/path/file.htm?q=x#foo",
-                                   "c.com/path/file.htm?q=x#foo", true, false },
-    { "ab",            "ftp://www.abc.com/path/file.htm?q=x#foo",
-                       "ftp://www.abc.com/path/file.htm?q=x#foo",
-                                   "c.com/path/file.htm?q=x#foo", true, false },
-    { "ab",            "ftp://abc.com/path/file.htm?q=x#foo",
-                       "ftp://abc.com/path/file.htm?q=x#foo",
-                               "c.com/path/file.htm?q=x#foo",     true, false },
-    { "https://www.ab",  "https://www.abc.com/path/file.htm?q=x#foo",
-                         "https://www.abc.com/path/file.htm?q=x#foo",
-                                       "c.com/path/file.htm?q=x#foo",
-                                                                  true, false },
-    { "www.ab",      "https://www.abc.com/path/file.htm?q=x#foo",
-                     "https://www.abc.com/path/file.htm?q=x#foo",
-                                   "c.com/path/file.htm?q=x#foo", true, false },
-    { "ab",          "https://www.abc.com/path/file.htm?q=x#foo",
-                     "https://www.abc.com/path/file.htm?q=x#foo",
-                                   "c.com/path/file.htm?q=x#foo", true, false },
-    { "ab",          "https://abc.com/path/file.htm?q=x#foo",
-                     "https://abc.com/path/file.htm?q=x#foo",
-                               "c.com/path/file.htm?q=x#foo",     true, false },
+      // Test similar behavior for the ftp and https schemes.
+      {"ftp://www.ab", "ftp://www.abc.com/path/file.htm?q=x#foo",
+       "ftp://www.abc.com/path/file.htm?q=x#foo", "c.com/path/file.htm?q=x#foo",
+       true, false},
+      {"www.ab", "ftp://www.abc.com/path/file.htm?q=x#foo",
+       "ftp://www.abc.com/path/file.htm?q=x#foo", "c.com/path/file.htm?q=x#foo",
+       true, false},
+      {"ab", "ftp://www.abc.com/path/file.htm?q=x#foo",
+       "ftp://www.abc.com/path/file.htm?q=x#foo", "c.com/path/file.htm?q=x#foo",
+       true, false},
+      {"ab", "ftp://abc.com/path/file.htm?q=x#foo",
+       "ftp://abc.com/path/file.htm?q=x#foo", "c.com/path/file.htm?q=x#foo",
+       true, false},
+      {"https://www.ab", "https://www.abc.com/path/file.htm?q=x#foo",
+       "https://www.abc.com/path/file.htm?q=x#foo",
+       "c.com/path/file.htm?q=x#foo", true, false},
+      {"www.ab", "https://www.abc.com/path/file.htm?q=x#foo",
+       "https://www.abc.com/path/file.htm?q=x#foo",
+       "c.com/path/file.htm?q=x#foo", true, false},
+      {"ab", "https://www.abc.com/path/file.htm?q=x#foo",
+       "https://www.abc.com/path/file.htm?q=x#foo",
+       "c.com/path/file.htm?q=x#foo", true, false},
+      {"ab", "https://abc.com/path/file.htm?q=x#foo",
+       "https://abc.com/path/file.htm?q=x#foo", "c.com/path/file.htm?q=x#foo",
+       true, false},
   };
 
   for (auto& test_case : cases) {
@@ -3058,9 +3165,8 @@
     std::string fill_into_edit;
     AutocompleteMatchType::Type type;
   };
-  const Match kEmptyMatch = {
-    kNotApplicable, kNotApplicable, kNotApplicable, kNotApplicable,
-    AutocompleteMatchType::NUM_TYPES};
+  const Match kEmptyMatch = {kNotApplicable, kNotApplicable, kNotApplicable,
+                             kNotApplicable, AutocompleteMatchType::NUM_TYPES};
 
   omnibox::EntityInfo entity_info;
   entity_info.set_name("xy");
@@ -3153,10 +3259,8 @@
     for (; j < matches.size(); ++j) {
       const Match& match = test_case.matches[j];
       SCOPED_TRACE(" and match index: " + base::NumberToString(j));
-      EXPECT_EQ(match.contents,
-                base::UTF16ToUTF8(matches[j].contents));
-      EXPECT_EQ(match.description,
-                base::UTF16ToUTF8(matches[j].description));
+      EXPECT_EQ(match.contents, base::UTF16ToUTF8(matches[j].contents));
+      EXPECT_EQ(match.description, base::UTF16ToUTF8(matches[j].description));
       EXPECT_EQ(match.query_params,
                 matches[j].search_terms_args->additional_query_params);
       EXPECT_EQ(match.fill_into_edit,
@@ -3183,10 +3287,9 @@
     AutocompleteMatchType::Type type;
     bool from_keyword;
   };
-  const Match kEmptyMatch = { kNotApplicable,
-                              false,
-                              AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
-                              false };
+  const Match kEmptyMatch = {kNotApplicable, false,
+                             AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
+                             false};
 
   struct {
     const std::string input_text;
@@ -3294,17 +3397,16 @@
   ClearAllResults();
 
   std::string input_str("abc");
-  QueryForInputAndWaitForFetcherResponses(
-      ASCIIToUTF16(input_str), false, "this is a bad non-json response",
-      std::string());
+  QueryForInputAndWaitForFetcherResponses(ASCIIToUTF16(input_str), false,
+                                          "this is a bad non-json response",
+                                          std::string());
 
   const ACMatches& matches = provider_->matches();
 
   // Should have exactly one "search what you typed" match
   ASSERT_EQ(1U, matches.size());
   EXPECT_EQ(input_str, base::UTF16ToUTF8(matches[0].contents));
-  EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
-            matches[0].type);
+  EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, matches[0].type);
 }
 
 // A basic test that verifies that the XSSI guarded JSON response is parsed
@@ -3314,9 +3416,7 @@
     std::string contents;
     AutocompleteMatchType::Type type;
   };
-  const Match kEmptyMatch = {
-      kNotApplicable, AutocompleteMatchType::NUM_TYPES
-  };
+  const Match kEmptyMatch = {kNotApplicable, AutocompleteMatchType::NUM_TYPES};
 
   struct Cases {
     const std::string input_text;
@@ -3648,8 +3748,8 @@
 
 TEST_F(SearchProviderTest, TestDeleteMatch) {
   const char kDeleteUrl[] = "https://www.google.com/complete/deleteitem?q=foo";
-  AutocompleteMatch match(
-      provider_.get(), 0, true, AutocompleteMatchType::SEARCH_SUGGEST);
+  AutocompleteMatch match(provider_.get(), 0, true,
+                          AutocompleteMatchType::SEARCH_SUGGEST);
   match.RecordAdditionalInfo(SearchProvider::kDeletionUrlKey, kDeleteUrl);
 
   // Test a successful deletion request.
diff --git a/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/AddToBookmarksToolbarButtonController.java b/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/AddToBookmarksToolbarButtonController.java
index 345483d..d9c3fad 100644
--- a/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/AddToBookmarksToolbarButtonController.java
+++ b/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/AddToBookmarksToolbarButtonController.java
@@ -95,8 +95,7 @@
                 /* supportsTinting= */ true,
                 /* iphCommandBuilder= */ null,
                 AdaptiveToolbarButtonVariant.ADD_TO_BOOKMARKS,
-                /* tooltipTextResId= */ Resources.ID_NULL,
-                /* showBackgroundHighlight= */ true);
+                /* tooltipTextResId= */ Resources.ID_NULL);
         mActivityLifecycleDispatcher = activityLifecycleDispatcher;
         mTabBookmarkerSupplier = tabBookmarkerSupplier;
         mTrackerSupplier = trackerSupplier;
@@ -129,7 +128,6 @@
                         AdaptiveToolbarButtonVariant.ADD_TO_BOOKMARKS,
                         /* actionChipLabelResId= */ Resources.ID_NULL,
                         /* tooltipTextResId= */ Resources.ID_NULL,
-                        /* showBackgroundHighlight= */ true,
                         /* hasErrorBadge= */ false);
 
         mIsTablet = DeviceFormFactor.isNonMultiDisplayContextOnTablet(mContext);
diff --git a/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BrowserStateBrowserControlsVisibilityDelegate.java b/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BrowserStateBrowserControlsVisibilityDelegate.java
index e27dc477..84ce777 100644
--- a/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BrowserStateBrowserControlsVisibilityDelegate.java
+++ b/chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BrowserStateBrowserControlsVisibilityDelegate.java
@@ -10,6 +10,7 @@
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.CommandLine;
+import org.chromium.base.lifetime.Destroyable;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.Supplier;
 import org.chromium.build.annotations.NullMarked;
@@ -24,8 +25,8 @@
  * running activity.
  */
 @NullMarked
-public class BrowserStateBrowserControlsVisibilityDelegate
-        extends BrowserControlsVisibilityDelegate {
+public class BrowserStateBrowserControlsVisibilityDelegate extends BrowserControlsVisibilityDelegate
+        implements Destroyable {
     /** Minimum duration (in milliseconds) that the controls are shown when requested. */
     @VisibleForTesting public static final long MINIMUM_SHOW_DURATION_MS = 3000;
 
@@ -129,6 +130,7 @@
         sDisableOverridesForTesting = true;
     }
 
+    /** Performs clean-up. */
     @Override
     public void destroy() {
         mHandler.removeCallbacksAndMessages(null);
diff --git a/chrome/browser/browser_switcher/browser_switcher_navigation_throttle.cc b/chrome/browser/browser_switcher/browser_switcher_navigation_throttle.cc
index 2663ec0..a4b8eb6 100644
--- a/chrome/browser/browser_switcher/browser_switcher_navigation_throttle.cc
+++ b/chrome/browser/browser_switcher/browser_switcher_navigation_throttle.cc
@@ -20,6 +20,7 @@
 #include "components/no_state_prefetch/browser/no_state_prefetch_contents.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/navigation_throttle_registry.h"
 #include "content/public/browser/web_contents.h"
 #include "net/base/url_util.h"
 
@@ -31,8 +32,9 @@
 void OpenBrowserSwitchPage(base::WeakPtr<content::WebContents> web_contents,
                            const GURL& url,
                            ui::PageTransition transition_type) {
-  if (!web_contents)
+  if (!web_contents) {
     return;
+  }
 
   GURL about_url(chrome::kChromeUIBrowserSwitchURL);
   about_url = net::AppendQueryParameter(about_url, "url", url.spec());
@@ -95,24 +97,27 @@
 }  // namespace
 
 // static
-std::unique_ptr<content::NavigationThrottle>
-BrowserSwitcherNavigationThrottle::MaybeCreateThrottleFor(
-    content::NavigationHandle* navigation) {
+void BrowserSwitcherNavigationThrottle::MaybeCreateAndAdd(
+    content::NavigationThrottleRegistry& registry) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
+  content::NavigationHandle& handle = registry.GetNavigationHandle();
   content::BrowserContext* browser_context =
-      navigation->GetWebContents()->GetBrowserContext();
+      handle.GetWebContents()->GetBrowserContext();
   Profile* profile = Profile::FromBrowserContext(browser_context);
 
-  if (!profile->IsRegularProfile())
-    return nullptr;
+  if (!profile->IsRegularProfile()) {
+    return;
+  }
 
-  if (!navigation->IsInPrimaryMainFrame())
-    return nullptr;
+  if (!handle.IsInPrimaryMainFrame()) {
+    return;
+  }
 
-  return std::make_unique<navigation_interception::InterceptNavigationThrottle>(
-      navigation, base::BindRepeating(&MaybeLaunchAlternativeBrowser),
-      navigation_interception::SynchronyMode::kSync, std::nullopt);
+  registry.AddThrottle(
+      std::make_unique<navigation_interception::InterceptNavigationThrottle>(
+          registry, base::BindRepeating(&MaybeLaunchAlternativeBrowser),
+          navigation_interception::SynchronyMode::kSync, std::nullopt));
 }
 
 }  // namespace browser_switcher
diff --git a/chrome/browser/browser_switcher/browser_switcher_navigation_throttle.h b/chrome/browser/browser_switcher/browser_switcher_navigation_throttle.h
index 245387dd..dee6b82c 100644
--- a/chrome/browser/browser_switcher/browser_switcher_navigation_throttle.h
+++ b/chrome/browser/browser_switcher/browser_switcher_navigation_throttle.h
@@ -5,7 +5,9 @@
 #ifndef CHROME_BROWSER_BROWSER_SWITCHER_BROWSER_SWITCHER_NAVIGATION_THROTTLE_H_
 #define CHROME_BROWSER_BROWSER_SWITCHER_BROWSER_SWITCHER_NAVIGATION_THROTTLE_H_
 
-#include "content/public/browser/navigation_throttle.h"
+namespace content {
+class NavigationThrottleRegistry;
+}  // namespace content
 
 namespace browser_switcher {
 
@@ -19,9 +21,8 @@
   BrowserSwitcherNavigationThrottle& operator=(
       const BrowserSwitcherNavigationThrottle&) = delete;
 
-  // Creates a |NavigationThrottle| if needed for the navigation.
-  static std::unique_ptr<content::NavigationThrottle> MaybeCreateThrottleFor(
-      content::NavigationHandle* navigation);
+  // Creates a `NavigationThrottle` if needed for the navigation.
+  static void MaybeCreateAndAdd(content::NavigationThrottleRegistry& registry);
 };
 
 }  // namespace browser_switcher
diff --git a/chrome/browser/browser_switcher/browser_switcher_navigation_throttle_unittest.cc b/chrome/browser/browser_switcher/browser_switcher_navigation_throttle_unittest.cc
index eec9050f..aaa68dc 100644
--- a/chrome/browser/browser_switcher/browser_switcher_navigation_throttle_unittest.cc
+++ b/chrome/browser/browser_switcher/browser_switcher_navigation_throttle_unittest.cc
@@ -20,6 +20,7 @@
 #include "content/public/browser/navigation_throttle.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/mock_navigation_handle.h"
+#include "content/public/test/mock_navigation_throttle_registry.h"
 #include "content/public/test/test_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -80,9 +81,8 @@
     return std::make_unique<NiceMock<MockNavigationHandle>>(url, main_rfh());
   }
 
-  std::unique_ptr<NavigationThrottle> CreateNavigationThrottle(
-      content::NavigationHandle* handle) {
-    return BrowserSwitcherNavigationThrottle::MaybeCreateThrottleFor(handle);
+  void CreateNavigationThrottle(content::NavigationThrottleRegistry& registry) {
+    BrowserSwitcherNavigationThrottle::MaybeCreateAndAdd(registry);
   }
 
   MockBrowserSwitcherSitelist* sitelist() { return sitelist_; }
@@ -102,19 +102,26 @@
   EXPECT_CALL(*sitelist(), GetDecision(_)).WillOnce(Return(stay()));
   std::unique_ptr<MockNavigationHandle> handle =
       CreateMockNavigationHandle(GURL("https://example.com/"));
-  std::unique_ptr<NavigationThrottle> throttle =
-      CreateNavigationThrottle(handle.get());
-  EXPECT_EQ(NavigationThrottle::PROCEED, throttle->WillStartRequest());
+  content::MockNavigationThrottleRegistry registry(
+      handle.get(),
+      content::MockNavigationThrottleRegistry::RegistrationMode::kHold);
+  CreateNavigationThrottle(registry);
+  ASSERT_EQ(1u, registry.throttles().size());
+  EXPECT_EQ(NavigationThrottle::PROCEED,
+            registry.throttles()[0]->WillStartRequest());
 }
 
 TEST_F(BrowserSwitcherNavigationThrottleTest, LaunchesOnStartRequest) {
   EXPECT_CALL(*sitelist(), GetDecision(_)).WillOnce(Return(go()));
   std::unique_ptr<MockNavigationHandle> handle =
       CreateMockNavigationHandle(GURL("https://example.com/"));
-  std::unique_ptr<NavigationThrottle> throttle =
-      CreateNavigationThrottle(handle.get());
+  content::MockNavigationThrottleRegistry registry(
+      handle.get(),
+      content::MockNavigationThrottleRegistry::RegistrationMode::kHold);
+  CreateNavigationThrottle(registry);
+  ASSERT_EQ(1u, registry.throttles().size());
   EXPECT_EQ(NavigationThrottle::CANCEL_AND_IGNORE,
-            throttle->WillStartRequest());
+            registry.throttles()[0]->WillStartRequest());
   base::RunLoop().RunUntilIdle();
 }
 
@@ -126,15 +133,19 @@
       CreateMockNavigationHandle(GURL("https://yahoo.com/"));
   ON_CALL(*handle, WasServerRedirect()).WillByDefault(Return(false));
 
-  std::unique_ptr<NavigationThrottle> throttle =
-      CreateNavigationThrottle(handle.get());
-  EXPECT_EQ(NavigationThrottle::PROCEED, throttle->WillStartRequest());
+  content::MockNavigationThrottleRegistry registry(
+      handle.get(),
+      content::MockNavigationThrottleRegistry::RegistrationMode::kHold);
+  CreateNavigationThrottle(registry);
+  EXPECT_EQ(1u, registry.throttles().size());
+  EXPECT_EQ(NavigationThrottle::PROCEED,
+            registry.throttles()[0]->WillStartRequest());
 
   ON_CALL(*handle, WasServerRedirect()).WillByDefault(Return(true));
 
   handle->set_url(GURL("https://bing.com/"));
   EXPECT_EQ(NavigationThrottle::CANCEL_AND_IGNORE,
-            throttle->WillRedirectRequest());
+            registry.throttles()[0]->WillRedirectRequest());
   base::RunLoop().RunUntilIdle();
 }
 
@@ -145,14 +156,15 @@
   handle->set_has_committed(true);
   handle->set_is_in_primary_main_frame(false);
 
-  std::unique_ptr<NavigationThrottle> throttle_non_primary_main_frame =
-      CreateNavigationThrottle(handle.get());
-  EXPECT_EQ(nullptr, throttle_non_primary_main_frame.get());
+  content::MockNavigationThrottleRegistry registry(
+      handle.get(),
+      content::MockNavigationThrottleRegistry::RegistrationMode::kHold);
+  CreateNavigationThrottle(registry);
+  EXPECT_EQ(0u, registry.throttles().size());
 
   handle->set_is_in_primary_main_frame(true);
-  std::unique_ptr<NavigationThrottle> throttle_in_primary_main_frame =
-      CreateNavigationThrottle(handle.get());
-  EXPECT_NE(nullptr, throttle_in_primary_main_frame.get());
+  CreateNavigationThrottle(registry);
+  EXPECT_EQ(1u, registry.throttles().size());
 }
 
 }  // namespace browser_switcher
diff --git a/chrome/browser/browsing_data/counters/site_settings_counter.cc b/chrome/browser/browsing_data/counters/site_settings_counter.cc
index b5d91f2..8b004195 100644
--- a/chrome/browser/browsing_data/counters/site_settings_counter.cc
+++ b/chrome/browser/browsing_data/counters/site_settings_counter.cc
@@ -111,7 +111,7 @@
   const std::vector<std::string> tab_discard_exceptions =
       performance_manager::user_tuning::prefs::GetTabDiscardExceptionsBetween(
           pref_service_, period_start, period_end);
-  for (auto exception : tab_discard_exceptions) {
+  for (const auto& exception : tab_discard_exceptions) {
     url_matcher::util::FilterComponents components;
     bool is_valid = url_matcher::util::FilterToComponents(
         exception, &components.scheme, &components.host,
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 2ee8a27d..57a1acd3 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -65,6 +65,7 @@
 #include "chrome/browser/child_process_host_flags.h"
 #include "chrome/browser/chrome_browser_main_extra_parts_nacl_deprecation.h"
 #include "chrome/browser/chrome_content_browser_client_binder_policies.h"
+#include "chrome/browser/chrome_content_browser_client_navigation_throttles.h"
 #include "chrome/browser/chrome_content_browser_client_parts.h"
 #include "chrome/browser/content_settings/cookie_settings_factory.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
@@ -277,7 +278,6 @@
 #include "components/no_state_prefetch/common/no_state_prefetch_final_status.h"
 #include "components/no_state_prefetch/common/no_state_prefetch_url_loader_throttle.h"
 #include "components/omnibox/common/omnibox_features.h"
-#include "components/page_load_metrics/browser/metrics_navigation_throttle.h"
 #include "components/page_load_metrics/browser/metrics_web_contents_observer.h"
 #include "components/payments/content/payment_handler_navigation_throttle.h"
 #include "components/payments/content/payment_request_display_manager.h"
@@ -5399,18 +5399,11 @@
 
 void ChromeContentBrowserClient::CreateThrottlesForNavigation(
     content::NavigationThrottleRegistry& registry) {
+  CreateAndAddChromeThrottlesForNavigation(registry);
+
   content::NavigationHandle& handle = registry.GetNavigationHandle();
-  if (handle.IsInMainFrame()) {
-    // MetricsNavigationThrottle requires that it runs before
-    // NavigationThrottles that may delay or cancel navigations, so only
-    // NavigationThrottles that don't delay or cancel navigations (e.g.
-    // throttles that are only observing callbacks without affecting navigation
-    // behavior) should be added before MetricsNavigationThrottle.
-    // TODO(https://crbug.com/412524375): This assumption is fragile. This
-    // should be cared by adding an attribute flag to
-    // NavigationThrottleRegistry::AddThrottle().
-    page_load_metrics::MetricsNavigationThrottle::CreateAndAdd(registry);
-  }
+  // TODO(https://crbug.com/412524375): Move the following code to
+  // CreateAndAddChromeThrottlesForNavigation().
 
 #if BUILDFLAG(IS_ANDROID)
   // TODO(davidben): This is insufficient to integrate with prerender properly.
@@ -5419,10 +5412,8 @@
       prerender::ChromeNoStatePrefetchContentsDelegate::FromWebContents(
           handle.GetWebContents());
   if (!no_state_prefetch_contents) {
-    registry.MaybeAddThrottle(
-        navigation_interception::InterceptNavigationDelegate::
-            MaybeCreateThrottleFor(
-                &handle, navigation_interception::SynchronyMode::kAsync));
+    navigation_interception::InterceptNavigationDelegate::MaybeCreateAndAdd(
+        registry, navigation_interception::SynchronyMode::kAsync);
   }
   registry.AddThrottle(InterceptOMADownloadNavigationThrottle::Create(&handle));
 
@@ -5436,8 +5427,7 @@
 #elif BUILDFLAG(ENABLE_PLATFORM_APPS)
   // Redirect some navigations to apps that have registered matching URL
   // handlers ('url_handlers' in the manifest).
-  registry.MaybeAddThrottle(
-      PlatformAppNavigationRedirector::MaybeCreateThrottleFor(&handle));
+  PlatformAppNavigationRedirector::MaybeCreateAndAdd(registry);
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS)
@@ -5556,7 +5546,7 @@
       PasswordManagerNavigationThrottle::MaybeCreateThrottleFor(&handle));
 
   registry.AddThrottle(std::make_unique<PolicyBlocklistNavigationThrottle>(
-      &handle, handle.GetWebContents()->GetBrowserContext()));
+      registry, handle.GetWebContents()->GetBrowserContext()));
 
   // Before setting up SSL error detection, configure SSLErrorHandler to invoke
   // the relevant extension API whenever an SSL interstitial is shown.
@@ -5636,9 +5626,8 @@
 #endif
 
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
-  registry.MaybeAddThrottle(
-      browser_switcher::BrowserSwitcherNavigationThrottle::
-          MaybeCreateThrottleFor(&handle));
+  browser_switcher::BrowserSwitcherNavigationThrottle::MaybeCreateAndAdd(
+      registry);
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS)
@@ -5712,7 +5701,7 @@
   registry.MaybeAddThrottle(MaybeCreateNavigationAblationThrottle(&handle));
 
 #if !BUILDFLAG(IS_ANDROID)
-  registry.MaybeAddThrottle(MaybeCreateWebViewSidePanelThrottleFor(&handle));
+  MaybeCreateAndAddWebViewSidePanelThrottle(registry);
 #endif
 
   auto* privacy_sandbox_settings =
@@ -5770,6 +5759,9 @@
   registry.MaybeAddThrottle(
       web_app::IsolatedWebAppThrottle::MaybeCreateThrottleFor(&handle));
 #endif  // !BUILDFLAG(IS_ANDROID)
+
+  // Add new throttles in CreateAndAddChromeThrottlesForNavigation() rather than
+  // here.
 }
 
 std::vector<std::unique_ptr<content::CommitDeferringCondition>>
diff --git a/chrome/browser/chrome_content_browser_client_navigation_throttles.cc b/chrome/browser/chrome_content_browser_client_navigation_throttles.cc
new file mode 100644
index 0000000..475a92c
--- /dev/null
+++ b/chrome/browser/chrome_content_browser_client_navigation_throttles.cc
@@ -0,0 +1,27 @@
+// Copyright 2025 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/chrome_content_browser_client_navigation_throttles.h"
+
+#include "components/page_load_metrics/browser/metrics_navigation_throttle.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/navigation_throttle_registry.h"
+
+void CreateAndAddChromeThrottlesForNavigation(
+    content::NavigationThrottleRegistry& registry) {
+  content::NavigationHandle& handle = registry.GetNavigationHandle();
+  if (handle.IsInMainFrame()) {
+    // MetricsNavigationThrottle requires that it runs before
+    // NavigationThrottles that may delay or cancel navigations, so only
+    // NavigationThrottles that don't delay or cancel navigations (e.g.
+    // throttles that are only observing callbacks without affecting navigation
+    // behavior) should be added before MetricsNavigationThrottle.
+    // TODO(https://crbug.com/412524375): This assumption is fragile. This
+    // should be cared by adding an attribute flag to
+    // NavigationThrottleRegistry::AddThrottle().
+    page_load_metrics::MetricsNavigationThrottle::CreateAndAdd(registry);
+  }
+
+  // Add new throttles here.
+}
\ No newline at end of file
diff --git a/chrome/browser/chrome_content_browser_client_navigation_throttles.h b/chrome/browser/chrome_content_browser_client_navigation_throttles.h
new file mode 100644
index 0000000..1d9fcb70
--- /dev/null
+++ b/chrome/browser/chrome_content_browser_client_navigation_throttles.h
@@ -0,0 +1,15 @@
+// Copyright 2025 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_CHROME_CONTENT_BROWSER_CLIENT_NAVIGATION_THROTTLE_H_
+#define CHROME_BROWSER_CHROME_CONTENT_BROWSER_CLIENT_NAVIGATION_THROTTLE_H_
+
+namespace content {
+class NavigationThrottleRegistry;
+}  // namespace content
+
+void CreateAndAddChromeThrottlesForNavigation(
+    content::NavigationThrottleRegistry& registry);
+
+#endif  // CHROME_BROWSER_CHROME_CONTENT_BROWSER_CLIENT_NAVIGATION_THROTTLE_H_
\ No newline at end of file
diff --git a/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImpl.java b/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImpl.java
index 6c536e8..9878e56 100644
--- a/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImpl.java
+++ b/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImpl.java
@@ -222,8 +222,7 @@
         ServiceStatus serviceStatus =
                 CollaborationServiceFactory.getForProfile(profile).getServiceStatus();
 
-        if (serviceStatus.signinStatus == SigninStatus.NOT_SIGNED_IN
-                && !signinManager.isSigninAllowed()) {
+        if (serviceStatus.signinStatus == SigninStatus.SIGNIN_DISABLED) {
             // The signin option is disabled manually by the user in settings.
             openSigninSettingsModel(resultCallback);
             return;
diff --git a/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImplUnitTest.java b/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImplUnitTest.java
index e4e3059..7926e7b 100644
--- a/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImplUnitTest.java
+++ b/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImplUnitTest.java
@@ -217,7 +217,13 @@
         createDelegate(FlowType.JOIN);
         long resultCallback = 1;
 
-        doReturn(false).when(mSigninManager).isSigninAllowed();
+        doReturn(
+                        new ServiceStatus(
+                                SigninStatus.SIGNIN_DISABLED,
+                                /* syncStatus= */ 0,
+                                /* collaborationStatus= */ 0))
+                .when(mCollaborationService)
+                .getServiceStatus();
 
         mCollaborationControllerDelegateImpl.showAuthenticationUi(resultCallback);
         verify(mLoadingFullscreenCoordinator).closeLoadingScreen();
diff --git a/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationIntegrationTest.java b/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationIntegrationTest.java
index 986f7f8..cb95023 100644
--- a/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationIntegrationTest.java
+++ b/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationIntegrationTest.java
@@ -9,10 +9,12 @@
 import static androidx.test.espresso.action.ViewActions.scrollTo;
 import static androidx.test.espresso.assertion.ViewAssertions.matches;
 import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayingAtLeast;
 import static androidx.test.espresso.matcher.ViewMatchers.withId;
 import static androidx.test.espresso.matcher.ViewMatchers.withText;
 
 import static org.hamcrest.Matchers.allOf;
+import static org.mockito.Mockito.doReturn;
 
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.addBlankTabs;
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.clickFirstCardFromTabSwitcher;
@@ -21,15 +23,25 @@
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.verifyTabSwitcherCardCount;
 import static org.chromium.ui.test.util.ViewUtils.onViewWaiting;
 
+import android.view.View;
+
+import androidx.test.espresso.UiController;
+import androidx.test.espresso.ViewAction;
 import androidx.test.filters.MediumTest;
 
+import org.hamcrest.Matcher;
 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.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 
+import org.chromium.base.Callback;
 import org.chromium.base.ThreadUtils;
+import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.DoNotBatch;
@@ -37,14 +49,17 @@
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.data_sharing.DataSharingServiceFactory;
+import org.chromium.chrome.browser.data_sharing.DataSharingTabManager;
 import org.chromium.chrome.browser.data_sharing.DataSharingUiDelegateAndroid;
 import org.chromium.chrome.browser.data_sharing.FakeDataSharingUIDelegateImpl;
 import org.chromium.chrome.browser.firstrun.FirstRunStatus;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.share.ShareDelegate;
 import org.chromium.chrome.browser.sync.SyncTestRule;
 import org.chromium.chrome.browser.tab.TabLaunchType;
+import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncServiceFactory;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.R;
 import org.chromium.components.data_sharing.DataSharingSDKDelegateBridge;
@@ -53,15 +68,21 @@
 import org.chromium.components.data_sharing.GroupToken;
 import org.chromium.components.signin.identitymanager.ConsentLevel;
 import org.chromium.components.signin.test.util.TestAccounts;
+import org.chromium.components.sync.UserSelectableType;
+import org.chromium.components.tab_group_sync.LocalTabGroupId;
+import org.chromium.components.tab_group_sync.SavedTabGroup;
+import org.chromium.components.tab_group_sync.TabGroupSyncService;
 import org.chromium.ui.test.util.DeviceRestriction;
 import org.chromium.ui.test.util.GmsCoreVersionRestriction;
 
+import java.util.Arrays;
+import java.util.HashSet;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /** Instrumentation tests for {@link CollaborationService}. */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
-@EnableFeatures({ChromeFeatureList.DATA_SHARING})
+@EnableFeatures({ChromeFeatureList.DATA_SHARING, ChromeFeatureList.TAB_GROUP_SYNC_ANDROID})
 @DoNotBatch(reason = "Tabs can't be closed reliably between tests.")
 // TODO(crbug.com/399444939) Re-enable on automotive devices if needed.
 // Only run on device non-auto and with valid Google services.
@@ -73,15 +94,21 @@
 
     private static final long WAIT_TIMEOUT_MS = 1000L;
     private static final String TEST_COLLABORATION_ID = "collaboration_id";
+    private static final String TEST_ACCESS_TOKEN = "access_token";
 
     @Rule(order = 0)
     public SyncTestRule mActivityTestRule = new SyncTestRule();
 
+    @Rule(order = 1)
+    public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+
     private FakeDataSharingUIDelegateImpl mDataSharingUIDelegate;
     private DataSharingSDKDelegateTestImpl mDataSharingSDKDelegate;
 
     private Profile mProfile;
     private String mUrl;
+    @Mock private ObservableSupplier<ShareDelegate> mShareDelegateSupplier;
+    @Mock private ShareDelegate mShareDelegate;
 
     public CollaborationIntegrationTest() {
         DataSharingUiDelegateAndroid.setForTesting(mDataSharingUIDelegate);
@@ -212,7 +239,11 @@
     public void testCollaborationCreateFlow() {
         final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
         final AtomicBoolean createCalled = new AtomicBoolean();
-        mDataSharingUIDelegate.setShowCreateFlowRunnable(() -> createCalled.set(true));
+        Callback<Boolean> callback =
+                (success) -> {
+                    createCalled.set(true);
+                };
+        mDataSharingUIDelegate.setShowCreateFlowCallback(callback);
 
         // Create a tab group and enter TabGridDialog.
         addBlankTabs(cta, false, 3);
@@ -262,4 +293,104 @@
         onViewWaiting(withText(R.string.collaboration_sync_description))
                 .check(matches(isDisplayed()));
     }
+
+    @Test
+    @MediumTest
+    public void testDataSharingShowShare() {
+        // Sign in and sets selected types for tab groups.
+        mActivityTestRule.setUpAccountAndSignInForTesting();
+        mActivityTestRule.setSelectedTypes(
+                true,
+                new HashSet<>(
+                        Arrays.asList(
+                                UserSelectableType.TABS, UserSelectableType.SAVED_TAB_GROUPS)));
+
+        // Create a tab group and open tab grid dialog.
+        final ChromeTabbedActivity cta = (ChromeTabbedActivity) mActivityTestRule.getActivity();
+        addBlankTabs(cta, false, 2);
+        enterTabSwitcher(cta);
+        mergeAllNormalTabsToAGroup(cta);
+        LocalTabGroupId localTabGroupId =
+                new LocalTabGroupId(
+                        cta.getTabModelSelector().getModel(false).getTabAt(0).getTabGroupId());
+        clickFirstCardFromTabSwitcher(cta);
+        CriteriaHelper.pollUiThread(() -> isDialogFullyVisible(cta));
+
+        // Setting create flow callback to show share sheet with share link.
+        Callback<Boolean> callback =
+                (success) -> {
+                    mDataSharingUIDelegate.forceGroupCreation(
+                            TEST_COLLABORATION_ID, TEST_ACCESS_TOKEN);
+                };
+        mDataSharingUIDelegate.setShowCreateFlowCallback(callback);
+        setupShareDelegateSupplier();
+
+        onView(withId(R.id.share_button)).perform(relaxedClick());
+        prepareToShareTabGroup(/* owner= */ true, localTabGroupId, TEST_COLLABORATION_ID);
+
+        // Check share button changes to manage.
+        onViewWaiting(withText(R.string.tab_grid_manage_button_text)).check(matches(isDisplayed()));
+    }
+
+    private static ViewAction relaxedClick() {
+        final ViewAction clickAction = click();
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isDisplayingAtLeast(51);
+            }
+
+            @Override
+            public String getDescription() {
+                return clickAction.getDescription();
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                clickAction.perform(uiController, view);
+            }
+        };
+    }
+
+    private boolean isDialogFullyVisible(ChromeTabbedActivity cta) {
+        View dialogView = cta.findViewById(R.id.dialog_parent_view);
+        View dialogContainerView = cta.findViewById(R.id.dialog_container_view);
+        return dialogView.getVisibility() == View.VISIBLE && dialogContainerView.getAlpha() == 1f;
+    }
+
+    // Prepares the tab group to be shared.
+    private void prepareToShareTabGroup(
+            boolean owner, LocalTabGroupId tabGroupId, String collaborationId) {
+        TabGroupSyncService tabGroupSyncService = getTabGroupSyncService();
+        assert (tabGroupSyncService != null && collaborationId != null);
+        mActivityTestRule.getFakeServerHelper().addCollaboration(collaborationId);
+        tabGroupSyncService.setCollaborationAvailableInFinderForTesting(collaborationId);
+        SavedTabGroup savedGroup = tabGroupSyncService.getGroup(tabGroupId);
+        assert (savedGroup != null && savedGroup.collaborationId == null);
+        mActivityTestRule.getFakeServerHelper().addCollaborationGroupToFakeServer(collaborationId);
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    mActivityTestRule.getSyncService().triggerRefresh();
+                });
+    }
+
+    /** Return the {@link} to TabGroupSyncService for the current profile. */
+    private TabGroupSyncService getTabGroupSyncService() {
+        return ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    return TabGroupSyncServiceFactory.getForProfile(mProfile);
+                });
+    }
+
+    // Mock share delegate and return success for opening the share sheet.
+    private void setupShareDelegateSupplier(){
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    doReturn(mShareDelegate).when(mShareDelegateSupplier).get();
+                    var rootUiCoordinator =
+                            mActivityTestRule.getActivity().getRootUiCoordinatorForTesting();
+                    DataSharingTabManager dstm = rootUiCoordinator.getDataSharingTabManager();
+                    dstm.setShareDelegateSupplierForTesting(mShareDelegateSupplier);
+                });
+    }
 }
diff --git a/chrome/browser/commerce/android/java/src/org/chromium/chrome/browser/commerce/PriceNotificationSettingsFragment.java b/chrome/browser/commerce/android/java/src/org/chromium/chrome/browser/commerce/PriceNotificationSettingsFragment.java
index fde524b..d1b6d100 100644
--- a/chrome/browser/commerce/android/java/src/org/chromium/chrome/browser/commerce/PriceNotificationSettingsFragment.java
+++ b/chrome/browser/commerce/android/java/src/org/chromium/chrome/browser/commerce/PriceNotificationSettingsFragment.java
@@ -6,7 +6,6 @@
 
 import static org.chromium.build.NullUtil.assumeNonNull;
 
-import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -16,6 +15,7 @@
 import androidx.annotation.VisibleForTesting;
 import androidx.preference.Preference;
 
+import org.chromium.base.Callback;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.ObservableSupplierImpl;
@@ -27,7 +27,7 @@
 import org.chromium.chrome.browser.preferences.PrefServiceUtil;
 import org.chromium.chrome.browser.settings.ChromeBaseSettingsFragment;
 import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
-import org.chromium.components.browser_ui.notifications.NotificationManagerProxyImpl;
+import org.chromium.components.browser_ui.notifications.BaseNotificationManagerProxyFactory;
 import org.chromium.components.browser_ui.notifications.NotificationProxyUtils;
 import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
 import org.chromium.components.browser_ui.settings.SettingsUtils;
@@ -127,45 +127,60 @@
      * state of notifications and the price tracking channel
      */
     private void updateMobileNotificationsText() {
-        if (mMobileNotificationsText == null) return;
+        arePriceTrackingNotificationsEnabled(
+                (enabled) -> {
+                    if (mMobileNotificationsText == null) return;
+                    String linkText =
+                            getString(R.string.chrome_notification_settings_for_price_tracking);
 
-        String linkText = getString(R.string.chrome_notification_settings_for_price_tracking);
+                    String settingsFullText;
+                    if (enabled) {
+                        settingsFullText =
+                                getString(
+                                        R.string.price_notifications_settings_mobile_description_on,
+                                        linkText);
+                    } else {
+                        settingsFullText =
+                                getString(
+                                        R.string
+                                                .price_notifications_settings_mobile_description_off,
+                                        linkText);
+                    }
 
-        String settingsFullText;
-        if (arePriceTrackingNotificationsEnabled()) {
-            settingsFullText =
-                    getString(
-                            R.string.price_notifications_settings_mobile_description_on, linkText);
-        } else {
-            settingsFullText =
-                    getString(
-                            R.string.price_notifications_settings_mobile_description_off, linkText);
-        }
+                    SpanApplier.SpanInfo info =
+                            new SpanApplier.SpanInfo(
+                                    "<link>",
+                                    "</link>",
+                                    new ChromeClickableSpan(
+                                            getContext(), (view) -> launchAppSettings()));
 
-        SpanApplier.SpanInfo info =
-                new SpanApplier.SpanInfo(
-                        "<link>",
-                        "</link>",
-                        new ChromeClickableSpan(getContext(), (view) -> launchAppSettings()));
-        SpanApplier.applySpans(settingsFullText, info);
-
-        mMobileNotificationsText.setSummary(SpanApplier.applySpans(settingsFullText, info));
+                    mMobileNotificationsText.setSummary(
+                            SpanApplier.applySpans(settingsFullText, info));
+                });
     }
 
     /**
-     * @return True if both app-level and price tracking notifications are enabled.
+     * Check if both app-level and price tracking notifications are enabled.
+     *
+     * @param callback Callback to return the result.
      */
-    private boolean arePriceTrackingNotificationsEnabled() {
-        NotificationChannel channel =
-                NotificationManagerProxyImpl.getInstance()
-                        .getNotificationChannel(
-                                ChromeChannelDefinitions.ChannelId.PRICE_DROP_DEFAULT);
-        if (NotificationProxyUtils.areNotificationsEnabled()
-                && channel != null
-                && channel.getImportance() != NotificationManager.IMPORTANCE_NONE) {
-            return true;
+    private void arePriceTrackingNotificationsEnabled(Callback<Boolean> callback) {
+        if (!NotificationProxyUtils.areNotificationsEnabled()) {
+            callback.onResult(false);
+            return;
         }
-        return false;
+        BaseNotificationManagerProxyFactory.create()
+                .getNotificationChannel(
+                        ChromeChannelDefinitions.ChannelId.PRICE_DROP_DEFAULT,
+                        (channel) -> {
+                            if (channel != null
+                                    && channel.getImportance()
+                                            != NotificationManager.IMPORTANCE_NONE) {
+                                callback.onResult(true);
+                            } else {
+                                callback.onResult(false);
+                            }
+                        });
     }
 
     /** Launch app settings so the user can view or change notification settings. */
diff --git a/chrome/browser/commerce/coupons/android/java/src/org/chromium/chrome/browser/commerce/coupons/DiscountsButtonController.java b/chrome/browser/commerce/coupons/android/java/src/org/chromium/chrome/browser/commerce/coupons/DiscountsButtonController.java
index 15d6329..45d3ace 100644
--- a/chrome/browser/commerce/coupons/android/java/src/org/chromium/chrome/browser/commerce/coupons/DiscountsButtonController.java
+++ b/chrome/browser/commerce/coupons/android/java/src/org/chromium/chrome/browser/commerce/coupons/DiscountsButtonController.java
@@ -45,8 +45,7 @@
                 /* supportsTinting= */ true,
                 /* iphCommandBuilder= */ null,
                 AdaptiveToolbarButtonVariant.DISCOUNTS,
-                /* tooltipTextResId= */ Resources.ID_NULL,
-                /* showBackgroundHighlight= */ true);
+                /* tooltipTextResId= */ Resources.ID_NULL);
 
         mBottomSheetController = bottomSheetController;
         mBottomSheetObserver =
diff --git a/chrome/browser/commerce/price_insights/android/java/src/org/chromium/chrome/browser/price_insights/PriceInsightsButtonController.java b/chrome/browser/commerce/price_insights/android/java/src/org/chromium/chrome/browser/price_insights/PriceInsightsButtonController.java
index 775957a..e585d2c1 100644
--- a/chrome/browser/commerce/price_insights/android/java/src/org/chromium/chrome/browser/price_insights/PriceInsightsButtonController.java
+++ b/chrome/browser/commerce/price_insights/android/java/src/org/chromium/chrome/browser/price_insights/PriceInsightsButtonController.java
@@ -73,8 +73,7 @@
                 /* supportsTinting= */ true,
                 /* iphCommandBuilder= */ null,
                 AdaptiveToolbarButtonVariant.PRICE_INSIGHTS,
-                /* tooltipTextResId= */ Resources.ID_NULL,
-                /* showBackgroundHighlight= */ true);
+                /* tooltipTextResId= */ Resources.ID_NULL);
 
         mContext = context;
         mBottomSheetController = bottomSheetController;
diff --git a/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceTrackingButtonController.java b/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceTrackingButtonController.java
index b75fb3e..16fb221 100644
--- a/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceTrackingButtonController.java
+++ b/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceTrackingButtonController.java
@@ -71,8 +71,7 @@
                 /* supportsTinting= */ true,
                 /* iphCommandBuilder= */ null,
                 AdaptiveToolbarButtonVariant.PRICE_TRACKING,
-                /* tooltipTextResId= */ Resources.ID_NULL,
-                /* showBackgroundHighlight= */ false);
+                /* tooltipTextResId= */ Resources.ID_NULL);
         mSnackbarManager = snackbarManager;
         mTabBookmarkerSupplier = tabBookmarkerSupplier;
         mBottomSheetController = bottomSheetController;
@@ -96,7 +95,6 @@
                         /* buttonVariant= */ AdaptiveToolbarButtonVariant.PRICE_TRACKING,
                         /* actionChipLabelResId= */ Resources.ID_NULL,
                         /* tooltipTextResId= */ Resources.ID_NULL,
-                        /* showBackgroundHighlight= */ true,
                         /* hasErrorBadge= */ false);
 
         mBottomSheetObserver =
diff --git a/chrome/browser/data_sharing/android/java/res/values/dimens.xml b/chrome/browser/data_sharing/android/java/res/values/dimens.xml
index 78adafc..a8d48c0 100644
--- a/chrome/browser/data_sharing/android/java/res/values/dimens.xml
+++ b/chrome/browser/data_sharing/android/java/res/values/dimens.xml
@@ -29,5 +29,6 @@
     <dimen name="recent_activity_content_area_min_height">200dp</dimen>
     <dimen name="recent_activity_favicon_size">22dp</dimen>
     <dimen name="recent_activity_favicon_bg_size">40dp</dimen>
+    <dimen name="recent_activity_menu_width">258dp</dimen>
 
 </resources>
diff --git a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManager.java b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManager.java
index 1c6375c..e23095e29 100644
--- a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManager.java
+++ b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManager.java
@@ -98,7 +98,7 @@
     private final ObservableSupplier<TabModelSelector> mTabModelSelectorSupplier;
     private final DataSharingTabGroupsDelegate mDataSharingTabGroupsDelegate;
     private final Supplier<BottomSheetController> mBottomSheetControllerSupplier;
-    private final ObservableSupplier<ShareDelegate> mShareDelegateSupplier;
+    private ObservableSupplier<ShareDelegate> mShareDelegateSupplier;
     private final WindowAndroid mWindowAndroid;
     private final Resources mResources;
     private final OneshotSupplier<TabGroupUiActionHandler> mTabGroupUiActionHandlerSupplier;
@@ -603,7 +603,6 @@
                                 DataSharingStringConfig.StringKey.LEARN_ABOUT_SHARED_TAB_GROUPS,
                                 R.string.collaboration_learn_about_shared_groups)
                         .build();
-
         String sessionId =
                 uiDelegate.showCreateFlow(
                         new DataSharingCreateUiConfig.Builder()
@@ -756,7 +755,7 @@
                                 R.string.collaboration_group_is_full_description)
                         .setResourceId(
                                 DataSharingStringConfig.StringKey.ACTIVITY_LOGS_TITLE,
-                                R.string.data_sharing_shared_tab_group_activity)
+                                R.string.data_sharing_shared_tab_groups_activity)
                         .build();
 
         DataSharingManageUiConfig.ManageCallback manageCallback =
@@ -956,4 +955,10 @@
     BulkFaviconUtil getBulkFaviconUtilForTesting() {
         return mBulkFaviconUtil;
     }
+
+    /** Override ShareDelegateSupplier for testing. */
+    public void setShareDelegateSupplierForTesting(
+            ObservableSupplier<ShareDelegate> shareDelegateSupplier) {
+        mShareDelegateSupplier = shareDelegateSupplier;
+    }
 }
diff --git a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/FakeDataSharingUIDelegateImpl.java b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/FakeDataSharingUIDelegateImpl.java
index e1cd88110..b5a82c7 100644
--- a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/FakeDataSharingUIDelegateImpl.java
+++ b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/FakeDataSharingUIDelegateImpl.java
@@ -6,6 +6,7 @@
 
 import androidx.annotation.Nullable;
 
+import org.chromium.base.Callback;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.components.data_sharing.DataSharingUIDelegate;
 import org.chromium.components.data_sharing.configs.DataSharingAvatarBitmapConfig;
@@ -20,8 +21,9 @@
 public class FakeDataSharingUIDelegateImpl implements DataSharingUIDelegate {
 
     private @Nullable Runnable mShowJoinFlowRunnable;
-    private @Nullable Runnable mShowCreateFlowRunnable;
+    private @Nullable Callback<Boolean> mShowCreateFlowCallback;
     private @Nullable Runnable mShowManageFlowRunnable;
+    private @Nullable DataSharingCreateUiConfig mCreateUiConfig;
 
     public FakeDataSharingUIDelegateImpl() {}
 
@@ -33,7 +35,11 @@
 
     @Override
     public String showCreateFlow(DataSharingCreateUiConfig createUiConfig) {
-        if (mShowCreateFlowRunnable != null) mShowCreateFlowRunnable.run();
+        assert this.mCreateUiConfig == null;
+        this.mCreateUiConfig = createUiConfig;
+        if (mShowCreateFlowCallback != null) {
+            mShowCreateFlowCallback.onResult(true);
+        }
         return "";
     }
 
@@ -61,13 +67,30 @@
         mShowJoinFlowRunnable = runnable;
     }
 
-    /* Set a runnable to be called when showCreateFlow() is called. */
-    public void setShowCreateFlowRunnable(Runnable runnable) {
-        mShowCreateFlowRunnable = runnable;
+    /* Set a callback to be called when showCreateFlow() is called. */
+    public void setShowCreateFlowCallback(Callback<Boolean> callback) {
+        mShowCreateFlowCallback = callback;
     }
 
     /* Set a runnable to be called when showManageFlow() is called. */
     public void setShowManageFlowRunnable(Runnable runnable) {
         mShowManageFlowRunnable = runnable;
     }
+
+    /* Creates group data and calls onGroupCreatedWithWait when showCreateFlow() is called. */
+    public void forceGroupCreation(String collaborationId, String accessToken) {
+        org.chromium.components.sync.protocol.GroupData groupData =
+                org.chromium.components.sync.protocol.GroupData.newBuilder()
+                        .setGroupId(collaborationId)
+                        .setAccessToken(accessToken)
+                        .build();
+        if (mCreateUiConfig == null || mCreateUiConfig.getCreateCallback() == null) return;
+        mCreateUiConfig
+                .getCreateCallback()
+                .onGroupCreatedWithWait(
+                        groupData,
+                        (success) -> {
+                            assert success;
+                        });
+    }
 }
diff --git a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/recent_activity/RecentActivityListCoordinator.java b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/recent_activity/RecentActivityListCoordinator.java
index edff4551..77210e0 100644
--- a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/recent_activity/RecentActivityListCoordinator.java
+++ b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/recent_activity/RecentActivityListCoordinator.java
@@ -159,14 +159,14 @@
             ModelList modelList = new ModelList();
             modelList.add(
                     BrowserUiListMenuUtils.buildMenuListItem(
-                            R.string.data_sharing_shared_tab_group_activity,
+                            R.string.data_sharing_shared_tab_groups_activity,
                             R.id.see_full_activity,
                             0,
                             /* enabled= */ true));
             ListMenu.Delegate delegate =
                     (model) -> {
                         int textId = model.get(ListMenuItemProperties.TITLE_ID);
-                        if (textId == R.string.data_sharing_shared_tab_group_activity) {
+                        if (textId == R.string.data_sharing_shared_tab_groups_activity) {
                             mShowFullActivityRunnable.run();
                         }
                     };
@@ -194,6 +194,8 @@
                         }
                     };
 
+            menuView.setMenuMaxWidth(
+                    view.getResources().getDimensionPixelSize(R.dimen.recent_activity_menu_width));
             menuView.setDelegate(listMenuDelegate);
             menuView.tryToFitLargestItem(true);
             menuView.showMenu();
diff --git a/chrome/browser/data_sharing/desktop/data_sharing_sdk_delegate_desktop.cc b/chrome/browser/data_sharing/desktop/data_sharing_sdk_delegate_desktop.cc
index f6b4b531..807f87f 100644
--- a/chrome/browser/data_sharing/desktop/data_sharing_sdk_delegate_desktop.cc
+++ b/chrome/browser/data_sharing/desktop/data_sharing_sdk_delegate_desktop.cc
@@ -46,7 +46,7 @@
                 ->page_handler();
         CHECK(handler);
         auto mojom_params = data_sharing::mojom::ReadGroupsParams::New();
-        for (auto group_param : params.group_params()) {
+        for (const auto& group_param : params.group_params()) {
           auto param = data_sharing::mojom::ReadGroupParams::New();
           param->group_id = group_param.group_id();
           param->consistency_token = group_param.consistency_token();
diff --git a/chrome/browser/devtools/devtools_file_helper.cc b/chrome/browser/devtools/devtools_file_helper.cc
index d5e02056..a7e5c57d 100644
--- a/chrome/browser/devtools/devtools_file_helper.cc
+++ b/chrome/browser/devtools/devtools_file_helper.cc
@@ -238,7 +238,7 @@
     const HandlePermissionsCallback& handle_permissions_callback) {
   auto file_system_paths =
       storage_->GetDraggedFileSystemPaths(GURL(file_system_url));
-  for (auto file_system_path : file_system_paths) {
+  for (const auto& file_system_path : file_system_paths) {
     InnerAddFileSystem(handle_permissions_callback, kDefaultFileSystemType,
                        file_system_path);
   }
@@ -432,7 +432,7 @@
         prefs::kDevToolsFileSystemPaths,
         base::BindRepeating(RunOnUIThread, change_handler_on_ui));
   }
-  for (auto file_system_path : file_system_paths_) {
+  for (const auto& file_system_path : file_system_paths_) {
     auto path = base::FilePath::FromUTF8Unsafe(file_system_path.first);
     auto file_system =
         storage_->RegisterFileSystem(path, file_system_path.second);
@@ -490,7 +490,7 @@
   remaining.swap(file_system_paths_);
   DCHECK(file_watcher_.get());
 
-  for (auto file_system_path : GetActiveFileSystemPaths()) {
+  for (const auto& file_system_path : GetActiveFileSystemPaths()) {
     if (remaining.find(file_system_path.first) == remaining.end()) {
       auto path = base::FilePath::FromUTF8Unsafe(file_system_path.first);
       auto file_system =
@@ -503,7 +503,7 @@
     file_system_paths_[file_system_path.first] = file_system_path.second;
   }
 
-  for (auto file_system : remaining) {
+  for (const auto& file_system : remaining) {
     delegate_->FileSystemRemoved(file_system.first);
     base::FilePath path = base::FilePath::FromUTF8Unsafe(file_system.first);
     storage_->UnregisterFileSystem(path);
diff --git a/chrome/browser/download/chrome_download_manager_delegate.cc b/chrome/browser/download/chrome_download_manager_delegate.cc
index f6edfd2..00a2081 100644
--- a/chrome/browser/download/chrome_download_manager_delegate.cc
+++ b/chrome/browser/download/chrome_download_manager_delegate.cc
@@ -1043,6 +1043,11 @@
            .is_attachment() &&
       offline_pages::OfflinePageUtils::CanDownloadAsOfflinePage(url,
                                                                 mime_type)) {
+#if BUILDFLAG(IS_ANDROID)
+    if (profile_->IsOffTheRecord()) {
+      return false;
+    }
+#endif  // BUILDFLAG(IS_ANDROID)
     offline_pages::OfflinePageUtils::ScheduleDownload(
         web_contents, offline_pages::kDownloadNamespace, url,
         offline_pages::OfflinePageUtils::DownloadUIActionFlags::ALL,
diff --git a/chrome/browser/download/download_prefs.cc b/chrome/browser/download/download_prefs.cc
index 86e7eb9e..e61ac542 100644
--- a/chrome/browser/download/download_prefs.cc
+++ b/chrome/browser/download/download_prefs.cc
@@ -525,7 +525,7 @@
 
 void DownloadPrefs::SaveAutoOpenState() {
   std::string extensions;
-  for (auto it : auto_open_by_user_) {
+  for (const auto& it : auto_open_by_user_) {
 #if BUILDFLAG(IS_WIN)
     // TODO(phajdan.jr): Why we're using Sys conversion here, but not in ctor?
     std::string this_extension = base::SysWideToUTF8(it);
diff --git a/chrome/browser/enterprise/data_controls/chrome_dlp_rules_manager.cc b/chrome/browser/enterprise/data_controls/chrome_dlp_rules_manager.cc
index 65bb803..28d9010a 100644
--- a/chrome/browser/enterprise/data_controls/chrome_dlp_rules_manager.cc
+++ b/chrome/browser/enterprise/data_controls/chrome_dlp_rules_manager.cc
@@ -188,7 +188,7 @@
   }
 
   std::map<Level, std::set<std::string>> result;
-  for (auto it : destination_level_map) {
+  for (const auto& it : destination_level_map) {
     if (it.first == kWildCardMatching) {
       result[it.second] = {it.first};
     } else if (it.second >= wildcard_level &&
diff --git a/chrome/browser/enterprise/profile_management/oidc_auth_response_capture_navigation_throttle.cc b/chrome/browser/enterprise/profile_management/oidc_auth_response_capture_navigation_throttle.cc
index e865d83..06c5b23c 100644
--- a/chrome/browser/enterprise/profile_management/oidc_auth_response_capture_navigation_throttle.cc
+++ b/chrome/browser/enterprise/profile_management/oidc_auth_response_capture_navigation_throttle.cc
@@ -113,7 +113,7 @@
         profile_management::features::kOidcAuthAdditionalUrls.Get(), ",",
         base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
 
-    for (std::string url : urls) {
+    for (const std::string& url : urls) {
       allowed_urls.push_back(url);
     }
   }
@@ -147,7 +147,7 @@
         profile_management::features::kOidcAuthAdditionalHosts.Get(), ",",
         base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
 
-    for (std::string host : hosts) {
+    for (const std::string& host : hosts) {
       allowed_hosts.push_back(host);
     }
   }
diff --git a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_apitest.cc b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_apitest.cc
index 930bb111..3de9dca 100644
--- a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_apitest.cc
+++ b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_apitest.cc
@@ -666,8 +666,8 @@
 #endif
 IN_PROC_BROWSER_TEST_F(EnterpriseReportingPrivateApiTest,
                        MAYBE_GetFileSystemInfo_Success) {
-  // Use the test runner process and binary as test parameters, as it will always
-  // be running.
+  // Use the test runner process and binary as test parameters, as it will
+  // always be running.
   auto test_runner_file_path =
       device_signals::GetProcessExePath(base::Process::Current().Pid());
 
@@ -1113,14 +1113,8 @@
   RunTest(kTestJS);
 }
 
-// TODO(crbug.com/414870123): Flaky on win.
-#if BUILDFLAG(IS_WIN)
-#define MAYBE_ReportingPolicyEnabled DISABLED_ReportingPolicyEnabled
-#else
-#define MAYBE_ReportingPolicyEnabled ReportingPolicyEnabled
-#endif
 IN_PROC_BROWSER_TEST_F(EnterpriseReportDataMaskingEventTest,
-                       MAYBE_ReportingPolicyEnabled) {
+                       ReportingPolicyEnabled) {
   auto event_validator = event_report_validator_helper_->CreateValidator();
 
   api::enterprise_reporting_private::TriggeredRuleInfo rule_info;
@@ -1137,6 +1131,8 @@
       api::enterprise_reporting_private::EventResult::kEventResultDataMasked;
   event.url = "https://foo.com";
   event.triggered_rule_info.push_back(std::move(rule_info));
+  base::RunLoop run_loop;
+  event_validator.SetDoneClosure(run_loop.QuitClosure());
   event_validator.ExpectDataMaskingEvent("test-user@chromium.org",
                                          profile()->GetPath().AsUTF8Unsafe(),
                                          std::move(event));
@@ -1147,6 +1143,7 @@
       profile()->GetPrefs(), true, {"sensitiveDataEvent"}, {});
 
   RunTest(kTestJS);
+  run_loop.Run();
 }
 
 class EnterpriseOnDataMaskingRulesTriggeredTest
diff --git a/chrome/browser/extensions/api/image_writer_private/operation.cc b/chrome/browser/extensions/api/image_writer_private/operation.cc
index 2dc7ca87..b50823c 100644
--- a/chrome/browser/extensions/api/image_writer_private/operation.cc
+++ b/chrome/browser/extensions/api/image_writer_private/operation.cc
@@ -27,6 +27,10 @@
 namespace extensions {
 namespace image_writer {
 
+crypto::obsolete::Md5 MakeMd5HasherForImageWriter() {
+  return crypto::obsolete::Md5();
+}
+
 namespace {
 
 // Returns true if the file at |image_path| is an archived image.
@@ -260,8 +264,6 @@
     return;
   }
 
-  base::MD5Init(&md5_context_);
-
   base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
   if (!file.IsValid()) {
     Error(error::kImageOpenError);
@@ -274,7 +276,8 @@
     return;
   }
 
-  PostTask(base::BindOnce(&Operation::MD5Chunk, this, std::move(file), 0,
+  PostTask(base::BindOnce(&Operation::MD5Chunk, this, std::move(file),
+                          MakeMd5HasherForImageWriter(), 0,
                           base::checked_cast<size_t>(file_size),
                           std::move(callback)));
 }
@@ -285,6 +288,7 @@
 
 void Operation::MD5Chunk(
     base::File file,
+    crypto::obsolete::Md5 md5,
     size_t bytes_processed,
     size_t bytes_total,
     base::OnceCallback<void(const std::string&)> callback) {
@@ -299,22 +303,20 @@
 
   if (read_size == 0) {
     // Nothing to read, we are done.
-    base::MD5Digest digest;
-    base::MD5Final(&digest, &md5_context_);
-    std::move(callback).Run(base::MD5DigestToBase16(digest));
+    std::move(callback).Run(base::ToLowerASCII(base::HexEncode(md5.Finish())));
   } else {
     int64_t offset = base::checked_cast<int64_t>(bytes_processed);
     auto target = base::span(buffer).first(read_size);
 
     if (file.ReadAndCheck(offset, target)) {
       // Process data.
-      base::MD5Update(&md5_context_, target);
+      md5.Update(target);
       bytes_processed += read_size;
       int percent_curr = (bytes_processed * kProgressComplete) / bytes_total;
       SetProgress(percent_curr);
 
       PostTask(base::BindOnce(&Operation::MD5Chunk, this, std::move(file),
-                              bytes_processed, bytes_total,
+                              std::move(md5), bytes_processed, bytes_total,
                               std::move(callback)));
       // Skip closing the file.
       return;
diff --git a/chrome/browser/extensions/api/image_writer_private/operation.h b/chrome/browser/extensions/api/image_writer_private/operation.h
index 37103c1c..b56140c 100644
--- a/chrome/browser/extensions/api/image_writer_private/operation.h
+++ b/chrome/browser/extensions/api/image_writer_private/operation.h
@@ -12,7 +12,6 @@
 #include "base/files/file.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/functional/callback.h"
-#include "base/hash/md5.h"
 #include "base/memory/ref_counted_memory.h"
 #include "base/memory/weak_ptr.h"
 #include "base/task/sequenced_task_runner.h"
@@ -20,6 +19,7 @@
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/common/extensions/api/image_writer_private.h"
+#include "crypto/obsolete/md5.h"
 #include "extensions/common/extension_id.h"
 
 #if BUILDFLAG(IS_CHROMEOS)
@@ -204,6 +204,7 @@
 
   // Incrementally calculates the MD5 sum of a file.
   void MD5Chunk(base::File file,
+                crypto::obsolete::Md5 md5,
                 size_t bytes_processed,
                 size_t bytes_total,
                 const base::OnceCallback<void(const std::string&)> callback);
@@ -221,10 +222,6 @@
   image_writer_api::Stage stage_;
   int progress_;
 
-  // MD5 contexts don't play well with smart pointers.  Just going to allocate
-  // memory here.  This requires that we only do one MD5 sum at a time.
-  base::MD5Context md5_context_;
-
   // Cleanup operations that must be run.  All these functions are run on
   // `task_runner_`.
   std::vector<base::OnceClosure> cleanup_functions_;
diff --git a/chrome/browser/extensions/api/image_writer_private/write_from_url_operation_unittest.cc b/chrome/browser/extensions/api/image_writer_private/write_from_url_operation_unittest.cc
index ab317fe..11901dd 100644
--- a/chrome/browser/extensions/api/image_writer_private/write_from_url_operation_unittest.cc
+++ b/chrome/browser/extensions/api/image_writer_private/write_from_url_operation_unittest.cc
@@ -17,6 +17,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/test/url_loader_interceptor.h"
+#include "crypto/obsolete/md5.h"
 
 namespace extensions {
 namespace image_writer {
@@ -202,12 +203,10 @@
 }
 
 TEST_F(ImageWriterWriteFromUrlOperationTest, VerifyFile) {
-  base::HeapArray<char> char_buffer =
-      base::HeapArray<char>::Uninit(kTestFileSize);
-  base::ReadFile(test_utils_.GetImagePath(), char_buffer);
-  base::MD5Digest expected_digest;
-  base::MD5Sum(base::as_bytes(char_buffer.as_span()), &expected_digest);
-  std::string expected_hash = base::MD5DigestToBase16(expected_digest);
+  auto buffer = base::HeapArray<uint8_t>::Uninit(kTestFileSize);
+  base::ReadFile(test_utils_.GetImagePath(), buffer);
+  std::string expected_hash = base::ToLowerASCII(
+      base::HexEncode(crypto::obsolete::Md5::HashForTesting(buffer.as_span())));
 
   scoped_refptr<WriteFromUrlOperationForTest> operation =
       CreateOperation(GURL(""), expected_hash);
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index 06e7b27f0..c2b4aff 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -669,7 +669,7 @@
       remove_list.push_back(extension->id());
     }
   }
-  for (auto extension_id : remove_list) {
+  for (const auto& extension_id : remove_list) {
     std::u16string error;
     if (!extension_registrar_->UninstallExtension(
             extension_id, UNINSTALL_REASON_INTERNAL_MANAGEMENT, &error)) {
diff --git a/chrome/browser/first_party_sets/first_party_sets_overrides_policy_handler.cc b/chrome/browser/first_party_sets/first_party_sets_overrides_policy_handler.cc
index 92d3eb2..2f42e20 100644
--- a/chrome/browser/first_party_sets/first_party_sets_overrides_policy_handler.cc
+++ b/chrome/browser/first_party_sets/first_party_sets_overrides_policy_handler.cc
@@ -87,7 +87,7 @@
           policy_value->GetDict());
 
   // Output warnings that occur when parsing the policy.
-  for (ParseWarning parse_warning : warnings) {
+  for (const ParseWarning& parse_warning : warnings) {
     errors->AddError(policy_name(), IDS_POLICY_SCHEMA_VALIDATION_ERROR,
                      ParseWarningTypeToString(parse_warning.type()),
                      parse_warning.path(),
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 4786e1f4..a7987fc5 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -2541,7 +2541,7 @@
   },
   {
     "name": "enable-accessibility-manage-broadcast-recevier-on-background",
-    "owners": [ "avvall@google.com", "elabadysayed@google.com", "//ui/accessibility/OWNERS" ],
+    "owners": ["elabadysayed@google.com", "//ui/accessibility/OWNERS" ],
     "expiry_milestone": 145
   },
   {
diff --git a/chrome/browser/glic/host/context/glic_focused_tab_manager.cc b/chrome/browser/glic/host/context/glic_focused_tab_manager.cc
index 19dfce9..094a5ac0 100644
--- a/chrome/browser/glic/host/context/glic_focused_tab_manager.cc
+++ b/chrome/browser/glic/host/context/glic_focused_tab_manager.cc
@@ -16,6 +16,9 @@
 #include "chrome/browser/ui/browser_window.h"
 #include "content/public/common/url_constants.h"
 #include "ui/views/widget/widget.h"
+#if BUILDFLAG(IS_MAC)
+#include "ui/base/cocoa/appkit_utils.h"
+#endif
 
 namespace glic {
 
@@ -278,6 +281,12 @@
 }
 
 BrowserWindowInterface* GlicFocusedTabManager::ComputeBrowserCandidate() {
+#if BUILDFLAG(IS_MAC)
+  if (!ui::IsActiveApplication()) {
+    return nullptr;
+  }
+#endif
+
   if (window_controller_->IsAttached()) {
     // When attached, we only allow focus if attached window is active.
     Browser* const attached_browser = window_controller_->attached_browser();
diff --git a/chrome/browser/glic/widget/glic_window_animator.cc b/chrome/browser/glic/widget/glic_window_animator.cc
index 50b7baf..52b02c6e 100644
--- a/chrome/browser/glic/widget/glic_window_animator.cc
+++ b/chrome/browser/glic/widget/glic_window_animator.cc
@@ -16,25 +16,11 @@
 
 namespace glic {
 
-namespace {
-
-constexpr static int kResizeAnimationDurationMs = 300;
-
-}  // namespace
-
 GlicWindowAnimator::GlicWindowAnimator(GlicWindowController* window_controller)
     : window_controller_(window_controller) {}
 
 GlicWindowAnimator::~GlicWindowAnimator() = default;
 
-void GlicWindowAnimator::RunCloseAnimation(GlicButton* glic_button,
-                                           base::OnceClosure callback) {
-  // The widget is going away so it's fine to replace any existing animation.
-  AnimateBounds(glic_button->GetBoundsWithInset(),
-                base::Milliseconds(kResizeAnimationDurationMs),
-                std::move(callback));
-}
-
 void GlicWindowAnimator::AnimateBounds(const gfx::Rect& target_bounds,
                                        base::TimeDelta duration,
                                        base::OnceClosure callback) {
diff --git a/chrome/browser/glic/widget/glic_window_animator.h b/chrome/browser/glic/widget/glic_window_animator.h
index 446f284..db6b1e7c 100644
--- a/chrome/browser/glic/widget/glic_window_animator.h
+++ b/chrome/browser/glic/widget/glic_window_animator.h
@@ -27,9 +27,6 @@
   GlicWindowAnimator& operator=(const GlicWindowAnimator&) = delete;
   ~GlicWindowAnimator() override;
 
-  // Runs the attached close animation for the Glic widget.
-  void RunCloseAnimation(GlicButton* glic_button, base::OnceClosure callback);
-
   // Animate the window size, maintaining the position of the top right corner.
   // If there is already a running bounds change animation, update that
   // animation's target size.
diff --git a/chrome/browser/glic/widget/glic_window_controller.h b/chrome/browser/glic/widget/glic_window_controller.h
index acc5306..8983e7b 100644
--- a/chrome/browser/glic/widget/glic_window_controller.h
+++ b/chrome/browser/glic/widget/glic_window_controller.h
@@ -207,12 +207,9 @@
 
   // Possible states for the glic window. Public for testing.
   //   * Closed (aka hidden, invisible)
-  //   * OpenAnimation (showing an animation built into chrome, independent of
-  //     the content of the glic window)
   //   * Waiting for glic to load (the open animation has finished, but the
   //     glic window contents is not yet ready)
   //   * Open (aka showing, visible)
-  //   * CloseAnimation
   //   * Detaching
   //   * ClosingToReopenDetached
   enum class State {
@@ -221,7 +218,6 @@
     kOpen,
     kDetaching,
     kClosingToReopenDetached,
-    kCloseAnimation,
   };
   virtual State state() const = 0;
 
diff --git a/chrome/browser/glic/widget/glic_window_controller_impl.cc b/chrome/browser/glic/widget/glic_window_controller_impl.cc
index 5b835ec..7ecb640 100644
--- a/chrome/browser/glic/widget/glic_window_controller_impl.cc
+++ b/chrome/browser/glic/widget/glic_window_controller_impl.cc
@@ -1009,7 +1009,7 @@
 
 void GlicWindowControllerImpl::CloseInternal(
     std::optional<mojom::InvocationSource> reopen_detached_source) {
-  if (state_ == State::kCloseAnimation || state_ == State::kClosed) {
+  if (state_ == State::kClosed) {
     return;
   }
 
@@ -1024,16 +1024,7 @@
     glic_window_animator_->SetGlicWebViewVisibility(false);
   }
 
-  if (attached_browser_) {
-    SetWindowState(State::kCloseAnimation);
-    GlicButton* glic_button = GetGlicButton(*attached_browser_);
-    glic_window_animator_->RunCloseAnimation(
-        glic_button, base::BindOnce(&GlicWindowControllerImpl::CloseFinish,
-                                    weak_ptr_factory_.GetWeakPtr(),
-                                    reopen_detached, reopen_detached_source));
-  } else {
     CloseFinish(reopen_detached, reopen_detached_source);
-  }
 }
 
 void GlicWindowControllerImpl::CloseFinish(
@@ -1322,7 +1313,7 @@
 }
 
 bool GlicWindowControllerImpl::IsShowing() const {
-  return !(state_ == State::kClosed || state_ == State::kCloseAnimation);
+  return !(state_ == State::kClosed);
 }
 
 bool GlicWindowControllerImpl::IsPanelOrFreShowing() const {
diff --git a/chrome/browser/keyboard_accessory/android/BUILD.gn b/chrome/browser/keyboard_accessory/android/BUILD.gn
index df30f7b..d745bbf 100644
--- a/chrome/browser/keyboard_accessory/android/BUILD.gn
+++ b/chrome/browser/keyboard_accessory/android/BUILD.gn
@@ -55,7 +55,6 @@
     "//chrome/browser/password_manager/android/access_loss:test_support",
     "//chrome/browser/password_manager/android/grouped_affiliations:test_utils",
     "//chrome/browser/plus_addresses",
-    "//chrome/browser/plus_addresses:impl",
     "//chrome/test:test_support",
     "//components/autofill/content/browser:test_support",
     "//components/autofill/core/browser",
diff --git a/chrome/browser/media/router/discovery/access_code/access_code_cast_pref_updater_impl.cc b/chrome/browser/media/router/discovery/access_code/access_code_cast_pref_updater_impl.cc
index cc39284..05fd2b0 100644
--- a/chrome/browser/media/router/discovery/access_code/access_code_cast_pref_updater_impl.cc
+++ b/chrome/browser/media/router/discovery/access_code/access_code_cast_pref_updater_impl.cc
@@ -26,7 +26,7 @@
   // multiple times, we use ip-based deduping of stored media sinks to
   // ensure that the same sink (possibly with a different older stored named)
   // isn't stored twice in the cast list.
-  for (auto existing_sink_ids :
+  for (const auto& existing_sink_ids :
        AccessCodeCastPrefUpdaterImpl::GetMatchingIPEndPoints(
            pref_service_->GetDict(prefs::kAccessCodeCastDevices),
            sink.cast_data().ip_endpoint)) {
diff --git a/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service.cc b/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service.cc
index 35a4355..fc66b7fe2 100644
--- a/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service.cc
+++ b/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service.cc
@@ -732,7 +732,7 @@
 
 void AccessCodeCastSinkService::InitExpirationTimers(
     const std::vector<MediaSinkInternal>& cast_sinks) {
-  for (auto cast_sink : cast_sinks) {
+  for (const auto& cast_sink : cast_sinks) {
     SetExpirationTimer(cast_sink.id());
   }
 }
@@ -912,7 +912,7 @@
 void AccessCodeCastSinkService::AddStoredDevicesToMediaRouter(
     const std::vector<MediaSinkInternal>& cast_sinks) {
   std::vector<MediaSinkInternal> cast_sinks_to_add;
-  for (auto cast_sink : cast_sinks) {
+  for (const auto& cast_sink : cast_sinks) {
     AddSinkResultCallback callback =
         base::BindOnce(AddRememberedSinkMetricsCallback);
     AddSinkToMediaRouter(cast_sink, std::move(callback));
diff --git a/chrome/browser/media/router/mojo/media_router_desktop.cc b/chrome/browser/media/router/mojo/media_router_desktop.cc
index 9d998c9b..9be7ae6 100644
--- a/chrome/browser/media/router/mojo/media_router_desktop.cc
+++ b/chrome/browser/media/router/mojo/media_router_desktop.cc
@@ -98,9 +98,9 @@
     std::vector<MediaRoute> routes_a,
     std::vector<MediaRoute> routes_b) {
   std::vector<MediaRoute> routes;
-  for (auto route_a : routes_a) {
+  for (const auto& route_a : routes_a) {
     bool route_seen = false;
-    for (auto route_b : routes_b) {
+    for (const auto& route_b : routes_b) {
       if (route_a.media_route_id() == route_b.media_route_id()) {
         route_seen = true;
       }
diff --git a/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/NotificationWrapperBuilderFactory.java b/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/NotificationWrapperBuilderFactory.java
index 868aeca..0e6b3b7 100644
--- a/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/NotificationWrapperBuilderFactory.java
+++ b/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/NotificationWrapperBuilderFactory.java
@@ -10,7 +10,7 @@
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.browser.notifications.channels.ChromeChannelDefinitions;
-import org.chromium.components.browser_ui.notifications.NotificationManagerProxyImpl;
+import org.chromium.components.browser_ui.notifications.BaseNotificationManagerProxyFactory;
 import org.chromium.components.browser_ui.notifications.NotificationMetadata;
 import org.chromium.components.browser_ui.notifications.NotificationWrapperBuilder;
 import org.chromium.components.browser_ui.notifications.channels.ChannelsInitializer;
@@ -44,7 +44,7 @@
 
         ChannelsInitializer channelsInitializer =
                 new ChannelsInitializer(
-                        NotificationManagerProxyImpl.getInstance(),
+                        BaseNotificationManagerProxyFactory.create(),
                         ChromeChannelDefinitions.getInstance(),
                         context.getResources());
 
diff --git a/chrome/browser/on_device_translation/language_pack_util.h b/chrome/browser/on_device_translation/language_pack_util.h
index 246f6da9..e5157293 100644
--- a/chrome/browser/on_device_translation/language_pack_util.h
+++ b/chrome/browser/on_device_translation/language_pack_util.h
@@ -5,9 +5,11 @@
 #ifndef CHROME_BROWSER_ON_DEVICE_TRANSLATION_LANGUAGE_PACK_UTIL_H_
 #define CHROME_BROWSER_ON_DEVICE_TRANSLATION_LANGUAGE_PACK_UTIL_H_
 
-#include <array>
+#include <optional>
 #include <set>
+#include <string>
 #include <string_view>
+#include <vector>
 
 #include "base/containers/fixed_flat_map.h"
 
@@ -146,7 +148,7 @@
     const LanguagePackComponentConfig& config);
 
 // The config for the TranslateKit en-es language pack.
-const LanguagePackComponentConfig kTranslateKitEnEsConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitEnEsConfig = {
     .language1 = SupportedLanguage::kEn,
     .language2 = SupportedLanguage::kEs,
     .public_key_sha = {0x63, 0xbd, 0x10, 0x98, 0x4e, 0xaa, 0xc3, 0xbe,
@@ -156,7 +158,7 @@
 };
 
 // The config for the TranslateKit en-ja language pack.
-const LanguagePackComponentConfig kTranslateKitEnJaConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitEnJaConfig = {
     .language1 = SupportedLanguage::kEn,
     .language2 = SupportedLanguage::kJa,
     .public_key_sha = {0x7d, 0x22, 0x33, 0x74, 0x1c, 0xa8, 0x62, 0x58,
@@ -166,7 +168,7 @@
 };
 
 // The config for the TranslateKit ar-en language pack.
-const LanguagePackComponentConfig kTranslateKitArEnConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitArEnConfig = {
     .language1 = SupportedLanguage::kAr,
     .language2 = SupportedLanguage::kEn,
     .public_key_sha = {0xa1, 0xb3, 0x67, 0xf5, 0x5a, 0x8a, 0xfc, 0x1b,
@@ -175,7 +177,7 @@
                        0x6c, 0x5b, 0x90, 0x57, 0x79, 0x03, 0x74, 0x93}};
 
 // The config for the TranslateKit bn-en language pack.
-const LanguagePackComponentConfig kTranslateKitBnEnConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitBnEnConfig = {
     .language1 = SupportedLanguage::kBn,
     .language2 = SupportedLanguage::kEn,
     .public_key_sha = {0x62, 0x6f, 0x69, 0x31, 0x9c, 0x78, 0xf3, 0x92,
@@ -184,7 +186,7 @@
                        0x77, 0xa8, 0x59, 0xab, 0x34, 0xe9, 0xad, 0xaf}};
 
 // The config for the TranslateKit de-en language pack.
-const LanguagePackComponentConfig kTranslateKitDeEnConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitDeEnConfig = {
     .language1 = SupportedLanguage::kDe,
     .language2 = SupportedLanguage::kEn,
     .public_key_sha = {0xd7, 0x55, 0x73, 0xae, 0x4f, 0x45, 0x74, 0xab,
@@ -193,7 +195,7 @@
                        0x21, 0xbd, 0x81, 0x71, 0x91, 0x80, 0xdb, 0x13}};
 
 // The config for the TranslateKit en-fr language pack.
-const LanguagePackComponentConfig kTranslateKitEnFrConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitEnFrConfig = {
     .language1 = SupportedLanguage::kEn,
     .language2 = SupportedLanguage::kFr,
     .public_key_sha = {0xe5, 0xa2, 0x55, 0xf1, 0x20, 0x0a, 0xb3, 0x0f,
@@ -202,7 +204,7 @@
                        0x73, 0x1d, 0x96, 0xe4, 0x99, 0x14, 0x14, 0x45}};
 
 // The config for the TranslateKit en-hi language pack.
-const LanguagePackComponentConfig kTranslateKitEnHiConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitEnHiConfig = {
     .language1 = SupportedLanguage::kEn,
     .language2 = SupportedLanguage::kHi,
     .public_key_sha = {0xa9, 0x81, 0xd8, 0xbc, 0x0d, 0x1d, 0xdf, 0x28,
@@ -211,7 +213,7 @@
                        0x90, 0xb5, 0x4a, 0xc7, 0x2f, 0xc9, 0xc2, 0xab}};
 
 // The config for the TranslateKit en-it language pack.
-const LanguagePackComponentConfig kTranslateKitEnItConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitEnItConfig = {
     .language1 = SupportedLanguage::kEn,
     .language2 = SupportedLanguage::kIt,
     .public_key_sha = {0xd7, 0xc3, 0x40, 0x59, 0x34, 0x55, 0x61, 0x1e,
@@ -220,7 +222,7 @@
                        0x9e, 0x37, 0x55, 0xe8, 0x0e, 0xce, 0x42, 0x3e}};
 
 // The config for the TranslateKit en-ko language pack.
-const LanguagePackComponentConfig kTranslateKitEnKoConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitEnKoConfig = {
     .language1 = SupportedLanguage::kEn,
     .language2 = SupportedLanguage::kKo,
     .public_key_sha = {0xe2, 0x0c, 0x8f, 0x9e, 0x6d, 0x33, 0x06, 0x3c,
@@ -229,7 +231,7 @@
                        0xbd, 0xf4, 0x9b, 0x94, 0x02, 0x35, 0xcc, 0x7a}};
 
 // The config for the TranslateKit en-nl language pack.
-const LanguagePackComponentConfig kTranslateKitEnNlConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitEnNlConfig = {
     .language1 = SupportedLanguage::kEn,
     .language2 = SupportedLanguage::kNl,
     .public_key_sha = {0x7d, 0xb7, 0x14, 0x60, 0x47, 0xda, 0xd9, 0xaa,
@@ -238,7 +240,7 @@
                        0xf0, 0x70, 0x41, 0xb0, 0x90, 0xc3, 0x89, 0x6b}};
 
 // The config for the TranslateKit en-pl language pack.
-const LanguagePackComponentConfig kTranslateKitEnPlConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitEnPlConfig = {
     .language1 = SupportedLanguage::kEn,
     .language2 = SupportedLanguage::kPl,
     .public_key_sha = {0x10, 0x03, 0x30, 0xeb, 0xe4, 0x8f, 0x45, 0xf3,
@@ -247,7 +249,7 @@
                        0xb8, 0xf8, 0xa1, 0x6b, 0x61, 0xb9, 0x71, 0x91}};
 
 // The config for the TranslateKit en-pt language pack.
-const LanguagePackComponentConfig kTranslateKitEnPtConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitEnPtConfig = {
     .language1 = SupportedLanguage::kEn,
     .language2 = SupportedLanguage::kPt,
     .public_key_sha = {0xcd, 0x31, 0x4d, 0x32, 0x6e, 0x09, 0xe9, 0x16,
@@ -256,7 +258,7 @@
                        0xbd, 0xd1, 0xe3, 0xa9, 0x38, 0x73, 0xd9, 0xee}};
 
 // The config for the TranslateKit en-ru language pack.
-const LanguagePackComponentConfig kTranslateKitEnRuConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitEnRuConfig = {
     .language1 = SupportedLanguage::kEn,
     .language2 = SupportedLanguage::kRu,
     .public_key_sha = {0x31, 0xe6, 0xdd, 0x3f, 0x9d, 0x86, 0xd1, 0x3d,
@@ -265,7 +267,7 @@
                        0xba, 0x2c, 0x44, 0x62, 0x58, 0xa2, 0x33, 0xc3}};
 
 // The config for the TranslateKit en-th language pack.
-const LanguagePackComponentConfig kTranslateKitEnThConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitEnThConfig = {
     .language1 = SupportedLanguage::kEn,
     .language2 = SupportedLanguage::kTh,
     .public_key_sha = {0x73, 0xab, 0x92, 0x29, 0x91, 0xea, 0x31, 0x87,
@@ -274,7 +276,7 @@
                        0xa1, 0x63, 0xa5, 0x8b, 0x3c, 0x97, 0x4e, 0xe2}};
 
 // The config for the TranslateKit en-tr language pack.
-const LanguagePackComponentConfig kTranslateKitEnTrConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitEnTrConfig = {
     .language1 = SupportedLanguage::kEn,
     .language2 = SupportedLanguage::kTr,
     .public_key_sha = {0xc5, 0x75, 0x1d, 0x50, 0x3e, 0x87, 0xaf, 0xad,
@@ -283,7 +285,7 @@
                        0x60, 0x36, 0x57, 0xff, 0xb7, 0x2b, 0xd8, 0x6e}};
 
 // The config for the TranslateKit en-vi language pack.
-const LanguagePackComponentConfig kTranslateKitEnViConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitEnViConfig = {
     .language1 = SupportedLanguage::kEn,
     .language2 = SupportedLanguage::kVi,
     .public_key_sha = {0xb3, 0xff, 0x8e, 0xa7, 0x44, 0x40, 0xce, 0xc4,
@@ -292,7 +294,7 @@
                        0x66, 0x09, 0x36, 0x18, 0xe4, 0xd4, 0x39, 0x33}};
 
 // The config for the TranslateKit en-zh language pack.
-const LanguagePackComponentConfig kTranslateKitEnZhConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitEnZhConfig = {
     .language1 = SupportedLanguage::kEn,
     .language2 = SupportedLanguage::kZh,
     .public_key_sha = {0x1e, 0x44, 0x3e, 0x1c, 0x99, 0x33, 0x56, 0xd2,
@@ -301,7 +303,7 @@
                        0x1f, 0x9c, 0x1e, 0xe3, 0x9d, 0x7e, 0x55, 0xc5}};
 
 // The config for the TranslateKit en-zh-Hant language pack.
-const LanguagePackComponentConfig kTranslateKitEnZhHantConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitEnZhHantConfig = {
     .language1 = SupportedLanguage::kEn,
     .language2 = SupportedLanguage::kZhHant,
     .public_key_sha = {0xe8, 0x50, 0xdf, 0x7d, 0x99, 0xef, 0x90, 0x2e,
@@ -310,7 +312,7 @@
                        0xcb, 0x70, 0x7e, 0x63, 0xcf, 0xaa, 0x5f, 0x3b}};
 
 // The config for the TranslateKit bg-en language pack.
-const LanguagePackComponentConfig kTranslateKitBgEnConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitBgEnConfig = {
     .language1 = SupportedLanguage::kBg,
     .language2 = SupportedLanguage::kEn,
     .public_key_sha = {0x6c, 0xdf, 0x57, 0x5f, 0x99, 0x8e, 0x24, 0xb1,
@@ -319,7 +321,7 @@
                        0x69, 0xea, 0x47, 0xd1, 0xf5, 0xc7, 0xc3, 0xe5}};
 
 // The config for the TranslateKit cs-en language pack.
-const LanguagePackComponentConfig kTranslateKitCsEnConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitCsEnConfig = {
     .language1 = SupportedLanguage::kCs,
     .language2 = SupportedLanguage::kEn,
     .public_key_sha = {0xb1, 0xc4, 0xea, 0x3f, 0xb5, 0xcf, 0xba, 0x79,
@@ -328,7 +330,7 @@
                        0x6e, 0x83, 0x9a, 0xfd, 0xea, 0xbb, 0x82, 0x65}};
 
 // The config for the TranslateKit da-en language pack.
-const LanguagePackComponentConfig kTranslateKitDaEnConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitDaEnConfig = {
     .language1 = SupportedLanguage::kDa,
     .language2 = SupportedLanguage::kEn,
     .public_key_sha = {0x03, 0xed, 0xf8, 0x44, 0x84, 0xaa, 0x9e, 0x13,
@@ -337,7 +339,7 @@
                        0xcd, 0x7d, 0x6b, 0x7d, 0x38, 0x89, 0xe7, 0x7d}};
 
 // The config for the TranslateKit el-en language pack.
-const LanguagePackComponentConfig kTranslateKitElEnConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitElEnConfig = {
     .language1 = SupportedLanguage::kEl,
     .language2 = SupportedLanguage::kEn,
     .public_key_sha = {0x29, 0xa0, 0x99, 0xad, 0x64, 0x3f, 0x23, 0x88,
@@ -346,7 +348,7 @@
                        0x28, 0xf6, 0x46, 0x39, 0x5f, 0x88, 0x61, 0xed}};
 
 // The config for the TranslateKit en-fi language pack.
-const LanguagePackComponentConfig kTranslateKitEnFiConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitEnFiConfig = {
     .language1 = SupportedLanguage::kEn,
     .language2 = SupportedLanguage::kFi,
     .public_key_sha = {0x92, 0xb7, 0xbb, 0xb7, 0xc7, 0xad, 0x82, 0x5b,
@@ -355,7 +357,7 @@
                        0x48, 0x35, 0xc7, 0x93, 0x77, 0x0a, 0x81, 0x85}};
 
 // The config for the TranslateKit en-hr language pack.
-const LanguagePackComponentConfig kTranslateKitEnHrConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitEnHrConfig = {
     .language1 = SupportedLanguage::kEn,
     .language2 = SupportedLanguage::kHr,
     .public_key_sha = {0x6a, 0x1a, 0x78, 0x89, 0x45, 0x2b, 0x32, 0xf1,
@@ -364,7 +366,7 @@
                        0x3c, 0x55, 0x03, 0xed, 0x63, 0x21, 0x17, 0xdb}};
 
 // The config for the TranslateKit en-hu language pack.
-const LanguagePackComponentConfig kTranslateKitEnHuConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitEnHuConfig = {
     .language1 = SupportedLanguage::kEn,
     .language2 = SupportedLanguage::kHu,
     .public_key_sha = {0xf4, 0xee, 0xf9, 0x6f, 0x3c, 0x96, 0x24, 0xc3,
@@ -373,7 +375,7 @@
                        0x75, 0x7d, 0xb4, 0x55, 0x9e, 0x3e, 0xc9, 0x24}};
 
 // The config for the TranslateKit en-id language pack.
-const LanguagePackComponentConfig kTranslateKitEnIdConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitEnIdConfig = {
     .language1 = SupportedLanguage::kEn,
     .language2 = SupportedLanguage::kId,
     .public_key_sha = {0xa1, 0x07, 0xf5, 0x27, 0xbd, 0x5a, 0x87, 0x1f,
@@ -382,7 +384,7 @@
                        0x47, 0x85, 0x8c, 0x1d, 0x88, 0x40, 0x1d, 0x1d}};
 
 // The config for the TranslateKit en-iw language pack.
-const LanguagePackComponentConfig kTranslateKitEnIwConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitEnIwConfig = {
     .language1 = SupportedLanguage::kEn,
     .language2 = SupportedLanguage::kIw,
     .public_key_sha = {0x31, 0x28, 0xaf, 0x49, 0xe0, 0x6e, 0x38, 0xc8,
@@ -391,7 +393,7 @@
                        0x40, 0x33, 0x8e, 0x09, 0x5d, 0x78, 0x6f, 0x3c}};
 
 // The config for the TranslateKit en-lt language pack.
-const LanguagePackComponentConfig kTranslateKitEnLtConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitEnLtConfig = {
     .language1 = SupportedLanguage::kEn,
     .language2 = SupportedLanguage::kLt,
     .public_key_sha = {0xe3, 0x60, 0xdd, 0x89, 0xe4, 0x3e, 0x7a, 0x2a,
@@ -400,7 +402,7 @@
                        0xef, 0x82, 0x05, 0x97, 0x12, 0xcc, 0xa5, 0xfa}};
 
 // The config for the TranslateKit en-no language pack.
-const LanguagePackComponentConfig kTranslateKitEnNoConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitEnNoConfig = {
     .language1 = SupportedLanguage::kEn,
     .language2 = SupportedLanguage::kNo,
     .public_key_sha = {0xc7, 0xce, 0xeb, 0x48, 0x16, 0x9e, 0x4e, 0x88,
@@ -409,7 +411,7 @@
                        0xa6, 0x99, 0x90, 0xf1, 0xfa, 0x5a, 0x99, 0xa0}};
 
 // The config for the TranslateKit en-ro language pack.
-const LanguagePackComponentConfig kTranslateKitEnRoConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitEnRoConfig = {
     .language1 = SupportedLanguage::kEn,
     .language2 = SupportedLanguage::kRo,
     .public_key_sha = {0x0a, 0x2f, 0xeb, 0x3c, 0xac, 0xf3, 0x64, 0xbf,
@@ -418,7 +420,7 @@
                        0x47, 0x24, 0x37, 0x15, 0xcc, 0x7f, 0x03, 0xa7}};
 
 // The config for the TranslateKit en-sk language pack.
-const LanguagePackComponentConfig kTranslateKitEnSkConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitEnSkConfig = {
     .language1 = SupportedLanguage::kEn,
     .language2 = SupportedLanguage::kSk,
     .public_key_sha = {0xf4, 0xe9, 0x21, 0x11, 0x45, 0xae, 0x0a, 0x0c,
@@ -427,7 +429,7 @@
                        0x25, 0xa5, 0x65, 0xfa, 0xdf, 0xd6, 0xf5, 0x9d}};
 
 // The config for the TranslateKit en-sl language pack.
-const LanguagePackComponentConfig kTranslateKitEnSlConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitEnSlConfig = {
     .language1 = SupportedLanguage::kEn,
     .language2 = SupportedLanguage::kSl,
     .public_key_sha = {0xe1, 0x5f, 0xa1, 0xfb, 0x33, 0xbb, 0x42, 0xe5,
@@ -436,7 +438,7 @@
                        0x50, 0x6a, 0x71, 0x6b, 0xe0, 0xbe, 0x65, 0xed}};
 
 // The config for the TranslateKit en-sv language pack.
-const LanguagePackComponentConfig kTranslateKitEnSvConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitEnSvConfig = {
     .language1 = SupportedLanguage::kEn,
     .language2 = SupportedLanguage::kSv,
     .public_key_sha = {0xf7, 0xff, 0xca, 0x43, 0xfb, 0x2e, 0xcd, 0x44,
@@ -445,7 +447,7 @@
                        0xdd, 0xc4, 0x87, 0xed, 0xf9, 0x22, 0x0a, 0x6b}};
 
 // The config for the TranslateKit en-uk language pack.
-const LanguagePackComponentConfig kTranslateKitEnUkConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitEnUkConfig = {
     .language1 = SupportedLanguage::kEn,
     .language2 = SupportedLanguage::kUk,
     .public_key_sha = {0xe0, 0x84, 0x14, 0x12, 0x2b, 0xce, 0xb0, 0xbc,
@@ -453,7 +455,7 @@
                        0xa3, 0x39, 0x69, 0x87, 0x82, 0x86, 0x10, 0xd3,
                        0x55, 0x54, 0x50, 0x94, 0x01, 0xec, 0xc5, 0xe3}};
 
-const LanguagePackComponentConfig kTranslateKitEnKnConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitEnKnConfig = {
     .language1 = SupportedLanguage::kEn,
     .language2 = SupportedLanguage::kKn,
     .public_key_sha = {0x5c, 0x9d, 0x42, 0x31, 0xca, 0xf4, 0x97, 0x21,
@@ -461,7 +463,7 @@
                        0xa3, 0x8e, 0x84, 0x72, 0x60, 0xc6, 0x12, 0x54,
                        0x9b, 0x49, 0xc8, 0xef, 0xa4, 0xae, 0x16, 0x79}};
 
-const LanguagePackComponentConfig kTranslateKitEnTaConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitEnTaConfig = {
     .language1 = SupportedLanguage::kEn,
     .language2 = SupportedLanguage::kTa,
     .public_key_sha = {0xab, 0xf0, 0x56, 0xfa, 0x09, 0xfe, 0x6f, 0x40,
@@ -469,7 +471,7 @@
                        0xc1, 0xdd, 0x3c, 0xc0, 0x41, 0x49, 0x14, 0xe4,
                        0xc2, 0xd5, 0xf6, 0xbd, 0xcd, 0x9c, 0xc4, 0x5d}};
 
-const LanguagePackComponentConfig kTranslateKitEnTeConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitEnTeConfig = {
     .language1 = SupportedLanguage::kEn,
     .language2 = SupportedLanguage::kTe,
     .public_key_sha = {0xc7, 0x1c, 0x20, 0x31, 0x2a, 0x3f, 0xe8, 0x66,
@@ -477,7 +479,7 @@
                        0x0f, 0x4f, 0x47, 0x69, 0x1a, 0x83, 0xf7, 0xca,
                        0x73, 0x0d, 0x8b, 0x86, 0xd8, 0x12, 0xf0, 0x39}};
 
-const LanguagePackComponentConfig kTranslateKitEnMrConfig = {
+inline constexpr LanguagePackComponentConfig kTranslateKitEnMrConfig = {
     .language1 = SupportedLanguage::kEn,
     .language2 = SupportedLanguage::kMr,
     .public_key_sha = {0x5e, 0x81, 0xf0, 0xcf, 0x35, 0xbc, 0x9b, 0x58,
@@ -486,7 +488,7 @@
                        0x9d, 0x2e, 0xe3, 0xde, 0x5e, 0x42, 0x00, 0x02}};
 
 // The config for each language pack.
-constexpr auto kLanguagePackComponentConfigMap =
+inline constexpr auto kLanguagePackComponentConfigMap =
     base::MakeFixedFlatMap<LanguagePackKey, const LanguagePackComponentConfig*>(
         {{LanguagePackKey::kEn_Es, &kTranslateKitEnEsConfig},
          {LanguagePackKey::kEn_Ja, &kTranslateKitEnJaConfig},
diff --git a/chrome/browser/page_info/web_view_side_panel_throttle.cc b/chrome/browser/page_info/web_view_side_panel_throttle.cc
index 893aa895..ffda410 100644
--- a/chrome/browser/page_info/web_view_side_panel_throttle.cc
+++ b/chrome/browser/page_info/web_view_side_panel_throttle.cc
@@ -8,6 +8,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/navigation_throttle.h"
+#include "content/public/browser/navigation_throttle_registry.h"
 #include "content/public/browser/page_navigator.h"
 #include "content/public/browser/web_contents.h"
 #include "net/base/url_util.h"
@@ -45,47 +46,50 @@
 WebViewSidePanelWebContentsUserData::~WebViewSidePanelWebContentsUserData() =
     default;
 
-std::unique_ptr<content::NavigationThrottle>
-MaybeCreateWebViewSidePanelThrottleFor(content::NavigationHandle* handle) {
+void MaybeCreateAndAddWebViewSidePanelThrottle(
+    content::NavigationThrottleRegistry& registry) {
   // Only install throttle for WebContents that are in the WebViewSidePanel.
-  if (!handle || !handle->IsInPrimaryMainFrame() || !handle->GetWebContents() ||
-      !handle->GetWebContents()->GetUserData(
+  auto& handle = registry.GetNavigationHandle();
+  if (!handle.IsInPrimaryMainFrame() || !handle.GetWebContents() ||
+      !handle.GetWebContents()->GetUserData(
           kWebViewSidePanelWebContentsUserDataKey)) {
-    return nullptr;
+    return;
   }
-  const GURL& observed_url = handle->GetURL();
-  return std::make_unique<navigation_interception::InterceptNavigationThrottle>(
-      handle,
-      base::BindRepeating(
-          [](const GURL& observed_url, content::NavigationHandle* handle,
-             bool should_run_async,
-             navigation_interception::InterceptNavigationThrottle::
-                 ResultCallback result_callback) {
-            DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-            CHECK(!should_run_async);
-            auto* data = static_cast<WebViewSidePanelWebContentsUserData*>(
-                handle->GetWebContents()->GetUserData(
-                    kWebViewSidePanelWebContentsUserDataKey));
-            // The delegate is stored in a WeakPtr. Check if it is still there.
-            if (!data->delegate()) {
-              std::move(result_callback).Run(true);
-              return;
-            }
-            const GURL& next_url = handle->GetURL();
-            if (CanNavigateInPanel(handle, observed_url, next_url)) {
-              std::move(result_callback).Run(false);
-              return;
-            }
+  const GURL& observed_url = handle.GetURL();
+  registry.AddThrottle(
+      std::make_unique<navigation_interception::InterceptNavigationThrottle>(
+          registry,
+          base::BindRepeating(
+              [](const GURL& observed_url, content::NavigationHandle* handle,
+                 bool should_run_async,
+                 navigation_interception::InterceptNavigationThrottle::
+                     ResultCallback result_callback) {
+                DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+                CHECK(!should_run_async);
+                auto* data = static_cast<WebViewSidePanelWebContentsUserData*>(
+                    handle->GetWebContents()->GetUserData(
+                        kWebViewSidePanelWebContentsUserDataKey));
+                // The delegate is stored in a WeakPtr. Check if it is still
+                // there.
+                if (!data->delegate()) {
+                  std::move(result_callback).Run(true);
+                  return;
+                }
+                const GURL& next_url = handle->GetURL();
+                if (CanNavigateInPanel(handle, observed_url, next_url)) {
+                  std::move(result_callback).Run(false);
+                  return;
+                }
 
-            content::OpenURLParams params(
-                next_url, content::Referrer(handle->GetReferrer()),
-                WindowOpenDisposition::NEW_FOREGROUND_TAB,
-                handle->GetPageTransition(), handle->IsRendererInitiated());
-            params.initiator_origin = handle->GetInitiatorOrigin();
-            params.initiator_base_url = handle->GetInitiatorBaseUrl();
-            data->delegate()->OpenUrlInBrowser(params);
-            std::move(result_callback).Run(true);
-          },
-          observed_url),
-      navigation_interception::SynchronyMode::kSync, std::nullopt);
+                content::OpenURLParams params(
+                    next_url, content::Referrer(handle->GetReferrer()),
+                    WindowOpenDisposition::NEW_FOREGROUND_TAB,
+                    handle->GetPageTransition(), handle->IsRendererInitiated());
+                params.initiator_origin = handle->GetInitiatorOrigin();
+                params.initiator_base_url = handle->GetInitiatorBaseUrl();
+                data->delegate()->OpenUrlInBrowser(params);
+                std::move(result_callback).Run(true);
+              },
+              observed_url),
+          navigation_interception::SynchronyMode::kSync, std::nullopt));
 }
diff --git a/chrome/browser/page_info/web_view_side_panel_throttle.h b/chrome/browser/page_info/web_view_side_panel_throttle.h
index b7f3137..cbabbf6 100644
--- a/chrome/browser/page_info/web_view_side_panel_throttle.h
+++ b/chrome/browser/page_info/web_view_side_panel_throttle.h
@@ -12,25 +12,27 @@
 
 namespace content {
 struct OpenURLParams;
-class NavigationHandle;
 class NavigationThrottle;
+class NavigationThrottleRegistry;
 }  // namespace content
 
 extern const char kWebViewSidePanelWebContentsUserDataKey[];
 
 // Holds a handler to open a URL in a new tab in the browser that the sidepanel
 // of this webcontents is associated with. The NavigationThrottle from
-// |MaybeCreateWebViewSidePanelThrottleFor| will check if this UserData is present
-// and if it is present, intercepts navigations if |IsNavigationAllowed|
-// and opens them using |OpenUrlInBrowser| instead.
-class WebViewSidePanelWebContentsUserData : public base::SupportsUserData::Data {
+// `MaybeCreateAndAddWebViewSidePanelThrottle` will check if this UserData is
+// present and if it is present, intercepts navigations if `IsNavigationAllowed`
+// and opens them using `OpenUrlInBrowser` instead.
+class WebViewSidePanelWebContentsUserData
+    : public base::SupportsUserData::Data {
  public:
   class Delegate {
    public:
     virtual void OpenUrlInBrowser(const content::OpenURLParams& params) = 0;
   };
 
-  explicit WebViewSidePanelWebContentsUserData(base::WeakPtr<Delegate> delegate);
+  explicit WebViewSidePanelWebContentsUserData(
+      base::WeakPtr<Delegate> delegate);
   ~WebViewSidePanelWebContentsUserData() override;
 
   Delegate* delegate() { return delegate_.get(); }
@@ -41,7 +43,7 @@
 
 // Installs a NavigationThrottle if an WebViewSidePanelWebContentsUserData is
 // associated with the WebContents of this navigation.
-std::unique_ptr<content::NavigationThrottle>
-MaybeCreateWebViewSidePanelThrottleFor(content::NavigationHandle* handle);
+void MaybeCreateAndAddWebViewSidePanelThrottle(
+    content::NavigationThrottleRegistry& registry);
 
 #endif  // CHROME_BROWSER_PAGE_INFO_WEB_VIEW_SIDE_PANEL_THROTTLE_H_
diff --git a/chrome/browser/page_load_metrics/observers/tab_strip_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/tab_strip_page_load_metrics_observer.cc
index 937c759..690560b 100644
--- a/chrome/browser/page_load_metrics/observers/tab_strip_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/tab_strip_page_load_metrics_observer.cc
@@ -75,10 +75,8 @@
     return CONTINUE_OBSERVING;
   }
   base::TimeTicks now = base::TimeTicks::Now();
-  std::vector<std::vector<content::WebContents*>> all_web_contents =
-      GetAllWebContents();
-  for (std::vector<content::WebContents*> tab_strip_web_contents :
-       all_web_contents) {
+
+  for (const auto& tab_strip_web_contents : GetAllWebContents()) {
     for (content::WebContents* web_contents : tab_strip_web_contents) {
       if (web_contents) {
         base::TimeTicks last_active = web_contents->GetLastActiveTimeTicks();
@@ -113,10 +111,7 @@
 
 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
 TabStripPageLoadMetricsObserver::OnShown() {
-  std::vector<std::vector<content::WebContents*>> all_web_contents =
-      GetAllWebContents();
-  for (std::vector<content::WebContents*> tab_strip_web_contents :
-       all_web_contents) {
+  for (const auto& tab_strip_web_contents : GetAllWebContents()) {
     const int count = tab_strip_web_contents.size();
     for (int i = 0; i < count; i++) {
       if (tab_strip_web_contents.at(i) == web_contents_.get()) {
diff --git a/chrome/browser/password_manager/android/BUILD.gn b/chrome/browser/password_manager/android/BUILD.gn
index 0af05157..ca888f9a 100644
--- a/chrome/browser/password_manager/android/BUILD.gn
+++ b/chrome/browser/password_manager/android/BUILD.gn
@@ -665,7 +665,6 @@
     "//chrome/browser/password_manager/android/access_loss:test_support",
     "//chrome/browser/password_manager/android/add_username_dialog:android",
     "//chrome/browser/plus_addresses",
-    "//chrome/browser/plus_addresses:impl",
     "//chrome/browser/prefs",
     "//chrome/browser/sync",
     "//chrome/browser/touch_to_fill/password_manager/password_generation/android:public",
diff --git a/chrome/browser/pdf/pdf_extension_js_test.cc b/chrome/browser/pdf/pdf_extension_js_test.cc
index 1c168c0..b17e27cc 100644
--- a/chrome/browser/pdf/pdf_extension_js_test.cc
+++ b/chrome/browser/pdf/pdf_extension_js_test.cc
@@ -624,14 +624,7 @@
   RunTestsInJsModule("ink2_size_selector_test.js", "test.pdf");
 }
 
-// TODO(crbug.com/416359681): Ink2ViewerToolbar is flaky on Mac12 and Mac13
-// tests.
-#if !BUILDFLAG(IS_MAC)
-#define MAYBE_Ink2ViewerToolbar Ink2ViewerToolbar
-#else
-#define MAYBE_Ink2ViewerToolbar DISABLED_Ink2ViewerToolbar
-#endif
-IN_PROC_BROWSER_TEST_P(PDFExtensionJSInk2Test, MAYBE_Ink2ViewerToolbar) {
+IN_PROC_BROWSER_TEST_P(PDFExtensionJSInk2Test, Ink2ViewerToolbar) {
   RunTestsInJsModule("ink2_viewer_toolbar_test.js", "test.pdf");
 }
 
diff --git a/chrome/browser/performance_manager/user_tuning/cpu_health_tracker.cc b/chrome/browser/performance_manager/user_tuning/cpu_health_tracker.cc
index 7b9f6cd..bfb2947 100644
--- a/chrome/browser/performance_manager/user_tuning/cpu_health_tracker.cc
+++ b/chrome/browser/performance_manager/user_tuning/cpu_health_tracker.cc
@@ -69,7 +69,7 @@
 
 int CpuHealthTracker::GetTotalCpuPercentUsage(ActionableTabsResult tabs) {
   int total_cpu = 0;
-  for (resource_attribution::PageContext context : tabs) {
+  for (const resource_attribution::PageContext& context : tabs) {
     auto iter = tab_page_measurements_.find(context);
     if (iter != tab_page_measurements_.end()) {
       total_cpu += iter->second.value();
diff --git a/chrome/browser/performance_manager/user_tuning/performance_detection_manager.cc b/chrome/browser/performance_manager/user_tuning/performance_detection_manager.cc
index c5ab1a3..5c8a8daa5 100644
--- a/chrome/browser/performance_manager/user_tuning/performance_detection_manager.cc
+++ b/chrome/browser/performance_manager/user_tuning/performance_detection_manager.cc
@@ -70,7 +70,7 @@
 
   std::vector<const PageNode*> eligible_nodes;
   std::vector<resource_attribution::PageContext> eligible_page_contexts;
-  for (resource_attribution::PageContext context : tabs) {
+  for (const resource_attribution::PageContext& context : tabs) {
     const PageNode* page_node = context.GetPageNode();
     if (page_node) {
       eligible_nodes.emplace_back(page_node);
diff --git a/chrome/browser/plus_addresses/BUILD.gn b/chrome/browser/plus_addresses/BUILD.gn
index 6d0184c..fd7367c 100644
--- a/chrome/browser/plus_addresses/BUILD.gn
+++ b/chrome/browser/plus_addresses/BUILD.gn
@@ -12,15 +12,12 @@
 
   public_deps = [
     "//base",
-    "//chrome/browser/profiles:profile",
-    "//components/plus_addresses",
-  ]
-
-  deps = [
     "//chrome/browser/affiliations",
+    "//chrome/browser/profiles:profile",
     "//chrome/browser/sync:factories",
     "//components/affiliations/core/browser:affiliations",
     "//components/autofill/core/common:features",
+    "//components/plus_addresses",
     "//components/plus_addresses:features",
     "//components/plus_addresses:hats_utils",
     "//components/plus_addresses/settings",
@@ -36,28 +33,7 @@
     "plus_address_service_factory.cc",
     "plus_address_setting_service_factory.cc",
   ]
-
-  public_deps = [
-    "//base",
-    "//chrome/browser:browser_public_dependencies",
-    "//chrome/browser/profiles:profile",
-    "//components/plus_addresses",
-  ]
-
-  deps = [
-    ":plus_addresses",
-    "//chrome/browser/affiliations",
-    "//chrome/browser/sync",
-    "//components/affiliations/core/browser:affiliations",
-    "//components/autofill/core/common:features",
-    "//components/plus_addresses:features",
-    "//components/plus_addresses:hats_utils",
-    "//components/plus_addresses/settings",
-    "//components/signin/public/identity_manager",
-    "//components/sync/model",
-    "//components/variations/service",
-    "//services/network/public/cpp",
-  ]
+  deps = [ ":plus_addresses" ]
 }
 
 if (!is_android) {
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index dba44001..91c3a30 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -2468,7 +2468,7 @@
           base::BindRepeating(&PopulatePolicyHandlerParameters),
           base::BindRepeating(&GetChromePolicyDetails),
           AreFuturePoliciesEnabledByDefault()));
-  for (PolicyToPreferenceMapEntry entry : kSimplePolicyMap) {
+  for (const PolicyToPreferenceMapEntry& entry : kSimplePolicyMap) {
     handlers->AddHandler(std::make_unique<SimplePolicyHandler>(
         entry.policy_name, entry.preference_path, entry.value_type));
   }
diff --git a/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_util.cc b/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_util.cc
index fe1cb448..4e31514 100644
--- a/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_util.cc
+++ b/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_util.cc
@@ -1455,7 +1455,7 @@
         needs_update[key_value.first] = lcpp_origin;
       }
     }
-    for (auto it : needs_update) {
+    for (const auto& it : needs_update) {
       origin_map_->UpdateData(it.first, it.second);
     }
   }
diff --git a/chrome/browser/prefs/session_startup_pref.cc b/chrome/browser/prefs/session_startup_pref.cc
index 266dc5f..b6b0ed4 100644
--- a/chrome/browser/prefs/session_startup_pref.cc
+++ b/chrome/browser/prefs/session_startup_pref.cc
@@ -92,8 +92,9 @@
     // Always save the URLs, that way the UI can remain consistent even if the
     // user changes the startup type pref.
     base::Value::List url_pref_list;
-    for (GURL url : pref.urls)
+    for (const GURL& url : pref.urls) {
       url_pref_list.Append(url.spec());
+    }
     prefs->SetList(prefs::kURLsToRestoreOnStartup, std::move(url_pref_list));
   }
 }
diff --git a/chrome/browser/preloading/navigation_ablation_throttle.cc b/chrome/browser/preloading/navigation_ablation_throttle.cc
index 25def7f..edeada8 100644
--- a/chrome/browser/preloading/navigation_ablation_throttle.cc
+++ b/chrome/browser/preloading/navigation_ablation_throttle.cc
@@ -75,7 +75,7 @@
     return AblationType::kDefaultSearchQuery;
   }
 
-  for (auto url_ref : default_search->url_refs()) {
+  for (const auto& url_ref : default_search->url_refs()) {
     if (url::Origin::Create(navigation_url) ==
         url::Origin::Create(GURL(url_ref.GetURL()))) {
       return AblationType::kDefaultSearchHost;
diff --git a/chrome/browser/profiles/gaia_info_update_service.cc b/chrome/browser/profiles/gaia_info_update_service.cc
index cecf493..1b27c0f 100644
--- a/chrome/browser/profiles/gaia_info_update_service.cc
+++ b/chrome/browser/profiles/gaia_info_update_service.cc
@@ -233,7 +233,7 @@
     // Regenerate based on the info from signed-in accounts (if not available
     // now, it will be regenerated soon via OnExtendedAccountInfoUpdated() once
     // downloaded).
-    for (gaia::ListedAccount account :
+    for (const gaia::ListedAccount& account :
          accounts_in_cookie_jar_info.GetPotentiallyInvalidSignedInAccounts()) {
       UpdateAnyAccount(
           identity_manager_->FindExtendedAccountInfoByAccountId(account.id));
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudToolbarButtonController.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudToolbarButtonController.java
index 415b19b9..05c8c25a 100644
--- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudToolbarButtonController.java
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudToolbarButtonController.java
@@ -51,8 +51,7 @@
                 /* supportsTinting= */ true,
                 /* iphCommandBuilder= */ null,
                 AdaptiveToolbarButtonVariant.READ_ALOUD,
-                /* tooltipTextResId= */ Resources.ID_NULL,
-                /* showBackgroundHighlight= */ true);
+                /* tooltipTextResId= */ Resources.ID_NULL);
         mControllerSupplier = controllerSupplier;
         mTrackerSupplier = trackerSupplier;
     }
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index 14bacc2..1b785aa 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -9,8 +9,10 @@
 #include <algorithm>
 #include <memory>
 #include <set>
+#include <string>
 #include <utility>
 
+#include "base/check.h"
 #include "base/command_line.h"
 #include "base/containers/contains.h"
 #include "base/feature_list.h"
@@ -45,10 +47,6 @@
 #include "chrome/browser/download/download_prefs.h"
 #include "chrome/browser/download/download_stats.h"
 #include "chrome/browser/glic/glic_enabling.h"
-#if BUILDFLAG(ENABLE_GLIC)
-#include "chrome/browser/glic/glic_keyed_service.h"
-#include "chrome/browser/glic/glic_keyed_service_factory.h"
-#endif  // BUILDFLAG(ENABLE_GLIC)
 #include "chrome/browser/language/language_model_manager_factory.h"
 #include "chrome/browser/media/router/media_router_feature.h"
 #include "chrome/browser/navigation_predictor/navigation_predictor_features.h"
@@ -261,6 +259,11 @@
 #include "extensions/common/extension.h"
 #endif
 
+#if BUILDFLAG(ENABLE_GLIC)
+#include "chrome/browser/glic/glic_keyed_service.h"
+#include "chrome/browser/glic/glic_keyed_service_factory.h"
+#endif  // BUILDFLAG(ENABLE_GLIC)
+
 #if BUILDFLAG(ENABLE_PDF)
 #include "chrome/browser/pdf/pdf_extension_util.h"
 #include "components/pdf/browser/pdf_frame_util.h"
@@ -319,6 +322,10 @@
 #include "chrome/browser/ui/toasts/toast_controller.h"
 #include "chrome/browser/ui/toasts/toast_features.h"
 #include "chrome/browser/ui/webui/webui_embedding_context.h"
+#include "components/tabs/public/split_tab_data.h"
+#include "components/tabs/public/split_tab_id.h"
+#include "components/tabs/public/tab_interface.h"
+#include "ui/base/page_transition_types.h"
 #endif
 
 using base::UserMetricsAction;
@@ -526,13 +533,14 @@
        {IDC_CONTENT_CONTEXT_USE_PASSKEY_FROM_ANOTHER_DEVICE, 153},
        {IDC_CONTENT_CONTEXT_RELOAD_GLIC, 154},
        {IDC_CONTENT_CONTEXT_CLOSE_GLIC, 155},
+       {IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW, 156},
        // To add new items:
        //   - Add one more line above this comment block, using the UMA value
        //     from the line below this comment block.
        //   - Increment the UMA value in that latter line.
        //   - Add the new item to the RenderViewContextMenuItem enum in
        //     tools/metrics/histograms/metadata/ui/enums.xml.
-       {0, 156}});
+       {0, 157}});
 
   // These UMA values are for the ContextMenuOptionDesktop enum, used for
   // the ContextMenu.SelectedOptionDesktop histograms.
@@ -568,12 +576,13 @@
        // Removed: {IDC_CONTENT_CONTEXT_TRANSLATEIMAGEWITHLENS, 28},
        {IDC_CONTENT_CONTEXT_SEARCHWEBFORNEWTAB, 29},
        {IDC_CONTENT_CONTEXT_OPENLINKPREVIEW, 30},
+       {IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW, 31},
        // To add new items:
        //   - Add one more line above this comment block, using the UMA value
        //     from the line below this comment block.
        //   - Increment the UMA value in that latter line.
        //   - Add the new item to the ContextMenuOptionDesktop enum in
-       //     tools/metrics/histograms/enums.xml.
+       //     tools/metrics/histograms/metadata/enums.xml.
        {0, 31}});
 
   return *(type == UmaEnumIdLookupType::GeneralEnumId ? kGeneralMap
@@ -638,6 +647,7 @@
   return id == IDC_CONTENT_CONTEXT_OPENLINKNEWTAB ||
          id == IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW ||
          id == IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD ||
+         id == IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW ||
          (id >= IDC_OPEN_LINK_IN_PROFILE_FIRST &&
           id <= IDC_OPEN_LINK_IN_PROFILE_LAST);
 }
@@ -856,6 +866,8 @@
                                       kGlicCloseMenuItem);
 DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(RenderViewContextMenu,
                                       kGlicReloadMenuItem);
+DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(RenderViewContextMenu,
+                                      kOpenLinkInSplitMenuItem);
 DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(RenderViewContextMenu, kRegionSearchItem);
 DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(RenderViewContextMenu,
                                       kSearchForImageItem);
@@ -1783,6 +1795,25 @@
     }
 
 #if !BUILDFLAG(IS_ANDROID)
+    // Opening a link in split view should also go through the same constraints
+    // as opening a link in a new tab since a split view tab is a new tab that
+    // is then joined with the current active tab.
+    Browser* const browser = GetBrowser();
+    if (base::FeatureList::IsEnabled(features::kSideBySide) && browser &&
+        browser->is_type_normal() && show_open_in_new_tab) {
+      menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW,
+                                      IDS_CONTENT_CONTEXT_OPENLINKSPLITVIEW);
+      const int command_index =
+          menu_model_.GetIndexOfCommandId(IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW)
+              .value();
+      menu_model_.SetIsNewFeatureAt(
+          command_index,
+          UserEducationService::MaybeShowNewBadge(
+              GetBrowserContext(), features::kSideBySideLinkMenuNewBadge));
+      menu_model_.SetElementIdentifierAt(command_index,
+                                         kOpenLinkInSplitMenuItem);
+    }
+
     if (base::FeatureList::IsEnabled(blink::features::kLinkPreview) &&
         !is_link_to_iwa) {
       menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_OPENLINKPREVIEW,
@@ -2908,6 +2939,7 @@
     case IDC_CONTENT_CONTEXT_OPENLINKNEWTAB:
     case IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW:
     case IDC_CONTENT_CONTEXT_OPENLINKPREVIEW:
+    case IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW:
       return navigation_allowed && params_.link_url.is_valid() &&
              IsOpenLinkAllowedByDlp(params_.link_url);
 
@@ -3266,6 +3298,11 @@
       ExecOpenLinkPreview();
       break;
 
+    case IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW:
+#if !BUILDFLAG(IS_ANDROID)
+      OpenLinkInSplitView();
+#endif  // !BUILDFLAG(IS_ANDROID)
+      break;
     case IDC_CONTENT_CONTEXT_SAVELINKAS:
       CheckSupervisedUserURLFilterAndSaveLinkAs();
       break;
@@ -4869,6 +4906,40 @@
 }
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
+#if !BUILDFLAG(IS_ANDROID)
+void RenderViewContextMenu::OpenLinkInSplitView() {
+  Browser* const browser = GetBrowser();
+  CHECK(browser);
+  CHECK(browser->is_type_normal());
+
+  TabStripModel* const tab_strip_model = browser->tab_strip_model();
+  tabs::TabInterface* const source_tab =
+      tabs::TabInterface::GetFromContents(source_web_contents_);
+  if (source_tab->IsSplit()) {
+    // Navigate the inactive tab to the URL
+    const split_tabs::SplitTabId split_id = source_tab->GetSplit().value();
+    for (tabs::TabInterface* tab :
+         tab_strip_model->GetSplitData(split_id)->ListTabs()) {
+      if (tab != source_tab) {
+        // Navigate the tab that wasn't the source of the context menu to the
+        // URL
+        tab->GetContents()->GetController().LoadURL(
+            params_.link_url, content::Referrer(),
+            ui::PageTransition::PAGE_TRANSITION_LINK, std::string());
+        break;
+      }
+    }
+  } else {  // Create new split tab
+    const int active_index = tab_strip_model->active_index();
+    tab_strip_model->delegate()->AddTabAt(
+        params_.link_url, active_index + 1, true,
+        tab_strip_model->GetTabGroupForTab(active_index));
+    tab_strip_model->AddToNewSplit({active_index},
+                                   split_tabs::SplitTabLayout::kVertical);
+  }
+}
+#endif  // !BUILDFLAG(IS_ANDROID)
+
 bool RenderViewContextMenu::IsLinkToIsolatedWebApp() const {
   // Using `unfiltered_link_url`, because `link_url` is being replaced with
   // about:blank#blocked if the source is a normal site.
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.h b/chrome/browser/renderer_context_menu/render_view_context_menu.h
index ef71da5..912be875 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.h
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.h
@@ -110,6 +110,7 @@
   DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kComposeMenuItem);
   DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kGlicCloseMenuItem);
   DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kGlicReloadMenuItem);
+  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kOpenLinkInSplitMenuItem);
   DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kRegionSearchItem);
   DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kSearchForImageItem);
   DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kSearchForVideoFrameItem);
@@ -457,6 +458,14 @@
   void ShowClipboardHistoryMenu(int event_flags);
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
+#if !BUILDFLAG(IS_ANDROID)
+  // Opens the link in a new split view so that the linked page will be visible
+  // next to the active tab. If the active tab is already in the split view,
+  // then the tab that wasn't the source of the link will be navigated to the
+  // link instead.
+  void OpenLinkInSplitView();
+#endif  // !BUILDFLAG(IS_ANDROID)
+
   // The destination URL to use if the user tries to search for or navigate to
   // a text selection.
   GURL selection_navigation_url_;
@@ -577,6 +586,7 @@
            IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW,
            IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD,
            IDC_OPEN_LINK_IN_PROFILE_FIRST, IDC_OPEN_LINK_IN_PROFILE_LAST,
+           IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW,
 
            // Open link commands that appear in certain scenarios.
            IDC_CONTENT_CONTEXT_OPENLINKBOOKMARKAPP,
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
index d19b088e..a49b6a0 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
@@ -214,7 +214,8 @@
         {media::kContextMenuSaveVideoFrameAs,
          media::kContextMenuSearchForVideoFrame,
          toast_features::kLinkCopiedToast, toast_features::kImageCopiedToast,
-         toast_features::kVideoFrameCopiedToast},
+         toast_features::kVideoFrameCopiedToast, features::kSideBySide,
+         features::kSideBySideLinkMenuNewBadge},
         {});
   }
 
@@ -994,6 +995,7 @@
       // Content contextual commands.
       IDC_CONTENT_CONTEXT_OPENLINKNEWTAB, IDC_CONTENT_CONTEXT_COPYIMAGE,
       IDC_CONTENT_CONTEXT_COPYIMAGELOCATION, IDC_CONTENT_CONTEXT_INSPECTELEMENT,
+      IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW,
       // Other commands (we only test a subset).
       IDC_VIEW_SOURCE};
   for (int command_id : kCommandsToTest) {
@@ -1026,6 +1028,7 @@
       // Content contextual commands.
       IDC_CONTENT_CONTEXT_OPENLINKNEWTAB, IDC_CONTENT_CONTEXT_COPYIMAGE,
       IDC_CONTENT_CONTEXT_COPYIMAGELOCATION, IDC_CONTENT_CONTEXT_INSPECTELEMENT,
+      IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW,
       // Other commands (we only test a subset).
       IDC_VIEW_SOURCE};
   for (int command_id : kCommandsToTest) {
@@ -1054,6 +1057,7 @@
   // Verify other commands are disabled.
   static constexpr int kCommandsDisabledInLockedFullscreen[] = {
       IDC_VIEW_SOURCE, IDC_CONTENT_CONTEXT_OPENLINKNEWTAB,
+      IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW,
       IDC_CONTENT_CONTEXT_INSPECTELEMENT};
   for (int command_id : kCommandsDisabledInLockedFullscreen) {
     EXPECT_FALSE(menu->IsCommandIdEnabled(command_id))
@@ -1075,6 +1079,7 @@
       // Content contextual commands.
       IDC_CONTENT_CONTEXT_OPENLINKNEWTAB, IDC_CONTENT_CONTEXT_COPYIMAGE,
       IDC_CONTENT_CONTEXT_COPYIMAGELOCATION, IDC_CONTENT_CONTEXT_INSPECTELEMENT,
+      IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW,
       // Other commands (we only test a subset).
       IDC_VIEW_SOURCE};
   for (int command_id : kCommandsToTest) {
@@ -1100,6 +1105,7 @@
   // Verify other commands are disabled.
   static constexpr int kCommandsDisabledForOnTask[] = {
       IDC_VIEW_SOURCE, IDC_CONTENT_CONTEXT_OPENLINKNEWTAB,
+      IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW,
       IDC_CONTENT_CONTEXT_INSPECTELEMENT};
   for (int command_id : kCommandsDisabledForOnTask) {
     EXPECT_FALSE(menu->IsCommandIdEnabled(command_id))
@@ -1121,6 +1127,7 @@
   ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKNEWTAB));
   ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW));
   ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_COPYLINKLOCATION));
+  ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW));
   ASSERT_FALSE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKBOOKMARKAPP));
   ASSERT_FALSE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKINPROFILE));
   ASSERT_FALSE(menu->IsItemInRangePresent(IDC_OPEN_LINK_IN_PROFILE_FIRST,
@@ -1140,6 +1147,7 @@
   ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW));
   ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKBOOKMARKAPP));
   ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_COPYLINKLOCATION));
+  ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW));
   ASSERT_FALSE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKINPROFILE));
   ASSERT_FALSE(menu->IsItemInRangePresent(IDC_OPEN_LINK_IN_PROFILE_FIRST,
                                           IDC_OPEN_LINK_IN_PROFILE_LAST));
@@ -1156,6 +1164,7 @@
 
   ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKNEWTAB));
   ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW));
+  ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW));
   ASSERT_FALSE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKBOOKMARKAPP));
   ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_COPYLINKLOCATION));
   ASSERT_FALSE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKINPROFILE));
@@ -1175,6 +1184,7 @@
 
   ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKNEWTAB));
   ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW));
+  ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW));
   ASSERT_FALSE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKBOOKMARKAPP));
   ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_COPYLINKLOCATION));
   ASSERT_FALSE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKINPROFILE));
@@ -1210,6 +1220,7 @@
   ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKNEWTAB));
   ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW));
   ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_COPYLINKLOCATION));
+  ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW));
   ASSERT_FALSE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKBOOKMARKAPP));
   ASSERT_FALSE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKINPROFILE));
   ASSERT_FALSE(menu->IsItemInRangePresent(IDC_OPEN_LINK_IN_PROFILE_FIRST,
@@ -1230,6 +1241,7 @@
 
   ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKNEWTAB));
   ASSERT_FALSE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW));
+  ASSERT_FALSE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW));
   ASSERT_FALSE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKBOOKMARKAPP));
   ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_COPYLINKLOCATION));
   ASSERT_FALSE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKINPROFILE));
@@ -1250,6 +1262,7 @@
 
   ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKNEWTAB));
   ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW));
+  ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW));
   ASSERT_FALSE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKBOOKMARKAPP));
   ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_COPYLINKLOCATION));
   ASSERT_FALSE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKINPROFILE));
@@ -1271,6 +1284,7 @@
 
   ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKNEWTAB));
   ASSERT_FALSE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW));
+  ASSERT_FALSE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW));
   ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKBOOKMARKAPP));
   ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_COPYLINKLOCATION));
   ASSERT_FALSE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKINPROFILE));
@@ -1294,6 +1308,7 @@
 
   ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKNEWTAB));
   ASSERT_FALSE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW));
+  ASSERT_FALSE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW));
   ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKBOOKMARKAPP));
   ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_COPYLINKLOCATION));
   ASSERT_FALSE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKINPROFILE));
@@ -1309,6 +1324,7 @@
 
   ASSERT_FALSE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKNEWTAB));
   ASSERT_FALSE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW));
+  ASSERT_FALSE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW));
   ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_COPYLINKLOCATION));
   ASSERT_FALSE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKINPROFILE));
   ASSERT_FALSE(menu->IsItemInRangePresent(IDC_OPEN_LINK_IN_PROFILE_FIRST,
@@ -2082,6 +2098,7 @@
 
     ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKNEWTAB));
     ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW));
+    ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW));
     ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_COPYLINKLOCATION));
     // With only one profile exists, we don't add any items to the context menu
     // for opening links in other profiles.
@@ -2108,6 +2125,7 @@
 
     ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKNEWTAB));
     ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW));
+    ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW));
     ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_COPYLINKLOCATION));
     // With the second profile not yet open and thus not active, an inline entry
     // to open the link with the secondary profile is not displayed.
@@ -2149,6 +2167,7 @@
 
     ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKNEWTAB));
     ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW));
+    ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW));
     ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_COPYLINKLOCATION));
     // With the second profile open, an inline entry to open the link with the
     // secondary profile is displayed.
@@ -2168,6 +2187,7 @@
 
     ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKNEWTAB));
     ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW));
+    ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW));
     ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_COPYLINKLOCATION));
     // With the second profile closed but active, an inline entry to open the
     // link with the secondary profile is displayed.
@@ -2186,6 +2206,7 @@
 
     ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKNEWTAB));
     ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW));
+    ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW));
     ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_COPYLINKLOCATION));
     // With at least two secondary profiles, they are displayed in a submenu.
     raw_ptr<ui::MenuModel> model = nullptr;
@@ -3468,6 +3489,7 @@
 
   EXPECT_FALSE(menu1->IsItemEnabled(IDC_CONTENT_CONTEXT_OPENLINKNEWTAB));
   EXPECT_FALSE(menu1->IsItemEnabled(IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW));
+  ASSERT_FALSE(menu1->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW));
   EXPECT_FALSE(menu1->IsItemEnabled(IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD));
 }
 
@@ -3483,4 +3505,52 @@
   EXPECT_FALSE(menu1->IsItemEnabled(IDC_CONTENT_CONTEXT_OPENLINKPREVIEW));
 }
 
+IN_PROC_BROWSER_TEST_P(ContextMenuBrowserTest, DoNotShowSplitTabInWebApp) {
+  const GURL test_url("http://www.example.com/");
+  const AppId app_id = InstallTestWebApp(GURL(kAppUrl1));
+  Browser* const app_window = OpenTestWebApp(app_id);
+  ASSERT_FALSE(app_window->is_type_normal());
+
+  std::unique_ptr<TestRenderViewContextMenu> menu =
+      CreateContextMenuMediaTypeNoneInWebContents(
+          app_window->tab_strip_model()->GetActiveWebContents(), test_url,
+          test_url);
+
+  EXPECT_FALSE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW));
+}
+
+IN_PROC_BROWSER_TEST_P(ContextMenuBrowserTest, OpenLinkInNewSplitTab) {
+  const GURL test_url("http://www.example.com/");
+  std::unique_ptr<TestRenderViewContextMenu> menu =
+      CreateContextMenuMediaTypeNone(test_url, test_url);
+
+  EXPECT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW));
+
+  TabStripModel* const tab_strip_model = browser()->tab_strip_model();
+  ASSERT_EQ(tab_strip_model->count(), 1);
+  menu->ExecuteCommand(IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW, 0);
+  ASSERT_EQ(tab_strip_model->count(), 2);
+  EXPECT_EQ(tab_strip_model->active_index(), 1);
+  EXPECT_TRUE(tab_strip_model->GetActiveTab()->IsSplit());
+  EXPECT_TRUE(tab_strip_model->GetTabAtIndex(0)->IsSplit());
+}
+
+IN_PROC_BROWSER_TEST_P(ContextMenuBrowserTest, OpenLinkInExistingSplitTab) {
+  const GURL test_url("http://www.example.com/");
+  TabStripModel* const tab_strip_model = browser()->tab_strip_model();
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), test_url));
+  chrome::NewSplitTab(browser());
+  tab_strip_model->ActivateTabAt(0);
+  ASSERT_NE(tab_strip_model->GetWebContentsAt(1)->GetURL(), test_url);
+
+  std::unique_ptr<TestRenderViewContextMenu> menu =
+      CreateContextMenuMediaTypeNone(test_url, test_url);
+
+  EXPECT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW));
+  menu->ExecuteCommand(IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW, 0);
+  ASSERT_EQ(tab_strip_model->count(), 2);
+  EXPECT_TRUE(tab_strip_model->GetTabAtIndex(0)->IsSplit());
+  EXPECT_EQ(tab_strip_model->GetWebContentsAt(1)->GetURL(), test_url);
+}
+
 }  // namespace
diff --git a/chrome/browser/resources/ash/settings/os_languages_page/os_japanese_dictionary_expand.html b/chrome/browser/resources/ash/settings/os_languages_page/os_japanese_dictionary_expand.html
index 2c204f00..ee53bf0 100644
--- a/chrome/browser/resources/ash/settings/os_languages_page/os_japanese_dictionary_expand.html
+++ b/chrome/browser/resources/ash/settings/os_languages_page/os_japanese_dictionary_expand.html
@@ -46,7 +46,7 @@
   </cr-expand-button>
   <div class="separator"></div>
   <cr-icon-button class="icon-clear"
-    on-click="deleteDictionary_"
+    on-click="showDeleteDialog_"
     title="DeleteDictionary"
   >
   </cr-icon-button>
@@ -86,3 +86,14 @@
     </cr-button>
   </template>
 </iron-collapse>
+
+<template is="dom-if" if="[[showingDeleteDialog_]]" restamp>
+  <cr-dialog close-text="delete" show-on-attach>
+    <div slot="title">Delete this dictionary</div>
+    <div slot="body">[[dict.name]] will be permanently deleted. This action can't be undone</div>
+    <div slot="button-container">
+        <cr-button class="cancel-button" on-click="hideDeleteDialog_">Cancel</cr-button>
+        <cr-button class="action-button" on-click="deleteDictionary_">Delete</cr-button>
+    </div>
+  </cr-dialog>
+</template>
\ No newline at end of file
diff --git a/chrome/browser/resources/ash/settings/os_languages_page/os_japanese_dictionary_expand.ts b/chrome/browser/resources/ash/settings/os_languages_page/os_japanese_dictionary_expand.ts
index 37d9898..e666415b 100644
--- a/chrome/browser/resources/ash/settings/os_languages_page/os_japanese_dictionary_expand.ts
+++ b/chrome/browser/resources/ash/settings/os_languages_page/os_japanese_dictionary_expand.ts
@@ -43,6 +43,9 @@
       syncedEntriesCount: {
         type: Number,
       },
+      showingDeleteDialog_: {
+        type: Boolean,
+      },
     };
   }
 
@@ -56,6 +59,8 @@
   // Whether or not this container UI is expanded or folded.
   private expanded_ = false;
 
+  private showingDeleteDialog_ = false;
+
   // Adds a new entry locally to create an entry-row component.
   private addEntry_(): void {
     // This changes the entries array from the parent component which it will
@@ -87,6 +92,15 @@
     if (dictionarySaved) {
       this.dispatchSavedEvent_();
     }
+    this.showingDeleteDialog_ = false;
+  }
+
+  private hideDeleteDialog_() {
+    this.showingDeleteDialog_ = false;
+  }
+
+  private showDeleteDialog_() {
+    this.showingDeleteDialog_ = true;
   }
 
   // Export dictionary.
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/BUILD.gn
index aef67f6..dc5b2ab7 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/BUILD.gn
@@ -159,6 +159,7 @@
   "log_page/log.ts",
   "log_page/log_loader.ts",
   "offscreen/offscreen.ts",
+  "offscreen/liblouis_worker.ts",
   "panel/i_search_ui.ts",
   "panel/menu_manager.ts",
   "panel/panel.ts",
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/background/braille/liblouis.ts b/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/background/braille/liblouis.ts
index 5b73f87..17949a9 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/background/braille/liblouis.ts
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/background/braille/liblouis.ts
@@ -7,8 +7,19 @@
  */
 import {TestImportManager} from '/common/testing/test_import_manager.js';
 
+import {OffscreenCommandType} from '../../common/offscreen_command_type.js';
+
 type LoadCallback = (instance: LibLouis) => void;
 type MessageCallback = (message: Object) => void;
+type SerializedMessageEvent = {
+  command: OffscreenCommandType,
+  data: string,
+};
+type SerializedErrorEvent = {
+  command: OffscreenCommandType,
+  message: string,
+};
+
 
 interface Dictionary {
   [key: string]: any;
@@ -25,8 +36,6 @@
   /** Next message ID to be used. Incremented with each sent message. */
   private nextMessageId_ = 1;
 
-  worker?: Worker;
-
   /**
    * @param wasmPath Path to .wasm file for the module.
    * @param tablesDir Path to tables directory.
@@ -35,16 +44,27 @@
       wasmPath: string, _tablesDir?: string, loadCallback?: LoadCallback) {
     this.wasmPath_ = wasmPath;
 
-    this.loadOrReload_(loadCallback);
+    chrome.runtime.onMessage
+        .addListener(
+            (message: any|undefined, _sender: chrome.runtime.MessageSender,
+             _sendResponse: (value: any) => void) =>
+                this.handleMessageFromOffscreen_(message))
+
+            this.loadOrReload_(loadCallback);
   }
 
-  /**
-   * Convenience method to wait for the constructor to resolve its callback.
-   * @param wasmPath Path to .wasm file for the module.
-   * @param tablesDir Path to tables directory.
-   */
-  static async create(wasmPath: string, tablesDir?: string): Promise<LibLouis> {
-    return new Promise(resolve => new LibLouis(wasmPath, tablesDir, resolve));
+  private handleMessageFromOffscreen_(message: any|undefined): boolean {
+    switch (message['command']) {
+      case OffscreenCommandType.LIBLOUIS_MESSAGE:
+        this.onInstanceMessage_(message);
+        break;
+      case OffscreenCommandType.LIBLOUIS_ERROR:
+        this.onInstanceError_(message);
+        break;
+    }
+    // Returns false as the response is not asynchronous and the callback does
+    // not need to be kept alive.
+    return false;
   }
 
   isLoaded(): boolean {
@@ -87,9 +107,6 @@
    * @param callback Callback to receive the reply.
    */
   rpc(command: string, message: Dictionary, callback: MessageCallback): void {
-    if (!this.worker) {
-      throw Error('Cannot send RPC: liblouis instance not loaded');
-    }
     const messageId = '' + this.nextMessageId_++;
     message['message_id'] = messageId;
     message['command'] = command;
@@ -97,25 +114,33 @@
     if (LibLouis.DEBUG) {
       globalThis.console.debug('RPC -> ' + json);
     }
-    this.worker.postMessage(json);
     this.pendingRpcCallbacks_[messageId] = callback;
+
+    chrome.runtime.sendMessage(
+        undefined,
+        {command: OffscreenCommandType.LIBLOUIS_RPC, messageJson: json},
+        undefined, (error: any) => {
+          if (error && error.message) {
+            throw Error(error.message);
+          }
+        });
   }
 
   /** Invoked when the Web Assembly instance successfully loads. */
   private onInstanceLoad_(): void {}
 
   /** Invoked when the Web Assembly instance fails to load. */
-  private onInstanceError_(e: ErrorEvent): void {
-    globalThis.console.error('Error in liblouis ' + e.message);
+  private onInstanceError_(serializedEvent: SerializedErrorEvent): void {
+    globalThis.console.error('Error in liblouis ' + serializedEvent.message);
     this.loadOrReload_();
   }
 
   /** Invoked when the Web Assembly instance posts a message. */
-  private onInstanceMessage_(e: MessageEvent): void {
+  private onInstanceMessage_(serializedEvent: SerializedMessageEvent): void {
     if (LibLouis.DEBUG) {
-      globalThis.console.debug('RPC <- ' + e.data);
+      globalThis.console.debug('RPC <- ' + serializedEvent.data);
     }
-    const message = /** @type {!Object} */ (JSON.parse(e.data));
+    const message = /** @type {!Object} */ (JSON.parse(serializedEvent.data));
     const messageId = message['in_reply_to'];
     if (messageId === undefined) {
       globalThis.console.warn(
@@ -133,11 +158,11 @@
   }
 
   private loadOrReload_(loadCallback?: LoadCallback): void {
-    this.worker = new Worker(this.wasmPath_);
-    this.worker.addEventListener(
-        'message', e => this.onInstanceMessage_(e), false /* useCapture */);
-    this.worker.addEventListener(
-        'error', e => this.onInstanceError_(e), false /* useCapture */);
+    chrome.runtime.sendMessage(undefined, {
+      command: OffscreenCommandType.LIBLOUIS_START_WORKER,
+      wasmPath: this.wasmPath_
+    });
+
     this.rpc('load', {}, () => {
       this.isLoaded_ = true;
       loadCallback && loadCallback(this);
@@ -193,7 +218,7 @@
     translate(
         text: string, formTypeMap: number[]|number,
         callback: TranslateCallback): void {
-      if (!this.instance_.worker) {
+      if (!this.instance_.isLoaded()) {
         callback(
             null /*cells*/, null /*textToBraille*/, null /*brailleToText*/);
         return;
@@ -236,7 +261,7 @@
      * @param callback Callback for result.
      */
     backTranslate(cells: ArrayBuffer, callback: BackTranslateCallback): void {
-      if (!this.instance_.worker) {
+      if (!this.instance_.isLoaded()) {
         callback(null /*text*/);
         return;
       }
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/background/braille/liblouis_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/background/braille/liblouis_test.js
index c7a7b3a8..8720b89 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/background/braille/liblouis_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/background/braille/liblouis_test.js
@@ -15,9 +15,8 @@
   async setUpDeferred() {
     await super.setUpDeferred();
 
-    const path = chrome.runtime.getURL(
-        'chromevox/third_party/liblouis/liblouis_wrapper.js');
-    this.liblouis = await LibLouis.create(path, '');
+    this.liblouis = BrailleTranslatorManager.instance.getLibLouisForTest();
+    await new Promise((resolve) => this.waitForLibLouisLoad(resolve));
   }
 
   async backTranslate(tableNames, buffer) {
@@ -30,6 +29,14 @@
     return new Promise(
         resolve => translator.translate(text, [], (...args) => resolve(args)));
   }
+
+  async waitForLibLouisLoad(resolve) {
+    if (this.liblouis.isLoaded()) {
+      resolve()
+    } else {
+      setTimeout(() => this.waitForLibLouisLoad(resolve), 500);
+    }
+  }
 };
 
 function assertEqualsUint8Array(expected, actual) {
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/common/offscreen_command_type.ts b/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/common/offscreen_command_type.ts
index 6b80882..8ea174c 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/common/offscreen_command_type.ts
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/common/offscreen_command_type.ts
@@ -12,6 +12,10 @@
   EARCON_CANCEL_PROGRESS = 'EarconCancelProgress',
   EARCON_RESET_PAN = 'EarconSesetPan',
   EARCON_SET_POSITION_FOR_RECT = 'EarconSetPositionForRect',
+  LIBLOUIS_START_WORKER = 'LibLouisStartWorker',
+  LIBLOUIS_RPC = 'LibLouisRPC',
+  LIBLOUIS_MESSAGE = 'LibLouisMessage',
+  LIBLOUIS_ERROR = 'LibLouisError',
   ON_CLIPBOARD_DATA_CHANGED = 'onClipboardDataChanged',
   ON_KEY_DOWN = 'onKeyDown',
   ON_KEY_UP = 'onKeyUp',
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/offscreen/liblouis_worker.ts b/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/offscreen/liblouis_worker.ts
new file mode 100644
index 0000000..54107e19
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/offscreen/liblouis_worker.ts
@@ -0,0 +1,69 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {OffscreenCommandType} from '../common/offscreen_command_type.js';
+
+type MessageSender = chrome.runtime.MessageSender;
+type SendResponse = (value: any) => void;
+
+export class LibLouisWorker {
+  private worker_: Worker|null = null;
+
+  static instance?: LibLouisWorker;
+
+  constructor() {
+    chrome.runtime.onMessage.addListener(
+        (message: any|undefined, _sender: MessageSender,
+         sendResponse: SendResponse) =>
+            this.handleMessageFromServiceWorker_(message, sendResponse));
+  }
+
+  private handleMessageFromServiceWorker_(
+      message: any|undefined, sendResponse: SendResponse): boolean {
+    switch (message['command']) {
+      case OffscreenCommandType.LIBLOUIS_START_WORKER:
+        this.startWorker_(message['wasmPath']);
+        break;
+      case OffscreenCommandType.LIBLOUIS_RPC:
+        if (!this.worker_) {
+          sendResponse(
+              {message: 'Cannot send RPC: liblouis worker not started.'});
+        } else {
+          this.worker_.postMessage(message['messageJson']);
+          // No error.
+          sendResponse({});
+        }
+        break;
+    }
+    return false;
+  }
+
+  static init(): void {
+    if (LibLouisWorker.instance) {
+      throw 'Error: trying to create two instances of singleton ' +
+          'LibLouisWorker.';
+    }
+    LibLouisWorker.instance = new LibLouisWorker();
+  }
+
+  private startWorker_(wasmPath: string): void {
+    this.worker_ = new Worker(wasmPath);
+    this.worker_.addEventListener(
+        'message', e => this.onInstanceMessage_(e), false /* useCapture */);
+    this.worker_.addEventListener(
+        'error', e => this.onInstanceError_(e), false /* useCapture */);
+  }
+
+  private onInstanceMessage_(e: MessageEvent): void {
+    chrome.runtime.sendMessage(
+        undefined,
+        {command: OffscreenCommandType.LIBLOUIS_MESSAGE, data: e.data});
+  }
+
+  private onInstanceError_(e: ErrorEvent): void {
+    chrome.runtime.sendMessage(
+        undefined,
+        {command: OffscreenCommandType.LIBLOUIS_ERROR, message: e.message});
+  }
+}
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/offscreen/offscreen.ts b/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/offscreen/offscreen.ts
index 272bfe1..b54b367 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/offscreen/offscreen.ts
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/offscreen/offscreen.ts
@@ -6,6 +6,8 @@
 import {InternalKeyEvent} from '../common/internal_key_event.js';
 import {OffscreenCommandType} from '../common/offscreen_command_type.js';
 
+import {LibLouisWorker} from './liblouis_worker.js';
+
 type MessageSender = chrome.runtime.MessageSender;
 type SendResponse = (value: any) => void;
 
@@ -195,3 +197,4 @@
 OffscreenClipboardHandler.init();
 OffscreenSpeechSynthesis.init();
 EarconEngine.init();
+LibLouisWorker.init();
diff --git a/chrome/browser/resources/settings/glic_page/glic_page.ts b/chrome/browser/resources/settings/glic_page/glic_page.ts
index 355029dd..dfe76caae 100644
--- a/chrome/browser/resources/settings/glic_page/glic_page.ts
+++ b/chrome/browser/resources/settings/glic_page/glic_page.ts
@@ -94,6 +94,13 @@
     };
   }
 
+  static get observers() {
+    return [
+      'onTabContextEnabledChanged_(' +
+          `prefs.${SettingsGlicPageFeaturePrefName.TAB_CONTEXT_ENABLED}.value)`,
+    ];
+  }
+
   private shortcutInput_: string;
   private focusToggleShortcutInput_: string;
   private removedShortcut_: string|null = null;
@@ -117,10 +124,6 @@
     this.registeredFocusToggleShortcut_ =
         await this.browserProxy_.getGlicFocusToggleShortcut();
     await CrSettingsPrefs.initialized;
-    this.tabAccessToggleExpanded_ =
-        this.getPref<boolean>(
-                SettingsGlicPageFeaturePrefName.TAB_CONTEXT_ENABLED)
-            .value;
   }
 
   private onGlicPageClick_() {
@@ -222,10 +225,14 @@
     }
   }
 
+  // Update the tab access collapsible any time the tab access pref changes.
+  private onTabContextEnabledChanged_(enabled: boolean) {
+    this.tabAccessToggleExpanded_ = enabled;
+  }
+
   private onTabAccessToggleChange_(event: CustomEvent) {
     const target = event.target as SettingsToggleButtonElement;
     const enabled = target.checked;
-    this.tabAccessToggleExpanded_ = enabled;
     this.metricsBrowserProxy_.recordAction(
         'Glic.Settings.TabContext' + (enabled ? '.Enabled' : '.Disabled'));
   }
diff --git a/chrome/browser/resources/side_panel/read_anything/app.ts b/chrome/browser/resources/side_panel/read_anything/app.ts
index 2112914..ef181b2 100644
--- a/chrome/browser/resources/side_panel/read_anything/app.ts
+++ b/chrome/browser/resources/side_panel/read_anything/app.ts
@@ -41,10 +41,6 @@
 
 const linkDataAttribute = 'link';
 
-// The maximum speech length that should be used with remote voices
-// due to a TTS engine bug with voices timing out on too-long text.
-export const MAX_SPEECH_LENGTH: number = 175;
-
 export interface AppElement {
   $: {
     toolbar: ReadAnythingToolbarElement,
@@ -131,9 +127,6 @@
 
   protected accessor localeToDisplayName_: {[locale: string]: string} = {};
 
-  // Metrics captured for logging.
-  private playSessionStartTime: number = -1;
-
   private notificationManager_ = VoiceNotificationManager.getInstance();
   private logger_: ReadAnythingLogger = ReadAnythingLogger.getInstance();
   private styleUpdater_: AppStyleUpdater;
@@ -210,7 +203,6 @@
       this.hasContent_ = false;
       this.firstTextNodeSetForReadAloud = null;
       this.nodeStore_.clearDomNodes();
-      this.clearReadAloudState();
     }
 
     this.settingsPrefs_ = {
@@ -490,7 +482,7 @@
     this.hasContent_ = false;
     if (this.isReadAloudEnabled_) {
       this.speech_.cancel();
-      this.clearReadAloudState();
+      this.speechController_.clearReadAloudState();
     }
   }
 
@@ -514,7 +506,7 @@
     const previousWordBoundaryState = {...this.wordBoundaries_.state};
 
     this.speech_.cancel();
-    this.clearReadAloudState();
+    this.speechController_.clearReadAloudState();
     const container = this.$.container;
 
     // Remove all children from container. Use `replaceChildren` rather than
@@ -894,11 +886,10 @@
 
   protected onPlayPauseClick_() {
     if (this.speechController_.isSpeechActive()) {
-      this.logSpeechPlaySession_();
       this.speechController_.stopSpeech(PauseActionSource.BUTTON_CLICK);
     } else {
-      this.playSessionStartTime = Date.now();
       this.playSpeech();
+      this.speechController_.onPlay();
     }
   }
 
@@ -919,12 +910,16 @@
     this.previewVoicePlaying_ = this.speechController_.getPreviewVoicePlaying();
   }
 
-  onPause() {
-    // Restore links if they're enabled when speech pauses. Don't restore links
-    // if it's paused from a non-pause button (e.g. voice previews) so the links
-    // don't flash off and on.
-    if (chrome.readingMode.linksEnabled &&
-        this.speechController_.isPausedFromButton()) {
+  onStop() {
+    if (!chrome.readingMode.linksEnabled) {
+      return;
+    }
+
+    // Restore links if they're enabled when speech pauses via button click or
+    // when it finishes the page.
+    const pauseSource = this.speechController_.getPauseSource();
+    if ((pauseSource === PauseActionSource.BUTTON_CLICK) ||
+        pauseSource === PauseActionSource.SPEECH_FINISHED) {
       this.updateLinks_();
     }
   }
@@ -944,16 +939,6 @@
     this.resetSpeechPostSettingChange_();
   }
 
-  private logSpeechPlaySession_() {
-    // Don't log a playback session just in case something has gotten out of
-    // sync and we call stopSpeech before playSpeech.
-    if (this.playSessionStartTime > 0) {
-      this.logger_.logSpeechPlaySession(
-          this.playSessionStartTime, this.selectedVoice_);
-      this.playSessionStartTime = -1;
-    }
-  }
-
   protected playNextGranularity_() {
     this.speechController_.setIsSpeechBeingRepositioned(true);
 
@@ -964,7 +949,7 @@
     chrome.readingMode.movePositionToNextGranularity();
 
     if (!this.highlightAndPlayMessage()) {
-      this.onSpeechFinished();
+      this.speechController_.onSpeechFinished();
     }
   }
 
@@ -982,7 +967,7 @@
 
     if (!this.highlightAndPlayMessage(/*isInterrupted=*/ false,
                                       /*isMovingBackward=*/ true)) {
-      this.onSpeechFinished();
+      this.speechController_.onSpeechFinished();
     }
   }
 
@@ -1014,7 +999,7 @@
           if (!this.highlightAndPlayInterruptedMessage()) {
             // Ensure we're updating Read Aloud state if there's no text to
             // speak.
-            this.onSpeechFinished();
+            this.speechController_.onSpeechFinished();
           }
         }
       }
@@ -1061,7 +1046,7 @@
             this.firstTextNodeSetForReadAloud);
         if (!this.highlightAndPlayMessage()) {
           // Ensure we're updating Read Aloud state if there's no text to speak.
-          this.onSpeechFinished();
+          this.speechController_.onSpeechFinished();
         }
       }
     }
@@ -1143,7 +1128,7 @@
       // includes the selection.
       this.highlighter_.resetPreviousHighlight();
       if (!this.highlightAndPlayMessage()) {
-        this.onSpeechFinished();
+        this.speechController_.onSpeechFinished();
       }
     }, playFromSelectionTimeout);
 
@@ -1220,31 +1205,6 @@
     return this.highlightAndPlayMessage(isInterrupted, isMovingBackward);
   }
 
-  // Gets the accessible text boundary for the given string.
-  getAccessibleTextLength(utteranceText: string): number {
-    // Splicing on commas won't work for all locales, but since this is a
-    // simple strategy for splicing text in languages that do use commas
-    // that reduces the need for calling getAccessibleBoundary.
-    // TODO(crbug.com/40927698): Investigate if we can utilize comma splices
-    // directly in the utils methods called by #getAccessibleBoundary.
-    const lastCommaIndex =
-        utteranceText.substring(0, MAX_SPEECH_LENGTH).lastIndexOf(', ');
-
-    // To prevent infinite looping, only use the lastCommaIndex if it's not the
-    // first character. Otherwise, use getAccessibleBoundary to prevent
-    // repeatedly splicing on the first comma of the same substring.
-    if (lastCommaIndex > 0) {
-      return lastCommaIndex;
-    }
-
-    // TODO: crbug.com/40927698 - getAccessibleBoundary breaks on the nearest
-    // word boundary, but if there's some type of punctuation (such as a comma),
-    // it would be preferable to break on the punctuation so the pause in
-    // speech sounds more natural.
-    return chrome.readingMode.getAccessibleBoundary(
-        utteranceText, MAX_SPEECH_LENGTH);
-  }
-
   private playText(utteranceText: string) {
     // This check is needed due limits of TTS audio for remote voices. See
     // crbug.com/1176078 for more details.
@@ -1252,12 +1212,9 @@
     // maximum text length if we're using a local voice. If we do somehow
     // attempt to speak text that's too long, this will be able to be handled
     // by listening for a text-too-long error in message.onerror.
-    const isTextTooLong = this.selectedVoice_?.localService ?
-        false :
-        utteranceText.length > MAX_SPEECH_LENGTH;
-    const endBoundary = isTextTooLong ?
-        this.getAccessibleTextLength(utteranceText) :
-        utteranceText.length;
+    const isTextTooLong = this.speechController_.isTextTooLong(utteranceText);
+    const endBoundary = this.speechController_.getUtteranceEndBoundary(
+        utteranceText, isTextTooLong);
     this.playTextWithBoundaries(utteranceText, isTextTooLong, endBoundary);
   }
 
@@ -1290,7 +1247,7 @@
       chrome.readingMode.movePositionToNextGranularity();
       // Continue speaking with the next block of text.
       if (!this.highlightAndPlayMessage()) {
-        this.onSpeechFinished();
+        this.speechController_.onSpeechFinished();
       }
     };
 
@@ -1323,7 +1280,8 @@
       // this is still preferable to no speech.
       this.speech_.cancel();
       this.playTextWithBoundaries(
-          utteranceText, true, this.getAccessibleTextLength(utteranceText));
+          utteranceText, true,
+          this.speechController_.getUtteranceEndBoundary(utteranceText, true));
       return;
     }
     if (error.error === 'invalid-argument') {
@@ -1374,24 +1332,6 @@
     return utteranceText;
   }
 
-  private onSpeechFinished() {
-    this.logger_.logSpeechStopSource(
-        chrome.readingMode.contentFinishedStopSource);
-    this.clearReadAloudState();
-
-    // Show links when speech finishes playing.
-    if (chrome.readingMode.linksEnabled) {
-      this.updateLinks_();
-    }
-    this.logSpeechPlaySession_();
-  }
-
-  private clearReadAloudState() {
-    this.speechController_.reset();
-    this.highlighter_.clearHighlightFormatting();
-    this.wordBoundaries_.resetToDefaultState();
-  }
-
   protected onSelectVoice_(
       event: CustomEvent<{selectedVoice: SpeechSynthesisVoice}>) {
     event.preventDefault();
diff --git a/chrome/browser/resources/side_panel/read_anything/read_aloud/speech_controller.ts b/chrome/browser/resources/side_panel/read_anything/read_aloud/speech_controller.ts
index 9f02745..c081528 100644
--- a/chrome/browser/resources/side_panel/read_anything/read_aloud/speech_controller.ts
+++ b/chrome/browser/resources/side_panel/read_anything/read_aloud/speech_controller.ts
@@ -17,8 +17,12 @@
 import {WordBoundaries} from './word_boundaries.js';
 import type {WordBoundaryState} from './word_boundaries.js';
 
+// The maximum speech length that should be used with remote voices
+// due to a TTS engine bug with voices timing out on too-long text.
+export const MAX_SPEECH_LENGTH: number = 175;
+
 export interface SpeechListener {
-  onPause(): void;
+  onStop(): void;
   onIsSpeechActiveChange(): void;
   onIsAudioCurrentlyPlayingChange(): void;
   onEngineStateChange(): void;
@@ -39,6 +43,7 @@
 
   constructor() {
     // Send over the initial state.
+    this.clearReadAloudState();
     this.isSpeechActiveChanged(this.isSpeechActive());
   }
 
@@ -151,6 +156,10 @@
     chrome.readingMode.preprocessTextForSpeech();
   }
 
+  onPlay() {
+    this.model_.setPlaySessionStartTime(Date.now());
+  }
+
   stopSpeech(pauseSource: PauseActionSource) {
     this.setIsSpeechActive(false);
     this.setIsAudioCurrentlyPlaying(false);
@@ -166,13 +175,14 @@
     // synth.pause() and synth.resume() for speech to resume from where it left
     // off.
     if (this.isPausedFromButton()) {
+      this.logSpeechPlaySession_();
       this.speech_.pause();
     } else {
       // Canceling clears all the Utterances that are queued up via synth.play()
       this.speech_.cancel();
     }
 
-    this.listeners_.forEach(l => l.onPause());
+    this.listeners_.forEach(l => l.onStop());
   }
 
   setOnSpeechSynthesisUtteranceStart(message: SpeechSynthesisUtterance) {
@@ -286,6 +296,21 @@
     }
   }
 
+  onSpeechFinished() {
+    this.clearReadAloudState();
+    this.model_.setPauseSource(PauseActionSource.SPEECH_FINISHED);
+    this.listeners_.forEach(l => l.onStop());
+    this.logger_.logSpeechStopSource(
+        chrome.readingMode.contentFinishedStopSource);
+    this.logSpeechPlaySession_();
+  }
+
+  clearReadAloudState() {
+    this.reset();
+    this.highlighter_.clearHighlightFormatting();
+    this.wordBoundaries_.resetToDefaultState();
+  }
+
   setPreviousReadingPositionIfExists(
       previousWordBoundaryState: WordBoundaryState,
       previousSpeechPlayingState: SpeechPlayingState) {
@@ -343,6 +368,39 @@
         axNodeIds, scrollIntoView, shouldUpdateSentenceHighlight);
   }
 
+  isTextTooLong(text: string): boolean {
+    return !this.voicePackController_.getCurrentVoice()?.localService &&
+        text.length > MAX_SPEECH_LENGTH;
+  }
+
+  getUtteranceEndBoundary(text: string, isTextTooLong: boolean): number {
+    return isTextTooLong ? this.getAccessibleTextLength_(text) : text.length;
+  }
+
+  // Gets the accessible text boundary for the given string.
+  private getAccessibleTextLength_(text: string): number {
+    // Splicing on commas won't work for all locales, but since this is a
+    // simple strategy for splicing text in languages that do use commas
+    // that reduces the need for calling getAccessibleBoundary.
+    // TODO(crbug.com/40927698): Investigate if we can utilize comma splices
+    // directly in the utils methods called by #getAccessibleBoundary.
+    const lastCommaIndex =
+        text.substring(0, MAX_SPEECH_LENGTH).lastIndexOf(', ');
+
+    // To prevent infinite looping, only use the lastCommaIndex if it's not the
+    // first character. Otherwise, use getAccessibleBoundary to prevent
+    // repeatedly splicing on the first comma of the same substring.
+    if (lastCommaIndex > 0) {
+      return lastCommaIndex;
+    }
+
+    // TODO: crbug.com/40927698 - getAccessibleBoundary breaks on the nearest
+    // word boundary, but if there's some type of punctuation (such as a comma),
+    // it would be preferable to break on the punctuation so the pause in
+    // speech sounds more natural.
+    return chrome.readingMode.getAccessibleBoundary(text, MAX_SPEECH_LENGTH);
+  }
+
   private isSpeechActiveChanged(isSpeechActive: boolean) {
     this.listeners_.forEach(l => l.onIsSpeechActiveChange());
     chrome.readingMode.onSpeechPlayingStateChanged(isSpeechActive);
@@ -354,6 +412,15 @@
     this.speech_.speak(message);
   }
 
+  private logSpeechPlaySession_() {
+    const startTime = this.model_.getPlaySessionStartTime();
+    if (startTime) {
+      this.logger_.logSpeechPlaySession(
+          startTime, this.voicePackController_.getCurrentVoice());
+      this.model_.setPlaySessionStartTime(null);
+    }
+  }
+
   static getInstance(): SpeechController {
     return instance || (instance = new SpeechController());
   }
diff --git a/chrome/browser/resources/side_panel/read_anything/read_aloud/speech_model.ts b/chrome/browser/resources/side_panel/read_anything/read_aloud/speech_model.ts
index 709bb37..59f0769 100644
--- a/chrome/browser/resources/side_panel/read_anything/read_aloud/speech_model.ts
+++ b/chrome/browser/resources/side_panel/read_anything/read_aloud/speech_model.ts
@@ -8,6 +8,7 @@
   VOICE_PREVIEW,
   VOICE_SETTINGS_CHANGE,
   ENGINE_INTERRUPT,
+  SPEECH_FINISHED,
 }
 
 export interface SpeechPlayingState {
@@ -67,6 +68,9 @@
   // last position so we can check if it's still in the new page.
   private lastReadingPosition_: ReadingPosition|null = null;
 
+  // Used for logging play time.
+  private playSessionStartTime_: number|null = null;
+
   reset(): void {
     this.speechPlayingState_ = {
       isSpeechTreeInitialized: false,
@@ -80,6 +84,14 @@
     this.previewVoicePlaying_ = null;
   }
 
+  getPlaySessionStartTime(): number|null {
+    return this.playSessionStartTime_;
+  }
+
+  setPlaySessionStartTime(time: number|null): void {
+    this.playSessionStartTime_ = time;
+  }
+
   getLastPosition(): ReadingPosition|null {
     return this.lastReadingPosition_;
   }
diff --git a/chrome/browser/resources/side_panel/read_anything/read_anything.ts b/chrome/browser/resources/side_panel/read_anything/read_anything.ts
index 152287ef..f941149 100644
--- a/chrome/browser/resources/side_panel/read_anything/read_anything.ts
+++ b/chrome/browser/resources/side_panel/read_anything/read_anything.ts
@@ -9,7 +9,6 @@
 export {PageCallbackRouter} from '//resources/cr_components/color_change_listener/color_change_listener.mojom-webui.js';
 export type {CrActionMenuElement} from '//resources/cr_elements/cr_action_menu/cr_action_menu.js';
 export type {AppElement} from './app.js';
-export {MAX_SPEECH_LENGTH} from './app.js';
 export {AppStyleUpdater} from './app_style_updater.js';
 export {getCurrentSpeechRate, playFromSelectionTimeout, spinnerDebounceTimeout, ToolbarEvent} from './common.js';
 export {getNewIndex, isArrow, isForwardArrow, isHorizontalArrow} from './keyboard_util.js';
@@ -24,7 +23,7 @@
 export {MetricsBrowserProxy, MetricsBrowserProxyImpl, ReadAloudHighlightState, ReadAloudSettingsChange, ReadAnythingNewPage, ReadAnythingSettingsChange, ReadAnythingSpeechError, ReadAnythingVoiceType} from './metrics_browser_proxy.js';
 export {NodeStore} from './node_store.js';
 export {currentReadHighlightClass, previousReadHighlightClass, ReadAloudHighlighter} from './read_aloud/highlighter.js';
-export {SpeechController, SpeechListener} from './read_aloud/speech_controller.js';
+export {MAX_SPEECH_LENGTH, SpeechController, SpeechListener} from './read_aloud/speech_controller.js';
 export {PauseActionSource, SpeechEngineState, SpeechModel} from './read_aloud/speech_model.js';
 export {VoiceLanguageListener, VoicePackController} from './read_aloud/voice_pack_controller.js';
 export {VoicePackModel} from './read_aloud/voice_pack_model.js';
diff --git a/chrome/browser/safe_browsing/android/BUILD.gn b/chrome/browser/safe_browsing/android/BUILD.gn
index 58fd64ba..066d23e 100644
--- a/chrome/browser/safe_browsing/android/BUILD.gn
+++ b/chrome/browser/safe_browsing/android/BUILD.gn
@@ -131,6 +131,7 @@
   testonly = true
   resources_package = "org.chromium.chrome.browser.safe_browsing.settings"
   sources = [
+    "java/src/org/chromium/chrome/browser/safe_browsing/AdvancedProtectionMediatorIntegrationTest.java",
     "javatests/src/org/chromium/chrome/browser/safe_browsing/settings/EnhancedProtectionSettingsFragmentTest.java",
     "javatests/src/org/chromium/chrome/browser/safe_browsing/settings/SafeBrowsingSettingsFragmentTest.java",
     "javatests/src/org/chromium/chrome/browser/safe_browsing/settings/StandardProtectionSettingsFragmentTest.java",
@@ -151,6 +152,7 @@
     "//chrome/test/android:chrome_java_integration_test_support",
     "//components/browser_ui/settings/android:java",
     "//components/browser_ui/widget/android:java",
+    "//components/permissions/android:core_java",
     "//components/policy/android:policy_java_test_support",
     "//components/prefs/android:java",
     "//components/signin/public/android:java",
diff --git a/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/AdvancedProtectionMediator.java b/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/AdvancedProtectionMediator.java
index f06b569..b1f39c50 100644
--- a/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/AdvancedProtectionMediator.java
+++ b/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/AdvancedProtectionMediator.java
@@ -10,8 +10,10 @@
 
 import androidx.fragment.app.Fragment;
 
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.shared_preferences.SharedPreferencesManager;
 import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.ChromeSharedPreferences;
 import org.chromium.chrome.browser.privacy.settings.PrivacySettingsNavigation;
@@ -49,6 +51,8 @@
                 mShouldShowMessageOnStartup = advancedProtectionSetting;
             }
         }
+
+        recordStartupHistograms(provider);
     }
 
     public void destroy() {
@@ -111,4 +115,11 @@
                 ChromePreferenceKeys.OS_ADVANCED_PROTECTION_SETTING_UPDATED_TIME,
                 System.currentTimeMillis());
     }
+
+    private void recordStartupHistograms(
+            @Nullable OsAdditionalSecurityPermissionProvider provider) {
+        RecordHistogram.recordBooleanHistogram(
+                "SafeBrowsing.Android.AdvancedProtection.Enabled",
+                provider != null && provider.isAdvancedProtectionRequestedByOs());
+    }
 }
diff --git a/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/AdvancedProtectionMediatorIntegrationTest.java b/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/AdvancedProtectionMediatorIntegrationTest.java
new file mode 100644
index 0000000..83c70f36
--- /dev/null
+++ b/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/AdvancedProtectionMediatorIntegrationTest.java
@@ -0,0 +1,66 @@
+// Copyright 2025 The Chromium Authors
+// 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.safe_browsing;
+
+import androidx.test.filters.MediumTest;
+
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.util.Batch;
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.Features.DisableFeatures;
+import org.chromium.base.test.util.HistogramWatcher;
+import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
+import org.chromium.chrome.test.util.AdvancedProtectionTestRule;
+import org.chromium.components.permissions.PermissionsAndroidFeatureList;
+
+/** Integration test for {@link AdvancedProtectionMediator}. */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+@DisableFeatures(PermissionsAndroidFeatureList.OS_ADDITIONAL_SECURITY_PERMISSION_KILL_SWITCH)
+@Batch(Batch.PER_CLASS)
+public class AdvancedProtectionMediatorIntegrationTest {
+    @ClassRule
+    public static final AdvancedProtectionTestRule sAdvancedProtectionTestRule =
+            new AdvancedProtectionTestRule();
+
+    @Rule
+    public final ChromeTabbedActivityTestRule mActivityTestRule =
+            new ChromeTabbedActivityTestRule();
+
+    private static final String ADVANCED_PROTECTION_UMA =
+            "SafeBrowsing.Android.AdvancedProtection.Enabled";
+
+    @Before
+    public void setUp() {
+        sAdvancedProtectionTestRule.setIsAdvancedProtectionRequestedByOs(false);
+    }
+
+    @Test
+    @MediumTest
+    public void testUmaOnStartup_AdvancedProtectionEnabled() {
+        sAdvancedProtectionTestRule.setIsAdvancedProtectionRequestedByOs(true);
+        HistogramWatcher watcher =
+                HistogramWatcher.newSingleRecordWatcher(ADVANCED_PROTECTION_UMA, true);
+        mActivityTestRule.startMainActivityOnBlankPage();
+        watcher.pollInstrumentationThreadUntilSatisfied();
+    }
+
+    @Test
+    @MediumTest
+    public void testUmaOnStartup_AdvancedProtectionDisabled() {
+        sAdvancedProtectionTestRule.setIsAdvancedProtectionRequestedByOs(false);
+        HistogramWatcher watcher =
+                HistogramWatcher.newSingleRecordWatcher(ADVANCED_PROTECTION_UMA, false);
+        mActivityTestRule.startMainActivityOnBlankPage();
+        watcher.pollInstrumentationThreadUntilSatisfied();
+    }
+}
diff --git a/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc b/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc
index 430d58b..15a81484 100644
--- a/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc
+++ b/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc
@@ -174,7 +174,7 @@
   if (service_->IsHashManuallyBlocklisted(request.digests().sha256()))
     return true;
 
-  for (auto bin_itr : request.archived_binary()) {
+  for (const auto& bin_itr : request.archived_binary()) {
     if (service_->IsHashManuallyBlocklisted(bin_itr.digests().sha256()))
       return true;
   }
diff --git a/chrome/browser/sessions/session_restore_delegate.cc b/chrome/browser/sessions/session_restore_delegate.cc
index 92b7823..fd27913 100644
--- a/chrome/browser/sessions/session_restore_delegate.cc
+++ b/chrome/browser/sessions/session_restore_delegate.cc
@@ -118,7 +118,7 @@
   } else {
     std::vector<content::WebContents*> web_contents_vector;
     web_contents_vector.reserve(tabs.size());
-    for (auto tab : tabs) {
+    for (const auto& tab : tabs) {
       CHECK(tab.contents());
       web_contents_vector.push_back(tab.contents());
     }
diff --git a/chrome/browser/speech/on_device_speech_recognition_impl.cc b/chrome/browser/speech/on_device_speech_recognition_impl.cc
index e7a6511..16077071 100644
--- a/chrome/browser/speech/on_device_speech_recognition_impl.cc
+++ b/chrome/browser/speech/on_device_speech_recognition_impl.cc
@@ -176,7 +176,7 @@
 
   std::vector<std::string> accept_languages;
   language_prefs_->GetAcceptLanguagesList(&accept_languages);
-  for (auto accept_language : accept_languages) {
+  for (const auto& accept_language : accept_languages) {
     if (l10n_util::GetLanguage(base::ToLowerASCII(accept_language)) ==
         l10n_util::GetLanguage(base::ToLowerASCII(language))) {
       return true;
diff --git a/chrome/browser/sync/android/fake_server_helper_android.cc b/chrome/browser/sync/android/fake_server_helper_android.cc
index 3897d39..3962c2c 100644
--- a/chrome/browser/sync/android/fake_server_helper_android.cc
+++ b/chrome/browser/sync/android/fake_server_helper_android.cc
@@ -18,13 +18,20 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/sync/sync_service_factory.h"
+#include "components/data_sharing/public/group_data.h"
 #include "components/sync/base/data_type.h"
 #include "components/sync/base/time.h"
+#include "components/sync/model/data_type_sync_bridge.h"
+#include "components/sync/model/in_memory_metadata_change_list.h"
+#include "components/sync/model/mutable_data_batch.h"
+#include "components/sync/protocol/collaboration_group_specifics.pb.h"
+#include "components/sync/protocol/entity_data.h"
 #include "components/sync/protocol/entity_specifics.pb.h"
 #include "components/sync/protocol/sync_entity.pb.h"
 #include "components/sync/protocol/sync_enums.pb.h"
 #include "components/sync/service/sync_service_impl.h"
 #include "components/sync/test/bookmark_entity_builder.h"
+#include "components/sync/test/collaboration_group_util.h"
 #include "components/sync/test/entity_builder_factory.h"
 #include "components/sync/test/fake_server.h"
 #include "components/sync/test/fake_server_network_resources.h"
@@ -402,3 +409,32 @@
       reinterpret_cast<fake_server::FakeServer*>(fake_server);
   fake_server_ptr->RemoveCollaboration(collaboration_id);
 }
+
+static void JNI_FakeServerHelper_AddCollaborationGroupToFakeServer(
+    JNIEnv* env,
+    jlong fake_server,
+    std::string& collaboration_id) {
+  const data_sharing::GroupId group_id =
+      data_sharing::GroupId(collaboration_id);
+  const sync_pb::CollaborationGroupSpecifics collab_specifics =
+      collaboration_group_utils::MakeCollaborationGroupSpecifics(
+          group_id.value());
+
+  sync_pb::EntitySpecifics entity_specifics;
+  *entity_specifics.mutable_collaboration_group() = collab_specifics;
+
+  sync_pb::SyncEntity::CollaborationMetadata metadata;
+  metadata.set_collaboration_id(collaboration_id);
+
+  std::string client_tag = collab_specifics.collaboration_id();
+  int64_t creation_time =
+      collab_specifics.changed_at_timestamp_millis_since_unix_epoch();
+  int64_t update_time = creation_time;
+
+  fake_server::FakeServer* fake_server_ptr =
+      reinterpret_cast<fake_server::FakeServer*>(fake_server);
+  fake_server_ptr->InjectEntity(
+      syncer::PersistentUniqueClientEntity::CreateFromSharedSpecificsForTesting(
+          "non_unique_name", client_tag, entity_specifics, creation_time,
+          update_time, metadata));
+}
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 0e9fa60..60d0906 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -420,7 +420,6 @@
     "//chrome/browser/ui/page_info",
     "//chrome/browser/ui/page_info:impl",
     "//chrome/browser/ui/plus_addresses",
-    "//chrome/browser/ui/plus_addresses:impl",
     "//chrome/browser/ui/prefs",
     "//chrome/browser/ui/prefs:impl",
     "//chrome/browser/ui/safety_hub",
@@ -4102,8 +4101,8 @@
       "views/frame/multi_contents_resize_area.h",
       "views/frame/multi_contents_view.cc",
       "views/frame/multi_contents_view.h",
-      "views/frame/multi_contents_view_drag_entrypoint_controller.cc",
-      "views/frame/multi_contents_view_drag_entrypoint_controller.h",
+      "views/frame/multi_contents_view_drop_target_controller.cc",
+      "views/frame/multi_contents_view_drop_target_controller.h",
       "views/frame/native_browser_frame.h",
       "views/frame/native_browser_frame_factory.cc",
       "views/frame/native_browser_frame_factory.h",
diff --git a/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtils.java b/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtils.java
index 6fa36ead..8665245a 100644
--- a/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtils.java
+++ b/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtils.java
@@ -174,4 +174,18 @@
                         context, R.color.gm3_baseline_surface_container_highest_dark)
                 : defaultBackground;
     }
+
+    /**
+     * Returns the background color for the grid tab switcher message card based on the enabled flag
+     * and incognito.
+     *
+     * @param context {@link Context} used to retrieve colors.
+     * @return The background color.
+     */
+    public static @ColorInt int getMessageCardBackgroundColor(Context context) {
+        // TODO(crbug.com/414404094): Add semantic color for incognito.
+        return useNewGtsSurfaceColor()
+                ? SemanticColorUtils.getColorSurfaceContainerLow(context)
+                : SemanticColorUtils.getCardBackgroundColor(context);
+    }
 }
diff --git a/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtilsUnitTest.java b/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtilsUnitTest.java
index 49d6ec54..1c405959 100644
--- a/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtilsUnitTest.java
+++ b/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtilsUnitTest.java
@@ -102,6 +102,12 @@
         assertEquals(
                 ContextCompat.getColor(mContext, R.color.gm3_baseline_surface_dim_dark),
                 tabCardViewBackgroundColorIncognito);
+
+        int messageCardBackgroundColor =
+                SurfaceColorUpdateUtils.getMessageCardBackgroundColor(mContext);
+        assertEquals(
+                SemanticColorUtils.getColorSurfaceContainerLow(mContext),
+                messageCardBackgroundColor);
     }
 
     @Test
@@ -133,6 +139,11 @@
                 ContextCompat.getColor(
                         mContext, R.color.gm3_baseline_surface_container_highest_dark),
                 tabCardViewBackgroundColorIncognito);
+
+        int messageCardBackgroundColor =
+                SurfaceColorUpdateUtils.getMessageCardBackgroundColor(mContext);
+        assertEquals(
+                SemanticColorUtils.getCardBackgroundColor(mContext), messageCardBackgroundColor);
     }
 
     @Test
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/VoiceToolbarButtonController.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/VoiceToolbarButtonController.java
index 3e1ade8..90b2105d 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/VoiceToolbarButtonController.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/VoiceToolbarButtonController.java
@@ -77,8 +77,7 @@
                 /* supportsTinting= */ true,
                 /* iphCommandBuilder= */ null,
                 AdaptiveToolbarButtonVariant.VOICE,
-                /* tooltipTextResId= */ R.string.adaptive_toolbar_button_preference_voice_search,
-                /* showBackgroundHighlight= */ true);
+                /* tooltipTextResId= */ R.string.adaptive_toolbar_button_preference_voice_search);
         mTrackerSupplier = trackerSupplier;
         mVoiceSearchDelegate = voiceSearchDelegate;
     }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/AdaptiveToolbarButtonController.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/AdaptiveToolbarButtonController.java
index a00f865..a9241d9 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/AdaptiveToolbarButtonController.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/AdaptiveToolbarButtonController.java
@@ -277,7 +277,6 @@
                             receivedButtonSpec.getButtonVariant(),
                             receivedButtonSpec.getActionChipLabelResId(),
                             receivedButtonSpec.getHoverTooltipTextId(),
-                            receivedButtonSpec.shouldShowBackgroundHighlight(),
                             receivedButtonSpec.hasErrorBadge()));
         }
         return mButtonData;
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/AdaptiveToolbarButtonControllerTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/AdaptiveToolbarButtonControllerTest.java
index 104217bb..7fbcbb8 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/AdaptiveToolbarButtonControllerTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/AdaptiveToolbarButtonControllerTest.java
@@ -97,8 +97,7 @@
                         /* iphCommandBuilder= */ null,
                         /* isEnabled= */ true,
                         AdaptiveToolbarButtonVariant.UNKNOWN,
-                        /* tooltipTextResId= */ Resources.ID_NULL,
-                        /* showBackgroundHighlight= */ false);
+                        /* tooltipTextResId= */ Resources.ID_NULL);
         mConfiguration.screenWidthDp = 420;
         doReturn(mProfile).when(mProfile).getOriginalProfile();
         mProfileSupplier = new ObservableSupplierImpl<>();
@@ -473,7 +472,6 @@
                 variant,
                 /* actionChipLabelResId= */ 0,
                 /* tooltipTextResId= */ Resources.ID_NULL,
-                /* showBackgroundHighlight= */ false,
                 /* hasErrorBadge= */ false);
     }
 }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/OpenInBrowserButtonController.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/OpenInBrowserButtonController.java
index 340d9e0..ec24c1b7 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/OpenInBrowserButtonController.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/OpenInBrowserButtonController.java
@@ -55,8 +55,7 @@
                 /* supportsTinting= */ true,
                 /* iphCommandBuilder= */ null,
                 AdaptiveToolbarButtonVariant.OPEN_IN_BROWSER,
-                /* tooltipTextResId= */ R.string.menu_open_in_product,
-                /* showBackgroundHighlight= */ true);
+                /* tooltipTextResId= */ R.string.menu_open_in_product);
         setShouldShowOnIncognitoTabs(true);
         mOpenInBrowserRunnable = openInBrowserRunnable;
         mTrackerSupplier = trackerSupplier;
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/OptionalNewTabButtonController.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/OptionalNewTabButtonController.java
index 4348774..e89bf46 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/OptionalNewTabButtonController.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/OptionalNewTabButtonController.java
@@ -110,8 +110,7 @@
                 /* supportsTinting= */ true,
                 /* iphCommandBuilder= */ null,
                 AdaptiveToolbarButtonVariant.NEW_TAB,
-                /* tooltipTextResId= */ R.string.new_tab_title,
-                /* showBackgroundHighlight= */ true);
+                /* tooltipTextResId= */ R.string.new_tab_title);
         setShouldShowOnIncognitoTabs(true);
 
         mContext = context;
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/TranslateToolbarButtonController.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/TranslateToolbarButtonController.java
index 2b0f736..dc373bb85 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/TranslateToolbarButtonController.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/TranslateToolbarButtonController.java
@@ -49,8 +49,7 @@
                 /* supportsTinting= */ true,
                 null,
                 AdaptiveToolbarButtonVariant.TRANSLATE,
-                /* tooltipTextResId= */ Resources.ID_NULL,
-                /* showBackgroundHighlight= */ true);
+                /* tooltipTextResId= */ Resources.ID_NULL);
         mTrackerSupplier = trackerSupplier;
     }
 
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/BaseButtonDataProvider.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/BaseButtonDataProvider.java
index 44ec4d5..570de2cb 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/BaseButtonDataProvider.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/BaseButtonDataProvider.java
@@ -53,8 +53,6 @@
      * @param adaptiveButtonVariant Enum value of {@link AdaptiveToolbarButtonVariant}, used for
      *     metrics.
      * @param tooltipTextResId String to show as a tooltip when the button is hovered over.
-     * @param showBackgroundHighlight Whether to use a custom background drawable to handle
-     *     highlight and focus UI states.
      */
     public BaseButtonDataProvider(
             Supplier<Tab> activeTabSupplier,
@@ -65,8 +63,7 @@
             boolean supportsTinting,
             @Nullable IphCommandBuilder iphCommandBuilder,
             @AdaptiveToolbarButtonVariant int adaptiveButtonVariant,
-            @StringRes int tooltipTextResId,
-            boolean showBackgroundHighlight) {
+            @StringRes int tooltipTextResId) {
         mActiveTabSupplier = activeTabSupplier;
         mModalDialogManager = modalDialogManager;
         if (mModalDialogManager != null) {
@@ -103,8 +100,7 @@
                         /* iphCommandBuilder= */ iphCommandBuilder,
                         /* isEnabled= */ true,
                         adaptiveButtonVariant,
-                        tooltipTextResId,
-                        showBackgroundHighlight);
+                        tooltipTextResId);
     }
 
     /**
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/BaseButtonDataProviderTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/BaseButtonDataProviderTest.java
index 9a2921c..88d5cf0 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/BaseButtonDataProviderTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/BaseButtonDataProviderTest.java
@@ -60,10 +60,9 @@
                     contentDescription,
                     actionChipLabelResId,
                     supportsTinting,
-                    null,
+                    /* iphCommandBuilder= */ null,
                     adaptiveButtonVariant,
-                    Resources.ID_NULL,
-                    false);
+                    /* tooltipTextResId= */ Resources.ID_NULL);
         }
 
         @Override
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/ButtonData.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/ButtonData.java
index 01211e40..ff2634e0 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/ButtonData.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/ButtonData.java
@@ -7,7 +7,6 @@
 import android.graphics.drawable.Drawable;
 import android.view.View;
 
-import androidx.annotation.DrawableRes;
 import androidx.annotation.StringRes;
 
 import org.chromium.build.annotations.NullMarked;
@@ -31,13 +30,6 @@
     /** Returns {@code true} if the button is supposed to be enabled and clickable. */
     boolean isEnabled();
 
-    /** Sets the background resource that will be used to highlight the button. */
-    /* package */ void setBackgroundResource(@DrawableRes int resId);
-
-    /** Gets the background resource that will be used to highlight the button. */
-    @DrawableRes
-    /* package */ int getBackgroundResource();
-
     /**
      * Returns a {@link ButtonSpec} describing button properties which don't change often. When
      * feasible, a {@link ButtonDataProvider} should prefer to reuse a single {@code ButtonSpec}
@@ -58,7 +50,6 @@
         @AdaptiveToolbarButtonVariant private final int mButtonVariant;
         private final boolean mIsDynamicAction;
         @StringRes private final int mActionChipLabelResId;
-        private final boolean mShowBackgroundHighlight;
         @StringRes private final int mTooltipTextResId;
         private final boolean mHasErrorBadge;
 
@@ -72,7 +63,6 @@
                 @AdaptiveToolbarButtonVariant int buttonVariant,
                 int actionChipLabelResId,
                 int tooltipTextResId,
-                boolean showBackgroundHighlight,
                 boolean hasErrorBadge) {
             mDrawable = drawable;
             mOnClickListener = onClickListener;
@@ -84,7 +74,6 @@
             mIsDynamicAction = AdaptiveToolbarFeatures.isDynamicAction(mButtonVariant);
             mActionChipLabelResId = actionChipLabelResId;
             mTooltipTextResId = tooltipTextResId;
-            mShowBackgroundHighlight = showBackgroundHighlight;
             mHasErrorBadge = hasErrorBadge;
         }
 
@@ -146,14 +135,6 @@
         }
 
         /**
-         * Returns {@code true} if a background highlight on hover, keyboard focus, press etc.
-         * should be shown for the button.
-         */
-        public boolean shouldShowBackgroundHighlight() {
-            return mShowBackgroundHighlight;
-        }
-
-        /**
          * Returns {@code true} if the button has an error badge. False otherwise. The button's
          * height is increased to accommodate the larger icon when an error badge is present.
          */
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/ButtonDataImpl.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/ButtonDataImpl.java
index 56f85650..184ced4 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/ButtonDataImpl.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/ButtonDataImpl.java
@@ -8,12 +8,10 @@
 import android.graphics.drawable.Drawable;
 import android.view.View.OnClickListener;
 
-import androidx.annotation.DrawableRes;
 import androidx.annotation.StringRes;
 
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
-import org.chromium.chrome.browser.toolbar.R;
 import org.chromium.chrome.browser.toolbar.adaptive.AdaptiveToolbarButtonVariant;
 import org.chromium.chrome.browser.user_education.IphCommandBuilder;
 
@@ -24,7 +22,6 @@
 public class ButtonDataImpl implements ButtonData {
     private boolean mCanShow;
     private boolean mIsEnabled;
-    private @DrawableRes int mBackgroundResId;
 
     private @SuppressWarnings("NullAway.Init") ButtonSpec mButtonSpec;
 
@@ -39,8 +36,7 @@
             @Nullable IphCommandBuilder iphCommandBuilder,
             boolean isEnabled,
             @AdaptiveToolbarButtonVariant int buttonVariant,
-            int tooltipTextResId,
-            boolean showBackgroundHighlight) {
+            int tooltipTextResId) {
         this(
                 canShow,
                 drawable,
@@ -51,8 +47,7 @@
                 iphCommandBuilder,
                 isEnabled,
                 buttonVariant,
-                tooltipTextResId,
-                showBackgroundHighlight);
+                tooltipTextResId);
     }
 
     public ButtonDataImpl(
@@ -65,13 +60,9 @@
             @Nullable IphCommandBuilder iphCommandBuilder,
             boolean isEnabled,
             @AdaptiveToolbarButtonVariant int buttonVariant,
-            @StringRes int tooltipTextResId,
-            boolean showBackgroundHighlight) {
+            @StringRes int tooltipTextResId) {
         mCanShow = canShow;
         mIsEnabled = isEnabled;
-        if (showBackgroundHighlight) {
-            mBackgroundResId = R.drawable.default_icon_background;
-        }
         mButtonSpec =
                 new ButtonSpec(
                         drawable,
@@ -83,7 +74,6 @@
                         buttonVariant,
                         actionChipLabelResId,
                         tooltipTextResId,
-                        showBackgroundHighlight,
                         /* hasErrorBadge= */ false);
     }
 
@@ -98,16 +88,6 @@
     }
 
     @Override
-    public void setBackgroundResource(@DrawableRes int resId) {
-        mBackgroundResId = resId;
-    }
-
-    @Override
-    public int getBackgroundResource() {
-        return mBackgroundResId;
-    }
-
-    @Override
     public ButtonSpec getButtonSpec() {
         return mButtonSpec;
     }
@@ -138,7 +118,6 @@
                         currentSpec.getButtonVariant(),
                         currentSpec.getActionChipLabelResId(),
                         currentSpec.getHoverTooltipTextId(),
-                        currentSpec.shouldShowBackgroundHighlight(),
                         currentSpec.hasErrorBadge());
         setButtonSpec(newSpec);
     }
@@ -157,7 +136,6 @@
                         currentSpec.getButtonVariant(),
                         newActionChipResourceId,
                         currentSpec.getHoverTooltipTextId(),
-                        currentSpec.shouldShowBackgroundHighlight(),
                         currentSpec.hasErrorBadge());
         setButtonSpec(newSpec);
     }
@@ -176,7 +154,6 @@
                         currentSpec.getButtonVariant(),
                         currentSpec.getActionChipLabelResId(),
                         currentSpec.getHoverTooltipTextId(),
-                        currentSpec.shouldShowBackgroundHighlight(),
                         currentSpec.hasErrorBadge());
         setButtonSpec(newSpec);
     }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonCoordinator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonCoordinator.java
index dafdac0f..d1d1304 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonCoordinator.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonCoordinator.java
@@ -20,7 +20,6 @@
 import org.chromium.base.supplier.Supplier;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
-import org.chromium.chrome.browser.toolbar.R;
 import org.chromium.chrome.browser.toolbar.adaptive.AdaptiveToolbarFeatures;
 import org.chromium.chrome.browser.user_education.IphCommandBuilder;
 import org.chromium.chrome.browser.user_education.UserEducationHelper;
@@ -175,12 +174,7 @@
 
         // Reset background alpha, in case the IPH onDismiss callback doesn't fire.
         mMediator.setBackgroundAlpha(255);
-        if (buttonData != null) {
-            buttonData.setBackgroundResource(
-                    isIncognito
-                            ? R.drawable.optional_button_background_baseline
-                            : R.drawable.optional_button_background);
-        }
+        mMediator.setIsIncognitoBranded(isIncognito);
         mMediator.updateButton(buttonData);
     }
 
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonCoordinatorTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonCoordinatorTest.java
index 8c6bbecd..41e10a3 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonCoordinatorTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonCoordinatorTest.java
@@ -210,7 +210,6 @@
                         AdaptiveToolbarButtonVariant.UNKNOWN,
                         /* actionChipLabelResId= */ 0,
                         /* tooltipTextResId= */ Resources.ID_NULL,
-                        /* showBackgroundHighlight= */ false,
                         /* hasErrorBadge= */ true);
         ButtonDataImpl buttonData = new ButtonDataImpl();
         buttonData.setButtonSpec(buttonSpec);
@@ -238,8 +237,7 @@
                         mockIphCommandBuilder,
                         /* isEnabled= */ isEnabled,
                         AdaptiveToolbarButtonVariant.UNKNOWN,
-                        /* tooltipTextResId= */ Resources.ID_NULL,
-                        /* showBackgroundHighlight= */ false);
+                        /* tooltipTextResId= */ Resources.ID_NULL);
 
         View backgroundView = Mockito.mock(View.class);
         doReturn(View.VISIBLE).when(backgroundView).getVisibility();
@@ -276,8 +274,7 @@
                         mockIphCommandBuilder,
                         /* isEnabled= */ isEnabled,
                         AdaptiveToolbarButtonVariant.UNKNOWN,
-                        /* tooltipTextResId= */ Resources.ID_NULL,
-                        /* showBackgroundHighlight= */ false);
+                        /* tooltipTextResId= */ Resources.ID_NULL);
 
         View backgroundView = Mockito.mock(View.class);
         doReturn(View.GONE).when(backgroundView).getVisibility();
@@ -315,8 +312,7 @@
                         mockIphCommandBuilder,
                         /* isEnabled= */ isEnabled,
                         AdaptiveToolbarButtonVariant.UNKNOWN,
-                        /* tooltipTextResId= */ Resources.ID_NULL,
-                        /* showBackgroundHighlight= */ false);
+                        /* tooltipTextResId= */ Resources.ID_NULL);
 
         ArgumentCaptor<Runnable> onShowCallbackCaptor = ArgumentCaptor.forClass(Runnable.class);
         ArgumentCaptor<Runnable> onDismissCallbackCaptor = ArgumentCaptor.forClass(Runnable.class);
@@ -361,8 +357,7 @@
                         mockIphCommandBuilder,
                         /* isEnabled= */ isEnabled,
                         AdaptiveToolbarButtonVariant.TEST_BUTTON,
-                        /* tooltipTextResId= */ Resources.ID_NULL,
-                        /* showBackgroundHighlight= */ false);
+                        /* tooltipTextResId= */ Resources.ID_NULL);
 
         mOptionalButtonCoordinator.updateButton(buttonData, /* isIncognito= */ false);
 
@@ -402,8 +397,7 @@
                         mockIphCommandBuilder,
                         /* isEnabled= */ isEnabled,
                         AdaptiveToolbarButtonVariant.TEST_BUTTON,
-                        /* tooltipTextResId= */ Resources.ID_NULL,
-                        /* showBackgroundHighlight= */ false);
+                        /* tooltipTextResId= */ Resources.ID_NULL);
 
         mOptionalButtonCoordinator.updateButton(buttonData, /* isIncognito= */ false);
 
@@ -443,8 +437,7 @@
                         mockIphCommandBuilder,
                         /* isEnabled= */ isEnabled,
                         AdaptiveToolbarButtonVariant.TEST_BUTTON,
-                        /* tooltipTextResId= */ Resources.ID_NULL,
-                        /* showBackgroundHighlight= */ false);
+                        /* tooltipTextResId= */ Resources.ID_NULL);
 
         mOptionalButtonCoordinator.updateButton(buttonData, /* isIncognito= */ false);
 
@@ -471,8 +464,7 @@
                         /* iphCommandBuilder= */ null,
                         /* isEnabled= */ true,
                         AdaptiveToolbarButtonVariant.UNKNOWN,
-                        /* tooltipTextResId= */ Resources.ID_NULL,
-                        /* showBackgroundHighlight= */ false);
+                        /* tooltipTextResId= */ Resources.ID_NULL);
 
         // Call update button with an enabled button.
         mOptionalButtonCoordinator.updateButton(buttonData, /* isIncognito= */ false);
@@ -513,7 +505,6 @@
                         AdaptiveToolbarButtonVariant.UNKNOWN,
                         /* actionChipLabelResId= */ 0,
                         /* tooltipTextResId= */ Resources.ID_NULL,
-                        /* showBackgroundHighlight= */ false,
                         /* hasErrorBadge= */ false);
         ButtonDataImpl buttonData = new ButtonDataImpl();
         buttonData.setButtonSpec(buttonSpec);
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonMediator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonMediator.java
index d7badf8f9..8c1dd31 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonMediator.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonMediator.java
@@ -44,6 +44,10 @@
         mModel.set(OptionalButtonProperties.ICON_BACKGROUND_ALPHA, alpha);
     }
 
+    void setIsIncognitoBranded(boolean isIncognitoBranded) {
+        mModel.set(OptionalButtonProperties.IS_INCOGNITO_BRANDED, isIncognitoBranded);
+    }
+
     public void setOnBeforeHideTransitionCallback(Runnable onBeforeHideTransitionCallback) {
         mModel.set(
                 OptionalButtonProperties.ON_BEFORE_HIDE_TRANSITION_CALLBACK,
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonProperties.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonProperties.java
index 7b12feb..fcb847d 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonProperties.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonProperties.java
@@ -25,6 +25,8 @@
     public static final WritableObjectPropertyKey<ButtonData> BUTTON_DATA =
             new WritableObjectPropertyKey<>(/* skipEquality= */ true);
     public static final WritableBooleanPropertyKey IS_ENABLED = new WritableBooleanPropertyKey();
+    public static final WritableBooleanPropertyKey IS_INCOGNITO_BRANDED =
+            new WritableBooleanPropertyKey();
     public static final WritableObjectPropertyKey<Callback<Integer>> TRANSITION_STARTED_CALLBACK =
             new WritableObjectPropertyKey<>();
     public static final WritableObjectPropertyKey<Callback<Integer>> TRANSITION_FINISHED_CALLBACK =
@@ -46,6 +48,7 @@
     public static final PropertyKey[] ALL_KEYS = {
         BUTTON_DATA,
         IS_ENABLED,
+        IS_INCOGNITO_BRANDED,
         TRANSITION_STARTED_CALLBACK,
         TRANSITION_FINISHED_CALLBACK,
         ON_BEFORE_HIDE_TRANSITION_CALLBACK,
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonView.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonView.java
index 20bcb1e6..ffefe33 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonView.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonView.java
@@ -21,7 +21,6 @@
 import android.transition.TransitionManager;
 import android.transition.TransitionSet;
 import android.util.AttributeSet;
-import android.util.TypedValue;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
@@ -32,6 +31,7 @@
 import android.widget.TextView;
 
 import androidx.annotation.DimenRes;
+import androidx.annotation.DrawableRes;
 import androidx.annotation.IntDef;
 import androidx.appcompat.content.res.AppCompatResources;
 import androidx.appcompat.widget.TooltipCompat;
@@ -160,6 +160,14 @@
         setPaddingRelative(paddingStart, getPaddingTop(), getPaddingEnd(), getPaddingBottom());
     }
 
+    public void setIsIncognitoBranded(boolean isIncognitoBranded) {
+        @DrawableRes int backgroundDrawableRes = R.drawable.optional_button_background;
+        if (isIncognitoBranded) {
+            backgroundDrawableRes = R.drawable.optional_button_background_baseline;
+        }
+        mButton.setBackgroundResource(backgroundDrawableRes);
+    }
+
     public void cancelTransition() {
         if (isRunningTransition()) {
             TransitionManager.endTransitions(mTransitionRoot);
@@ -255,20 +263,6 @@
         mButton.setEnabled(buttonData.isEnabled());
         mActionChipLabel.setEnabled(buttonData.isEnabled());
 
-        // Set circular highlight for optional button when button variant is profile, share, voice
-        // search and new tab. Set box highlight for the rest of button variants.
-        int resId = buttonData.getBackgroundResource();
-        if (buttonData.getButtonSpec().shouldShowBackgroundHighlight()
-                && resId != Resources.ID_NULL) {
-            mButton.setBackgroundResource(resId);
-        } else {
-            TypedValue themeRes = new TypedValue();
-            getContext()
-                    .getTheme()
-                    .resolveAttribute(R.attr.selectableItemBackground, themeRes, true);
-            mButton.setBackgroundResource(themeRes.resourceId);
-        }
-
         // Set hover state tooltip text for optional toolbar buttons(e.g. share, voice search, new
         // tab and profile).
         if (buttonSpec.getHoverTooltipTextId() != ButtonSpec.INVALID_TOOLTIP_TEXT_ID
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonViewBinder.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonViewBinder.java
index 4666c4b..3500862 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonViewBinder.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonViewBinder.java
@@ -44,6 +44,8 @@
         } else if (OptionalButtonProperties.IS_ANIMATION_ALLOWED_PREDICATE.equals(propertyKey)) {
             view.setIsAnimationAllowedPredicate(
                     model.get(OptionalButtonProperties.IS_ANIMATION_ALLOWED_PREDICATE));
+        } else if (OptionalButtonProperties.IS_INCOGNITO_BRANDED.equals(propertyKey)) {
+            view.setIsIncognitoBranded(model.get(OptionalButtonProperties.IS_INCOGNITO_BRANDED));
         }
     }
 }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonViewTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonViewTest.java
index 419bb5b..49b924e 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonViewTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonViewTest.java
@@ -144,7 +144,6 @@
                         /* buttonVariant= */ AdaptiveToolbarButtonVariant.NEW_TAB,
                         /* actionChipLabelResId= */ Resources.ID_NULL,
                         /* tooltipTextResId= */ R.string.new_tab_title,
-                        /* showBackgroundHighlight= */ true,
                         /* hasErrorBadge= */ false);
         ButtonDataImpl buttonData = new ButtonDataImpl();
         buttonData.setButtonSpec(buttonSpec);
@@ -173,7 +172,6 @@
                         /* buttonVariant= */ AdaptiveToolbarButtonVariant.READER_MODE,
                         /* actionChipLabelResId= */ Resources.ID_NULL,
                         /* tooltipTextResId= */ Resources.ID_NULL,
-                        /* showBackgroundHighlight= */ false,
                         /* hasErrorBadge= */ false);
         ButtonDataImpl buttonData = new ButtonDataImpl();
         buttonData.setButtonSpec(buttonSpec);
@@ -201,7 +199,6 @@
                         /* buttonVariant= */ AdaptiveToolbarButtonVariant.READER_MODE,
                         /* actionChipLabelResId= */ actionChipLabelResId,
                         /* tooltipTextResId= */ Resources.ID_NULL,
-                        /* showBackgroundHighlight= */ false,
                         /* hasErrorBadge= */ false);
         ButtonDataImpl buttonData = new ButtonDataImpl();
         buttonData.setButtonSpec(buttonSpec);
@@ -228,7 +225,6 @@
                         /* buttonVariant= */ buttonVariant,
                         0,
                         tooltipTextIdRes,
-                        /* showBackgroundHighlight= */ true,
                         /* hasErrorBadge= */ false);
         ButtonDataImpl buttonData = new ButtonDataImpl();
         buttonData.setButtonSpec(buttonSpec);
@@ -254,7 +250,6 @@
                         /* buttonVariant= */ AdaptiveToolbarButtonVariant.UNKNOWN,
                         /* actionChipLabelResId= */ Resources.ID_NULL,
                         /* tooltipTextResId= */ Resources.ID_NULL,
-                        /* showBackgroundHighlight= */ false,
                         /* hasErrorBadge= */ false);
         ButtonDataImpl buttonData = new ButtonDataImpl();
         buttonData.setButtonSpec(buttonSpec);
@@ -827,7 +822,6 @@
                         originalButtonSpec.getButtonVariant(),
                         originalButtonSpec.getActionChipLabelResId(),
                         originalButtonSpec.getHoverTooltipTextId(),
-                        originalButtonSpec.shouldShowBackgroundHighlight(),
                         originalButtonSpec.hasErrorBadge()));
 
         mOptionalButtonView.updateButtonWithAnimation(readerModeButtonData);
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/OptionalBrowsingModeButtonControllerTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/OptionalBrowsingModeButtonControllerTest.java
index e0e78685..3226eab 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/OptionalBrowsingModeButtonControllerTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/OptionalBrowsingModeButtonControllerTest.java
@@ -212,7 +212,6 @@
                 /* iphCommandBuilder= */ null,
                 /* isEnabled= */ true,
                 buttonVariant,
-                /* tooltipTextResId= */ Resources.ID_NULL,
-                /* showBackgroundHighlight= */ false);
+                /* tooltipTextResId= */ Resources.ID_NULL);
     }
 }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/PhoneCaptureStateTokenTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/PhoneCaptureStateTokenTest.java
index 3b52e23..2133aa2 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/PhoneCaptureStateTokenTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/PhoneCaptureStateTokenTest.java
@@ -47,7 +47,16 @@
 
     private static ButtonData makeButtonDate() {
         // Uses default equals impl, reference quality, to compare. Values do not matter.
-        return new ButtonDataImpl(false, null, null, "", false, null, false, 0, 0, false);
+        return new ButtonDataImpl(
+                /* canShow= */ false,
+                /* drawable= */ null,
+                /* onClickListener= */ null,
+                /* contentDescription= */ "",
+                /* supportsTinting= */ false,
+                /* iphCommandBuilder= */ null,
+                /* isEnabled= */ false,
+                /* buttonVariant= */ 0,
+                /* tooltipTextResId= */ 0);
     }
 
     @Before
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
index 3680c42..84f6604 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
@@ -15,7 +15,6 @@
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.util.AttributeSet;
-import android.util.TypedValue;
 import android.view.View;
 import android.view.ViewStub;
 import android.view.accessibility.AccessibilityEvent;
@@ -485,20 +484,10 @@
 
         ButtonSpec buttonSpec = buttonData.getButtonSpec();
 
-        // Set hover highlight for buttons requesting a custom highlight. Set
-        // box hover highlight for the rest of button variants.
-        if (buttonData.getButtonSpec().shouldShowBackgroundHighlight()) {
-            mOptionalButton.setBackgroundResource(
-                    isIncognitoBranded()
-                            ? R.drawable.default_icon_background_baseline
-                            : R.drawable.default_icon_background);
-        } else {
-            TypedValue themeRes = new TypedValue();
-            getContext()
-                    .getTheme()
-                    .resolveAttribute(R.attr.selectableItemBackground, themeRes, true);
-            mOptionalButton.setBackgroundResource(themeRes.resourceId);
-        }
+        mOptionalButton.setBackgroundResource(
+                isIncognitoBranded()
+                        ? R.drawable.default_icon_background_baseline
+                        : R.drawable.default_icon_background);
 
         // Set hover tooltip text for voice search, share and new tab button on tablets.
         if (buttonSpec.getHoverTooltipTextId() != ButtonSpec.INVALID_TOOLTIP_TEXT_ID) {
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTabletUnitTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTabletUnitTest.java
index 02e07b1..2a2acab 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTabletUnitTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTabletUnitTest.java
@@ -410,7 +410,6 @@
                         /* buttonVariant= */ AdaptiveToolbarButtonVariant.READER_MODE,
                         0,
                         0,
-                        false,
                         /* hasErrorBadge= */ false);
         buttonData.setButtonSpec(buttonSpec);
         mToolbarTablet.updateOptionalButton(buttonData);
@@ -429,7 +428,6 @@
                         /* buttonVariant= */ AdaptiveToolbarButtonVariant.SHARE,
                         0,
                         R.string.adaptive_toolbar_button_preference_share,
-                        true,
                         /* hasErrorBadge= */ false);
         buttonData.setButtonSpec(buttonSpec);
         mToolbarTablet.updateOptionalButton(buttonData);
@@ -451,7 +449,6 @@
                         /* buttonVariant= */ AdaptiveToolbarButtonVariant.VOICE,
                         0,
                         R.string.adaptive_toolbar_button_preference_voice_search,
-                        true,
                         /* hasErrorBadge= */ false);
         buttonData.setButtonSpec(buttonSpec);
         mToolbarTablet.updateOptionalButton(buttonData);
@@ -473,7 +470,6 @@
                         /* buttonVariant= */ AdaptiveToolbarButtonVariant.NEW_TAB,
                         0,
                         R.string.new_tab_title,
-                        true,
                         /* hasErrorBadge= */ false);
         buttonData.setButtonSpec(buttonSpec);
         mToolbarTablet.updateOptionalButton(buttonData);
@@ -501,7 +497,6 @@
                         /* buttonVariant= */ AdaptiveToolbarButtonVariant.READER_MODE,
                         0,
                         0,
-                        false,
                         /* hasErrorBadge= */ false);
         buttonData.setButtonSpec(buttonSpec);
         mToolbarTablet.updateOptionalButton(buttonData);
@@ -530,7 +525,6 @@
                         /* buttonVariant= */ AdaptiveToolbarButtonVariant.SHARE,
                         0,
                         0,
-                        false,
                         /* hasErrorBadge= */ true);
         buttonData.setButtonSpec(buttonSpec);
         mToolbarTablet.updateOptionalButton(buttonData);
@@ -674,7 +668,6 @@
                         /* buttonVariant= */ AdaptiveToolbarButtonVariant.NEW_TAB,
                         0,
                         R.string.adaptive_toolbar_button_preference_new_tab,
-                        true,
                         /* hasErrorBadge= */ false);
         buttonData.setButtonSpec(buttonSpec);
         mToolbarTablet.updateOptionalButton(buttonData);
diff --git a/chrome/browser/ui/ash/capture_mode/sunfish_browsertest.cc b/chrome/browser/ui/ash/capture_mode/sunfish_browsertest.cc
index dc2fe95..6dbfd601 100644
--- a/chrome/browser/ui/ash/capture_mode/sunfish_browsertest.cc
+++ b/chrome/browser/ui/ash/capture_mode/sunfish_browsertest.cc
@@ -98,7 +98,8 @@
   VerifyActiveBehavior(BehaviorType::kSunfish);
 
   // Simulate showing the panel while the session is active.
-  controller->ShowSearchResultsPanel(gfx::ImageSkia(), GURL("kTestUrl1"));
+  controller->ShowSearchResultsPanel(gfx::ImageSkia());
+  controller->NavigateSearchResultsPanel(GURL("kTestUrl1"));
   ASSERT_TRUE(controller->IsActive());
   auto* search_results_view =
       controller->GetSearchResultsPanel()->search_results_view();
@@ -157,7 +158,8 @@
   VerifyActiveBehavior(BehaviorType::kSunfish);
 
   // Simulate showing the panel while the session is active.
-  controller->ShowSearchResultsPanel(gfx::ImageSkia(), GURL("kTestUrl1"));
+  controller->ShowSearchResultsPanel(gfx::ImageSkia());
+  controller->NavigateSearchResultsPanel(GURL("kTestUrl1"));
   ASSERT_TRUE(controller->IsActive());
   auto* search_results_view =
       controller->GetSearchResultsPanel()->search_results_view();
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index 68389dd..0654215e 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -1907,6 +1907,11 @@
   return window()->PreHandleMouseEvent(event);
 }
 
+void Browser::PreHandleDragUpdate(const content::DropData& drop_data,
+                                  const gfx::PointF& client_pt) {
+  window()->PreHandleDragUpdate(drop_data, client_pt);
+}
+
 content::KeyboardEventProcessingResult Browser::PreHandleKeyboardEvent(
     content::WebContents* source,
     const NativeWebKeyboardEvent& event) {
diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h
index 70de622..8722fba 100644
--- a/chrome/browser/ui/browser.h
+++ b/chrome/browser/ui/browser.h
@@ -101,6 +101,7 @@
 }
 
 namespace content {
+struct DropData;
 class NavigationHandle;
 class SessionStorageNamespace;
 }  // namespace content
@@ -775,6 +776,8 @@
   void SetFocusToLocationBar() override;
   bool PreHandleMouseEvent(content::WebContents* source,
                            const blink::WebMouseEvent& event) override;
+  void PreHandleDragUpdate(const content::DropData& drop_data,
+                           const gfx::PointF& client_pt) override;
   content::KeyboardEventProcessingResult PreHandleKeyboardEvent(
       content::WebContents* source,
       const input::NativeWebKeyboardEvent& event) override;
diff --git a/chrome/browser/ui/browser_window.h b/chrome/browser/ui/browser_window.h
index 6a94d43..3185b25 100644
--- a/chrome/browser/ui/browser_window.h
+++ b/chrome/browser/ui/browser_window.h
@@ -529,7 +529,11 @@
   // Allows the BrowserWindow object to handle the specified mouse event
   // before sending it to the renderer.
   virtual bool PreHandleMouseEvent(const blink::WebMouseEvent& event) = 0;
-
+  // Allows the BrowserWindow object to handle a mouse drag update
+  // before sending it to the renderer.
+  // `point` is relative to the content view.
+  virtual void PreHandleDragUpdate(const content::DropData& drop_data,
+                                   const gfx::PointF& point) = 0;
   // Allows the BrowserWindow object to handle the specified keyboard event
   // before sending it to the renderer.
   virtual content::KeyboardEventProcessingResult PreHandleKeyboardEvent(
diff --git a/chrome/browser/ui/commerce/product_specifications_entry_point_controller.cc b/chrome/browser/ui/commerce/product_specifications_entry_point_controller.cc
index 4865d8f..f7ad00c 100644
--- a/chrome/browser/ui/commerce/product_specifications_entry_point_controller.cc
+++ b/chrome/browser/ui/commerce/product_specifications_entry_point_controller.cc
@@ -184,7 +184,8 @@
   std::set<GURL> urls;
   auto candidate_products =
       current_entry_point_info_->similar_candidate_products;
-  for (auto url_info : shopping_service_->GetUrlInfosForActiveWebWrappers()) {
+  for (const auto& url_info :
+       shopping_service_->GetUrlInfosForActiveWebWrappers()) {
     if (base::Contains(candidate_products, url_info.url)) {
       urls.insert(url_info.url);
     }
diff --git a/chrome/browser/ui/global_media_controls/media_notification_service.cc b/chrome/browser/ui/global_media_controls/media_notification_service.cc
index 6bb92e0..dd9c010 100644
--- a/chrome/browser/ui/global_media_controls/media_notification_service.cc
+++ b/chrome/browser/ui/global_media_controls/media_notification_service.cc
@@ -340,8 +340,9 @@
   // notification items and Remote Playback presentation routes should be shown
   // as media session notification items.
   std::optional<std::string> cast_presentation_route_id;
-  for (auto route : media_router::WebContentsPresentationManager::Get(contents)
-                        ->GetMediaRoutes()) {
+  for (const auto& route :
+       media_router::WebContentsPresentationManager::Get(contents)
+           ->GetMediaRoutes()) {
     if (route.media_source().IsCastPresentationUrl()) {
       cast_presentation_route_id = route.media_route_id();
       break;
diff --git a/chrome/browser/ui/hats/hats_service_desktop.cc b/chrome/browser/ui/hats/hats_service_desktop.cc
index 2dcfc08..d60f599 100644
--- a/chrome/browser/ui/hats/hats_service_desktop.cc
+++ b/chrome/browser/ui/hats/hats_service_desktop.cc
@@ -734,7 +734,7 @@
   // trigger. If fields are set for a trigger, they must be provided.
   CHECK_EQ(product_specific_bits_data.size(),
            survey_config.product_specific_bits_data_fields.size());
-  for (auto field_value : product_specific_bits_data) {
+  for (const auto& field_value : product_specific_bits_data) {
     CHECK(base::Contains(survey_config.product_specific_bits_data_fields,
                          field_value.first));
   }
@@ -743,7 +743,7 @@
   // trigger. If fields are set for a trigger, they must be provided.
   CHECK_EQ(product_specific_string_data.size(),
            survey_config.product_specific_string_data_fields.size());
-  for (auto field_value : product_specific_string_data) {
+  for (const auto& field_value : product_specific_string_data) {
     CHECK(base::Contains(survey_config.product_specific_string_data_fields,
                          field_value.first));
   }
diff --git a/chrome/browser/ui/lens/BUILD.gn b/chrome/browser/ui/lens/BUILD.gn
index f72e3f8..fea398e 100644
--- a/chrome/browser/ui/lens/BUILD.gn
+++ b/chrome/browser/ui/lens/BUILD.gn
@@ -15,6 +15,7 @@
     "lens_overlay_entry_point_controller.h",
     "lens_overlay_gen204_controller.h",
     "lens_overlay_image_helper.h",
+    "lens_searchbox_controller.h",
     "lens_overlay_side_panel_navigation_throttle.h",
     "lens_overlay_untrusted_ui.h",
     "lens_search_controller.h",
@@ -44,7 +45,6 @@
     "lens_permission_bubble_controller.h",
     "lens_preselection_bubble.h",
     "lens_search_contextualization_controller.h",
-    "lens_searchbox_controller.h",
     "page_content_type_conversions.h",
     "ref_counted_lens_overlay_client_logs.h",
   ]
diff --git a/chrome/browser/ui/lens/lens_overlay_controller.cc b/chrome/browser/ui/lens/lens_overlay_controller.cc
index 620e377db..73c26c6a 100644
--- a/chrome/browser/ui/lens/lens_overlay_controller.cc
+++ b/chrome/browser/ui/lens/lens_overlay_controller.cc
@@ -53,6 +53,7 @@
 #include "chrome/browser/ui/lens/lens_permission_bubble_controller.h"
 #include "chrome/browser/ui/lens/lens_preselection_bubble.h"
 #include "chrome/browser/ui/lens/lens_search_controller.h"
+#include "chrome/browser/ui/lens/lens_searchbox_controller.h"
 #include "chrome/browser/ui/lens/page_content_type_conversions.h"
 #include "chrome/browser/ui/search/omnibox_utils.h"
 #include "chrome/browser/ui/tabs/public/tab_features.h"
@@ -63,7 +64,6 @@
 #include "chrome/browser/ui/views/side_panel/side_panel_enums.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_ui.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_util.h"
-#include "chrome/browser/ui/webui/util/image_util.h"
 #include "chrome/browser/ui/webui/webui_embedding_context.h"
 #include "chrome/common/chrome_render_frame.mojom.h"
 #include "chrome/common/pref_names.h"
@@ -79,7 +79,6 @@
 #include "components/optimization_guide/content/browser/page_content_proto_provider.h"
 #include "components/optimization_guide/content/browser/page_context_eligibility.h"
 #include "components/permissions/permission_request_manager.h"
-#include "components/sessions/content/session_tab_helper.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/tabs/public/tab_interface.h"
 #include "components/viz/common/frame_timing_details.h"
@@ -95,7 +94,6 @@
 #include "content/public/browser/web_contents_user_data.h"
 #include "content/public/browser/web_ui.h"
 #include "net/base/network_change_notifier.h"
-#include "net/base/url_util.h"
 #include "pdf/buildflags.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
@@ -145,9 +143,6 @@
 // Currently 50 MB
 constexpr int kMaxDomTextLengthForOcrSimilarity = 50 * 1000 * 1000;
 
-// The url query param key for the search query.
-inline constexpr char kTextQueryParameterKey[] = "q";
-
 // Copy the objects of a vector into another without transferring
 // ownership.
 std::vector<lens::mojom::OverlayObjectPtr> CopyObjects(
@@ -458,20 +453,6 @@
   side_panel_ghost_loader_page_.Bind(std::move(page));
 }
 
-void LensOverlayController::SetSidePanelSearchboxHandler(
-    std::unique_ptr<LensSearchboxHandler> handler) {
-  side_panel_searchbox_handler_ = std::move(handler);
-}
-
-void LensOverlayController::SetContextualSearchboxHandler(
-    std::unique_ptr<LensSearchboxHandler> handler) {
-  overlay_searchbox_handler_ = std::move(handler);
-}
-
-void LensOverlayController::ResetSidePanelSearchboxHandler() {
-  side_panel_searchbox_handler_.reset();
-}
-
 uint64_t LensOverlayController::GetInvocationTimeSinceEpoch() {
   return invocation_time_since_epoch_.InMillisecondsSinceUnixEpoch();
 }
@@ -555,18 +536,6 @@
   return state_ == State::kClosing || state_ == State::kClosingSidePanel;
 }
 
-bool LensOverlayController::IsContextualSearchbox() {
-  // TODO(crbug.com/405441183): This logic will break the side panel searchbox
-  // if there is no overlay, so it should be moved to a shared location.
-  return GetPageClassification() ==
-         metrics::OmniboxEventProto::CONTEXTUAL_SEARCHBOX;
-}
-
-void LensOverlayController::GetIsContextualSearchbox(
-    GetIsContextualSearchboxCallback callback) {
-  std::move(callback).Run(IsContextualSearchbox());
-}
-
 bool LensOverlayController::IsScreenshotPossible(
     content::RenderWidgetHostView* view) {
   return view && view->IsSurfaceAvailableForCopy();
@@ -678,7 +647,7 @@
     const std::string& source_language,
     const std::string& target_language) {
   // Remove the selection thumbnail, if it exists.
-  SetSearchboxThumbnail(std::string());
+  GetLensSearchboxController()->SetSearchboxThumbnail(std::string());
   ClearRegionSelection();
   // Set the coachmark text.
   if (preselection_widget_) {
@@ -865,11 +834,11 @@
 }
 
 const GURL& LensOverlayController::GetPageURLForTesting() {
-  return GetPageURL();
+  return lens_search_controller_->GetPageURL();
 }
 
 SessionID LensOverlayController::GetTabIdForTesting() {
-  return GetTabId();
+  return GetLensSearchboxController()->GetTabId();
 }
 
 metrics::OmniboxEventProto::PageClassification
@@ -878,19 +847,19 @@
 }
 
 const std::string& LensOverlayController::GetThumbnailForTesting() {
-  return GetThumbnail();
+  return GetLensSearchboxController()->GetThumbnail();
 }
 
 void LensOverlayController::OnTextModifiedForTesting() {
-  OnTextModified();
+  GetLensSearchboxController()->OnTextModified();
 }
 
 void LensOverlayController::OnThumbnailRemovedForTesting() {
-  OnThumbnailRemoved();
+  GetLensSearchboxController()->OnThumbnailRemoved();
 }
 
 void LensOverlayController::OnFocusChangedForTesting(bool focused) {
-  OnFocusChanged(focused);
+  GetLensSearchboxController()->OnFocusChanged(focused);
 }
 
 void LensOverlayController::OnZeroSuggestShownForTesting() {
@@ -1079,7 +1048,8 @@
   // TODO(crbug.com/401583049): Revisit if this should go through the
   // OnSuggestionAccepted flow or if there should be a more direct contextual
   // search flow.
-  OnSuggestionAccepted(destination_url, match_type, is_zero_prefix_suggestion);
+  GetLensSearchboxController()->OnSuggestionAccepted(
+      destination_url, match_type, is_zero_prefix_suggestion);
 }
 
 void LensOverlayController::StartContextualizationWithoutOverlay(
@@ -1139,36 +1109,27 @@
   initialization_data_->selected_region_ = std::move(box);
 }
 
-void LensOverlayController::SetSearchboxInputText(const std::string& text) {
-  if (side_panel_searchbox_handler_ &&
-      side_panel_searchbox_handler_->IsRemoteBound()) {
-    side_panel_searchbox_handler_->SetInputText(text);
-  } else {
-    // If the side panel was not bound at the time of request, we store the
-    // query as pending to send it to the searchbox on bind.
-    pending_text_query_ = text;
-  }
-}
-
-void LensOverlayController::SetSearchboxThumbnail(
-    const std::string& thumbnail_uri) {
-  if (side_panel_searchbox_handler_ &&
-      side_panel_searchbox_handler_->IsRemoteBound()) {
-    side_panel_searchbox_handler_->SetThumbnail(thumbnail_uri);
-    selected_region_thumbnail_uri_ = thumbnail_uri;
-  } else {
-    // If the side panel was not bound at the time of request, we store the
-    // thumbnail as pending to send it to the searchbox on bind.
-    pending_thumbnail_uri_ = thumbnail_uri;
-  }
-}
-
 void LensOverlayController::SetAdditionalSearchQueryParams(
     std::map<std::string, std::string> additional_search_query_params) {
   initialization_data_->additional_search_query_params_ =
       additional_search_query_params;
 }
 
+void LensOverlayController::ClearTextSelection() {
+  if (initialization_data_->selected_text_.has_value()) {
+    initialization_data_->selected_text_.reset();
+    page_->ClearTextSelection();
+  }
+}
+
+void LensOverlayController::ClearRegionSelection() {
+  GetLensSearchboxController()->SetSearchboxThumbnail("");
+  lens_selection_type_ = lens::UNKNOWN_SELECTION_TYPE;
+  initialization_data_->selected_region_.reset();
+  initialization_data_->selected_region_bitmap_.reset();
+  page_->ClearRegionSelection();
+}
+
 void LensOverlayController::ClearAllSelections() {
   page_->ClearAllSelections();
   initialization_data_->selected_region_.reset();
@@ -1179,13 +1140,64 @@
   }
 }
 
+void LensOverlayController::OnSearchboxFocusChanged(bool focused) {
+  if (!focused) {
+    return;
+  }
+
+  if (IsContextualSearchbox()) {
+    if (!csb_session_end_metrics_.searchbox_focused_) {
+      // This is the first time the searchbox is focused in this session.
+      // Record the time between the overlay being invoked and the searchbox
+      // being focused.
+      lens::RecordContextualSearchboxTimeToFirstFocus(
+          base::TimeTicks::Now() - invocation_time_,
+          initialization_data_->primary_content_type_);
+    } else {
+      RecordContextualSearchboxTimeToFocusAfterNavigation();
+    }
+    csb_session_end_metrics_.searchbox_focused_ = true;
+
+    if (state() == State::kLivePageAndResults) {
+      // If the live page is showing and the searchbox becomes focused, showing
+      // intent to issue a new query, upload the new page content for
+      // contextualization.
+      TryUpdatePageContextualization();
+    }
+  }
+}
+
+void LensOverlayController::ShowGhostLoaderErrorState() {
+  if (!IsContextualSearchbox()) {
+    return;
+  }
+  if (overlay_ghost_loader_page_) {
+    overlay_ghost_loader_page_->ShowErrorState();
+  }
+  if (side_panel_ghost_loader_page_) {
+    side_panel_ghost_loader_page_->ShowErrorState();
+  }
+}
+
+void LensOverlayController::OnZeroSuggestShown() {
+  if (!IsContextualSearchbox()) {
+    return;
+  }
+
+  if (state() == State::kOverlay) {
+    csb_session_end_metrics_.zps_shown_on_initial_query_ = true;
+  } else {
+    csb_session_end_metrics_.zps_shown_on_follow_up_query_ = true;
+  }
+}
+
 void LensOverlayController::IssueLensRequest(
     lens::mojom::CenterRotatedBoxPtr region,
     lens::LensOverlaySelectionType selection_type,
     std::optional<SkBitmap> region_bytes) {
   CHECK(initialization_data_);
   CHECK(region);
-  SetSearchboxInputText(std::string());
+  GetLensSearchboxController()->SetSearchboxInputText(std::string());
   initialization_data_->selected_region_ = region.Clone();
   initialization_data_->selected_text_.reset();
   initialization_data_->additional_search_query_params_.clear();
@@ -1220,6 +1232,41 @@
   }
 }
 
+void LensOverlayController::IssueSearchBoxRequest(
+    const std::string& search_box_text,
+    AutocompleteMatchType::Type match_type,
+    bool is_zero_prefix_suggestion,
+    std::map<std::string, std::string> additional_query_params) {
+  // Log the interaction time here so the time to fetch new page bytes is not
+  // intcluded.
+  RecordContextualSearchboxTimeToInteractionAfterNavigation();
+  RecordTimeToFirstInteraction(
+      lens::LensOverlayFirstInteractionType::kSearchbox);
+
+  // Do not attempt to contextualize if CSB is disabled, if recontextualization
+  // on each query is disabled, if the live page is not being displayed, or if
+  // the user is not in the contextual search flow (aka, issues an image request
+  // already).
+  if (!lens::features::IsLensOverlayContextualSearchboxEnabled() ||
+      !lens::features::ShouldLensOverlayRecontextualizeOnQuery() ||
+      state() != State::kLivePageAndResults || !IsContextualSearchbox()) {
+    IssueSearchBoxRequestPart2(search_box_text, match_type,
+                               is_zero_prefix_suggestion,
+                               additional_query_params);
+    return;
+  }
+
+  // If contextual searchbox is enabled, make sure the page bytes are current
+  // prior to issuing the search box request.
+  GetPageContextualization(
+      base::BindOnce(&LensOverlayController::UpdatePageContextualization,
+                     weak_factory_.GetWeakPtr())
+          .Then(base::BindOnce(
+              &LensOverlayController::IssueSearchBoxRequestPart2,
+              weak_factory_.GetWeakPtr(), search_box_text, match_type,
+              is_zero_prefix_suggestion, additional_query_params)));
+}
+
 void LensOverlayController::IssueContextualTextRequest(
     const std::string& text_query,
     lens::LensOverlaySelectionType selection_type) {
@@ -1233,9 +1280,9 @@
 void LensOverlayController::AddOverlayStateToSearchQuery(
     lens::SearchQuery& search_query) {
   // In the case where a query was triggered by a selection on the overlay or
-  // use of the searchbox, initialization_data_, additional_search_query_params_
-  // and selected_region_thumbnail_uri_ will have already been set. Record
-  // that state in a search query struct.
+  // use of the searchbox, initialization_data_ and
+  // additional_search_query_params_ will have already been set. Record that
+  // state in a search query struct.
   if (initialization_data_->selected_region_) {
     search_query.selected_region_ =
         initialization_data_->selected_region_->Clone();
@@ -1244,7 +1291,6 @@
     search_query.selected_region_bitmap_ =
         initialization_data_->selected_region_bitmap_;
   }
-  search_query.selected_region_thumbnail_uri_ = selected_region_thumbnail_uri_;
   if (initialization_data_->selected_text_.has_value()) {
     search_query.selected_text_ = initialization_data_->selected_text_.value();
   }
@@ -1449,7 +1495,7 @@
     const auto& tab_url = tab_->GetContents()->GetLastCommittedURL();
 
     auto bitmap_to_send = bitmap;
-    auto page_url = GetPageURL();
+    auto page_url = lens_search_controller_->GetPageURL();
     auto page_title = GetPageTitle();
     if (!IsPageContextEligible(
             tab_url, {}, lens_search_controller_->page_context_eligibility())) {
@@ -1517,8 +1563,8 @@
   }
 
   auto initialization_data = std::make_unique<OverlayInitializationData>(
-      screenshot, std::move(rgb_screenshot), color_palette, GetPageURL(),
-      GetPageTitle());
+      screenshot, std::move(rgb_screenshot), color_palette,
+      lens_search_controller_->GetPageURL(), GetPageTitle());
   initialization_data->significant_region_boxes_ =
       ConvertSignificantRegionBoxes(all_bounds);
   initialization_data->last_retrieved_most_visible_page_ = pdf_current_page;
@@ -2016,7 +2062,8 @@
   is_first_upload_handler_event_ = true;
   lens_overlay_query_controller_->SendUpdatedPageContent(
       initialization_data_->page_contents_,
-      initialization_data_->primary_content_type_, GetPageURL(), GetPageTitle(),
+      initialization_data_->primary_content_type_,
+      lens_search_controller_->GetPageURL(), GetPageTitle(),
       initialization_data_->last_retrieved_most_visible_page_,
       sending_bitmap ? bitmap : SkBitmap());
 
@@ -2186,7 +2233,6 @@
   }
 
   permission_bubble_controller_.reset();
-  side_panel_searchbox_handler_.reset();
   results_side_panel_coordinator_ = nullptr;
   side_panel_in_use_.reset();
   pre_initialization_suggest_inputs_.reset();
@@ -2218,19 +2264,19 @@
   page_.reset();
   languages_controller_.reset();
   scoped_tab_modal_ui_.reset();
-  pending_text_query_.reset();
-  pending_thumbnail_uri_.reset();
-  selected_region_thumbnail_uri_.clear();
   pending_region_.reset();
   fullscreen_observation_.Reset();
   immersive_mode_observer_.Reset();
   lens_overlay_blur_layer_delegate_.reset();
-  overlay_searchbox_handler_.reset();
   last_navigation_time_.reset();
 #if BUILDFLAG(IS_MAC)
   pref_change_registrar_.Reset();
 #endif  // BUILDFLAG(IS_MAC)
 
+  // Notify the searchbox controller to reset its handlers before the overlay
+  // is cleaned up. This is needed to prevent a dangling ptr.
+  GetLensSearchboxController()->ResetOverlaySearchboxHandler();
+
   // Cleanup all of the lens overlay related views. The overlay view is owned by
   // the browser view and is reused for each Lens overlay session. Clean it up
   // so it is ready for the next invocation.
@@ -2320,8 +2366,9 @@
     // original StartQueryFlow call.
     lens_overlay_query_controller_->SendUpdatedPageContent(
         initialization_data_->page_contents_,
-        initialization_data_->primary_content_type_, GetPageURL(),
-        GetPageTitle(), initialization_data_->last_retrieved_most_visible_page_,
+        initialization_data_->primary_content_type_,
+        lens_search_controller_->GetPageURL(), GetPageTitle(),
+        initialization_data_->last_retrieved_most_visible_page_,
         should_send_screenshot_on_init_
             ? initialization_data_->initial_screenshot_
             : SkBitmap());
@@ -2438,6 +2485,11 @@
   lens::RecordInvocation(invocation_source_, initial_document_type_);
 }
 
+bool LensOverlayController::IsContextualSearchbox() {
+  return lens_search_controller_->lens_searchbox_controller()
+      ->IsContextualSearchbox();
+}
+
 raw_ptr<views::View> LensOverlayController::CreateViewForOverlay() {
   // Grab the host view for the overlay which is owned by the browser view.
   auto* host_view = tab_->GetBrowserWindowInterface()->LensOverlayView();
@@ -2634,32 +2686,10 @@
   pending_suggest_inputs_callbacks_.Notify(GetLensSuggestInputs());
 }
 
-const GURL& LensOverlayController::GetPageURL() const {
-  if (lens::CanSharePageURLWithLensOverlay(pref_service_)) {
-    return tab_->GetContents()->GetVisibleURL();
-  }
-  return GURL::EmptyGURL();
-}
-
-SessionID LensOverlayController::GetTabId() const {
-  return sessions::SessionTabHelper::IdForTab(tab_->GetContents());
-}
-
 metrics::OmniboxEventProto::PageClassification
 LensOverlayController::GetPageClassification() const {
-  // There are two cases where we are assuming to be in a contextual flow:
-  // 1) We are in the zero state with the overlay CSB showing.
-  // 2) A user has made a contextual query and the live page is now showing.
-  if (state_ == State::kLivePageAndResults || state_ == State::kOverlay) {
-    return metrics::OmniboxEventProto::CONTEXTUAL_SEARCHBOX;
-  }
-  return selected_region_thumbnail_uri_.empty()
-             ? metrics::OmniboxEventProto::SEARCH_SIDE_PANEL_SEARCHBOX
-             : metrics::OmniboxEventProto::LENS_SIDE_PANEL_SEARCHBOX;
-}
-
-std::string& LensOverlayController::GetThumbnail() {
-  return selected_region_thumbnail_uri_;
+  return lens_search_controller_->lens_searchbox_controller()
+      ->GetPageClassification();
 }
 
 const lens::proto::LensOverlaySuggestInputs&
@@ -2671,123 +2701,6 @@
                               : pre_initialization_suggest_inputs_.value();
 }
 
-void LensOverlayController::OnTextModified() {
-  if (initialization_data_->selected_text_.has_value()) {
-    initialization_data_->selected_text_.reset();
-    page_->ClearTextSelection();
-  }
-}
-
-void LensOverlayController::ClearRegionSelection() {
-  selected_region_thumbnail_uri_.clear();
-  lens_selection_type_ = lens::UNKNOWN_SELECTION_TYPE;
-  initialization_data_->selected_region_.reset();
-  initialization_data_->selected_region_bitmap_.reset();
-  page_->ClearRegionSelection();
-}
-
-void LensOverlayController::OnThumbnailRemoved() {
-  // This is called by the searchbox after the thumbnail is
-  // removed from there, so no need to manually replace the
-  // searchbox thumbnail uri here.
-  ClearRegionSelection();
-}
-
-void LensOverlayController::OnSuggestionAccepted(
-    const GURL& destination_url,
-    AutocompleteMatchType::Type match_type,
-    bool is_zero_prefix_suggestion) {
-  std::string query_text = "";
-  std::map<std::string, std::string> additional_query_parameters;
-
-  net::QueryIterator query_iterator(destination_url);
-  while (!query_iterator.IsAtEnd()) {
-    std::string_view key = query_iterator.GetKey();
-    std::string_view value = query_iterator.GetUnescapedValue();
-    if (kTextQueryParameterKey == key) {
-      query_text = value;
-    } else {
-      additional_query_parameters.insert(std::make_pair(
-          query_iterator.GetKey(), query_iterator.GetUnescapedValue()));
-    }
-    query_iterator.Advance();
-  }
-
-  IssueSearchBoxRequest(query_text, match_type, is_zero_prefix_suggestion,
-                        additional_query_parameters);
-}
-
-void LensOverlayController::OnFocusChanged(bool focused) {
-  if (!focused) {
-    return;
-  }
-
-  if (IsContextualSearchbox()) {
-    if (!csb_session_end_metrics_.searchbox_focused_) {
-      // This is the first time the searchbox is focused in this session.
-      // Record the time between the overlay being invoked and the searchbox
-      // being focused.
-      lens::RecordContextualSearchboxTimeToFirstFocus(
-          base::TimeTicks::Now() - invocation_time_,
-          initialization_data_->primary_content_type_);
-    } else {
-      RecordContextualSearchboxTimeToFocusAfterNavigation();
-    }
-    csb_session_end_metrics_.searchbox_focused_ = true;
-
-    if (state() == State::kLivePageAndResults) {
-      // If the live page is showing and the searchbox becomes focused, showing
-      // intent to issue a new query, upload the new page content for
-      // contextualization.
-      TryUpdatePageContextualization();
-    }
-  }
-}
-
-void LensOverlayController::OnPageBound() {
-  // If the side panel closes before the remote gets bound,
-  // side_panel_searchbox_handler_ could become unset. Verify it is set before
-  // sending to the side panel.
-  if (!side_panel_searchbox_handler_ ||
-      !side_panel_searchbox_handler_->IsRemoteBound()) {
-    return;
-  }
-
-  // Send any pending inputs for the searchbox.
-  if (pending_text_query_.has_value()) {
-    side_panel_searchbox_handler_->SetInputText(*pending_text_query_);
-    pending_text_query_.reset();
-  }
-  if (pending_thumbnail_uri_.has_value()) {
-    SetSearchboxThumbnail(*pending_thumbnail_uri_);
-    pending_thumbnail_uri_.reset();
-  }
-}
-
-void LensOverlayController::ShowGhostLoaderErrorState() {
-  if (!IsContextualSearchbox()) {
-    return;
-  }
-  if (overlay_ghost_loader_page_) {
-    overlay_ghost_loader_page_->ShowErrorState();
-  }
-  if (side_panel_ghost_loader_page_) {
-    side_panel_ghost_loader_page_->ShowErrorState();
-  }
-}
-
-void LensOverlayController::OnZeroSuggestShown() {
-  if (!IsContextualSearchbox()) {
-    return;
-  }
-
-  if (state() == State::kOverlay) {
-    csb_session_end_metrics_.zps_shown_on_initial_query_ = true;
-  } else {
-    csb_session_end_metrics_.zps_shown_on_follow_up_query_ = true;
-  }
-}
-
 base::CallbackListSubscription
 LensOverlayController::GetLensSuggestInputsWhenReady(
     LensOverlaySuggestInputsCallback callback) {
@@ -3108,12 +3021,11 @@
     int selection_end_index) {
   initialization_data_->selected_region_.reset();
   initialization_data_->selected_region_bitmap_.reset();
-  selected_region_thumbnail_uri_.clear();
   initialization_data_->selected_text_ =
       std::make_pair(selection_start_index, selection_end_index);
 
-  SetSearchboxInputText(query);
-  SetSearchboxThumbnail(std::string());
+  GetLensSearchboxController()->SetSearchboxInputText(query);
+  GetLensSearchboxController()->SetSearchboxThumbnail(std::string());
 
   lens_overlay_query_controller_->SendTextOnlyQuery(
       query, lens_selection_type_,
@@ -3235,41 +3147,6 @@
   }
 }
 
-void LensOverlayController::IssueSearchBoxRequest(
-    const std::string& search_box_text,
-    AutocompleteMatchType::Type match_type,
-    bool is_zero_prefix_suggestion,
-    std::map<std::string, std::string> additional_query_params) {
-  // Log the interaction time here so the time to fetch new page bytes is not
-  // intcluded.
-  RecordContextualSearchboxTimeToInteractionAfterNavigation();
-  RecordTimeToFirstInteraction(
-      lens::LensOverlayFirstInteractionType::kSearchbox);
-
-  // Do not attempt to contextualize if CSB is disabled, if recontextualization
-  // on each query is disabled, if the live page is not being displayed, or if
-  // the user is not in the contextual search flow (aka, issues an image request
-  // already).
-  if (!lens::features::IsLensOverlayContextualSearchboxEnabled() ||
-      !lens::features::ShouldLensOverlayRecontextualizeOnQuery() ||
-      state() != State::kLivePageAndResults || !IsContextualSearchbox()) {
-    IssueSearchBoxRequestPart2(search_box_text, match_type,
-                               is_zero_prefix_suggestion,
-                               additional_query_params);
-    return;
-  }
-
-  // If contextual searchbox is enabled, make sure the page bytes are current
-  // prior to issuing the search box request.
-  GetPageContextualization(
-      base::BindOnce(&LensOverlayController::UpdatePageContextualization,
-                     weak_factory_.GetWeakPtr())
-          .Then(base::BindOnce(
-              &LensOverlayController::IssueSearchBoxRequestPart2,
-              weak_factory_.GetWeakPtr(), search_box_text, match_type,
-              is_zero_prefix_suggestion, additional_query_params)));
-}
-
 void LensOverlayController::IssueSearchBoxRequestPart2(
     const std::string& search_box_text,
     AutocompleteMatchType::Type match_type,
@@ -3352,7 +3229,7 @@
   // The searchbox text is set once the URL loads in the results frame, however,
   // adding it here allows the user to see the text query in the searchbox while
   // a long query loads.
-  SetSearchboxInputText(search_box_text);
+  GetLensSearchboxController()->SetSearchboxInputText(search_box_text);
 
   MaybeOpenSidePanel();
   // Only set the side panel to loading if the page is context eligible because
@@ -3477,13 +3354,6 @@
       total > 0 ? static_cast<float>(position) / total : 1.0f);
 }
 
-void LensOverlayController::HandleThumbnailCreated(
-    const std::string& thumbnail_bytes) {
-  selected_region_thumbnail_uri_ =
-      webui::MakeDataURIForImage(base::as_byte_span(thumbnail_bytes), "jpeg");
-  SetSearchboxThumbnail(selected_region_thumbnail_uri_);
-}
-
 void LensOverlayController::RecordTimeToFirstInteraction(
     lens::LensOverlayFirstInteractionType interaction_type) {
   if (search_performed_in_session_) {
@@ -3866,3 +3736,8 @@
       ->UpdateEntryPointsState(
           /*hide_toolbar_entrypoint=*/false);
 }
+
+lens::LensSearchboxController*
+LensOverlayController::GetLensSearchboxController() {
+  return lens_search_controller_->lens_searchbox_controller();
+}
diff --git a/chrome/browser/ui/lens/lens_overlay_controller.h b/chrome/browser/ui/lens/lens_overlay_controller.h
index 01faf49..0df117a 100644
--- a/chrome/browser/ui/lens/lens_overlay_controller.h
+++ b/chrome/browser/ui/lens/lens_overlay_controller.h
@@ -82,6 +82,7 @@
 class LensOverlayQueryController;
 class LensOverlaySidePanelCoordinator;
 class LensPermissionBubbleController;
+class LensSearchboxController;
 class LensOverlayEventHandler;
 struct SearchQuery;
 class SidePanelInUse;
@@ -132,14 +133,10 @@
                             lens::MimeType primary_content_type,
                             std::optional<uint32_t> pdf_page_count)>;
 
-using GetIsContextualSearchboxCallback =
-    lens::mojom::LensSidePanelPageHandler::GetIsContextualSearchboxCallback;
-
 // Manages all state associated with the lens overlay.
 // This class is not thread safe. It should only be used from the browser
 // thread.
-class LensOverlayController : public LensSearchboxClient,
-                              public lens::mojom::LensPageHandler,
+class LensOverlayController : public lens::mojom::LensPageHandler,
                               public content::WebContentsDelegate,
                               public FullscreenObserver,
                               public views::ViewObserver,
@@ -194,24 +191,6 @@
   void BindSidePanelGhostLoader(
       mojo::PendingRemote<lens::mojom::LensGhostLoaderPage> page);
 
-  // This method is used to set up communication between this instance and the
-  // searchbox WebUI. This is called by the WebUIController when the WebUI is
-  // executing javascript and has bound the handler. Takes ownership of
-  // `handler`.
-  void SetSidePanelSearchboxHandler(
-      std::unique_ptr<LensSearchboxHandler> handler);
-
-  // Passes ownership of the lens serachbox handler to the search bubble
-  // controller. This is called by the WebUIController when the WebUI is
-  // executing javascript and has bound the handler.
-  void SetContextualSearchboxHandler(
-      std::unique_ptr<LensSearchboxHandler> handler);
-
-  // This method is used to release the owned `SearchboxHandler`. It should be
-  // called before the embedding web contents is destroyed since it contains a
-  // reference to that web contents.
-  void ResetSidePanelSearchboxHandler();
-
   // Shows a toast in the side panel with the string provided in `message`. If
   // the side panel connection has not been established or was reset this is a
   // no-op.
@@ -345,9 +324,6 @@
   // Pass a result frame URL to load in the side panel.
   void LoadURLInResultsFrame(const GURL& url);
 
-  // Returns whether the searchbox is in contextual mode.
-  void GetIsContextualSearchbox(GetIsContextualSearchboxCallback callback);
-
   // Whether it's possible to capture a screenshot. virtual for testing.
   virtual bool IsScreenshotPossible(content::RenderWidgetHostView* view);
 
@@ -516,6 +492,7 @@
 
  protected:
   friend class LensSearchController;
+  friend class lens::LensSearchboxController;
   friend class lens::LensOverlaySidePanelCoordinator;
 
   // This is entry point for showing the overlay UI. This has no effect if state
@@ -587,27 +564,30 @@
   // Sets the post region selection on the overlay.
   void SetPostRegionSelection(lens::mojom::CenterRotatedBoxPtr);
 
-  // Sets the input text for the searchbox. If the searchbox has not been bound,
-  // it stores it in `pending_text_query_` instead.
-  // TODO(crbug.com/404941800): This method should be removed once the searchbox
-  // is owned by the side panel or search controller.
-  void SetSearchboxInputText(const std::string& text);
-
-  // Sets the thumbnail URI values on the searchbox if it is
-  // bound. If it hasn't yet been bound, stores the value in
-  // `pending_thumbnail_uri_` instead.
-  // TODO(crbug.com/404941800): This method should be removed once the searchbox
-  // is owned by the side panel or search controller.
-  void SetSearchboxThumbnail(const std::string& thumbnail_uri);
-
   // Stores the additional query parameters to pass to the query controller for
   // generating urls, set by the search box.
   void SetAdditionalSearchQueryParams(
       std::map<std::string, std::string> additional_search_query_params);
 
+  // Clears the selected text from the overlay if there is any.
+  void ClearTextSelection();
+
+  // Clears the selected region.
+  void ClearRegionSelection();
+
   // Clears any selections currently made in the overlay.
   void ClearAllSelections();
 
+  // Called by the searchbox controller when the focus on the searchbox changes.
+  void OnSearchboxFocusChanged(bool focused);
+
+  // Called by the searchbox controller when the ghost loader error state should
+  // be shown.
+  void ShowGhostLoaderErrorState();
+
+  // Called by the searchbox controller when zero suggest is shown.
+  void OnZeroSuggestShown();
+
   // Makes a Lens request and updates all state related to the Lens request. If
   // region_bitmap is provided, it will use those bytes to send to the Lens
   // server instead of cropping the region from the full page screenshot.
@@ -621,6 +601,13 @@
                               lens::LensOverlaySelectionType selection_type,
                               std::optional<SkBitmap> region_bitmap);
 
+  // Tries to update the page content and then issues a searchbox request.
+  void IssueSearchBoxRequest(
+      const std::string& search_box_text,
+      AutocompleteMatchType::Type match_type,
+      bool is_zero_prefix_suggestion,
+      std::map<std::string, std::string> additional_query_params);
+
   // Issues a contextual text request to the query controller.
   void IssueContextualTextRequest(
       const std::string& text_query,
@@ -655,9 +642,6 @@
   // to update the progress bar.
   void HandlePageContentUploadProgress(uint64_t position, uint64_t total);
 
-  // Handles the creation of a new thumbnail based on the user selection.
-  void HandleThumbnailCreated(const std::string& thumbnail_bytes);
-
  private:
   // Data class for constructing overlay and storing overlay state for
   // kSuspended state.
@@ -965,9 +949,6 @@
   // Called when the UI needs to create the view to show in the overlay.
   raw_ptr<views::View> CreateViewForOverlay();
 
-  // Clears the selected region.
-  void ClearRegionSelection();
-
   // content::WebContentsDelegate:
   bool HandleContextMenu(content::RenderFrameHost& render_frame_host,
                          const content::ContextMenuParams& params) override;
@@ -1006,23 +987,10 @@
   // Called when the Lens backend handshake is complete.
   void OnHandshakeComplete();
 
-  // Overridden from LensSearchboxClient:
-  const GURL& GetPageURL() const override;
-  SessionID GetTabId() const override;
-  metrics::OmniboxEventProto::PageClassification GetPageClassification()
-      const override;
-  std::string& GetThumbnail() override;
-  const lens::proto::LensOverlaySuggestInputs& GetLensSuggestInputs()
-      const override;
-  void OnTextModified() override;
-  void OnThumbnailRemoved() override;
-  void OnSuggestionAccepted(const GURL& destination_url,
-                            AutocompleteMatchType::Type match_type,
-                            bool is_zero_prefix_suggestion) override;
-  void OnFocusChanged(bool focused) override;
-  void OnPageBound() override;
-  void ShowGhostLoaderErrorState() override;
-  void OnZeroSuggestShown() override;
+  // Gets the page classification from the searchbox controller.
+  metrics::OmniboxEventProto::PageClassification GetPageClassification() const;
+
+  const lens::proto::LensOverlaySuggestInputs& GetLensSuggestInputs() const;
 
   // Adds a callback to be called when the Lens backend handshake is finished.
   // If the handshake is already finished, the callback will be called
@@ -1123,13 +1091,6 @@
                                       int selection_start_index,
                                       int selection_end_index);
 
-  // Tries to update the page content and then issues a searchbox request.
-  void IssueSearchBoxRequest(
-      const std::string& search_box_text,
-      AutocompleteMatchType::Type match_type,
-      bool is_zero_prefix_suggestion,
-      std::map<std::string, std::string> additional_query_params);
-
   // Handles a request (either region or multimodal) trigger by sending
   // the request to the query controller.
   void IssueSearchBoxRequestPart2(
@@ -1198,6 +1159,9 @@
   // points since the state of the overlay has changed.
   void UpdateEntryPointsState();
 
+  // Shorthand to grab the LensSearchboxController for this instance of Lens.
+  lens::LensSearchboxController* GetLensSearchboxController();
+
   // Owns the LensSearchController which owns this class
   raw_ptr<tabs::TabInterface> tab_;
 
@@ -1231,14 +1195,6 @@
   // shown. See StartContextualizationWithoutOverlay todo for more details.
   bool should_show_overlay_ = true;
 
-  // A pending text query to be loaded in the side panel. Needed when the side
-  // panel is not bound at the time of a text request.
-  std::optional<std::string> pending_text_query_ = std::nullopt;
-
-  // A pending thumbnail URI to be loaded in the side panel. Needed when the
-  // side panel is not bound at the time of a region request.
-  std::optional<std::string> pending_thumbnail_uri_ = std::nullopt;
-
   // A contextual search request to be issued once the overlay is initialized.
   base::OnceClosure pending_contextual_search_request_;
 
@@ -1255,11 +1211,6 @@
   // side panel is done closing and the callback is invoked.
   std::optional<lens::LensOverlayDismissalSource> last_dismissal_source_;
 
-  // Thumbnail URI referencing the data defined by the user image selection on
-  // the overlay. If the user hasn't made any selection or has made a text
-  // selection this will contain an empty string. Returned by GetThumbnail().
-  std::string selected_region_thumbnail_uri_;
-
   // The selection type of the current Lens request. If the
   // user is not currently viewing results for a Lens query, this will be
   // set to UNKNOWN_SELECTION_TYPE.
@@ -1467,29 +1418,6 @@
   // requests to be sent upon query end.
   std::unique_ptr<lens::LensOverlayGen204Controller> gen204_controller_;
 
-  // Searchbox handler for passing in image and text selections. The handler is
-  // null if the WebUI containing the searchbox has not been initialized yet,
-  // like in the case of side panel opening. In addition, the handler may be
-  // initialized, but the remote not yet set because the WebUI calls SetPage()
-  // once it is ready to receive data from C++. Therefore, we must always check
-  // that:
-  //      1) searchbox_handler_ exists and
-  //      2) searchbox_handler_->IsRemoteBound() is true.
-  // TODO(crbug.com/404941800): This should be owned by the side panel
-  // coordinator (or maybe the LensSearchController?). However, this would
-  // require making the coordinator a LensSearchboxHandler, so it is to be done
-  // in its own CL.
-  std::unique_ptr<LensSearchboxHandler> side_panel_searchbox_handler_;
-
-  // Handler for the contextual searchbox in the overlay. The handler is
-  // null if the WebUI containing the searchbox has not been initialized yet.
-  // In addition, the handler may be initialized, but the remote not yet set
-  // because the WebUI calls SetPage() once it is ready to receive data from
-  // C++. Therefore, we must always check that:
-  //      1) contextual_searchbox_handler_ exists and
-  //      2) contextual_searchbox_handler_->IsRemoteBound() is true.
-  std::unique_ptr<LensSearchboxHandler> overlay_searchbox_handler_;
-
   // The controller for sending requests to get the list of supported languages.
   // Requests are only made if the WebUI has not already cached the languages
   // and none of the update cache conditions are met.
diff --git a/chrome/browser/ui/lens/lens_overlay_proto_converter.cc b/chrome/browser/ui/lens/lens_overlay_proto_converter.cc
index ebb68f5..ecdddbe 100644
--- a/chrome/browser/ui/lens/lens_overlay_proto_converter.cc
+++ b/chrome/browser/ui/lens/lens_overlay_proto_converter.cc
@@ -85,7 +85,7 @@
   lens::mojom::PolygonPtr polygon = lens::mojom::Polygon::New();
 
   std::vector<lens::mojom::VertexPtr> vertices;
-  for (auto vertex : proto_polygon.vertex()) {
+  for (const auto& vertex : proto_polygon.vertex()) {
     vertices.push_back(lens::mojom::Vertex::New(vertex.x(), vertex.y()));
   }
   polygon->vertex = std::move(vertices);
@@ -133,7 +133,7 @@
   geometry->bounding_box = std::move(center_rotated_box);
 
   std::vector<lens::mojom::PolygonPtr> polygons;
-  for (auto polygon : response_geometry.segmentation_polygon()) {
+  for (const auto& polygon : response_geometry.segmentation_polygon()) {
     polygons.push_back(CreatePolygonMojomFromProto(polygon));
   }
   geometry->segmentation_polygon = std::move(polygons);
@@ -170,7 +170,7 @@
     lens::WritingDirection writing_direction) {
   lens::mojom::LinePtr line = lens::mojom::Line::New();
   std::vector<lens::mojom::WordPtr> words;
-  for (auto word : proto_line.words()) {
+  for (const auto& word : proto_line.words()) {
     words.push_back(
         CreateWordMojomFromProto(word, region_crop_box, writing_direction));
   }
@@ -348,7 +348,7 @@
   lens::mojom::ParagraphPtr paragraph = lens::mojom::Paragraph::New();
   paragraph->content_language = proto_paragraph.content_language();
   std::vector<lens::mojom::LinePtr> lines;
-  for (auto line : proto_paragraph.lines()) {
+  for (const auto& line : proto_paragraph.lines()) {
     lines.push_back(CreateLineMojomFromProto(
         line, region_crop_box, proto_paragraph.writing_direction()));
   }
@@ -414,7 +414,7 @@
   }
 
   auto response_objects = response.objects_response().overlay_objects();
-  for (auto response_object : response_objects) {
+  for (const auto& response_object : response_objects) {
     if (!response_object.has_interaction_properties() ||
         !response_object.interaction_properties().select_on_tap()) {
       continue;
diff --git a/chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.cc b/chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.cc
index e8827807..575a285e 100644
--- a/chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.cc
+++ b/chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/ui/lens/lens_overlay_side_panel_web_view.h"
 #include "chrome/browser/ui/lens/lens_overlay_url_builder.h"
 #include "chrome/browser/ui/lens/lens_search_controller.h"
+#include "chrome/browser/ui/lens/lens_searchbox_controller.h"
 #include "chrome/browser/ui/lens/page_content_type_conversions.h"
 #include "chrome/browser/ui/tabs/public/tab_features.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_content_proxy.h"
@@ -192,7 +193,7 @@
   // This is called from the destructor of the WebView. Synchronously clear all
   // state associated with the WebView.
   if (side_panel_web_view_) {
-    GetLensOverlayController()->ResetSidePanelSearchboxHandler();
+    GetLensSearchboxController()->ResetSidePanelSearchboxHandler();
     side_panel_web_view_ = nullptr;
   }
 }
@@ -276,15 +277,16 @@
   if (lens_mode.empty()) {
     GetLensOverlayController()->SetAdditionalSearchQueryParams(
         /*additional_search_query_params=*/{});
-    GetLensOverlayController()->SetSearchboxThumbnail("");
+    GetLensSearchboxController()->SetSearchboxThumbnail("");
     GetLensOverlayController()->ClearAllSelections();
-    GetLensOverlayController()->SetSearchboxThumbnail(std::string());
+    GetLensSearchboxController()->SetSearchboxThumbnail(std::string());
   }
 
   // Grab the current state of the overlay and use it to update populate the
   // query stack and currently loaded query.
   lens::SearchQuery search_query(query, search_url);
   GetLensOverlayController()->AddOverlayStateToSearchQuery(search_query);
+  GetLensSearchboxController()->AddSearchboxStateToSearchQuery(search_query);
 
   // Add what was the currently loaded search query to the query stack,
   // if it is present.
@@ -299,7 +301,7 @@
   initialization_data_->currently_loaded_search_query_ = search_query;
 
   // Update searchbox and selection state to match the new query.
-  GetLensOverlayController()->SetSearchboxInputText(query);
+  GetLensSearchboxController()->SetSearchboxInputText(query);
 }
 void LensOverlaySidePanelCoordinator::PopAndLoadQueryFromHistory() {
   if (initialization_data_->search_query_history_stack_.empty()) {
@@ -337,8 +339,8 @@
   }
   GetLensOverlayController()->SetAdditionalSearchQueryParams(
       query.additional_search_query_params_);
-  GetLensOverlayController()->SetSearchboxInputText(query.search_query_text_);
-  GetLensOverlayController()->SetSearchboxThumbnail(
+  GetLensSearchboxController()->SetSearchboxInputText(query.search_query_text_);
+  GetLensSearchboxController()->SetSearchboxThumbnail(
       query.selected_region_thumbnail_uri_);
 
   const bool is_contextual_query =
@@ -406,7 +408,7 @@
 
 void LensOverlaySidePanelCoordinator::GetIsContextualSearchbox(
     GetIsContextualSearchboxCallback callback) {
-  GetLensOverlayController()->GetIsContextualSearchbox(std::move(callback));
+  GetLensSearchboxController()->GetIsContextualSearchbox(std::move(callback));
 }
 
 void LensOverlaySidePanelCoordinator::RequestSendFeedback() {
diff --git a/chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.h b/chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.h
index 6dcb705f..e6fedf6 100644
--- a/chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.h
+++ b/chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.h
@@ -43,6 +43,7 @@
 namespace lens {
 
 class LensOverlaySidePanelNavigationThrottle;
+class LensSearchboxController;
 
 // Data struct representing a previous search query.
 struct SearchQuery {
@@ -132,6 +133,11 @@
     return lens_search_controller_->lens_overlay_controller();
   }
 
+  // Return the LensSearchboxController that is part of this tab.
+  LensSearchboxController* GetLensSearchboxController() {
+    return lens_search_controller_->lens_searchbox_controller();
+  }
+
   // Handles rendering text highlights on the main browser window based on
   // navigations from the side panel. Returns true if handled, false otherwise.
   // `nav_url` refers to the URL that the side panel was set to navigate to. It
diff --git a/chrome/browser/ui/lens/lens_overlay_untrusted_ui.cc b/chrome/browser/ui/lens/lens_overlay_untrusted_ui.cc
index 7c292b9..af9ce9f 100644
--- a/chrome/browser/ui/lens/lens_overlay_untrusted_ui.cc
+++ b/chrome/browser/ui/lens/lens_overlay_untrusted_ui.cc
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-
 #include "chrome/browser/ui/lens/lens_overlay_untrusted_ui.h"
 
 #include "base/strings/strcat.h"
@@ -11,6 +10,8 @@
 #include "chrome/browser/ui/browser_element_identifiers.h"
 #include "chrome/browser/ui/lens/lens_overlay_controller.h"
 #include "chrome/browser/ui/lens/lens_overlay_theme_utils.h"
+#include "chrome/browser/ui/lens/lens_search_controller.h"
+#include "chrome/browser/ui/lens/lens_searchbox_controller.h"
 #include "chrome/browser/ui/webui/searchbox/lens_searchbox_handler.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/webui_url_constants.h"
@@ -315,13 +316,14 @@
 
 void LensOverlayUntrustedUI::BindInterface(
     mojo::PendingReceiver<searchbox::mojom::PageHandler> receiver) {
-  LensOverlayController& controller = GetLensOverlayController();
+  LensSearchboxController* controller =
+      GetLensSearchController().lens_searchbox_controller();
 
   auto handler = std::make_unique<LensSearchboxHandler>(
       std::move(receiver), Profile::FromWebUI(web_ui()),
       web_ui()->GetWebContents(),
-      /*metrics_reporter=*/nullptr, /*lens_searchbox_client=*/&controller);
-  controller.SetContextualSearchboxHandler(std::move(handler));
+      /*metrics_reporter=*/nullptr, /*lens_searchbox_client=*/controller);
+  controller->SetContextualSearchboxHandler(std::move(handler));
 }
 
 void LensOverlayUntrustedUI::BindInterface(
@@ -339,6 +341,13 @@
   help_bubble_handler_factory_receiver_.Bind(std::move(pending_receiver));
 }
 
+LensSearchController& LensOverlayUntrustedUI::GetLensSearchController() {
+  LensSearchController* controller =
+      LensSearchController::FromWebUIWebContents(web_ui()->GetWebContents());
+  CHECK(controller);
+  return *controller;
+}
+
 LensOverlayController& LensOverlayUntrustedUI::GetLensOverlayController() {
   LensOverlayController* controller =
       LensOverlayController::FromWebUIWebContents(web_ui()->GetWebContents());
diff --git a/chrome/browser/ui/lens/lens_overlay_untrusted_ui.h b/chrome/browser/ui/lens/lens_overlay_untrusted_ui.h
index f478dff..14a0b3b 100644
--- a/chrome/browser/ui/lens/lens_overlay_untrusted_ui.h
+++ b/chrome/browser/ui/lens/lens_overlay_untrusted_ui.h
@@ -18,6 +18,7 @@
 #include "ui/webui/resources/cr_components/help_bubble/help_bubble.mojom.h"
 #include "ui/webui/resources/cr_components/searchbox/searchbox.mojom-forward.h"
 
+class LensSearchController;
 class LensOverlayController;
 
 namespace ui {
@@ -81,6 +82,7 @@
   static constexpr std::string GetWebUIName() { return "LensOverlayUntrusted"; }
 
  private:
+  LensSearchController& GetLensSearchController();
   LensOverlayController& GetLensOverlayController();
 
   // lens::mojom::LensPageHandlerFactory:
diff --git a/chrome/browser/ui/lens/lens_overlay_url_builder.cc b/chrome/browser/ui/lens/lens_overlay_url_builder.cc
index 3bd6fe1..c3e35a7 100644
--- a/chrome/browser/ui/lens/lens_overlay_url_builder.cc
+++ b/chrome/browser/ui/lens/lens_overlay_url_builder.cc
@@ -469,7 +469,7 @@
 
 GURL RemoveIgnoredSearchURLParameters(const GURL& url) {
   GURL processed_url = url;
-  for (std::string query_param : kIgnoredSearchUrlQueryParameters) {
+  for (const std::string& query_param : kIgnoredSearchUrlQueryParameters) {
     processed_url = net::AppendOrReplaceQueryParameter(
         processed_url, query_param, std::nullopt);
   }
diff --git a/chrome/browser/ui/lens/lens_search_controller.cc b/chrome/browser/ui/lens/lens_search_controller.cc
index fce4034..b6270577 100644
--- a/chrome/browser/ui/lens/lens_search_controller.cc
+++ b/chrome/browser/ui/lens/lens_search_controller.cc
@@ -17,12 +17,20 @@
 #include "chrome/browser/ui/lens/lens_searchbox_controller.h"
 #include "chrome/browser/ui/tabs/public/tab_features.h"
 #include "chrome/browser/ui/webui/webui_embedding_context.h"
+#include "components/lens/lens_overlay_permission_utils.h"
 #include "components/omnibox/browser/autocomplete_match_type.h"
 #include "components/optimization_guide/content/browser/page_context_eligibility.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/gfx/geometry/rect.h"
 
 namespace {
+
+void CheckInitialized(bool initialized) {
+  CHECK(initialized)
+      << "The LensSearchController has not been initialized. Initialize() must "
+         "be called before using the LensSearchController.";
+}
+
 LensSearchController* GetLensSearchControllerFromTabInterface(
     tabs::TabInterface* tab_interface) {
   return tab_interface
@@ -47,6 +55,7 @@
   initialized_ = true;
   variations_client_ = variations_client;
   identity_manager_ = identity_manager;
+  pref_service_ = pref_service;
   theme_service_ = theme_service;
 
   // Create Gen204 controller first as query controller depends on it.
@@ -80,9 +89,7 @@
 
 void LensSearchController::OpenLensOverlay(
     lens::LensOverlayInvocationSource invocation_source) {
-  CHECK(initialized_)
-      << "The LensSearchController has not been initialized. Initialize() must "
-         "be called before using the LensSearchController.";
+  CheckInitialized(initialized_);
 
   // The UI should only show if the tab is in the foreground or if the tab web
   // contents is not in a crash state.
@@ -246,30 +253,38 @@
   return tab_;
 }
 
+const GURL& LensSearchController::GetPageURL() const {
+  if (lens::CanSharePageURLWithLensOverlay(pref_service_)) {
+    return tab_->GetContents()->GetVisibleURL();
+  }
+  return GURL::EmptyGURL();
+}
+
 base::WeakPtr<LensSearchController> LensSearchController::GetWeakPtr() {
   return weak_ptr_factory_.GetWeakPtr();
 }
 
 LensOverlayController* LensSearchController::lens_overlay_controller() {
-  CHECK(initialized_) << "The LensSearchController has not been initialized. "
-                         "Initialize() must "
-                         "be called before using the LensSearchController.";
+  CheckInitialized(initialized_);
   return lens_overlay_controller_.get();
 }
 
 lens::LensOverlaySidePanelCoordinator*
 LensSearchController::lens_overlay_side_panel_coordinator() {
-  CHECK(initialized_) << "The LensSearchController has not been initialized. "
-                         "Initialize() must "
-                         "be called before using the LensSearchController.";
+  CheckInitialized(initialized_);
+
   return lens_overlay_side_panel_coordinator_.get();
 }
 
+lens::LensSearchboxController*
+LensSearchController::lens_searchbox_controller() {
+  CheckInitialized(initialized_);
+  return lens_searchbox_controller_.get();
+}
+
 optimization_guide::PageContextEligibility*
 LensSearchController::page_context_eligibility() {
-  CHECK(initialized_) << "The LensSearchController has not been initialized. "
-                         "Initialize() must "
-                         "be called before using the LensSearchController.";
+  CheckInitialized(initialized_);
   if (page_context_eligibility_) {
     return page_context_eligibility_;
   }
@@ -370,6 +385,8 @@
 void LensSearchController::CloseLensPart2() {
   // Cleanup the query controller.
   lens_overlay_query_controller_.reset();
+  // Let the controllers know to cleanup.
+  lens_searchbox_controller_->CloseUI();
   state_ = State::kOff;
 }
 
@@ -403,5 +420,5 @@
 
 void LensSearchController::HandleThumbnailCreated(
     const std::string& thumbnail_bytes) {
-  lens_overlay_controller_->HandleThumbnailCreated(thumbnail_bytes);
+  lens_searchbox_controller_->HandleThumbnailCreated(thumbnail_bytes);
 }
diff --git a/chrome/browser/ui/lens/lens_search_controller.h b/chrome/browser/ui/lens/lens_search_controller.h
index 4336b7c..d4beb3ca 100644
--- a/chrome/browser/ui/lens/lens_search_controller.h
+++ b/chrome/browser/ui/lens/lens_search_controller.h
@@ -129,6 +129,9 @@
   // Returns the tab interface that owns this controller.
   tabs::TabInterface* GetTabInterface();
 
+  // Returns the page URL of the tab if Lens has access to it.
+  const GURL& GetPageURL() const;
+
   // Returns the weak pointer to this class.
   base::WeakPtr<LensSearchController> GetWeakPtr();
 
@@ -141,6 +144,9 @@
   // Returns the LensOverlaySidePanelCoordinator.
   lens::LensOverlaySidePanelCoordinator* lens_overlay_side_panel_coordinator();
 
+  // Returns the LensSearchboxController.
+  lens::LensSearchboxController* lens_searchbox_controller();
+
   optimization_guide::PageContextEligibility* page_context_eligibility();
 
   // Testing function for setting the page context eligibility API for this
@@ -306,6 +312,10 @@
   // incognito profiles.
   raw_ptr<signin::IdentityManager> identity_manager_;
 
+  // The pref service associated with the current profile. Owned by Profile,
+  // and thus guaranteed to outlive this instance.
+  raw_ptr<PrefService> pref_service_;
+
   // The theme service associated with the current profile. Owned by Profile,
   // and thus guaranteed to outlive this instance.
   raw_ptr<ThemeService> theme_service_;
diff --git a/chrome/browser/ui/lens/lens_searchbox_controller.cc b/chrome/browser/ui/lens/lens_searchbox_controller.cc
index d057ecde..b59c6ee27 100644
--- a/chrome/browser/ui/lens/lens_searchbox_controller.cc
+++ b/chrome/browser/ui/lens/lens_searchbox_controller.cc
@@ -4,12 +4,22 @@
 
 #include "chrome/browser/ui/lens/lens_searchbox_controller.h"
 
+#include "chrome/browser/ui/lens/lens_overlay_controller.h"
+#include "chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.h"
 #include "chrome/browser/ui/lens/lens_search_controller.h"
+#include "chrome/browser/ui/webui/util/image_util.h"
 #include "components/lens/proto/server/lens_overlay_response.pb.h"
+#include "components/sessions/content/session_tab_helper.h"
 #include "components/sessions/core/session_id.h"
+#include "net/base/url_util.h"
 #include "third_party/metrics_proto/omnibox_event.pb.h"
 #include "url/gurl.h"
 
+namespace {
+// The url query param key for the search query.
+inline constexpr char kTextQueryParameterKey[] = "q";
+}  // namespace
+
 namespace lens {
 
 LensSearchboxController::LensSearchboxController(
@@ -17,62 +27,197 @@
     : lens_search_controller_(lens_search_controller) {}
 LensSearchboxController::~LensSearchboxController() = default;
 
+void LensSearchboxController::SetSidePanelSearchboxHandler(
+    std::unique_ptr<LensSearchboxHandler> handler) {
+  side_panel_searchbox_handler_ = std::move(handler);
+}
+
+void LensSearchboxController::SetContextualSearchboxHandler(
+    std::unique_ptr<LensSearchboxHandler> handler) {
+  overlay_searchbox_handler_ = std::move(handler);
+}
+
+void LensSearchboxController::ResetOverlaySearchboxHandler() {
+  overlay_searchbox_handler_.reset();
+}
+
+void LensSearchboxController::ResetSidePanelSearchboxHandler() {
+  side_panel_searchbox_handler_.reset();
+}
+
+void LensSearchboxController::SetSearchboxInputText(const std::string& text) {
+  if (side_panel_searchbox_handler_ &&
+      side_panel_searchbox_handler_->IsRemoteBound()) {
+    side_panel_searchbox_handler_->SetInputText(text);
+  } else {
+    // If the side panel was not bound at the time of request, we store the
+    // query as pending to send it to the searchbox on bind.
+    pending_text_query_ = text;
+  }
+}
+
+void LensSearchboxController::SetSearchboxThumbnail(
+    const std::string& thumbnail_uri) {
+  if (side_panel_searchbox_handler_ &&
+      side_panel_searchbox_handler_->IsRemoteBound()) {
+    side_panel_searchbox_handler_->SetThumbnail(thumbnail_uri);
+    selected_region_thumbnail_uri_ = thumbnail_uri;
+  } else {
+    // If the side panel was not bound at the time of request, we store the
+    // thumbnail as pending to send it to the searchbox on bind.
+    pending_thumbnail_uri_ = thumbnail_uri;
+  }
+}
+
+void LensSearchboxController::HandleThumbnailCreated(
+    const std::string& thumbnail_bytes) {
+  selected_region_thumbnail_uri_ =
+      webui::MakeDataURIForImage(base::as_byte_span(thumbnail_bytes), "jpeg");
+  SetSearchboxThumbnail(selected_region_thumbnail_uri_);
+}
+
+void LensSearchboxController::CloseUI() {
+  overlay_searchbox_handler_.reset();
+  side_panel_searchbox_handler_.reset();
+  selected_region_thumbnail_uri_ = "";
+  pending_text_query_ = std::nullopt;
+  pending_thumbnail_uri_ = std::nullopt;
+}
+
+bool LensSearchboxController::IsContextualSearchbox() const {
+  // TODO(crbug.com/405441183): This logic will break the side panel searchbox
+  // if there is no overlay, so it should be moved to a shared location.
+  return GetPageClassification() ==
+         metrics::OmniboxEventProto::CONTEXTUAL_SEARCHBOX;
+}
+
+void LensSearchboxController::GetIsContextualSearchbox(
+    GetIsContextualSearchboxCallback callback) {
+  std::move(callback).Run(IsContextualSearchbox());
+}
+
 const GURL& LensSearchboxController::GetPageURL() const {
-  // TODO(crbug.com/413138792): Implement this method.
-  return GURL::EmptyGURL();
+  return lens_search_controller_->GetPageURL();
 }
 
 SessionID LensSearchboxController::GetTabId() const {
-  // TODO(crbug.com/413138792): Implement this method.
-  return SessionID::InvalidValue();
+  return sessions::SessionTabHelper::IdForTab(GetTabWebContents());
 }
 
 metrics::OmniboxEventProto::PageClassification
 LensSearchboxController::GetPageClassification() const {
-  // TODO(crbug.com/413138792): Implement this method.
-  return metrics::OmniboxEventProto::INVALID_SPEC;
+  // There are two cases where we are assuming to be in a contextual flow:
+  // 1) We are in the zero state with the overlay CSB showing.
+  // 2) A user has made a contextual query and the live page is now showing.
+  // TODO(crbug.com/404941800): Remove dependency on LensOverlayController.
+  // Instead, it should check if contextualization is currently active.
+  const LensOverlayController::State state =
+      lens_search_controller_->lens_overlay_controller()->state();
+  if (state == LensOverlayController::State::kLivePageAndResults ||
+      state == LensOverlayController::State::kOverlay) {
+    return metrics::OmniboxEventProto::CONTEXTUAL_SEARCHBOX;
+  }
+  return selected_region_thumbnail_uri_.empty()
+             ? metrics::OmniboxEventProto::SEARCH_SIDE_PANEL_SEARCHBOX
+             : metrics::OmniboxEventProto::LENS_SIDE_PANEL_SEARCHBOX;
 }
 
 std::string& LensSearchboxController::GetThumbnail() {
-  // TODO(crbug.com/413138792): Implement this method.
   return selected_region_thumbnail_uri_;
 }
 
 const lens::proto::LensOverlaySuggestInputs&
 LensSearchboxController::GetLensSuggestInputs() const {
-  // TODO(crbug.com/413138792): Implement this method.
-  return lens::proto::LensOverlaySuggestInputs().default_instance();
+  // TODO(crbug.com/413138792): Implement suggest inputs tracking in this class.
+  return lens_search_controller_->lens_overlay_controller()
+      ->GetLensSuggestInputs();
 }
 
 void LensSearchboxController::OnTextModified() {
-  // TODO(crbug.com/413138792): Implement this method.
+  // TOOD(crbug.com/404941800): Verify this doesn't break if the overlay is
+  // off.
+  lens_search_controller_->lens_overlay_controller()->ClearTextSelection();
 }
 
 void LensSearchboxController::OnThumbnailRemoved() {
-  // TODO(crbug.com/413138792): Implement this method.
+  // TOOD(crbug.com/404941800): Verify this doesn't break if the overlay is
+  // off.
+  lens_search_controller_->lens_overlay_controller()->ClearRegionSelection();
 }
 
 void LensSearchboxController::OnSuggestionAccepted(
     const GURL& destination_url,
     AutocompleteMatchType::Type match_type,
     bool is_zero_prefix_suggestion) {
-  // TODO(crbug.com/413138792): Implement this method.
+  std::string query_text = "";
+  std::map<std::string, std::string> additional_query_parameters;
+
+  net::QueryIterator query_iterator(destination_url);
+  while (!query_iterator.IsAtEnd()) {
+    std::string_view key = query_iterator.GetKey();
+    std::string_view value = query_iterator.GetUnescapedValue();
+    if (kTextQueryParameterKey == key) {
+      query_text = value;
+    } else {
+      additional_query_parameters.insert(std::make_pair(
+          query_iterator.GetKey(), query_iterator.GetUnescapedValue()));
+    }
+    query_iterator.Advance();
+  }
+
+  // TODO(crbug.com/413138792): Move the logic to issue a searchbox query to
+  // this class.
+  lens_search_controller_->lens_overlay_controller()->IssueSearchBoxRequest(
+      query_text, match_type, is_zero_prefix_suggestion,
+      additional_query_parameters);
 }
 
 void LensSearchboxController::OnFocusChanged(bool focused) {
-  // TODO(crbug.com/413138792): Implement this method.
+  // TOOD(crbug.com/404941800): Implement OnSearchboxFocusChanged logic in this
+  // class.
+  lens_search_controller_->lens_overlay_controller()->OnSearchboxFocusChanged(
+      focused);
 }
 
 void LensSearchboxController::OnPageBound() {
-  // TODO(crbug.com/413138792): Implement this method.
+  // If the side panel closes before the remote gets bound,
+  // side_panel_searchbox_handler_ could become unset. Verify it is set before
+  // sending to the side panel.
+  if (!side_panel_searchbox_handler_ ||
+      !side_panel_searchbox_handler_->IsRemoteBound()) {
+    return;
+  }
+
+  // Send any pending inputs for the searchbox.
+  if (pending_text_query_.has_value()) {
+    side_panel_searchbox_handler_->SetInputText(*pending_text_query_);
+    pending_text_query_.reset();
+  }
+  if (pending_thumbnail_uri_.has_value()) {
+    SetSearchboxThumbnail(*pending_thumbnail_uri_);
+    pending_thumbnail_uri_.reset();
+  }
 }
 
 void LensSearchboxController::ShowGhostLoaderErrorState() {
-  // TODO(crbug.com/413138792): Implement this method.
+  // TODO(crbug.com/413138792): Move ghost loader handling logic to this class.
+  lens_search_controller_->lens_overlay_controller()
+      ->ShowGhostLoaderErrorState();
 }
 
 void LensSearchboxController::OnZeroSuggestShown() {
-  // TODO(crbug.com/413138792): Implement this method.
+  // TODO(crbug.com/413138792): Move the OnZeroSuggestShown() logic to this
+  // class.
+  lens_search_controller_->lens_overlay_controller()->OnZeroSuggestShown();
+}
+
+content::WebContents* LensSearchboxController::GetTabWebContents() const {
+  return lens_search_controller_->GetTabInterface()->GetContents();
+}
+
+void LensSearchboxController::AddSearchboxStateToSearchQuery(
+    lens::SearchQuery& search_query) {
+  search_query.selected_region_thumbnail_uri_ = selected_region_thumbnail_uri_;
 }
 
 }  // namespace lens
diff --git a/chrome/browser/ui/lens/lens_searchbox_controller.h b/chrome/browser/ui/lens/lens_searchbox_controller.h
index d12faea..ff52349 100644
--- a/chrome/browser/ui/lens/lens_searchbox_controller.h
+++ b/chrome/browser/ui/lens/lens_searchbox_controller.h
@@ -5,16 +5,24 @@
 #ifndef CHROME_BROWSER_UI_LENS_LENS_SEARCHBOX_CONTROLLER_H_
 #define CHROME_BROWSER_UI_LENS_LENS_SEARCHBOX_CONTROLLER_H_
 
+#include "chrome/browser/lens/core/mojom/lens_side_panel.mojom.h"
 #include "chrome/browser/ui/webui/searchbox/lens_searchbox_client.h"
+#include "chrome/browser/ui/webui/searchbox/lens_searchbox_handler.h"
 #include "components/lens/proto/server/lens_overlay_response.pb.h"
 #include "components/sessions/core/session_id.h"
+#include "content/public/browser/web_contents.h"
 #include "third_party/metrics_proto/omnibox_event.pb.h"
 #include "url/gurl.h"
 
 class LensSearchController;
 
+using GetIsContextualSearchboxCallback =
+    lens::mojom::LensSidePanelPageHandler::GetIsContextualSearchboxCallback;
+
 namespace lens {
 
+struct SearchQuery;
+
 // Controller for the Lens searchbox. This class is responsible for handling
 // communications between the Lens WebUI searchbox and other Lens components.
 // This class is responsible for both the overlay and side panel searchboxes.
@@ -24,7 +32,51 @@
       LensSearchController* lens_search_controller);
   ~LensSearchboxController() override;
 
- private:
+  // This method is used to set up communication between this instance and the
+  // searchbox WebUI. This is called by the WebUIController when the WebUI is
+  // executing javascript and has bound the handler. Takes ownership of
+  // `handler`.
+  void SetSidePanelSearchboxHandler(
+      std::unique_ptr<LensSearchboxHandler> handler);
+
+  // Passes ownership of the lens searchbox handler to the search bubble
+  // controller. This is called by the WebUIController when the WebUI is
+  // executing javascript and has bound the handler.
+  void SetContextualSearchboxHandler(
+      std::unique_ptr<LensSearchboxHandler> handler);
+
+  // This method is used to release the owned `SearchboxHandler` for the
+  // overlay. It should be called before the overlay web contents is destroyed
+  // since it contains a reference to that web contents.
+  void ResetOverlaySearchboxHandler();
+
+  // This method is used to release the owned `SearchboxHandler`. It should be
+  // called before the side panel web contents is destroyed since it contains a
+  // reference to that web contents.
+  void ResetSidePanelSearchboxHandler();
+
+  // Sets the input text for the searchbox. If the searchbox has not been bound,
+  // it stores it in `pending_text_query_` instead.
+  void SetSearchboxInputText(const std::string& text);
+
+  // Sets the thumbnail URI values on the searchbox if it is
+  // bound. If it hasn't yet been bound, stores the value in
+  // `pending_thumbnail_uri_` instead.
+  void SetSearchboxThumbnail(const std::string& thumbnail_uri);
+
+  // Handles the creation of a new thumbnail based on the user selection.
+  void HandleThumbnailCreated(const std::string& thumbnail_bytes);
+
+  // Cleans up internal state associated with the searchbox.
+  void CloseUI();
+
+  // Gets whether this is currently a contextual searchbox.
+  bool IsContextualSearchbox() const;
+
+  // Returns whether the searchbox is in contextual mode by passing the result
+  // of IsContextualSearchbox() to the callback.
+  void GetIsContextualSearchbox(GetIsContextualSearchboxCallback callback);
+
   // Overridden from LensSearchboxClient:
   const GURL& GetPageURL() const override;
   SessionID GetTabId() const override;
@@ -43,11 +95,49 @@
   void ShowGhostLoaderErrorState() override;
   void OnZeroSuggestShown() override;
 
+  // Adds searchbox related state to the search query.
+  void AddSearchboxStateToSearchQuery(lens::SearchQuery& search_query);
+
+  // Returns the WebContents associated with the tab this instance of Lens is
+  // invoked on.
+  content::WebContents* GetTabWebContents() const;
+
   // Owns this.
   const raw_ptr<LensSearchController> lens_search_controller_;
 
-  // TODO(crbug.com/413138792): Implement temporary placeholder.
+  // Searchbox handler for passing in image and text selections. The handler is
+  // null if the WebUI containing the searchbox has not been initialized yet,
+  // like in the case of side panel opening. In addition, the handler may be
+  // initialized, but the remote not yet set because the WebUI calls SetPage()
+  // once it is ready to receive data from C++. Therefore, we must always check
+  // that:
+  //      1) searchbox_handler_ exists and
+  //      2) searchbox_handler_->IsRemoteBound() is true.
+  std::unique_ptr<LensSearchboxHandler> side_panel_searchbox_handler_;
+
+  // Handler for the contextual searchbox in the overlay. The handler is
+  // null if the WebUI containing the searchbox has not been initialized yet.
+  // In addition, the handler may be initialized, but the remote not yet set
+  // because the WebUI calls SetPage() once it is ready to receive data from
+  // C++. Therefore, we must always check that:
+  //      1) contextual_searchbox_handler_ exists and
+  //      2) contextual_searchbox_handler_->IsRemoteBound() is true.
+  // TODO(crbug.com/404941800): Does this actually need to be kept alive? Its
+  // currently unused.
+  std::unique_ptr<LensSearchboxHandler> overlay_searchbox_handler_;
+
+  // Thumbnail URI referencing the data defined by the user image selection on
+  // the overlay. If the user hasn't made any selection or has made a text
+  // selection this will contain an empty string. Returned by GetThumbnail().
   std::string selected_region_thumbnail_uri_;
+
+  // A pending text query to be loaded in the side panel. Needed when the side
+  // panel is not bound at the time of a text request.
+  std::optional<std::string> pending_text_query_ = std::nullopt;
+
+  // A pending thumbnail URI to be loaded in the side panel. Needed when the
+  // side panel is not bound at the time of a region request.
+  std::optional<std::string> pending_thumbnail_uri_ = std::nullopt;
 };
 }  // namespace lens
 
diff --git a/chrome/browser/ui/lens/lens_side_panel_untrusted_ui.cc b/chrome/browser/ui/lens/lens_side_panel_untrusted_ui.cc
index 512add2..d604642c 100644
--- a/chrome/browser/ui/lens/lens_side_panel_untrusted_ui.cc
+++ b/chrome/browser/ui/lens/lens_side_panel_untrusted_ui.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/browser/ui/lens/lens_side_panel_untrusted_ui.h"
 
-
 #include "base/strings/strcat.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/themes/theme_service_factory.h"
@@ -13,6 +12,7 @@
 #include "chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.h"
 #include "chrome/browser/ui/lens/lens_overlay_theme_utils.h"
 #include "chrome/browser/ui/lens/lens_search_controller.h"
+#include "chrome/browser/ui/lens/lens_searchbox_controller.h"
 #include "chrome/browser/ui/webui/searchbox/lens_searchbox_handler.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/branded_strings.h"
@@ -168,13 +168,14 @@
 
 void LensSidePanelUntrustedUI::BindInterface(
     mojo::PendingReceiver<searchbox::mojom::PageHandler> receiver) {
-  LensOverlayController& controller = GetLensOverlayController();
+  LensSearchboxController* controller =
+      GetLensSearchController().lens_searchbox_controller();
 
   auto handler = std::make_unique<LensSearchboxHandler>(
       std::move(receiver), Profile::FromWebUI(web_ui()),
       web_ui()->GetWebContents(),
-      /*metrics_reporter=*/nullptr, /*lens_searchbox_client=*/&controller);
-  controller.SetSidePanelSearchboxHandler(std::move(handler));
+      /*metrics_reporter=*/nullptr, /*lens_searchbox_client=*/controller);
+  controller->SetSidePanelSearchboxHandler(std::move(handler));
 }
 
 void LensSidePanelUntrustedUI::BindInterface(
diff --git a/chrome/browser/ui/omnibox/chrome_omnibox_client.cc b/chrome/browser/ui/omnibox/chrome_omnibox_client.cc
index 57f4deb6..06f5cc5 100644
--- a/chrome/browser/ui/omnibox/chrome_omnibox_client.cc
+++ b/chrome/browser/ui/omnibox/chrome_omnibox_client.cc
@@ -59,8 +59,8 @@
 #include "chrome/browser/ui/hats/hats_service.h"
 #include "chrome/browser/ui/hats/hats_service_factory.h"
 #include "chrome/browser/ui/layout_constants.h"
-#include "chrome/browser/ui/lens/lens_overlay_controller.h"
 #include "chrome/browser/ui/lens/lens_search_controller.h"
+#include "chrome/browser/ui/lens/lens_searchbox_controller.h"
 #include "chrome/browser/ui/location_bar/location_bar.h"
 #include "chrome/browser/ui/omnibox/chrome_omnibox_navigation_observer.h"
 #include "chrome/browser/ui/omnibox/omnibox_tab_helper.h"
@@ -117,12 +117,6 @@
 
 using predictors::AutocompleteActionPredictor;
 
-LensOverlayController* GetLensOverlayController(
-    content::WebContents* web_contents) {
-  return web_contents ? LensOverlayController::FromTabWebContents(web_contents)
-                      : nullptr;
-}
-
 LensSearchController* GetLensSearchController(
     content::WebContents* web_contents) {
   return web_contents ? LensSearchController::FromTabWebContents(web_contents)
@@ -792,9 +786,10 @@
 
 std::optional<lens::proto::LensOverlaySuggestInputs>
 ChromeOmniboxClient::GetLensOverlaySuggestInputs() const {
-  if (LensSearchboxClient* lens_overlay_controller =
-          GetLensOverlayController(location_bar_->GetWebContents())) {
-    return lens_overlay_controller->GetLensSuggestInputs();
+  if (LensSearchController* lens_search_controller =
+          GetLensSearchController(location_bar_->GetWebContents())) {
+    return lens_search_controller->lens_searchbox_controller()
+        ->GetLensSuggestInputs();
   }
   return std::nullopt;
 }
diff --git a/chrome/browser/ui/plus_addresses/BUILD.gn b/chrome/browser/ui/plus_addresses/BUILD.gn
index 174e46a4..c7cc493d 100644
--- a/chrome/browser/ui/plus_addresses/BUILD.gn
+++ b/chrome/browser/ui/plus_addresses/BUILD.gn
@@ -9,11 +9,10 @@
 # TODO(crbug.com/413572035): Pull in android and views impl and test code.
 
 source_set("plus_addresses") {
-  sources = [
+  public = [
     "plus_address_creation_controller.h",
     "plus_address_creation_view.h",
   ]
-
   public_deps = [
     "//components/plus_addresses:types",
     "//content/public/browser",
@@ -22,12 +21,11 @@
   ]
 
   if (toolkit_views) {
-    sources += [
+    public += [
       "plus_address_creation_controller_desktop.h",
       "plus_address_error_dialog.h",
       "plus_address_menu_model.h",
     ]
-
     public_deps += [
       "//base",
       "//components/autofill/core/browser",
@@ -37,44 +35,13 @@
       "//ui/menus",
     ]
 
-    deps = [
-      "//components/constrained_window",
-      "//components/plus_addresses/resources/strings",
-      "//components/plus_addresses/resources/strings:strings_grit",
-    ]
-  }
-}
-
-source_set("impl") {
-  sources = []
-  public_deps = [
-    "//chrome/browser:browser_public_dependencies",
-    "//components/plus_addresses:types",
-    "//content/public/browser",
-    "//ui/base",
-    "//url",
-  ]
-
-  if (toolkit_views) {
-    sources += [
+    sources = [
       "plus_address_error_dialog.cc",
       "plus_address_menu_model.cc",
     ]
-
-    public_deps += [
-      "//base",
-      "//components/autofill/core/browser",
-      "//components/autofill/core/common",
-      "//components/plus_addresses/metrics",
-      "//components/plus_addresses/settings",
-      "//ui/menus",
-    ]
-
     deps = [
-      ":plus_addresses",
       "//components/constrained_window",
       "//components/plus_addresses/resources/strings",
-      "//components/plus_addresses/resources/strings:strings_grit",
     ]
   }
 }
@@ -91,7 +58,6 @@
     defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
     deps = [
       ":plus_addresses",
-      "//base",
       "//base/test:test_support",
       "//chrome/browser/plus_addresses",
       "//chrome/test:test_support",
@@ -129,12 +95,9 @@
       "//components/plus_addresses:features",
       "//components/plus_addresses:prefs",
       "//components/plus_addresses:test_support",
-      "//components/plus_addresses:types",
-      "//components/plus_addresses/metrics",
       "//components/plus_addresses/settings:test_support",
       "//components/signin/public/identity_manager:test_support",
       "//components/sync_preferences:test_support",
-      "//content/public/browser",
       "//content/test:test_support",
       "//testing/gmock",
       "//testing/gtest",
diff --git a/chrome/browser/ui/safety_hub/notification_permission_review_service.cc b/chrome/browser/ui/safety_hub/notification_permission_review_service.cc
index 9dc5c31..2f9d813a 100644
--- a/chrome/browser/ui/safety_hub/notification_permission_review_service.cc
+++ b/chrome/browser/ui/safety_hub/notification_permission_review_service.cc
@@ -118,7 +118,7 @@
 std::set<ContentSettingsPattern> NotificationPermissionsReviewService::
     NotificationPermissionsResult::GetOrigins() const {
   std::set<ContentSettingsPattern> origins;
-  for (NotificationPermissions permission : notification_permissions_) {
+  for (const auto& permission : notification_permissions_) {
     origins.insert(permission.primary_pattern);
   }
   return origins;
@@ -134,7 +134,7 @@
     NotificationPermissionsResult::ToDictValue() const {
   base::Value::Dict result = BaseToDictValue();
   base::Value::List notification_permissions;
-  for (NotificationPermissions permission : notification_permissions_) {
+  for (const auto& permission : notification_permissions_) {
     base::Value::Dict permission_dict;
     permission_dict.Set(kSafetyHubOriginKey,
                         permission.primary_pattern.ToString());
diff --git a/chrome/browser/ui/safety_hub/revoked_permissions_service.cc b/chrome/browser/ui/safety_hub/revoked_permissions_service.cc
index 68b8ba9..f407ce2 100644
--- a/chrome/browser/ui/safety_hub/revoked_permissions_service.cc
+++ b/chrome/browser/ui/safety_hub/revoked_permissions_service.cc
@@ -269,7 +269,7 @@
 std::set<ContentSettingsPattern>
 RevokedPermissionsService::RevokedPermissionsResult::GetRevokedOrigins() const {
   std::set<ContentSettingsPattern> origins;
-  for (auto permission : revoked_permissions_) {
+  for (const auto& permission : revoked_permissions_) {
     origins.insert(permission.primary_pattern);
   }
   return origins;
@@ -279,7 +279,7 @@
 RevokedPermissionsService::RevokedPermissionsResult::ToDictValue() const {
   base::Value::Dict result = BaseToDictValue();
   base::Value::List revoked_origins;
-  for (auto permission : revoked_permissions_) {
+  for (const auto& permission : revoked_permissions_) {
     revoked_origins.Append(permission.primary_pattern.ToString());
   }
   result.Set(kRevokedPermissionsResultKey, std::move(revoked_origins));
diff --git a/chrome/browser/ui/tabs/organization/tab_declutter_controller.cc b/chrome/browser/ui/tabs/organization/tab_declutter_controller.cc
index d6d6bb5e..5a63f2c 100644
--- a/chrome/browser/ui/tabs/organization/tab_declutter_controller.cc
+++ b/chrome/browser/ui/tabs/organization/tab_declutter_controller.cc
@@ -240,7 +240,7 @@
   }
 
   int duplicate_tabs_decluttered = 0;
-  for (GURL url : urls) {
+  for (const GURL& url : urls) {
     // Sort the tabs with `url` based on the last committed navigation time and
     // close all the tabs except the oldest tab.
     std::vector<std::pair<tabs::TabInterface*, base::Time>> url_matching_tabs;
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/collaboration_messaging_observer.cc b/chrome/browser/ui/tabs/saved_tab_groups/collaboration_messaging_observer.cc
index adffb2d..96ba3e0 100644
--- a/chrome/browser/ui/tabs/saved_tab_groups/collaboration_messaging_observer.cc
+++ b/chrome/browser/ui/tabs/saved_tab_groups/collaboration_messaging_observer.cc
@@ -227,7 +227,7 @@
 void CollaborationMessagingObserver::OnMessagingBackendServiceInitialized() {
   CHECK(service_);
   auto messages = service_->GetMessages(std::nullopt);
-  for (auto message : messages) {
+  for (const auto& message : messages) {
     DispatchMessage(message, MessageDisplayStatus::kDisplay);
   }
 }
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service.cc b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service.cc
index 4d63e2e..feebf72 100644
--- a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service.cc
+++ b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service.cc
@@ -533,7 +533,8 @@
     ConnectLocalTabGroup(local_group_id, saved_guid);
   }
 
-  for (SavedTabGroup group : restored_groups_to_save_on_load_) {
+  for (SavedTabGroup& group : restored_groups_to_save_on_load_) {
+    // Move from vector about to be cleared below.
     SaveRestoredGroup(std::move(group));
   }
 
diff --git a/chrome/browser/ui/tabs/tab_strip_collection.cc b/chrome/browser/ui/tabs/tab_strip_collection.cc
index 8e5ead0..bd0d81a 100644
--- a/chrome/browser/ui/tabs/tab_strip_collection.cc
+++ b/chrome/browser/ui/tabs/tab_strip_collection.cc
@@ -276,8 +276,7 @@
 TabGroupTabCollection* TabStripCollection::AddTabGroup(
     std::unique_ptr<TabGroupTabCollection> group,
     int index) {
-  group_mapping_.insert({group->GetTabGroupId(), group.get()});
-
+  AddCollectionMapping(group.get());
   const int dst_index =
       (index == static_cast<int>(TabCountRecursive()))
           ? unpinned_collection_->ChildCount()
@@ -291,8 +290,7 @@
 std::unique_ptr<TabGroupTabCollection> TabStripCollection::RemoveGroup(
     TabGroupTabCollection* group) {
   CHECK(group_mapping_.contains(group->GetTabGroupId()));
-  group_mapping_.erase(group->GetTabGroupId());
-
+  RemoveCollectionMapping(group);
   return base::WrapUnique(static_cast<TabGroupTabCollection*>(
       unpinned_collection_->MaybeRemoveCollection(group).release()));
 }
@@ -400,7 +398,7 @@
   }
 
   // Insert split back into the parent.
-  split_mapping_.insert({split_id, split.get()});
+  AddCollectionMapping(split.get());
   parent_collection->AddCollection(std::move(split), dst_index);
 }
 
@@ -421,7 +419,7 @@
     parent_collection->AddTab(split->MaybeRemoveTab(tab), dst_index);
   }
 
-  split_mapping_.erase(split->GetSplitTabId());
+  RemoveCollectionMapping(split);
   parent_collection->MaybeRemoveCollection(split).reset();
 }
 
@@ -489,4 +487,49 @@
   }
 }
 
+void TabStripCollection::AddCollectionMapping(TabCollection* root_collection) {
+  if (root_collection->type() == TabCollection::Type::GROUP) {
+    TabGroupTabCollection* group_collection =
+        static_cast<TabGroupTabCollection*>(root_collection);
+    group_mapping_.insert(
+        {group_collection->GetTabGroupId(), group_collection});
+
+    for (const tabs::TabInterface* tab : *group_collection) {
+      if (tab->IsSplit()) {
+        const split_tabs::SplitTabId split_id = tab->GetSplit().value();
+        if (!split_mapping_.contains(split_id)) {
+          split_mapping_.insert(
+              {split_id, static_cast<SplitTabCollection*>(
+                             tab->GetParentCollection(GetPassKey()))});
+        }
+      }
+    }
+  } else if (root_collection->type() == TabCollection::Type::SPLIT) {
+    SplitTabCollection* split_collection =
+        static_cast<SplitTabCollection*>(root_collection);
+    split_mapping_.insert(
+        {split_collection->GetSplitTabId(), split_collection});
+  }
+}
+
+void TabStripCollection::RemoveCollectionMapping(
+    TabCollection* root_collection) {
+  if (root_collection->type() == TabCollection::Type::GROUP) {
+    TabGroupTabCollection* group_collection =
+        static_cast<TabGroupTabCollection*>(root_collection);
+    group_mapping_.erase(group_collection->GetTabGroupId());
+
+    for (const tabs::TabInterface* tab : *group_collection) {
+      if (tab->IsSplit()) {
+        split_mapping_.erase(tab->GetSplit().value());
+      }
+    }
+
+  } else if (root_collection->type() == TabCollection::Type::SPLIT) {
+    SplitTabCollection* split_collection =
+        static_cast<SplitTabCollection*>(root_collection);
+    split_mapping_.erase(split_collection->GetSplitTabId());
+  }
+}
+
 }  // namespace tabs
diff --git a/chrome/browser/ui/tabs/tab_strip_collection.h b/chrome/browser/ui/tabs/tab_strip_collection.h
index 5a3d3a52..bd72308 100644
--- a/chrome/browser/ui/tabs/tab_strip_collection.h
+++ b/chrome/browser/ui/tabs/tab_strip_collection.h
@@ -131,6 +131,12 @@
   ChildrenPtrs GetTabsAndCollectionsForMove(
       const std::vector<int>& tab_indices);
 
+  // Helper to centralize updates to `group_mapping_` and `split_mapping_`. If
+  // `root_collection` is a group, the appropriate splits need to group need to
+  // be updated in the `split_mapping_`.
+  void AddCollectionMapping(TabCollection* root_collection);
+  void RemoveCollectionMapping(TabCollection* root_collection);
+
   // All of the pinned tabs for this tabstrip is present in this collection.
   // This should be below `impl_` to avoid being a dangling pointer during
   // destruction.
diff --git a/chrome/browser/ui/tabs/tab_strip_model.cc b/chrome/browser/ui/tabs/tab_strip_model.cc
index 54a0471c..fc6133f 100644
--- a/chrome/browser/ui/tabs/tab_strip_model.cc
+++ b/chrome/browser/ui/tabs/tab_strip_model.cc
@@ -460,6 +460,10 @@
   // Calculate the next selected index before fixing openers.
   std::optional<int> next_selected_index = DetermineNewSelectedIndex(group_id);
 
+  std::map<split_tabs::SplitTabId,
+           std::vector<std::pair<tabs::TabInterface*, int>>>
+      splits_in_group;
+
   for (int index = static_cast<int>(tabs_in_group.end()) - 1;
        index >= static_cast<int>(tabs_in_group.start()); --index) {
     tabs::TabModel* tab = GetTabModelAtIndex(index);
@@ -470,6 +474,13 @@
       active_tab_model->WillEnterBackground(base::PassKey<TabStripModel>());
     }
 
+    if (tab->IsSplit()) {
+      split_tabs::SplitTabId split_id = tab->GetSplit().value();
+      if (!splits_in_group.contains(split_id)) {
+        splits_in_group[split_id] = GetTabsAndIndicesInSplit(split_id);
+      }
+    }
+
     // Track whether any of these tabs are selected.
     if (IsTabSelected(index)) {
       selected_tabs_removed = true;
@@ -494,6 +505,13 @@
         tabs::TabInterface::DetachReason::kInsertIntoOtherWindow, std::nullopt);
   }
 
+  // Remove the group collection before selection model update.
+  // This is because the selection model update includes looking into
+  // `contents_data_` for split tabs.
+  std::unique_ptr<tabs::TabGroupTabCollection> group_collection =
+      contents_data_->RemoveGroup(
+          contents_data_->GetTabGroupCollection(group_id));
+
   // Selection model update should be done as a bulk operation.
   if (closing_all_tabs) {
     selection_model_.Clear();
@@ -515,15 +533,19 @@
     }
   }
 
-  // Remove the group collection.
-  std::unique_ptr<tabs::TabGroupTabCollection> group_collection =
-      contents_data_->RemoveGroup(
-          contents_data_->GetTabGroupCollection(group_id));
-
   // Send group detach notification.
   OnTabGroupDetached(group_collection.get());
   group_model_->RemoveTabGroup(group_id, base::PassKey<TabStripModel>());
 
+  // Send possible split detach notification.
+  for (auto const& [split_id, tabs_with_indices] : splits_in_group) {
+    for (TabStripModelObserver& observer : observers_) {
+      observer.OnSplitTabRemoved(tabs_with_indices, split_id,
+                                 TabStripModelObserver::SplitTabRemoveReason::
+                                     kDetachedToAnotherTabstrip);
+    }
+  }
+
   // Notify tab is removed from model
   for (tabs::TabInterface* tab : *group_collection) {
     static_cast<tabs::TabModel*>(tab)->OnRemovedFromModel();
@@ -580,7 +602,8 @@
                                    index);
 
   for (int i = index;
-       i < index + static_cast<int>(group_collection->ChildCount()); i++) {
+       i < index + static_cast<int>(group_collection->TabCountRecursive());
+       i++) {
     selection_model_.IncrementFrom(index);
   }
 
@@ -593,9 +616,14 @@
 
   ValidateTabStripModel();
 
+  std::set<split_tabs::SplitTabId> splits_in_group;
   for (tabs::TabInterface* tab : *group_collection) {
     static_cast<tabs::TabModel*>(tab)->DidInsert(
         base::PassKey<TabStripModel>());
+
+    if (tab->IsSplit()) {
+      splits_in_group.insert(tab->GetSplit().value());
+    }
   }
 
   // Send add notifications for tabs.
@@ -620,6 +648,16 @@
   // Send group attach notification.
   OnTabGroupAttached(group_collection);
 
+  // Send split attach notification
+  for (const split_tabs::SplitTabId& split_id : splits_in_group) {
+    for (TabStripModelObserver& observer : observers_) {
+      observer.OnSplitTabCreated(GetTabsAndIndicesInSplit(split_id), split_id,
+                                 TabStripModelObserver::SplitTabAddReason::
+                                     kInsertedFromAnotherTabstrip,
+                                 *GetSplitData(split_id)->visual_data());
+    }
+  }
+
   return tabs_in_group;
 }
 
@@ -3798,7 +3836,7 @@
     int index,
     int to_position,
     tabs::TabInterface* tab,
-    TabStripSelectionChange& selection_change) {
+    const TabStripSelectionChange& selection_change) {
   TabStripModelChange::Move move;
   move.tab = tab;
   move.contents = tab->GetContents();
@@ -4036,7 +4074,7 @@
 
   UpdateSelectionModelForMoves(tab_indices, destination_index);
 
-  for (auto notification : notifications) {
+  for (const auto& notification : notifications) {
     const int final_index = GetIndexOfTab(notification.tab);
     tabs::TabInterface* tab = GetTabAtIndex(final_index);
     if (notification.initial_index != final_index) {
diff --git a/chrome/browser/ui/tabs/tab_strip_model.h b/chrome/browser/ui/tabs/tab_strip_model.h
index e66bd93f..bd9e5ee7 100644
--- a/chrome/browser/ui/tabs/tab_strip_model.h
+++ b/chrome/browser/ui/tabs/tab_strip_model.h
@@ -1051,10 +1051,11 @@
   // pinned tabs placement, group contiguity and selected tabs validity.
   void ValidateTabStripModel();
 
-  void SendMoveNotificationForTab(int index,
-                                  int to_position,
-                                  tabs::TabInterface* tab,
-                                  TabStripSelectionChange& selection_change);
+  void SendMoveNotificationForTab(
+      int index,
+      int to_position,
+      tabs::TabInterface* tab,
+      const TabStripSelectionChange& selection_change);
 
   void UpdateSelectionModelForMove(int initial_index,
                                    int final_index,
diff --git a/chrome/browser/ui/tabs/tab_strip_model_unittest.cc b/chrome/browser/ui/tabs/tab_strip_model_unittest.cc
index b2e88e1..2542bc00 100644
--- a/chrome/browser/ui/tabs/tab_strip_model_unittest.cc
+++ b/chrome/browser/ui/tabs/tab_strip_model_unittest.cc
@@ -454,6 +454,7 @@
 
   void SetUp() override {
     tabstrip_ = std::make_unique<TabStripModel>(delegate(), profile());
+    scoped_feature_list_.InitAndEnableFeature(features::kSideBySide);
     tabstrip()->AddObserver(observer());
     ASSERT_TRUE(tabstrip()->empty());
   }
@@ -989,6 +990,43 @@
   EXPECT_EQ(tabstrip()->active_index(), 3);
 }
 
+TEST_F(TabStripModelTest, TestDetachAndInsertGroupWithSplit) {
+  ASSERT_NO_FATAL_FAILURE(
+      PrepareTabstripForSelectionTest(tabstrip(), 5, 0, {2}));
+
+  tab_groups::TabGroupId group_id =
+      tabstrip()->AddToNewGroup(std::vector<int>{1, 2, 3});
+
+  tabstrip()->ActivateTabAt(
+      2, TabStripUserGestureDetails(
+             TabStripUserGestureDetails::GestureType::kOther));
+  split_tabs::SplitTabId split_id =
+      tabstrip()->AddToNewSplit({3}, split_tabs::SplitTabLayout::kVertical);
+
+  std::unique_ptr<DetachedTabGroup> detached_group =
+      tabstrip()->DetachTabGroupForInsertion(group_id);
+
+  EXPECT_EQ(detached_group->collection_->TabCountRecursive(), 3u);
+  EXPECT_FALSE(tabstrip()->group_model()->ContainsTabGroup(group_id));
+  EXPECT_EQ(tabstrip()->count(), 2);
+
+  // Reinsert the detached group.
+  tabstrip()->InsertDetachedTabGroupAt(std::move(detached_group), 0);
+
+  EXPECT_TRUE(tabstrip()->group_model()->ContainsTabGroup(group_id));
+  EXPECT_EQ(
+      tabstrip()->group_model()->GetTabGroup(group_id)->ListTabs().length(),
+      3u);
+  EXPECT_EQ(tabstrip()->group_model()->GetTabGroup(group_id)->ListTabs(),
+            gfx::Range(0, 3));
+  std::vector<tabs::TabInterface*> expected_split_tabs = {
+      tabstrip()->GetTabAtIndex(1), tabstrip()->GetTabAtIndex(2)};
+  EXPECT_EQ(tabstrip()->GetSplitData(split_id)->ListTabs(),
+            expected_split_tabs);
+  EXPECT_EQ(tabstrip()->count(), 5);
+  EXPECT_EQ("1 2s 3s 0 4", GetTabStripStateString(tabstrip()));
+}
+
 TEST_F(TabStripModelTest, InsertDetachedGroupSelectionObserver) {
   tabstrip()->AppendWebContents(CreateWebContentsWithID(1), false);
   tabstrip()->AppendWebContents(CreateWebContentsWithID(2), true);
@@ -3470,7 +3508,6 @@
 }
 
 TEST_F(TabStripModelTest, MoveSelectedTabsToWithSplit) {
-  scoped_feature_list()->InitAndEnableFeature(features::kSideBySide);
   ASSERT_NO_FATAL_FAILURE(
       PrepareTabstripForSelectionTest(tabstrip(), 10, 5, {2, 3, 6, 7}));
 
@@ -3487,7 +3524,6 @@
 }
 
 TEST_F(TabStripModelTest, MoveSelectedTabsToWithGroupAndSplit) {
-  scoped_feature_list()->InitAndEnableFeature(features::kSideBySide);
   ASSERT_NO_FATAL_FAILURE(
       PrepareTabstripForSelectionTest(tabstrip(), 13, 5, {2, 3}));
 
@@ -5581,7 +5617,6 @@
 }
 
 TEST_F(TabStripModelTest, SplitSelectionTestFromModel) {
-  scoped_feature_list()->InitAndEnableFeature(features::kSideBySide);
   TestTabStripModelDelegate delegate;
   TabStripModel tabstrip(&delegate, profile());
   EXPECT_TRUE(tabstrip.empty());
diff --git a/chrome/browser/ui/ui_features.cc b/chrome/browser/ui/ui_features.cc
index baa62c6..25a35fdf 100644
--- a/chrome/browser/ui/ui_features.cc
+++ b/chrome/browser/ui/ui_features.cc
@@ -133,6 +133,11 @@
              base::FEATURE_ENABLED_BY_DEFAULT);
 
 BASE_FEATURE(kSideBySide, "SideBySide", base::FEATURE_DISABLED_BY_DEFAULT);
+
+BASE_FEATURE(kSideBySideLinkMenuNewBadge,
+             "SideBySideLinkMenuNewBadge",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 bool IsNtpFooterEnabledWithoutSideBySide() {
   return (base::FeatureList::IsEnabled(ntp_features::kNtpFooter) &&
           !base::FeatureList::IsEnabled(features::kSideBySide));
diff --git a/chrome/browser/ui/ui_features.h b/chrome/browser/ui/ui_features.h
index b94f5cb..1adaa40 100644
--- a/chrome/browser/ui/ui_features.h
+++ b/chrome/browser/ui/ui_features.h
@@ -102,6 +102,9 @@
 BASE_DECLARE_FEATURE(KScrimForTabModal);
 
 BASE_DECLARE_FEATURE(kSideBySide);
+
+BASE_DECLARE_FEATURE(kSideBySideLinkMenuNewBadge);
+
 bool IsNtpFooterEnabledWithoutSideBySide();
 
 BASE_DECLARE_FEATURE(kTabDuplicateMetrics);
diff --git a/chrome/browser/ui/views/data_sharing/collaboration_controller_delegate_desktop.cc b/chrome/browser/ui/views/data_sharing/collaboration_controller_delegate_desktop.cc
index 2c037d4..e0e50c8 100644
--- a/chrome/browser/ui/views/data_sharing/collaboration_controller_delegate_desktop.cc
+++ b/chrome/browser/ui/views/data_sharing/collaboration_controller_delegate_desktop.cc
@@ -50,6 +50,12 @@
       ok_button_text_id = IDS_DATA_SHARING_NEED_SIGN_IN_CONTINUE_BUTTON;
       valid = true;
       break;
+    case collaboration::SigninStatus::kSigninDisabled:
+      title_id = IDS_DATA_SHARING_SIGNED_OUT;
+      body_id = IDS_DATA_SHARING_SIGNED_OUT_BODY;
+      ok_button_text_id = IDS_DATA_SHARING_SETTINGS;
+      valid = true;
+      break;
     case collaboration::SigninStatus::kSignedInPaused:
       title_id = IDS_DATA_SHARING_NEED_VERIFY_ACCOUNT;
       body_id = IDS_DATA_SHARING_NEED_VERIFY_ACCOUNT_BODY;
@@ -384,6 +390,9 @@
     case collaboration::SigninStatus::kNotSignedIn:
       ShowSignInAndSyncUi(profile);
       break;
+    case collaboration::SigninStatus::kSigninDisabled:
+      chrome::ShowSettingsSubPage(browser_, chrome::kSyncSetupSubPage);
+      break;
     case collaboration::SigninStatus::kSignedInPaused:
       signin_ui_util::ShowReauthForAccount(
           profile, GetAccountInfoFromProfile(profile).email,
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_main_page_view.cc b/chrome/browser/ui/views/extensions/extensions_menu_main_page_view.cc
index 16fe8fe7..cfc7e1c7 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_main_page_view.cc
+++ b/chrome/browser/ui/views/extensions/extensions_menu_main_page_view.cc
@@ -400,7 +400,7 @@
   CHECK_IS_TEST();
   std::vector<extensions::ExtensionId> extensions;
   extensions.reserve(requests_entries_.size());
-  for (auto entry : requests_entries_) {
+  for (const auto& entry : requests_entries_) {
     extensions.push_back(entry.first);
   }
   return extensions;
diff --git a/chrome/browser/ui/views/extensions/extensions_request_access_button.cc b/chrome/browser/ui/views/extensions/extensions_request_access_button.cc
index b9dfbcc0..eeb5391 100644
--- a/chrome/browser/ui/views/extensions/extensions_request_access_button.cc
+++ b/chrome/browser/ui/views/extensions/extensions_request_access_button.cc
@@ -46,7 +46,7 @@
   const extensions::ExtensionSet& enabled_extensions =
       extensions::ExtensionRegistry::Get(profile)->enabled_extensions();
   std::vector<const extensions::Extension*> extensions;
-  for (auto extension_id : extension_ids) {
+  for (const auto& extension_id : extension_ids) {
     extensions.push_back(enabled_extensions.GetByID(extension_id));
   }
   return extensions;
diff --git a/chrome/browser/ui/views/extensions/extensions_request_access_hover_card_coordinator.cc b/chrome/browser/ui/views/extensions/extensions_request_access_hover_card_coordinator.cc
index d6419e53..9797535 100644
--- a/chrome/browser/ui/views/extensions/extensions_request_access_hover_card_coordinator.cc
+++ b/chrome/browser/ui/views/extensions/extensions_request_access_hover_card_coordinator.cc
@@ -50,7 +50,7 @@
     dialog_builder.AddParagraph(ui::DialogModelLabel::CreateWithReplacement(
         IDS_EXTENSIONS_REQUEST_ACCESS_BUTTON_TOOLTIP_MULTIPLE_EXTENSIONS,
         ui::DialogModelLabel::CreateEmphasizedText(url)));
-    for (auto extension_id : extension_ids) {
+    for (const auto& extension_id : extension_ids) {
       ToolbarActionViewController* action =
           extensions_container->GetActionForId(extension_id);
       dialog_builder.AddMenuItem(
diff --git a/chrome/browser/ui/views/file_system_access/file_system_access_restore_permission_bubble_view.cc b/chrome/browser/ui/views/file_system_access/file_system_access_restore_permission_bubble_view.cc
index bdb125c..bf43e27 100644
--- a/chrome/browser/ui/views/file_system_access/file_system_access_restore_permission_bubble_view.cc
+++ b/chrome/browser/ui/views/file_system_access/file_system_access_restore_permission_bubble_view.cc
@@ -55,7 +55,7 @@
 
   // Add file/directory list.
   std::vector<base::FilePath> file_paths;
-  for (auto file : file_data) {
+  for (const auto& file : file_data) {
     file_paths.push_back(file.path_info.path);
   }
   AddChildView(FileSystemAccessScrollPanel::Create(file_paths));
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index 559a149..3278a02 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -123,6 +123,7 @@
 #include "chrome/browser/ui/views/frame/contents_layout_manager.h"
 #include "chrome/browser/ui/views/frame/immersive_mode_controller.h"
 #include "chrome/browser/ui/views/frame/multi_contents_view.h"
+#include "chrome/browser/ui/views/frame/multi_contents_view_drop_target_controller.h"
 #include "chrome/browser/ui/views/frame/native_browser_frame.h"
 #include "chrome/browser/ui/views/frame/scrim_view.h"
 #include "chrome/browser/ui/views/frame/tab_strip_region_view.h"
@@ -3598,11 +3599,11 @@
 }
 
 DownloadBubbleUIController* BrowserView::GetDownloadBubbleUIController() {
-    if (auto* download_controller =
-            browser_->GetFeatures().download_toolbar_ui_controller()) {
-      return download_controller->bubble_controller();
-    }
-    return nullptr;
+  if (auto* download_controller =
+          browser_->GetFeatures().download_toolbar_ui_controller()) {
+    return download_controller->bubble_controller();
+  }
+  return nullptr;
 }
 
 void BrowserView::ConfirmBrowserCloseWithPendingDownloads(
@@ -3651,6 +3652,14 @@
   return false;
 }
 
+void BrowserView::PreHandleDragUpdate(const content::DropData& drop_data,
+                                      const gfx::PointF& point) {
+  if (multi_contents_view_) {
+    multi_contents_view_->drop_target_controller().OnWebContentsDragUpdate(
+        drop_data, point);
+  }
+}
+
 content::KeyboardEventProcessingResult BrowserView::PreHandleKeyboardEvent(
     const NativeWebKeyboardEvent& event) {
   if ((event.GetType() != blink::WebInputEvent::Type::kRawKeyDown) &&
@@ -3909,12 +3918,15 @@
     std::vector<std::pair<tabs::TabInterface*, int>> tabs,
     split_tabs::SplitTabId split_id,
     SplitTabRemoveReason reason) {
-  const bool is_split_active = std::any_of(
-      tabs.begin(), tabs.end(), [](std::pair<tabs::TabInterface*, int>& tab) {
-        return tab.first->IsActivated();
-      });
+  CHECK(multi_contents_view_);
+  content::WebContents* active_web_contents =
+      multi_contents_view_->GetActiveContentsView()->web_contents();
 
-  if (is_split_active) {
+  if (std::any_of(tabs.begin(), tabs.end(),
+                  [active_web_contents](
+                      const std::pair<tabs::TabInterface*, int>& pair) {
+                    return pair.first->GetContents() == active_web_contents;
+                  })) {
     HideSplitView();
   }
 }
diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h
index fa2306f..c6900fb 100644
--- a/chrome/browser/ui/views/frame/browser_view.h
+++ b/chrome/browser/ui/views/frame/browser_view.h
@@ -72,6 +72,7 @@
 class BookmarkBarView;
 class Browser;
 class ContentsLayoutManager;
+struct DropData;
 class ExclusiveAccessBubbleViews;
 class FullscreenControlHost;
 class InfoBarContainerView;
@@ -662,6 +663,8 @@
   void UserChangedTheme(BrowserThemeChangeType theme_change_type) override;
   void ShowAppMenu() override;
   bool PreHandleMouseEvent(const blink::WebMouseEvent& event) override;
+  void PreHandleDragUpdate(const content::DropData& drop_data,
+                           const gfx::PointF& point) override;
   content::KeyboardEventProcessingResult PreHandleKeyboardEvent(
       const input::NativeWebKeyboardEvent& event) override;
   bool HandleKeyboardEvent(const input::NativeWebKeyboardEvent& event) override;
diff --git a/chrome/browser/ui/views/frame/multi_contents_view.cc b/chrome/browser/ui/views/frame/multi_contents_view.cc
index ca90a3fa..5ab73de 100644
--- a/chrome/browser/ui/views/frame/multi_contents_view.cc
+++ b/chrome/browser/ui/views/frame/multi_contents_view.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/contents_web_view.h"
 #include "chrome/browser/ui/views/frame/multi_contents_resize_area.h"
+#include "chrome/browser/ui/views/frame/multi_contents_view_drop_target_controller.h"
 #include "chrome/browser/ui/views/frame/top_container_background.h"
 #include "chrome/browser/ui/views/status_bubble_views.h"
 #include "content/public/browser/web_contents.h"
@@ -24,6 +25,8 @@
 #include "ui/views/layout/flex_layout_types.h"
 #include "ui/views/view_class_properties.h"
 
+DEFINE_ELEMENT_IDENTIFIER_VALUE(kMultiContentsViewDropTargetElementId);
+
 DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(MultiContentsView,
                                       kMultiContentsViewElementId);
 
@@ -60,6 +63,14 @@
   SetProperty(views::kElementIdentifierKey, kMultiContentsViewElementId);
   SetLayoutManager(std::make_unique<views::FlexLayout>())
       ->SetOrientation(views::LayoutOrientation::kHorizontal);
+
+  views::View* drop_target_view = AddChildView(std::make_unique<views::View>());
+  drop_target_view->SetProperty(views::kElementIdentifierKey,
+                                kMultiContentsViewDropTargetElementId);
+  drop_target_view->SetVisible(false);
+  drop_target_controller_ =
+      std::make_unique<MultiContentsViewDropTargetController>(
+          *drop_target_view);
 }
 
 MultiContentsView::~MultiContentsView() = default;
diff --git a/chrome/browser/ui/views/frame/multi_contents_view.h b/chrome/browser/ui/views/frame/multi_contents_view.h
index 8ad29b28..76a61915 100644
--- a/chrome/browser/ui/views/frame/multi_contents_view.h
+++ b/chrome/browser/ui/views/frame/multi_contents_view.h
@@ -18,6 +18,7 @@
 class BrowserView;
 class ContentsWebView;
 class MultiContentsResizeArea;
+class MultiContentsViewDropTargetController;
 
 namespace blink {
 class WebMouseEvent;
@@ -35,6 +36,10 @@
 class WebView;
 }  // namespace views
 
+// TODO(crbug.com/394369035): The drop target view will eventually have its
+// own class. Move this declaration into the class once ready.
+DECLARE_ELEMENT_IDENTIFIER_VALUE(kMultiContentsViewDropTargetElementId);
+
 // MultiContentsView shows up to two contents web views side by side, and
 // manages their layout relative to each other.
 class MultiContentsView : public views::View, public views::ResizeAreaDelegate {
@@ -105,6 +110,10 @@
   void OnPaint(gfx::Canvas* canvas) override;
   void OnThemeChanged() override;
 
+  MultiContentsViewDropTargetController& drop_target_controller() {
+    return *drop_target_controller_;
+  }
+
   void SetMinWidthForTesting(int width) {
     min_contents_width_for_testing_ = std::make_optional(width);
   }
@@ -175,6 +184,11 @@
   // each other.
   raw_ptr<MultiContentsResizeArea> resize_area_ = nullptr;
 
+  // Handles incoming drag events to show/hide the drop target for entering
+  // split view.
+  std::unique_ptr<MultiContentsViewDropTargetController>
+      drop_target_controller_;
+
   // The index in contents_views_ of the active contents view.
   int active_index_ = 0;
 
diff --git a/chrome/browser/ui/views/frame/multi_contents_view_drag_entrypoint_controller.cc b/chrome/browser/ui/views/frame/multi_contents_view_drag_entrypoint_controller.cc
deleted file mode 100644
index 800c929..0000000
--- a/chrome/browser/ui/views/frame/multi_contents_view_drag_entrypoint_controller.cc
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2025 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/ui/views/frame/multi_contents_view_drag_entrypoint_controller.h"
-
-#include "content/public/common/drop_data.h"
-#include "ui/views/view_class_properties.h"
-
-DEFINE_ELEMENT_IDENTIFIER_VALUE(kMultiContentsViewDropTargetElementId);
-
-MultiContentsViewDragEntrypointController::
-    MultiContentsViewDragEntrypointController(views::View& multi_contents_view)
-    : multi_contents_view_(multi_contents_view),
-      drop_target_view_(
-          *multi_contents_view.AddChildView(std::make_unique<views::View>())) {
-  drop_target_view_->SetProperty(views::kElementIdentifierKey,
-                                 kMultiContentsViewDropTargetElementId);
-  drop_target_view_->SetVisible(false);
-}
-
-void MultiContentsViewDragEntrypointController::OnWebContentsDragUpdate(
-    const content::DropData& data,
-    const gfx::PointF& point) {
-  CHECK_LE(point.x(), multi_contents_view_->width());
-
-  // TODO(crbug.com/394369035): Settle on an appropriate value for this.
-  constexpr int kDropEntryPointWidth = 100;
-
-  const bool should_show_drop_zone =
-      data.url.is_valid() &&
-      point.x() >= multi_contents_view_->width() - kDropEntryPointWidth;
-  // TODO(crbug.com/394369035): Add a timer to delay showing the drop zone.
-  drop_target_view_->SetVisible(should_show_drop_zone);
-}
diff --git a/chrome/browser/ui/views/frame/multi_contents_view_drag_entrypoint_controller.h b/chrome/browser/ui/views/frame/multi_contents_view_drag_entrypoint_controller.h
deleted file mode 100644
index e7c59be..0000000
--- a/chrome/browser/ui/views/frame/multi_contents_view_drag_entrypoint_controller.h
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2025 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_UI_VIEWS_FRAME_MULTI_CONTENTS_VIEW_DRAG_ENTRYPOINT_CONTROLLER_H_
-#define CHROME_BROWSER_UI_VIEWS_FRAME_MULTI_CONTENTS_VIEW_DRAG_ENTRYPOINT_CONTROLLER_H_
-
-#include "ui/base/interaction/element_identifier.h"
-#include "ui/views/view.h"
-
-namespace content {
-struct DropData;
-}  // namespace content
-
-// TODO(crbug.com/394369035): The drop target view will eventually have its
-// own class. Move this declaration into the class once ready.
-DECLARE_ELEMENT_IDENTIFIER_VALUE(kMultiContentsViewDropTargetElementId);
-
-// `MultiContentsViewDragEntrypointController` is responsible for handling
-// the drag-entrypoint of a single `MultiContentsView`. This includes dragging
-// links,  bookmarks, or tab headers to create a split view.
-// There exists one `MultiContentsViewDragEntrypointController` per
-// `MultiContentesView`.
-class MultiContentsViewDragEntrypointController final {
- public:
-  explicit MultiContentsViewDragEntrypointController(
-      views::View& multi_contents_view);
-  ~MultiContentsViewDragEntrypointController() = default;
-  MultiContentsViewDragEntrypointController(
-      const MultiContentsViewDragEntrypointController&) = delete;
-  MultiContentsViewDragEntrypointController& operator=(
-      const MultiContentsViewDragEntrypointController&) = delete;
-
-  // Handles a drag within the web contents area.
-  // `point` should be relative to the multi contents view.
-  void OnWebContentsDragUpdate(const content::DropData& data,
-                               const gfx::PointF& point);
-
- private:
-  const raw_ref<views::View> multi_contents_view_;
-
-  // The view that is displayed when drags hover over the "drop" region of
-  // the content area.
-  const raw_ref<views::View> drop_target_view_;
-};
-
-#endif  // CHROME_BROWSER_UI_VIEWS_FRAME_MULTI_CONTENTS_VIEW_DRAG_ENTRYPOINT_CONTROLLER_H_
diff --git a/chrome/browser/ui/views/frame/multi_contents_view_drop_target_controller.cc b/chrome/browser/ui/views/frame/multi_contents_view_drop_target_controller.cc
new file mode 100644
index 0000000..42cf722e
--- /dev/null
+++ b/chrome/browser/ui/views/frame/multi_contents_view_drop_target_controller.cc
@@ -0,0 +1,29 @@
+// Copyright 2025 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/ui/views/frame/multi_contents_view_drop_target_controller.h"
+
+#include "content/public/common/drop_data.h"
+#include "ui/views/view_class_properties.h"
+
+MultiContentsViewDropTargetController::MultiContentsViewDropTargetController(
+    views::View& drop_target_view)
+    : drop_target_view_(drop_target_view) {
+  CHECK_NE(nullptr, drop_target_view.parent());
+}
+
+void MultiContentsViewDropTargetController::OnWebContentsDragUpdate(
+    const content::DropData& data,
+    const gfx::PointF& point) {
+  CHECK_LE(point.x(), drop_target_view_->parent()->width());
+
+  // TODO(crbug.com/394369035): Settle on an appropriate value for this.
+  constexpr int kDropEntryPointWidth = 100;
+
+  const bool should_show_drop_zone =
+      data.url.is_valid() &&
+      point.x() >= drop_target_view_->parent()->width() - kDropEntryPointWidth;
+  // TODO(crbug.com/394369035): Add a timer to delay showing the drop zone.
+  drop_target_view_->SetVisible(should_show_drop_zone);
+}
diff --git a/chrome/browser/ui/views/frame/multi_contents_view_drop_target_controller.h b/chrome/browser/ui/views/frame/multi_contents_view_drop_target_controller.h
new file mode 100644
index 0000000..f2fb90f
--- /dev/null
+++ b/chrome/browser/ui/views/frame/multi_contents_view_drop_target_controller.h
@@ -0,0 +1,40 @@
+// Copyright 2025 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_UI_VIEWS_FRAME_MULTI_CONTENTS_VIEW_DROP_TARGET_CONTROLLER_H_
+#define CHROME_BROWSER_UI_VIEWS_FRAME_MULTI_CONTENTS_VIEW_DROP_TARGET_CONTROLLER_H_
+
+#include "ui/base/interaction/element_identifier.h"
+#include "ui/views/view.h"
+
+namespace content {
+struct DropData;
+}  // namespace content
+
+// `MultiContentsViewDropTargetController` is responsible for handling
+// the drag-entrypoint of a single `MultiContentsView`. This includes dragging
+// links,  bookmarks, or tab headers to create a split view.
+// There exists one `MultiContentsViewDropTargetController` per
+// `MultiContentesView`.
+class MultiContentsViewDropTargetController final {
+ public:
+  explicit MultiContentsViewDropTargetController(views::View& drop_target_view);
+  ~MultiContentsViewDropTargetController() = default;
+  MultiContentsViewDropTargetController(
+      const MultiContentsViewDropTargetController&) = delete;
+  MultiContentsViewDropTargetController& operator=(
+      const MultiContentsViewDropTargetController&) = delete;
+
+  // Handles a drag within the web contents area.
+  // `point` should be relative to the multi contents view.
+  void OnWebContentsDragUpdate(const content::DropData& data,
+                               const gfx::PointF& point);
+
+ private:
+  // The view that is displayed when drags hover over the "drop" region of
+  // the content area.
+  const raw_ref<views::View> drop_target_view_;
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_FRAME_MULTI_CONTENTS_VIEW_DROP_TARGET_CONTROLLER_H_
diff --git a/chrome/browser/ui/views/frame/multi_contents_view_drag_entrypoint_controller_unittest.cc b/chrome/browser/ui/views/frame/multi_contents_view_drop_target_controller_unittest.cc
similarity index 66%
rename from chrome/browser/ui/views/frame/multi_contents_view_drag_entrypoint_controller_unittest.cc
rename to chrome/browser/ui/views/frame/multi_contents_view_drop_target_controller_unittest.cc
index 8f1aa23..36202ba9 100644
--- a/chrome/browser/ui/views/frame/multi_contents_view_drag_entrypoint_controller_unittest.cc
+++ b/chrome/browser/ui/views/frame/multi_contents_view_drop_target_controller_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/views/frame/multi_contents_view_drag_entrypoint_controller.h"
+#include "chrome/browser/ui/views/frame/multi_contents_view_drop_target_controller.h"
 
 #include <memory>
 
@@ -17,28 +17,18 @@
 
 static constexpr gfx::PointF kDragPointForDropTargetShow(450, 450);
 
-views::View* FindChildWithElementId(views::View* parent,
-                                    ui::ElementIdentifier id) {
-  for (views::View* child : parent->children()) {
-    if (child->GetProperty(views::kElementIdentifierKey) == id) {
-      return child;
-    }
-  }
-  return nullptr;
-}
-
-class MultiContentsViewDragEntrypointControllerTest : public testing::Test {
+class MultiContentsViewDropTargetControllerTest : public testing::Test {
  public:
-  MultiContentsViewDragEntrypointControllerTest() = default;
-  ~MultiContentsViewDragEntrypointControllerTest() override = default;
+  MultiContentsViewDropTargetControllerTest() = default;
+  ~MultiContentsViewDropTargetControllerTest() override = default;
 
   void SetUp() override {
     multi_contents_view_ = std::make_unique<views::View>();
-    controller_ = std::make_unique<MultiContentsViewDragEntrypointController>(
-        *multi_contents_view_.get());
-
-    drop_target_view_ = FindChildWithElementId(
-        multi_contents_view_.get(), kMultiContentsViewDropTargetElementId);
+    drop_target_view_ =
+        multi_contents_view_->AddChildView(std::make_unique<views::View>());
+    drop_target_view_->SetVisible(false);
+    controller_ = std::make_unique<MultiContentsViewDropTargetController>(
+        *drop_target_view_);
 
     multi_contents_view_->SetSize(kMultiContentsViewSize);
   }
@@ -49,21 +39,19 @@
     multi_contents_view_.reset();
   }
 
-  MultiContentsViewDragEntrypointController& controller() {
-    return *controller_;
-  }
+  MultiContentsViewDropTargetController& controller() { return *controller_; }
 
   views::View& drop_target_view() { return *drop_target_view_; }
 
  private:
-  std::unique_ptr<MultiContentsViewDragEntrypointController> controller_;
+  std::unique_ptr<MultiContentsViewDropTargetController> controller_;
   std::unique_ptr<views::View> multi_contents_view_;
   raw_ptr<views::View> drop_target_view_;
 };
 
 // Tests that the drop target is shown when a drag reaches enters the "drop
 // area" and a valid url is being dragged.
-TEST_F(MultiContentsViewDragEntrypointControllerTest,
+TEST_F(MultiContentsViewDropTargetControllerTest,
        OnWebContentsDragUpdate_ShowDropTarget) {
   ASSERT_FALSE(drop_target_view().GetVisible());
 
@@ -76,7 +64,7 @@
 }
 
 // Tests that the drop target is hidden when an invalid url is being dragged.
-TEST_F(MultiContentsViewDragEntrypointControllerTest,
+TEST_F(MultiContentsViewDropTargetControllerTest,
        OnWebContentsDragUpdate_HideDropTargetOnInvalidURL) {
   drop_target_view().SetVisible(true);
   ASSERT_TRUE(drop_target_view().GetVisible());
@@ -88,7 +76,7 @@
 }
 
 // Tests that the drop target is hidden when a drag is not in the "drop area".
-TEST_F(MultiContentsViewDragEntrypointControllerTest,
+TEST_F(MultiContentsViewDropTargetControllerTest,
        OnWebContentsDragUpdate_HideDropTargetOnOutOfBounds) {
   drop_target_view().SetVisible(true);
   ASSERT_TRUE(drop_target_view().GetVisible());
diff --git a/chrome/browser/ui/views/frame/multi_contents_view_interactive_uitest.cc b/chrome/browser/ui/views/frame/multi_contents_view_interactive_uitest.cc
index 4b6aa7c..a84bef99 100644
--- a/chrome/browser/ui/views/frame/multi_contents_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/frame/multi_contents_view_interactive_uitest.cc
@@ -4,16 +4,23 @@
 
 #include "base/numerics/clamped_math.h"
 #include "base/test/scoped_feature_list.h"
+#include "chrome/browser/bookmarks/bookmark_model_factory.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser_element_identifiers.h"
 #include "chrome/browser/ui/browser_tabstrip.h"
 #include "chrome/browser/ui/tabs/test/split_tabs_interactive_test_mixin.h"
 #include "chrome/browser/ui/ui_features.h"
+#include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h"
+#include "chrome/browser/ui/views/bookmarks/bookmark_button.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/multi_contents_resize_area.h"
 #include "chrome/browser/ui/views/frame/multi_contents_view.h"
+#include "chrome/browser/ui/views/frame/multi_contents_view_drop_target_controller.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/test/base/interactive_test_utils.h"
 #include "chrome/test/interaction/interactive_browser_test.h"
+#include "components/bookmarks/browser/bookmark_model.h"
+#include "components/bookmarks/common/bookmark_pref_names.h"
 #include "components/tabs/public/split_tab_collection.h"
 #include "components/tabs/public/split_tab_visual_data.h"
 #include "content/public/test/browser_test.h"
@@ -409,3 +416,185 @@
       WaitForState(kMultiContentsViewSwapObserver, true), CheckTabIsActive(1),
       CheckActiveContentsHasFocus());
 }
+
+// TODO(crbug.com/414590951): There's limited support for testing drag and drop
+// on various platforms. These should be re-enabled as support is added.
+#if !BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_CHROMEOS)
+
+gfx::Point PointForDropTargetFromView(views::View* view) {
+  return view->GetBoundsInScreen().right_center() - gfx::Vector2d(10, 0);
+}
+
+class MultiContentsViewDragEntrypointsUiTest : public MultiContentsViewUiTest {
+ public:
+  using MultiContentsViewUiTest::MultiContentsViewUiTest;
+
+  void SetUp() override {
+    http_server_.ServeFilesFromSourceDirectory(GetChromeTestDataDir());
+    ASSERT_TRUE(http_server_.InitializeAndListen());
+    MultiContentsViewUiTest::SetUp();
+  }
+
+  void SetUpOnMainThread() override {
+    http_server_.StartAcceptingConnections();
+    InteractiveBrowserTest::SetUpOnMainThread();
+  }
+
+  GURL GetURL(std::string path) { return http_server_.GetURL(path); }
+
+  auto PointForDropTarget() const {
+    return base::BindLambdaForTesting(
+        [](views::View* view) { return PointForDropTargetFromView(view); });
+  }
+
+  // The standard DragMouseTo verb waits for the mouse to reach the
+  // destination. This version does not, since the mouse position sometimes
+  // doesn't get reported immediately (see `WaitForDropTargetVisible`).
+  auto DragMouseToWithoutWait(
+      ElementSpecifier target_view,
+      base::RepeatingCallback<gfx::Point(views::View*)> pos) {
+    return WithView(target_view, [pos = std::move(pos)](views::View* view) {
+      base::RunLoop press_loop(base::RunLoop::Type::kNestableTasksAllowed);
+      EXPECT_TRUE(ui_controls::SendMouseEventsNotifyWhenDone(
+          ui_controls::MouseButton::LEFT, ui_controls::MouseButtonState::DOWN,
+          press_loop.QuitClosure()));
+      press_loop.Run();
+      gfx::Point target_location = std::move(pos).Run(view);
+
+      EXPECT_TRUE(
+          ui_controls::SendMouseMove(target_location.x(), target_location.y()));
+    });
+  }
+
+  auto WaitForDropTargetVisible() {
+    // This method waits for the drop target to be visible, but also sends
+    // periodic mouse movement events while waiting. The mouse movements are
+    // needed to deflake this test on some Mac platforms: in the normal case,
+    // the initial mouse movement initiates a drag session, which later
+    // receives "drag updated" events from the OS. However, for some of the
+    // flakes, these updates are never sent by the OS. Manually generating
+    // the events seems to fix this.
+    // We really only need one event timed to execute after the drag session
+    // starts; an alternative approach would be to add observation to the
+    // Mac DnD client. Until then, periodic events does the trick.
+    //
+    // Note, both branches of AnyOf end with WaitForShow to ensure that the
+    // only way this step terminates successfully is if the view is shown.
+    return AnyOf(
+        RunSubsequence(WaitForShow(kMultiContentsViewDropTargetElementId)),
+        RunSubsequence(
+            Steps(
+                // Programmatically generate a list of mouse movement steps.
+                []() {
+                  constexpr int kMouseMovements = 20;
+                  constexpr base::TimeDelta kMovementDelay =
+                      base::Milliseconds(250);
+                  MultiStep mouse_moves;
+                  // Jitter applied to the mouse move destination to ensure it
+                  // changes between each step.
+                  int jitter = 3;
+                  for (int mouse_move_events = 0;
+                       mouse_move_events < kMouseMovements;
+                       ++mouse_move_events) {
+                    jitter *= -1;
+                    AddStep(mouse_moves, Do([kMovementDelay] {
+                              base::PlatformThread::Sleep(kMovementDelay);
+                            }));
+                    AddStep(
+                        mouse_moves,
+                        WithView(MultiContentsView::kMultiContentsViewElementId,
+                                 [jitter](views::View* view) {
+                                   gfx::Point target =
+                                       PointForDropTargetFromView(view);
+                                   EXPECT_TRUE(ui_controls::SendMouseMove(
+                                       target.x() + jitter, target.y()));
+                                 }));
+                  }
+                  return mouse_moves;
+                }()),
+            // This branch also waits for visibility to prevent it from exiting
+            // prematurely.
+            WaitForShow(kMultiContentsViewDropTargetElementId)));
+  }
+
+ private:
+  net::EmbeddedTestServer http_server_;
+};
+
+IN_PROC_BROWSER_TEST_F(MultiContentsViewDragEntrypointsUiTest,
+                       ShowsDropTargetOnLinkDragged) {
+  RunTestSequence(
+      AddInstrumentedTab(kNewTab, GetURL("/links.html"), 0),
+      CheckTabIsActive(0),
+      // Drag an href element to the drop target area. The drop
+      // target should be shown.
+      MoveMouseTo(kNewTab, DeepQuery{"#title1"}),
+      DragMouseToWithoutWait(MultiContentsView::kMultiContentsViewElementId,
+                             PointForDropTarget()),
+      WaitForDropTargetVisible());
+}
+
+IN_PROC_BROWSER_TEST_F(MultiContentsViewDragEntrypointsUiTest,
+                       DoesNotShowDropTargetOnNonURLDragged) {
+  RunTestSequence(
+      AddInstrumentedTab(kNewTab, GetURL("/button.html"), 0),
+      CheckTabIsActive(0),
+      // Dragging a non-url to the drop target area should have no
+      // effect.
+      MoveMouseTo(kNewTab, DeepQuery{"#button"}),
+      DragMouseToWithoutWait(MultiContentsView::kMultiContentsViewElementId,
+                             PointForDropTarget()),
+      WaitForHide(kMultiContentsViewDropTargetElementId));
+}
+
+class MultiContentsViewBookmarkDragEntrypointsUiTest
+    : public MultiContentsViewDragEntrypointsUiTest {
+ public:
+  using MultiContentsViewDragEntrypointsUiTest::
+      MultiContentsViewDragEntrypointsUiTest;
+
+  void SetUpOnMainThread() override {
+    MultiContentsViewDragEntrypointsUiTest::SetUpOnMainThread();
+    browser()->profile()->GetPrefs()->SetBoolean(
+        bookmarks::prefs::kShowBookmarkBar, true);
+  }
+
+  // Names the bookmark bar button for the given bookmark folder.
+  auto NameBookmarkButton(std::string assigned_name,
+                          std::u16string node_title) {
+    return NameViewRelative(
+        kBookmarkBarElementId, assigned_name,
+        base::BindLambdaForTesting([=](views::View* view) -> views::View* {
+          auto* const bookmark_bar = views::AsViewClass<BookmarkBarView>(view);
+          CHECK(bookmark_bar);
+          for (views::View* child : bookmark_bar->children()) {
+            auto* bookmark_button = views::AsViewClass<BookmarkButton>(child);
+            if (bookmark_button && bookmark_button->GetText() == node_title) {
+              return bookmark_button;
+            }
+          }
+          NOTREACHED() << "Bookmark button with title " << node_title
+                       << " not found.";
+        }));
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(MultiContentsViewBookmarkDragEntrypointsUiTest,
+                       ShowsDropTargetOnBookmarkedLinkDragged) {
+  bookmarks::BookmarkModel* const model =
+      BookmarkModelFactory::GetForBrowserContext(browser()->profile());
+  const std::u16string bookmark_title = u"Bookmark";
+  model->AddNewURL(model->bookmark_bar_node(), 0, u"Bookmark",
+                   GetURL("/links.html"));
+
+  const std::string kBookmarkButtonId = "bookmark_button";
+  RunTestSequence(
+      AddInstrumentedTab(kNewTab, GURL(chrome::kChromeUISettingsURL), 0),
+      CheckTabIsActive(0), WaitForShow(kBookmarkBarElementId),
+      NameBookmarkButton(kBookmarkButtonId, bookmark_title),
+      MoveMouseTo(kBookmarkButtonId),
+      DragMouseToWithoutWait(MultiContentsView::kMultiContentsViewElementId,
+                             PointForDropTarget()),
+      WaitForDropTargetVisible());
+}
+#endif  // !BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_CHROMEOS)
diff --git a/chrome/browser/ui/views/frame/scrim_view.cc b/chrome/browser/ui/views/frame/scrim_view.cc
index 63735dc..3ad6fc5 100644
--- a/chrome/browser/ui/views/frame/scrim_view.cc
+++ b/chrome/browser/ui/views/frame/scrim_view.cc
@@ -10,23 +10,22 @@
 #include "ui/compositor/layer_type.h"
 #include "ui/gfx/geometry/rounded_corners_f.h"
 #include "ui/views/accessibility/view_accessibility.h"
+#include "ui/views/background.h"
 
 ScrimView::ScrimView() {
   SetCanProcessEventsWithinSubtree(false);
   // This view must be painted to a layer so that it can be drawn on top of the
   // contents view. Otherwise, the scrim is drawn on the BrowserView's layer,
   // which always stays behind the content's layer.
-  SetPaintToLayer(ui::LAYER_SOLID_COLOR);
+  SetPaintToLayer();
+  layer()->SetFillsBoundsOpaquely(false);
   GetViewAccessibility().SetIsInvisible(true);
-  SetVisible(false);
-}
-
-void ScrimView::AddedToWidget() {
   // Maybe consider using a different color for the scrim?
   // kColorSysStateScrim is a semi-transparent black which has no effect on a
   // pure black background. In contrast, macOS sheet uses a semi-transparent
   // grey scrim which lightens a dark background.
-  layer()->SetColor(GetColorProvider()->GetColor(ui::kColorSysStateScrim));
+  SetBackground(views::CreateSolidBackground(ui::kColorSysStateScrim));
+  SetVisible(false);
 }
 
 void ScrimView::SetRoundedCorners(const gfx::RoundedCornersF& radii) {
diff --git a/chrome/browser/ui/views/frame/scrim_view.h b/chrome/browser/ui/views/frame/scrim_view.h
index 7a164fa5..3ddff36e 100644
--- a/chrome/browser/ui/views/frame/scrim_view.h
+++ b/chrome/browser/ui/views/frame/scrim_view.h
@@ -24,9 +24,6 @@
 
   ~ScrimView() override = default;
 
-  // views::View:
-  void AddedToWidget() override;
-
   void SetRoundedCorners(const gfx::RoundedCornersF& radii);
 };
 
diff --git a/chrome/browser/ui/views/global_media_controls/media_dialog_view.cc b/chrome/browser/ui/views/global_media_controls/media_dialog_view.cc
index 50f25677..72f39f8 100644
--- a/chrome/browser/ui/views/global_media_controls/media_dialog_view.cc
+++ b/chrome/browser/ui/views/global_media_controls/media_dialog_view.cc
@@ -445,10 +445,10 @@
 }
 
 MediaDialogView::~MediaDialogView() {
-  for (auto item_pair : observed_items_) {
+  for (auto& item_pair : observed_items_) {
     item_pair.second->RemoveObserver(this);
   }
-  for (auto item_pair : updated_items_) {
+  for (auto& item_pair : updated_items_) {
     item_pair.second->RemoveObserver(this);
   }
 }
diff --git a/chrome/browser/ui/views/global_media_controls/media_item_ui_device_selector_view.cc b/chrome/browser/ui/views/global_media_controls/media_item_ui_device_selector_view.cc
index 08f04f88..c02b1f3 100644
--- a/chrome/browser/ui/views/global_media_controls/media_item_ui_device_selector_view.cc
+++ b/chrome/browser/ui/views/global_media_controls/media_item_ui_device_selector_view.cc
@@ -238,7 +238,7 @@
   current_audio_device_entry_view_ = nullptr;
 
   bool current_device_still_exists = false;
-  for (auto description : device_descriptions) {
+  for (const auto& description : device_descriptions) {
     auto device_entry_view = std::make_unique<AudioDeviceEntryView>(
         base::BindRepeating(
             &MediaItemUIDeviceSelectorDelegate::OnAudioSinkChosen,
diff --git a/chrome/browser/ui/views/performance_controls/tab_list_view.cc b/chrome/browser/ui/views/performance_controls/tab_list_view.cc
index 11169ce1..1feeee6 100644
--- a/chrome/browser/ui/views/performance_controls/tab_list_view.cc
+++ b/chrome/browser/ui/views/performance_controls/tab_list_view.cc
@@ -26,8 +26,7 @@
       views::View::SetLayoutManager(std::make_unique<views::FlexLayout>());
   flex_layout->SetOrientation(views::LayoutOrientation::kVertical);
 
-  for (resource_attribution::PageContext context :
-       tab_list_model->page_contexts()) {
+  for (const auto& context : tab_list_model->page_contexts()) {
     AddChildView(std::make_unique<TabListRowView>(
         context, tab_list_model,
         base::BindOnce(&TabListView::RemoveRow, base::Unretained(this),
diff --git a/chrome/browser/ui/views/site_data/page_specific_site_data_dialog.cc b/chrome/browser/ui/views/site_data/page_specific_site_data_dialog.cc
index 2c31697..a56bbc2 100644
--- a/chrome/browser/ui/views/site_data/page_specific_site_data_dialog.cc
+++ b/chrome/browser/ui/views/site_data/page_specific_site_data_dialog.cc
@@ -229,7 +229,7 @@
     }
 
     std::vector<PageSpecificSiteDataDialogSite> sites;
-    for (auto site : sites_map) {
+    for (const auto& site : sites_map) {
       sites.push_back(site.second);
     }
 
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index 12a35b0..cca8929 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -1120,7 +1120,7 @@
     std::vector<std::pair<int, TabRendererData>> tabs_datas) {
   std::vector<TabContainer::TabInsertionParams> tabs_params;
 
-  for (auto tab_data : tabs_datas) {
+  for (const auto& tab_data : tabs_datas) {
     const int model_index = tab_data.first;
     CHECK(IsValidModelIndex(model_index))
         << "Attempted to add a tab with an invalid model index.";
diff --git a/chrome/browser/ui/views/tabs/tab_strip_action_container.cc b/chrome/browser/ui/views/tabs/tab_strip_action_container.cc
index c271096..26e150e5 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_action_container.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_action_container.cc
@@ -35,7 +35,7 @@
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/gfx/animation/tween.h"
 #include "ui/views/accessibility/view_accessibility.h"
-#include "ui/views/layout/box_layout.h"
+#include "ui/views/layout/flex_layout.h"
 #include "ui/views/mouse_watcher.h"
 #include "ui/views/mouse_watcher_view_host.h"
 #include "ui/views/view_class_properties.h"
@@ -283,7 +283,7 @@
 #if !BUILDFLAG(IS_MAC)
     std::unique_ptr<views::Separator> separator =
         std::make_unique<views::Separator>();
-    separator->SetBorderRadius(TabStyle::Get()->GetSeparatorMargins().right());
+    separator->SetBorderRadius(TabStyle::Get()->GetSeparatorCornerRadius());
     separator->SetPreferredSize(TabStyle::Get()->GetSeparatorSize());
 
     separator->SetColorId(kColorTabDividerFrameActive);
@@ -305,13 +305,11 @@
 #endif  // !BUILDFLAG(IS_MAC)
   }
 #endif  // BUILDFLAG(ENABLE_GLIC)
-
-  auto* const layout_manager =
-      SetLayoutManager(std::make_unique<views::BoxLayout>());
-  layout_manager->set_main_axis_alignment(
-      views::BoxLayout::MainAxisAlignment::kStart);
-  layout_manager->set_cross_axis_alignment(
-      views::BoxLayout::CrossAxisAlignment::kCenter);
+  SetLayoutManager(std::make_unique<views::FlexLayout>())
+      ->SetOrientation(views::LayoutOrientation::kHorizontal)
+      .SetMainAxisAlignment(views::LayoutAlignment::kStart)
+      .SetCrossAxisAlignment(views::LayoutAlignment::kCenter)
+      .SetCollapseMargins(false);
 }
 
 TabStripActionContainer::~TabStripActionContainer() {
diff --git a/chrome/browser/ui/views/tabs/tab_strip_combo_button.cc b/chrome/browser/ui/views/tabs/tab_strip_combo_button.cc
index 9358784..2f219a6 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_combo_button.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_combo_button.cc
@@ -38,9 +38,6 @@
 // LINT.ThenChange(//tools/metrics/histograms/metadata/tab/enums.xml:AccidentalClickType)
 
 constexpr int kButtonGapNoBackground = 14;
-constexpr int kSeparatorBorderRadius = 2;
-constexpr int kSeparatorWidth = 2;
-constexpr int kSeparatorHeight = 16;
 constexpr base::TimeDelta kAccidentalClickThreshold = base::Seconds(1);
 constexpr char kNewTabButtonAccidentalClickName[] =
     "Tabs.NewTabButton.AccidentalClicks";
@@ -105,8 +102,8 @@
 
   std::unique_ptr<views::Separator> separator =
       std::make_unique<views::Separator>();
-  separator->SetBorderRadius(kSeparatorBorderRadius);
-  separator->SetPreferredSize(gfx::Size(kSeparatorWidth, kSeparatorHeight));
+  separator->SetBorderRadius(TabStyle::Get()->GetSeparatorCornerRadius());
+  separator->SetPreferredSize(TabStyle::Get()->GetSeparatorSize());
   subscriptions_.push_back(browser->RegisterDidBecomeActive(base::BindRepeating(
       &TabStripComboButton::DidBecomeActive, base::Unretained(this))));
   subscriptions_.push_back(
diff --git a/chrome/browser/ui/views/translate/partial_translate_bubble_view.cc b/chrome/browser/ui/views/translate/partial_translate_bubble_view.cc
index ff6f17f..0c5d493a 100644
--- a/chrome/browser/ui/views/translate/partial_translate_bubble_view.cc
+++ b/chrome/browser/ui/views/translate/partial_translate_bubble_view.cc
@@ -551,7 +551,8 @@
                                views::MaximumFlexSizeRule::kPreferred));
   padding_view->SetProperty(
       views::kFlexBehaviorKey,
-      views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToZero,
+      views::FlexSpecification(views::LayoutOrientation::kHorizontal,
+                               views::MinimumFlexSizeRule::kScaleToZero,
                                views::MaximumFlexSizeRule::kUnbounded)
           .WithOrder(2));
   options_menu->SetProperty(views::kElementIdentifierKey, kOptionsMenuButton);
@@ -628,7 +629,8 @@
   error_message_label->SetProperty(views::kElementIdentifierKey, kErrorMessage);
   error_message_label->SetProperty(
       views::kFlexBehaviorKey,
-      views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToZero,
+      views::FlexSpecification(views::LayoutOrientation::kHorizontal,
+                               views::MinimumFlexSizeRule::kScaleToZero,
                                views::MaximumFlexSizeRule::kUnbounded));
 
   title_row->AddChildView(std::move(error_message_label));
@@ -840,8 +842,8 @@
                         horizontal_spacing * 4));
   language_title_label->SetProperty(views::kCrossAxisAlignmentKey,
                                     views::LayoutAlignment::kStart);
-  auto* title_row = form_view->AddChildView(std::make_unique<views::View>());
-  title_row->SetLayoutManager(std::make_unique<views::FlexLayout>());
+  auto* title_row =
+      form_view->AddChildView(std::make_unique<views::FlexLayoutView>());
   auto* title_label = title_row->AddChildView(std::move(language_title_label));
   auto* padding_view = title_row->AddChildView(std::make_unique<views::View>());
   title_row->AddChildView(CreateCloseButton());
@@ -854,7 +856,8 @@
   // when the bubble expands.
   padding_view->SetProperty(
       views::kFlexBehaviorKey,
-      views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToZero,
+      views::FlexSpecification(views::LayoutOrientation::kHorizontal,
+                               views::MinimumFlexSizeRule::kScaleToZero,
                                views::MaximumFlexSizeRule::kUnbounded)
           .WithOrder(2));
 
diff --git a/chrome/browser/ui/views/user_education/browser_user_education_service.cc b/chrome/browser/ui/views/user_education/browser_user_education_service.cc
index 8f458af..25f3f391 100644
--- a/chrome/browser/ui/views/user_education/browser_user_education_service.cc
+++ b/chrome/browser/ui/views/user_education/browser_user_education_service.cc
@@ -1753,6 +1753,12 @@
           141, "emshack@chromium.org",
           "Shown in the tab context menu when the user enters or exits split "
           "view.")));
+
+  registry.RegisterFeature(user_education::NewBadgeSpecification(
+      features::kSideBySideLinkMenuNewBadge,
+      user_education::Metadata(141, "emshack@chromium.org",
+                               "Shown in the link context menu to open the "
+                               "link in a new split tab.")));
 }
 
 std::unique_ptr<user_education::FeaturePromoControllerCommon>
diff --git a/chrome/browser/ui/views/web_apps/deprecated_apps_dialog_view.cc b/chrome/browser/ui/views/web_apps/deprecated_apps_dialog_view.cc
index bc98e81..71c0ccc 100644
--- a/chrome/browser/ui/views/web_apps/deprecated_apps_dialog_view.cc
+++ b/chrome/browser/ui/views/web_apps/deprecated_apps_dialog_view.cc
@@ -54,7 +54,7 @@
       content::WebContents* web_contents,
       base::RepeatingClosure on_icon_updated)
       : on_icon_updated_(on_icon_updated) {
-    for (extensions::ExtensionId app_id : deprecated_app_ids) {
+    for (const extensions::ExtensionId& app_id : deprecated_app_ids) {
       auto* browser_context = web_contents->GetBrowserContext();
       const extensions::Extension* extension =
           extensions::ExtensionRegistry::Get(browser_context)
@@ -255,7 +255,7 @@
 }
 
 void DeprecatedAppsDialogView::OnAccept() {
-  for (extensions::ExtensionId id : deprecated_app_ids_) {
+  for (const extensions::ExtensionId& id : deprecated_app_ids_) {
     extensions::ExtensionRegistrar::Get(web_contents_->GetBrowserContext())
         ->UninstallExtension(id, extensions::UNINSTALL_REASON_USER_INITIATED,
                              /*error=*/nullptr);
diff --git a/chrome/browser/ui/views/webauthn/authenticator_hybrid_and_security_key_sheet_view.cc b/chrome/browser/ui/views/webauthn/authenticator_hybrid_and_security_key_sheet_view.cc
index 4f459c4..f23d57b 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_hybrid_and_security_key_sheet_view.cc
+++ b/chrome/browser/ui/views/webauthn/authenticator_hybrid_and_security_key_sheet_view.cc
@@ -64,7 +64,7 @@
   header_view->SetMultiLine(true);
   header_view->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
 
-  for (auto description_text : descriptions) {
+  for (const auto& description_text : descriptions) {
     auto* description_view =
         description_column->AddChildView(std::make_unique<views::Label>(
             description_text, views::style::CONTEXT_DIALOG_BODY_TEXT));
diff --git a/chrome/browser/ui/webui/accessibility/accessibility_ui.cc b/chrome/browser/ui/webui/accessibility/accessibility_ui.cc
index ce38400..d7a9cff6 100644
--- a/chrome/browser/ui/webui/accessibility/accessibility_ui.cc
+++ b/chrome/browser/ui/webui/accessibility/accessibility_ui.cc
@@ -340,7 +340,7 @@
   std::string line = node_delegate->GetData().ToString();
   std::vector<std::string> attributes = base::SplitString(
       line, " ", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
-  for (std::string attribute : attributes) {
+  for (const std::string& attribute : attributes) {
     if (ui::AXTreeFormatter::MatchesPropertyFilters(property_filters, attribute,
                                                     false)) {
       str += attribute + " ";
@@ -988,7 +988,7 @@
     StopRecording(web_contents);
 
     std::string event_logs_str;
-    for (std::string log : event_logs_) {
+    for (const std::string& log : event_logs_) {
       event_logs_str += log;
       event_logs_str += "\n";
     }
diff --git a/chrome/browser/ui/webui/commerce/product_specifications_disclosure_dialog.cc b/chrome/browser/ui/webui/commerce/product_specifications_disclosure_dialog.cc
index 3bb3af72..cd43e2c 100644
--- a/chrome/browser/ui/webui/commerce/product_specifications_disclosure_dialog.cc
+++ b/chrome/browser/ui/webui/commerce/product_specifications_disclosure_dialog.cc
@@ -108,7 +108,7 @@
 base::Value::Dict DialogArgs::ToValue() {
   base::Value::Dict dialog_args_value;
   base::Value::List product_spec_urls;
-  for (auto url : urls) {
+  for (const auto& url : urls) {
     product_spec_urls.Append(url.spec());
   }
   dialog_args_value.Set(kDialogArgsName, std::move(name));
diff --git a/chrome/browser/ui/webui/omnibox/omnibox_page_handler.cc b/chrome/browser/ui/webui/omnibox/omnibox_page_handler.cc
index d1fb8f1..9a0097d 100644
--- a/chrome/browser/ui/webui/omnibox/omnibox_page_handler.cc
+++ b/chrome/browser/ui/webui/omnibox/omnibox_page_handler.cc
@@ -436,7 +436,7 @@
   BitmapFetcherService* bitmap_fetcher_service =
       BitmapFetcherServiceFactory::GetForBrowserContext(profile_);
 
-  for (std::string image_url : image_urls) {
+  for (const std::string& image_url : image_urls) {
     if (image_url.empty()) {
       continue;
     }
diff --git a/chrome/browser/ui/webui/searchbox/lens_searchbox_handler.cc b/chrome/browser/ui/webui/searchbox/lens_searchbox_handler.cc
index 4595d56..1835254 100644
--- a/chrome/browser/ui/webui/searchbox/lens_searchbox_handler.cc
+++ b/chrome/browser/ui/webui/searchbox/lens_searchbox_handler.cc
@@ -27,7 +27,7 @@
 
 namespace {
 // Interface that allows the Lens searchbox to interact with its embedder
-// (i.e., LensOverlayController).
+// (i.e., LensSearchboxController).
 class LensOmniboxClient : public OmniboxClient {
  public:
   LensOmniboxClient(Profile* profile,
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler.cc b/chrome/browser/ui/webui/settings/site_settings_handler.cc
index c29607f..689fb3f 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_handler.cc
@@ -460,9 +460,9 @@
 
   // site eTLD+1 : {owner site eTLD+1, # of sites in that related website set}
   std::map<std::string, std::pair<std::string, int>> rws_map;
-  for (auto rws : rws_owner_to_members) {
+  for (const auto& rws : rws_owner_to_members) {
     // Set rws owner and count of members for each eTLD+1
-    for (auto member : rws.second) {
+    for (const auto& member : rws.second) {
       rws_map[member] = {rws.first, rws.second.size()};
     }
   }
diff --git a/chrome/browser/vr/BUILD.gn b/chrome/browser/vr/BUILD.gn
index bea0012..5e2e667c 100644
--- a/chrome/browser/vr/BUILD.gn
+++ b/chrome/browser/vr/BUILD.gn
@@ -14,6 +14,12 @@
   import("//build/config/android/rules.gni")
 }
 
+if (translate_genders) {
+  _gender_suffix = "_OTHER"
+} else {
+  _gender_suffix = ""
+}
+
 assert(enable_vr)
 
 component("vr_ui") {
@@ -400,9 +406,9 @@
 
 repack("vr_test_pak") {
   sources = [
-    "$root_gen_dir/chrome/generated_resources_en-US.pak",
+    "$root_gen_dir/chrome/generated_resources_en-US${_gender_suffix}.pak",
     "$root_gen_dir/components/components_resources.pak",
-    "$root_gen_dir/components/strings/components_strings_en-US.pak",
+    "$root_gen_dir/components/strings/components_strings_en-US${_gender_suffix}.pak",
   ]
 
   output = "$root_out_dir/vr_test.pak"
diff --git a/chrome/browser/web_applications/extensions/extension_status_utils.cc b/chrome/browser/web_applications/extensions/extension_status_utils.cc
index 5b859199..b420b43 100644
--- a/chrome/browser/web_applications/extensions/extension_status_utils.cc
+++ b/chrome/browser/web_applications/extensions/extension_status_utils.cc
@@ -131,7 +131,7 @@
     std::vector<std::string> allowed_extension_ids =
         base::SplitString(kChromeAppAllowlist.Get(), ",", base::TRIM_WHITESPACE,
                           base::SPLIT_WANT_NONEMPTY);
-    for (std::string allowed_extension_id : allowed_extension_ids) {
+    for (const std::string& allowed_extension_id : allowed_extension_ids) {
       if (extension_id == allowed_extension_id)
         return false;
     }
diff --git a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_update_manager.cc b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_update_manager.cc
index ac57bb7..6db25ac 100644
--- a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_update_manager.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_update_manager.cc
@@ -310,7 +310,7 @@
     return;
   }
 
-  for (WebApp web_app : provider_->registrar_unsafe().GetApps()) {
+  for (const WebApp& web_app : provider_->registrar_unsafe().GetApps()) {
     if (!web_app.isolation_data().has_value() ||
         !web_app.isolation_data()->pending_update_info().has_value()) {
       continue;
diff --git a/chrome/browser/web_applications/isolated_web_apps/update_manifest/update_manifest_fetcher.cc b/chrome/browser/web_applications/isolated_web_apps/update_manifest/update_manifest_fetcher.cc
index ae0faee..8087c5e 100644
--- a/chrome/browser/web_applications/isolated_web_apps/update_manifest/update_manifest_fetcher.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/update_manifest/update_manifest_fetcher.cc
@@ -102,31 +102,13 @@
 
 void UpdateManifestFetcher::ParseUpdateManifest(
     const std::string& update_manifest_content) {
-  InitializeJsonParser();
+  base::JSONReader::Result result =
+      base::JSONReader::ReadAndReturnValueWithError(update_manifest_content,
+                                                    base::JSON_PARSE_RFC);
 
-  json_parser_->Parse(
-      update_manifest_content, base::JSON_PARSE_RFC,
-      base::BindOnce(&UpdateManifestFetcher::OnUpdateManifestParsed,
-                     base::Unretained(this)));
-}
-
-void UpdateManifestFetcher::InitializeJsonParser() {
-  CHECK(!json_parser_);
-  data_decoder_.GetService()->BindJsonParser(
-      json_parser_.BindNewPipeAndPassReceiver());
-  json_parser_.set_disconnect_handler(base::BindOnce(
-      &UpdateManifestFetcher::OnUpdateManifestParsed, base::Unretained(this),
-      std::nullopt, "JsonParser terminated unexpectedly"));
-}
-
-void UpdateManifestFetcher::OnUpdateManifestParsed(
-    std::optional<base::Value> result,
-    const std::optional<std::string>& error) {
   if (!result.has_value()) {
-    if (error.has_value()) {
-      LOG(ERROR) << "Unable to parse IWA Update Manifest JSON for URL " << url_
-                 << ". Error: was" << *error;
-    }
+    LOG(ERROR) << "Unable to parse IWA Update Manifest JSON for URL " << url_
+               << ". Error: was" << result.error().message;
     std::move(fetch_callback_).Run(base::unexpected(Error::kInvalidJson));
     return;
   }
diff --git a/chrome/browser/web_applications/isolated_web_apps/update_manifest/update_manifest_fetcher.h b/chrome/browser/web_applications/isolated_web_apps/update_manifest/update_manifest_fetcher.h
index 8e0fba52..2bc19cc 100644
--- a/chrome/browser/web_applications/isolated_web_apps/update_manifest/update_manifest_fetcher.h
+++ b/chrome/browser/web_applications/isolated_web_apps/update_manifest/update_manifest_fetcher.h
@@ -5,17 +5,11 @@
 #ifndef CHROME_BROWSER_WEB_APPLICATIONS_ISOLATED_WEB_APPS_UPDATE_MANIFEST_UPDATE_MANIFEST_FETCHER_H_
 #define CHROME_BROWSER_WEB_APPLICATIONS_ISOLATED_WEB_APPS_UPDATE_MANIFEST_UPDATE_MANIFEST_FETCHER_H_
 
-#include <optional>
-
 #include "base/functional/callback.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/types/expected.h"
-#include "base/values.h"
-#include "mojo/public/cpp/bindings/remote.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
-#include "services/data_decoder/public/cpp/data_decoder.h"
-#include "services/data_decoder/public/mojom/json_parser.mojom.h"
 #include "url/gurl.h"
 
 namespace network {
@@ -57,11 +51,6 @@
 
   void ParseUpdateManifest(const std::string& update_manifest_content);
 
-  void InitializeJsonParser();
-
-  void OnUpdateManifestParsed(std::optional<base::Value> result,
-                              const std::optional<std::string>& error);
-
   GURL url_;
   net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation_;
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
@@ -70,9 +59,6 @@
 
   std::unique_ptr<network::SimpleURLLoader> simple_url_loader_;
 
-  data_decoder::DataDecoder data_decoder_;
-  mojo::Remote<data_decoder::mojom::JsonParser> json_parser_;
-
   base::WeakPtrFactory<UpdateManifestFetcher> weak_factory_{this};
 };
 
diff --git a/chrome/browser/web_applications/isolated_web_apps/update_manifest/update_manifest_fetcher_unittest.cc b/chrome/browser/web_applications/isolated_web_apps/update_manifest/update_manifest_fetcher_unittest.cc
index ec054a2..ecec125 100644
--- a/chrome/browser/web_applications/isolated_web_apps/update_manifest/update_manifest_fetcher_unittest.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/update_manifest/update_manifest_fetcher_unittest.cc
@@ -16,7 +16,6 @@
 #include "net/http/http_status_code.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
-#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "services/network/public/mojom/url_response_head.mojom.h"
@@ -90,7 +89,6 @@
   }
 
   base::test::TaskEnvironment task_environment_;
-  data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
   network::TestURLLoaderFactory test_factory_;
   scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory_;
 };
diff --git a/chrome/browser/web_applications/user_uninstalled_preinstalled_web_app_prefs.cc b/chrome/browser/web_applications/user_uninstalled_preinstalled_web_app_prefs.cc
index eabd0f1..2962b83 100644
--- a/chrome/browser/web_applications/user_uninstalled_preinstalled_web_app_prefs.cc
+++ b/chrome/browser/web_applications/user_uninstalled_preinstalled_web_app_prefs.cc
@@ -41,9 +41,9 @@
 
   AppendExistingInstallUrlsPerAppId(app_id, install_urls);
 
-  for (auto install_url : install_urls)
+  for (const auto& install_url : install_urls) {
     url_list.Append(install_url.spec());
-
+  }
   if (!DoesAppIdExist(app_id)) {
     base::RecordAction(
         base::UserMetricsAction(kUserUninstalledPreinstalledAppAction));
@@ -191,7 +191,7 @@
     existing_urls.emplace(url.GetString());
   }
 
-  for (auto it : url_map) {
+  for (const auto& it : url_map) {
     if (only_default && !(it.first == WebAppManagement::kDefault))
       continue;
 
diff --git a/chrome/browser/web_applications/web_app.cc b/chrome/browser/web_applications/web_app.cc
index a2e7d21..869b4a1 100644
--- a/chrome/browser/web_applications/web_app.cc
+++ b/chrome/browser/web_applications/web_app.cc
@@ -1116,7 +1116,7 @@
            OptionalToStringValue(latest_install_source_));
 
   base::Value::Dict external_map;
-  for (auto it : management_to_external_config_map_) {
+  for (const auto& it : management_to_external_config_map_) {
     external_map.Set(base::ToString(it.first), it.second.AsDebugValue());
   }
 
diff --git a/chrome/browser/web_applications/web_app_icon_manager.cc b/chrome/browser/web_applications/web_app_icon_manager.cc
index d8373d6..31aea62 100644
--- a/chrome/browser/web_applications/web_app_icon_manager.cc
+++ b/chrome/browser/web_applications/web_app_icon_manager.cc
@@ -613,7 +613,7 @@
   TRACE_EVENT0("ui",
                "web_app_icon_manager::CheckForEmptyOrMissingIconFilesBlocking");
   WebAppIconManager::IconFilesCheck result;
-  for (auto it : purpose_to_sizes) {
+  for (const auto& it : purpose_to_sizes) {
     const IconPurpose& purpose = it.first;
     const SortedSizesPx& square_sizes = it.second;
     for (SquareSizePx size : square_sizes) {
diff --git a/chrome/browser/web_applications/web_app_registrar.cc b/chrome/browser/web_applications/web_app_registrar.cc
index 29bced9..a9c75ef 100644
--- a/chrome/browser/web_applications/web_app_registrar.cc
+++ b/chrome/browser/web_applications/web_app_registrar.cc
@@ -763,7 +763,7 @@
 std::optional<webapps::AppId> WebAppRegistrar::LookUpAppIdByInstallUrl(
     const GURL& install_url) const {
   for (const WebApp& web_app : GetApps()) {
-    for (auto it : web_app.management_to_external_config_map()) {
+    for (const auto& it : web_app.management_to_external_config_map()) {
       if (base::Contains(it.second.install_urls, install_url)) {
         return web_app.app_id();
       }
diff --git a/chrome/browser/webauthn/chrome_web_authentication_delegate.cc b/chrome/browser/webauthn/chrome_web_authentication_delegate.cc
index d8e7793..810a8f6 100644
--- a/chrome/browser/webauthn/chrome_web_authentication_delegate.cc
+++ b/chrome/browser/webauthn/chrome_web_authentication_delegate.cc
@@ -194,7 +194,7 @@
       PasskeyModelFactory::GetInstance()->GetForProfile(
           Profile::FromBrowserContext(web_contents->GetBrowserContext()));
   bool is_passkey_deleted = false;
-  for (auto passkey :
+  for (const auto& passkey :
        passkey_store->GetPasskeysForRelyingPartyId(relying_party_id)) {
     if (std::vector<uint8_t>(passkey.user_id().begin(),
                              passkey.user_id().end()) == user_id &&
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt
index e50eb21..4de393b2 100644
--- a/chrome/build/android-arm32.pgo.txt
+++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@
-chrome-android32-main-1746727053-c006a3a875b245a51c092aeb67b377805c30708e-8a7e3eb58b861bad419563c7eab04a42cd628814.profdata
+chrome-android32-main-1746748670-2ec1d34d52d69562137d213db1c31c74f5cd066f-1207e2173d79ef83a8e573890fa04e804afca8de.profdata
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt
index 9b87470..b05e3f8 100644
--- a/chrome/build/android-arm64.pgo.txt
+++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@
-chrome-android64-main-1746726018-ef891a08c418460a6c677b527a250b14a21e205a-25859c3ed77222679ee9535931365b4e8531cdeb.profdata
+chrome-android64-main-1746748670-e30c706e068b0e4acf605ef75468e713d1723c88-1207e2173d79ef83a8e573890fa04e804afca8de.profdata
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 6c7babc..ca0d68f 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1746705526-d3b858f55d3cb04e6c9c7e52b6464d531736ef8a-d85876276c358c68ff50521bcaa24e5d3998dc14.profdata
+chrome-linux-main-1746727053-8bffbdf05c0e2a814cf7e11b77bd671857ac0e9a-8a7e3eb58b861bad419563c7eab04a42cd628814.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 40d6783..5be0e9a 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1746727053-d8e673618cc0fda4407fdb86ef3cc8b00db58b8c-8a7e3eb58b861bad419563c7eab04a42cd628814.profdata
+chrome-mac-arm-main-1746748670-a98f730b785ee81b3fc972377e3fb791da3dda8f-1207e2173d79ef83a8e573890fa04e804afca8de.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 6d2f30d..95ce86e4 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1746705526-e440c23f778167c962a6909421e8516330996a14-d85876276c358c68ff50521bcaa24e5d3998dc14.profdata
+chrome-mac-main-1746727053-a3d66ff1fe809b42166d1a8ecea42b77ac02ce2a-8a7e3eb58b861bad419563c7eab04a42cd628814.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt
index e44e7275..96ec547 100644
--- a/chrome/build/win-arm64.pgo.txt
+++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@
-chrome-win-arm64-main-1746705526-85979f5b75f8352c207553c64967b8f1cfe8d570-d85876276c358c68ff50521bcaa24e5d3998dc14.profdata
+chrome-win-arm64-main-1746727053-9915f3cafb45e9b9e2f98fcd1d5170db100e5af0-8a7e3eb58b861bad419563c7eab04a42cd628814.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index cb3d110..0eaaef3 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1746705526-40334b70257e603470ff093884495a1ac7a52dbc-d85876276c358c68ff50521bcaa24e5d3998dc14.profdata
+chrome-win32-main-1746727053-8236c72987ba273680c6f01e32b6737857aea696-8a7e3eb58b861bad419563c7eab04a42cd628814.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index dd84077..17b73c02 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1746694515-0ede87d31ffde351912da9e2bd0803f2b55eb044-06dbc4719305742aa3f7c09eec1b2bc5f2612f5e.profdata
+chrome-win64-main-1746705526-0d35afe1bf28d770dc921d45bfb35fab421ede29-d85876276c358c68ff50521bcaa24e5d3998dc14.profdata
diff --git a/chrome/renderer/accessibility/phrase_segmentation/dependency_tree.cc b/chrome/renderer/accessibility/phrase_segmentation/dependency_tree.cc
index 77213255fa..5c0a69f 100644
--- a/chrome/renderer/accessibility/phrase_segmentation/dependency_tree.cc
+++ b/chrome/renderer/accessibility/phrase_segmentation/dependency_tree.cc
@@ -38,7 +38,7 @@
   bool updated = true;
   while (updated) {
     updated = false;
-    for (auto token : dep_head_array_) {
+    for (const auto& token : dep_head_array_) {
       // Expand parent boundary to include all child boundaries, and repeat
       // until all boundaries have been updated.
       if (token.dependency_head != token.absolute_index) {
diff --git a/chrome/renderer/accessibility/phrase_segmentation/token_boundaries.cc b/chrome/renderer/accessibility/phrase_segmentation/token_boundaries.cc
index 5a7aa40..e521376 100644
--- a/chrome/renderer/accessibility/phrase_segmentation/token_boundaries.cc
+++ b/chrome/renderer/accessibility/phrase_segmentation/token_boundaries.cc
@@ -47,7 +47,7 @@
 // tokens to become stranded.
 void TokenBoundaries::InitializeBoundaryWeightsFromTree(
     const DependencyTree& tree) {
-  for (auto token : tree.dep_head_array()) {
+  for (const auto& token : tree.dep_head_array()) {
     int weight = abs(token.dependency_head - token.absolute_index);
     if (weight == 0) {
       continue;
diff --git a/chrome/renderer/accessibility/read_anything/read_aloud_app_model.cc b/chrome/renderer/accessibility/read_anything/read_aloud_app_model.cc
index e0203e6..39606fcc 100644
--- a/chrome/renderer/accessibility/read_anything/read_aloud_app_model.cc
+++ b/chrome/renderer/accessibility/read_anything/read_aloud_app_model.cc
@@ -256,7 +256,7 @@
 
   // Reconstruct the tokenized sentence using the tokens.
   std::vector<std::u16string> u16string_tokens;
-  for (auto token : tokens) {
+  for (const auto& token : tokens) {
     u16string_tokens.emplace_back(base::UTF8ToUTF16(token));
   }
 
@@ -628,8 +628,7 @@
   if (base::Contains(current_granularity.segments, id)) {
     return true;
   }
-  for (a11y::ReadAloudCurrentGranularity granularity :
-       processed_granularities_on_current_page_) {
+  for (const auto& granularity : processed_granularities_on_current_page_) {
     if (base::Contains(granularity.segments, id)) {
       return true;
     }
diff --git a/chrome/renderer/accessibility/read_anything/read_anything_node_utils_unittest.cc b/chrome/renderer/accessibility/read_anything/read_anything_node_utils_unittest.cc
index 2595f7d..23138afd 100644
--- a/chrome/renderer/accessibility/read_anything/read_anything_node_utils_unittest.cc
+++ b/chrome/renderer/accessibility/read_anything/read_anything_node_utils_unittest.cc
@@ -112,6 +112,49 @@
   EXPECT_NE(text.find('\r'), std::string::npos);
 }
 
+TEST_F(ReadAnythingNodeUtilsTest, IsIgnored_ReturnsTrueWhenAXNodeIsIgnored) {
+  static constexpr ui::AXNodeID kId = 2;
+  ui::AXNodeData data = test::TextNode(kId);
+  data.role = ax::mojom::Role::kNone;
+  ui::AXTree tree;
+  ui::AXNode node(&tree, nullptr, kId, 0);
+  node.SetData(std::move(data));
+
+  EXPECT_TRUE(node.IsIgnored());
+  // The node should be ignored regardless of whether it is a PDF.
+  EXPECT_TRUE(a11y::IsIgnored(&node, /*is_pdf=*/false));
+  EXPECT_TRUE(a11y::IsIgnored(&node, /*is_pdf=*/true));
+}
+
+TEST_F(ReadAnythingNodeUtilsTest, IsIgnored_ControlElementsIgnored) {
+  const std::u16string sentence = u"One day more!";
+
+  static constexpr ui::AXNodeID kId = 2;
+  ui::AXNodeData control_not_text_field_data = test::TextNode(kId, sentence);
+  control_not_text_field_data.role = ax::mojom::Role::kSpinButton;
+
+  ui::AXNodeData text_field_data = test::TextNode(kId, sentence);
+  text_field_data.role = ax::mojom::Role::kTextField;
+
+  ui::AXNodeData select_data = test::TextNode(kId, sentence);
+  select_data.role = ax::mojom::Role::kRadioGroup;
+
+  ui::AXTree tree;
+  ui::AXNode node(&tree, nullptr, kId, 0);
+
+  // Control nodes are ignored.
+  node.SetData(std::move(control_not_text_field_data));
+  EXPECT_TRUE(a11y::IsIgnored(&node, false));
+
+  // Text field nodes are not ignored.
+  node.SetData(std::move(text_field_data));
+  EXPECT_FALSE(a11y::IsIgnored(&node, false));
+
+  // Select field nodes are ignored
+  node.SetData(std::move(select_data));
+  EXPECT_TRUE(a11y::IsIgnored(&node, false));
+}
+
 TEST_F(ReadAnythingNodeUtilsTest, IsSuperscript) {
   const std::u16string sentence =
       u"This is a superscript: <sup>superscript</sup>";
@@ -125,3 +168,104 @@
   node.SetData(std::move(data));
   EXPECT_TRUE(a11y::IsSuperscript(&node));
 }
+
+TEST_F(ReadAnythingNodeUtilsTest, GetHtmlTag_ReturnsDivForTextField) {
+  const std::u16string sentence1 =
+      u"Why do you write like it\'s going out of style?";
+  const std::u16string sentence2 =
+      u"Why do you write like you\'re running out of time?";
+
+  ui::AXNodeData data_with_html_tag = test::TextNode(2, sentence1);
+  data_with_html_tag.AddStringAttribute(ax::mojom::StringAttribute::kHtmlTag,
+                                        "p");
+  data_with_html_tag.role = ax::mojom::Role::kTextField;
+
+  ui::AXNodeData data_with_no_html_tag = test::TextNode(2, sentence2);
+  data_with_no_html_tag.AddStringAttribute(ax::mojom::StringAttribute::kHtmlTag,
+                                           "");
+  data_with_no_html_tag.role = ax::mojom::Role::kTextField;
+
+  ui::AXTree tree;
+  ui::AXNode node(&tree, nullptr, 2, 0);
+  node.SetData(std::move(data_with_html_tag));
+  EXPECT_EQ(a11y::GetHtmlTag(&node, false, false), "div");
+
+  node.SetData(std::move(data_with_no_html_tag));
+  EXPECT_EQ(a11y::GetHtmlTag(&node, false, false), "div");
+}
+
+TEST_F(ReadAnythingNodeUtilsTest, GetHtmlTag_ReturnsHeadingTag) {
+  const std::u16string sentence1 = u"Heading 1";
+  const std::u16string sentence2 = u"Heading 2";
+
+  ui::AXNodeData heading_data = test::TextNode(2, sentence1);
+  heading_data.AddIntAttribute(ax::mojom::IntAttribute::kHierarchicalLevel, 1);
+  heading_data.role = ax::mojom::Role::kHeading;
+
+  ui::AXTree tree;
+  ui::AXNode node(&tree, nullptr, 2, 0);
+  node.SetData(std::move(heading_data));
+  EXPECT_EQ(a11y::GetHtmlTag(&node, false, false), "h1");
+
+  heading_data.AddIntAttribute(ax::mojom::IntAttribute::kHierarchicalLevel, 2);
+  node.SetData(std::move(heading_data));
+  EXPECT_EQ(a11y::GetHtmlTag(&node, false, false), "h2");
+}
+
+TEST_F(ReadAnythingNodeUtilsTest, GetHtmlTag_MarkElementReturnsBold) {
+  const std::u16string sentence = u"Mark element";
+
+  ui::AXNodeData data = test::TextNode(2, sentence);
+  data.AddStringAttribute(ax::mojom::StringAttribute::kHtmlTag, "mark");
+
+  ui::AXTree tree;
+  ui::AXNode node(&tree, nullptr, 2, 0);
+  node.SetData(std::move(data));
+  EXPECT_EQ(a11y::GetHtmlTag(&node, false, false), "b");
+}
+
+TEST_F(ReadAnythingNodeUtilsTest, GetHtmlTag_ReturnsHtmlTagForDocs) {
+  const std::u16string sentence = u"Google docs document";
+
+  ui::AXNodeData data = test::TextNode(2, sentence);
+  data.AddStringAttribute(ax::mojom::StringAttribute::kHtmlTag, "svg");
+
+  ui::AXTree tree;
+  ui::AXNode node(&tree, nullptr, 2, 0);
+  node.SetData(std::move(data));
+
+  // SVG elements should be changed to div tags for Docs.
+  EXPECT_EQ(a11y::GetHtmlTag(&node, /* is_pdf= */ false, /* is_docs= */ true),
+            "div");
+
+  // Paragraphs with the g tag should be changed to the p tag for Docs.
+  data.AddStringAttribute(ax::mojom::StringAttribute::kHtmlTag, "g");
+  data.role = ax::mojom::Role::kParagraph;
+  node.SetData(std::move(data));
+  EXPECT_EQ(a11y::GetHtmlTag(&node, /* is_pdf= */ false, /* is_docs= */ true),
+            "p");
+}
+
+TEST_F(ReadAnythingNodeUtilsTest, GetHtmlTag_ReturnsExpectedTag) {
+  const std::u16string sentence = u"Tomorrow is another day";
+
+  ui::AXNodeData data = test::TextNode(2, sentence);
+  data.AddStringAttribute(ax::mojom::StringAttribute::kHtmlTag, "p");
+
+  ui::AXTree tree;
+  ui::AXNode node(&tree, nullptr, 2, 0);
+  node.SetData(std::move(data));
+  EXPECT_EQ(a11y::GetHtmlTag(&node, false, false), "p");
+
+  data.AddStringAttribute(ax::mojom::StringAttribute::kHtmlTag, "b");
+  node.SetData(std::move(data));
+  EXPECT_EQ(a11y::GetHtmlTag(&node, false, false), "b");
+
+  data.AddStringAttribute(ax::mojom::StringAttribute::kHtmlTag, "head");
+  node.SetData(std::move(data));
+  EXPECT_EQ(a11y::GetHtmlTag(&node, false, false), "head");
+
+  data.AddStringAttribute(ax::mojom::StringAttribute::kHtmlTag, "img");
+  node.SetData(std::move(data));
+  EXPECT_EQ(a11y::GetHtmlTag(&node, false, false), "img");
+}
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 09daa5a..ef8918c 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -10134,7 +10134,7 @@
       "../browser/ui/views/frame/browser_non_client_frame_view_unittest.cc",
       "../browser/ui/views/frame/browser_view_layout_unittest.cc",
       "../browser/ui/views/frame/browser_view_unittest.cc",
-      "../browser/ui/views/frame/multi_contents_view_drag_entrypoint_controller_unittest.cc",
+      "../browser/ui/views/frame/multi_contents_view_drop_target_controller_unittest.cc",
       "../browser/ui/views/frame/tab_strip_region_view_unittest.cc",
       "../browser/ui/views/frame/web_contents_close_handler_unittest.cc",
       "../browser/ui/views/global_media_controls/cast_device_footer_view_unittest.cc",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/MessageFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/MessageFacility.java
index e257900..5be4687 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/MessageFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/MessageFacility.java
@@ -12,14 +12,12 @@
 import android.os.Build;
 import android.view.View;
 
-import androidx.annotation.CallSuper;
 import androidx.test.espresso.action.GeneralLocation;
 import androidx.test.espresso.action.GeneralSwipeAction;
 import androidx.test.espresso.action.Press;
 import androidx.test.espresso.action.Swipe;
 import androidx.test.espresso.action.ViewActions;
 
-import org.chromium.base.test.transit.Elements;
 import org.chromium.base.test.transit.Facility;
 import org.chromium.base.test.transit.Station;
 import org.chromium.base.test.transit.Transition;
@@ -48,16 +46,12 @@
     public ViewElement<View> bannerElement;
     public ViewElement<View> iconElement;
 
-    @CallSuper
-    @Override
-    public void declareElements(Elements.Builder elements) {
+    public MessageFacility() {
         // Unscoped because other messages can appear and fail the exit condition.
         bannerElement =
-                elements.declareView(
-                        viewSpec(withId(R.id.message_banner)), ViewElement.unscopedOption());
+                declareView(viewSpec(withId(R.id.message_banner)), ViewElement.unscopedOption());
         iconElement =
-                elements.declareView(
-                        viewSpec(withId(R.id.message_icon)), ViewElement.unscopedOption());
+                declareView(viewSpec(withId(R.id.message_icon)), ViewElement.unscopedOption());
     }
 
     /** Dismiss the message banner. */
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/SnackbarFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/SnackbarFacility.java
index 567265c..df04b94 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/SnackbarFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/SnackbarFacility.java
@@ -15,7 +15,6 @@
 
 import androidx.annotation.Nullable;
 
-import org.chromium.base.test.transit.Elements;
 import org.chromium.base.test.transit.Facility;
 import org.chromium.base.test.transit.Station;
 import org.chromium.base.test.transit.ViewElement;
@@ -31,36 +30,27 @@
 public class SnackbarFacility<HostStationT extends Station<?>> extends Facility<HostStationT> {
     public static final String NO_BUTTON = "__NO_BUTTON__";
 
-    private final String mExpectedMessageSubstring;
-    private final String mExpectedButtonText;
     public ViewElement<View> messageElement;
     public ViewElement<View> buttonElement;
 
     public SnackbarFacility(
             @Nullable String expectedMessageSubstring, @Nullable String expectedButtonText) {
-        super();
-        mExpectedMessageSubstring = expectedMessageSubstring;
-        mExpectedButtonText = expectedButtonText;
-    }
-
-    @Override
-    public void declareElements(Elements.Builder elements) {
-        messageElement = elements.declareView(viewSpec(withId(R.id.snackbar_message)));
-        if (mExpectedMessageSubstring != null) {
-            elements.declareEnterCondition(
+        messageElement = declareView(viewSpec(withId(R.id.snackbar_message)));
+        if (expectedMessageSubstring != null) {
+            declareEnterCondition(
                     new ViewElementMatchesCondition(
-                            messageElement, withText(containsString(mExpectedMessageSubstring))));
+                            messageElement, withText(containsString(expectedMessageSubstring))));
         }
 
         ViewSpec<View> buttonSpec = viewSpec(withId(R.id.snackbar_button));
-        if (NO_BUTTON.equals(mExpectedButtonText)) {
-            elements.declareNoView(buttonSpec);
+        if (NO_BUTTON.equals(expectedButtonText)) {
+            declareNoView(buttonSpec);
         } else {
-            buttonElement = elements.declareView(buttonSpec);
-            if (mExpectedButtonText != null) {
-                elements.declareEnterCondition(
+            buttonElement = declareView(buttonSpec);
+            if (expectedButtonText != null) {
+                declareEnterCondition(
                         new ViewElementMatchesCondition(
-                                buttonElement, withText(mExpectedButtonText)));
+                                buttonElement, withText(expectedButtonText)));
             }
         }
     }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/SoftKeyboardFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/SoftKeyboardFacility.java
index 75aec37..81a6003 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/SoftKeyboardFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/SoftKeyboardFacility.java
@@ -6,7 +6,6 @@
 
 import androidx.test.espresso.Espresso;
 
-import org.chromium.base.test.transit.Elements;
 import org.chromium.base.test.transit.Facility;
 import org.chromium.base.test.transit.Station;
 import org.chromium.base.test.transit.Transition;
@@ -15,12 +14,12 @@
 
 /** Represents the soft keyboard shown, expecting it to hide after exiting the Facility. */
 public class SoftKeyboardFacility extends Facility<Station<?>> {
-    private SoftKeyboardElement mSoftKeyboardElement;
+    public SoftKeyboardElement softKeyboardElement;
 
     @Override
-    public void declareElements(Elements.Builder elements) {
-        mSoftKeyboardElement =
-                elements.declareElement(new SoftKeyboardElement(mHostStation.getActivityElement()));
+    public void declareExtraElements() {
+        softKeyboardElement =
+                declareElement(new SoftKeyboardElement(mHostStation.getActivityElement()));
     }
 
     /**
@@ -34,7 +33,7 @@
     public void close(ViewElement... viewElementsToSettle) {
         assertInPhase(Phase.ACTIVE);
 
-        if (mSoftKeyboardElement.get()) {
+        if (softKeyboardElement.get()) {
             // Keyboard was expected to be shown
 
             // If this fails, the keyboard was closed before, but not by this facility.
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/dom_distiller/ReaderModePreferencesDialog.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/dom_distiller/ReaderModePreferencesDialog.java
index a29fae2..b67f61eb 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/dom_distiller/ReaderModePreferencesDialog.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/dom_distiller/ReaderModePreferencesDialog.java
@@ -22,7 +22,6 @@
 
 import org.chromium.base.test.transit.CarryOn;
 import org.chromium.base.test.transit.Condition;
-import org.chromium.base.test.transit.Elements;
 import org.chromium.base.test.transit.ViewElement;
 import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.test.R;
@@ -40,8 +39,7 @@
     public ViewElement<Spinner> fontFamilySpinnerElement;
     public ViewElement<SeekBar> fontSizeSliderElement;
 
-    @Override
-    public void declareElements(Elements.Builder elements) {
+    public ReaderModePreferencesDialog() {
         /*
         DecorView
         ╰── LinearLayout
@@ -64,13 +62,11 @@
                                             ├── @id/font_size | AppCompatSeekBar
                                             ╰── "A" | MaterialTextView
         */
-        darkButtonElement = elements.declareView(viewSpec(Button.class, withText("Dark")));
-        sepiaButtonElement = elements.declareView(viewSpec(Button.class, withText("Sepia")));
-        lightButtonElement = elements.declareView(viewSpec(Button.class, withText("Light")));
-        fontFamilySpinnerElement =
-                elements.declareView(viewSpec(Spinner.class, withId(R.id.font_family)));
-        fontSizeSliderElement =
-                elements.declareView(viewSpec(SeekBar.class, withId(R.id.font_size)));
+        darkButtonElement = declareView(viewSpec(Button.class, withText("Dark")));
+        sepiaButtonElement = declareView(viewSpec(Button.class, withText("Sepia")));
+        lightButtonElement = declareView(viewSpec(Button.class, withText("Light")));
+        fontFamilySpinnerElement = declareView(viewSpec(Spinner.class, withId(R.id.font_family)));
+        fontSizeSliderElement = declareView(viewSpec(SeekBar.class, withId(R.id.font_size)));
     }
 
     public void pickColorLight(Condition condition) {
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/edge_to_edge/EdgeToEdgeBottomChinFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/edge_to_edge/EdgeToEdgeBottomChinFacility.java
index 508477e..899bc72 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/edge_to_edge/EdgeToEdgeBottomChinFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/edge_to_edge/EdgeToEdgeBottomChinFacility.java
@@ -7,7 +7,6 @@
 import org.chromium.base.test.transit.Condition;
 import org.chromium.base.test.transit.ConditionStatus;
 import org.chromium.base.test.transit.Element;
-import org.chromium.base.test.transit.Elements;
 import org.chromium.base.test.transit.Facility;
 import org.chromium.base.test.transit.Station;
 import org.chromium.base.test.transit.UiThreadCondition;
@@ -43,19 +42,19 @@
     }
 
     @Override
-    public void declareElements(Elements.Builder elements) {
+    public void declareExtraElements() {
         edgeToEdgeControllerElement =
-                elements.declareEnterConditionAsElement(
+                declareEnterConditionAsElement(
                         new EdgeToEdgeControllerCondition(mHostStation.getActivityElement()));
         bottomControlsStackerElement =
-                elements.declareEnterConditionAsElement(
+                declareEnterConditionAsElement(
                         new BottomControlsStackerCondition(mHostStation.getActivityElement()));
         bottomChinElement =
-                elements.declareEnterConditionAsElement(
+                declareEnterConditionAsElement(
                         new BottomChinCondition(bottomControlsStackerElement));
 
         if (mExpectPageOptIn != null) {
-            elements.declareEnterCondition(
+            declareEnterCondition(
                     UiThreadCondition.from(
                             "Expected page opt-in edge to edge: " + mExpectPageOptIn,
                             this::isPageOptInEdgeToEdge));
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/edge_to_edge/ViewportFitCoverPageStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/edge_to_edge/ViewportFitCoverPageStation.java
index 81a41b7..84e859b 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/edge_to_edge/ViewportFitCoverPageStation.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/edge_to_edge/ViewportFitCoverPageStation.java
@@ -8,7 +8,6 @@
 
 import org.chromium.base.supplier.Supplier;
 import org.chromium.base.test.transit.ConditionStatus;
-import org.chromium.base.test.transit.Elements;
 import org.chromium.base.test.transit.LogicalElement;
 import org.chromium.chrome.browser.browser_controls.BottomControlsStacker;
 import org.chromium.chrome.browser.browser_controls.BottomControlsStacker.LayerType;
@@ -53,24 +52,22 @@
     }
 
     @Override
-    public void declareElements(Elements.Builder elements) {
-        super.declareElements(elements);
+    public void declareExtraElements() {
+        super.declareExtraElements();
 
         // Ensure the web page elements are drawn and visible.
-        mAvoidBottomElement =
-                elements.declareElement(new HtmlElement(AVOID_BOTTOM_DIV, webContentsElement));
+        mAvoidBottomElement = declareElement(new HtmlElement(AVOID_BOTTOM_DIV, webContentsElement));
         mFullScreenButtonElement =
-                elements.declareElement(
-                        new HtmlElement(FULLSCREEN_MAIN_BUTTON, webContentsElement));
+                declareElement(new HtmlElement(FULLSCREEN_MAIN_BUTTON, webContentsElement));
 
         // Declare requiring EdgeToEdgeController, meaning #setDecorFitsSystemWindows(false)
-        elements.declareEnterCondition(new EdgeToEdgeControllerCondition(mActivityElement));
+        declareEnterCondition(new EdgeToEdgeControllerCondition(mActivityElement));
 
         // Ensure the bottom chin is on display.
         Supplier<BottomControlsStacker> bottomControlsStacker =
-                elements.declareEnterConditionAsElement(
+                declareEnterConditionAsElement(
                         new BottomControlsStackerCondition(mActivityElement));
-        elements.declareElement(
+        declareElement(
                 LogicalElement.uiThreadLogicalElement(
                         "Bottom chin is not on display",
                         this::isBottomChinShowing,
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/IncognitoTabSwitcherStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/IncognitoTabSwitcherStation.java
index cf7ce2c..acbff50 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/IncognitoTabSwitcherStation.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/IncognitoTabSwitcherStation.java
@@ -6,7 +6,6 @@
 
 import static androidx.test.espresso.matcher.ViewMatchers.isSelected;
 
-import org.chromium.base.test.transit.Elements;
 import org.chromium.base.test.transit.ViewElementMatchesCondition;
 import org.chromium.chrome.browser.hub.PaneId;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
@@ -34,10 +33,10 @@
     }
 
     @Override
-    public void declareElements(Elements.Builder elements) {
-        super.declareElements(elements);
+    public void declareExtraElements() {
+        super.declareExtraElements();
         assert incognitoTabsButtonElement != null;
-        elements.declareEnterCondition(
+        declareEnterCondition(
                 new ViewElementMatchesCondition(incognitoTabsButtonElement, isSelected()));
     }
 
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/NewTabGroupDialogFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/NewTabGroupDialogFacility.java
index 581375e..bd85084 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/NewTabGroupDialogFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/NewTabGroupDialogFacility.java
@@ -24,7 +24,6 @@
 
 import org.hamcrest.Matcher;
 
-import org.chromium.base.test.transit.Elements;
 import org.chromium.base.test.transit.Facility;
 import org.chromium.base.test.transit.Transition;
 import org.chromium.base.test.transit.ViewElement;
@@ -75,23 +74,23 @@
     }
 
     @Override
-    public void declareElements(Elements.Builder elements) {
+    public void declareExtraElements() {
         dialogElement =
-                elements.declareView(
+                declareView(
                         viewSpec(withId(R.id.visual_data_dialog_layout)),
                         ViewElement.displayingAtLeastOption(80));
-        elements.declareView(
+        declareView(
                 viewSpec(allOf(withId(R.id.visual_data_dialog_title), withText("New tab group"))));
 
         titleInputElement =
-                elements.declareView(
+                declareView(
                         viewSpec(
                                 withId(R.id.title_input_text),
                                 isAssignableFrom(EditText.class),
                                 withText(mTitle)));
 
         // TODO(crbug.com/346377124): Partially cut off in android_30_google_apis_x86.textpb
-        elements.declareView(
+        declareView(
                 viewSpec(withId(R.id.color_picker_container)),
                 ViewElement.displayingAtLeastOption(50));
         @TabGroupColorId List<Integer> colors = TabGroupColorUtils.getTabGroupColorIdList();
@@ -101,18 +100,18 @@
             @TabGroupColorId Integer color = colors.get(i);
             if (mSelectedColor != null) {
                 colorElements[i] =
-                        elements.declareView(
+                        declareView(
                                 colorPickerIconSpec(color, color.equals(mSelectedColor)),
                                 ViewElement.newOptions().unscoped().displayingAtLeast(60).build());
             } else {
                 colorElements[i] =
-                        elements.declareView(
+                        declareView(
                                 colorPickerIconSpec(color, /* selected= */ null),
                                 ViewElement.newOptions().unscoped().displayingAtLeast(60).build());
             }
         }
 
-        doneButtonElement = elements.declareView(viewSpec(withId(R.id.positive_button)));
+        doneButtonElement = declareView(viewSpec(withId(R.id.positive_button)));
     }
 
     private ViewSpec<View> colorPickerIconSpec(
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/RegularTabSwitcherStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/RegularTabSwitcherStation.java
index be111d97..16006ccd 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/RegularTabSwitcherStation.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/RegularTabSwitcherStation.java
@@ -9,7 +9,6 @@
 
 import static org.chromium.base.test.transit.ViewSpec.viewSpec;
 
-import org.chromium.base.test.transit.Elements;
 import org.chromium.base.test.transit.ViewElementMatchesCondition;
 import org.chromium.base.test.transit.ViewSpec;
 import org.chromium.chrome.browser.hub.PaneId;
@@ -41,13 +40,13 @@
     }
 
     @Override
-    public void declareElements(Elements.Builder elements) {
-        super.declareElements(elements);
+    public void declareExtraElements() {
+        super.declareExtraElements();
         assert regularTabsButtonElement != null;
-        elements.declareEnterCondition(
+        declareEnterCondition(
                 new ViewElementMatchesCondition(regularTabsButtonElement, isSelected()));
         if (!mRegularTabsExist) {
-            elements.declareView(EMPTY_STATE_TEXT);
+            declareView(EMPTY_STATE_TEXT);
         }
     }
 
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabGroupDialogFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabGroupDialogFacility.java
index 90e2b33d..e1da271 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabGroupDialogFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabGroupDialogFacility.java
@@ -18,7 +18,6 @@
 import androidx.annotation.Nullable;
 
 import org.chromium.base.ThreadUtils;
-import org.chromium.base.test.transit.Elements;
 import org.chromium.base.test.transit.Facility;
 import org.chromium.base.test.transit.Station;
 import org.chromium.base.test.transit.ViewElement;
@@ -102,7 +101,7 @@
     }
 
     @Override
-    public void declareElements(Elements.Builder elements) {
+    public void declareExtraElements() {
         if (ChromeFeatureList.isEnabled(ChromeFeatureList.DATA_SHARING)) {
             // TODO(ckitagawa): Add handling for an already shared group.
             if (isAllowedToShare()) {
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherCardFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherCardFacility.java
index 98491ec7..046152e 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherCardFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherCardFacility.java
@@ -21,7 +21,6 @@
 
 import org.hamcrest.Matcher;
 
-import org.chromium.base.test.transit.Elements;
 import org.chromium.base.test.transit.Facility;
 import org.chromium.base.test.transit.ViewElement;
 import org.chromium.base.test.transit.ViewSpec;
@@ -43,23 +42,23 @@
 
     @Override
     @CallSuper
-    public void declareElements(Elements.Builder elements) {
+    public void declareExtraElements() {
         Matcher<View> cardTitleMatcher =
                 allOf(withText(mTitle), withId(R.id.tab_title), withParent(withId(R.id.card_view)));
-        titleElement = elements.declareView(viewSpec(cardTitleMatcher));
+        titleElement = declareView(viewSpec(cardTitleMatcher));
 
         ViewSpec<View> cardSpec =
                 viewSpec(isAssignableFrom(TabGridView.class), hasDescendant(cardTitleMatcher));
-        cardViewElement = elements.declareView(cardSpec);
+        cardViewElement = declareView(cardSpec);
 
         if (mCardIndex != null) {
-            elements.declareEnterCondition(
+            declareEnterCondition(
                     new CardAtPositionCondition(
                             mCardIndex, mHostStation.recyclerViewElement, cardViewElement));
         }
     }
 
-    protected ViewElement<View> declareActionButton(Elements.Builder elements) {
-        return elements.declareView(cardViewElement.descendant(withId(R.id.action_button)));
+    protected ViewElement<View> declareActionButton() {
+        return declareView(cardViewElement.descendant(withId(R.id.action_button)));
     }
 }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherGroupCardFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherGroupCardFacility.java
index 9060f10..1a71b4c 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherGroupCardFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherGroupCardFacility.java
@@ -18,7 +18,6 @@
 
 import androidx.annotation.Nullable;
 
-import org.chromium.base.test.transit.Elements;
 import org.chromium.base.test.transit.Facility;
 import org.chromium.base.test.transit.ViewElement;
 import org.chromium.base.test.transit.ViewSpec;
@@ -68,11 +67,11 @@
     }
 
     @Override
-    public void declareElements(Elements.Builder elements) {
-        super.declareElements(elements);
-        menuButtonElement = declareActionButton(elements);
+    public void declareExtraElements() {
+        super.declareExtraElements();
+        menuButtonElement = declareActionButton();
 
-        elements.declareEnterCondition(
+        declareEnterCondition(
                 new TabGroupExistsCondition(
                         mHostStation.isIncognito(),
                         mTabIdsToGroup,
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherSearchStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherSearchStation.java
index af30dea..ac42047 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherSearchStation.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherSearchStation.java
@@ -21,7 +21,6 @@
 
 import org.hamcrest.Matcher;
 
-import org.chromium.base.test.transit.Elements;
 import org.chromium.base.test.transit.Station;
 import org.chromium.base.test.transit.ViewElement;
 import org.chromium.base.test.transit.ViewSpec;
@@ -45,12 +44,7 @@
     public TabSwitcherSearchStation(boolean isIncognito) {
         super(SearchActivity.class);
         mIsIncognito = isIncognito;
-    }
-
-    @Override
-    public void declareElements(Elements.Builder elements) {
-        super.declareElements(elements);
-        urlBarElement = elements.declareView(viewSpec(UrlBar.class, withId(R.id.url_bar)));
+        urlBarElement = declareView(viewSpec(UrlBar.class, withId(R.id.url_bar)));
     }
 
     public boolean isIncognito() {
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherTabCardFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherTabCardFacility.java
index 66038e3..02207dc 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherTabCardFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherTabCardFacility.java
@@ -8,7 +8,6 @@
 
 import androidx.annotation.Nullable;
 
-import org.chromium.base.test.transit.Elements;
 import org.chromium.base.test.transit.ViewElement;
 import org.chromium.chrome.test.transit.page.PageStation;
 
@@ -23,9 +22,9 @@
     }
 
     @Override
-    public void declareElements(Elements.Builder elements) {
-        super.declareElements(elements);
-        closeButtonElement = declareActionButton(elements);
+    public void declareExtraElements() {
+        super.declareExtraElements();
+        closeButtonElement = declareActionButton();
     }
 
     /** Clicks the tab card to show the page. */
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/IncognitoNewTabPageStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/IncognitoNewTabPageStation.java
index ffc0f59..10ffcf4e 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/IncognitoNewTabPageStation.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/IncognitoNewTabPageStation.java
@@ -14,7 +14,6 @@
 import android.view.View;
 
 import org.chromium.base.test.transit.Element;
-import org.chromium.base.test.transit.Elements;
 import org.chromium.base.test.transit.SimpleConditions;
 import org.chromium.base.test.transit.ViewElement;
 import org.chromium.chrome.R;
@@ -46,17 +45,16 @@
     }
 
     @Override
-    public void declareElements(Elements.Builder elements) {
-        super.declareElements(elements);
+    public void declareExtraElements() {
+        super.declareExtraElements();
 
-        urlBarElement = elements.declareView(URL_BAR);
-        iconElement = elements.declareView(viewSpec(withId(R.id.new_tab_incognito_icon)));
-        goneIncognitoTextElement =
-                elements.declareView(viewSpec(withText("You’ve gone Incognito")));
+        urlBarElement = declareView(URL_BAR);
+        iconElement = declareView(viewSpec(withId(R.id.new_tab_incognito_icon)));
+        goneIncognitoTextElement = declareView(viewSpec(withText("You’ve gone Incognito")));
         nativePageElement =
-                elements.declareEnterConditionAsElement(
+                declareEnterConditionAsElement(
                         new NativePageCondition<>(IncognitoNewTabPage.class, loadedTabElement));
-        elements.declareEnterCondition(
+        declareEnterCondition(
                 SimpleConditions.uiThreadCondition(
                         "Incognito NTP is loaded",
                         nativePageElement,
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/MvtsFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/MvtsFacility.java
index 6ad2b85d..9f8cccad 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/MvtsFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/MvtsFacility.java
@@ -13,7 +13,6 @@
 
 import android.view.View;
 
-import org.chromium.base.test.transit.Elements;
 import org.chromium.base.test.transit.MoreViewConditions.ViewHasChildrenCountCondition;
 import org.chromium.base.test.transit.ScrollableFacility;
 import org.chromium.base.test.transit.ViewElement;
@@ -50,16 +49,18 @@
     }
 
     @Override
-    public void declareElements(Elements.Builder elements) {
+    public void declareExtraElements() {
         // 1% visibility is enough because this layout is clipped by being inside scroll view in
         // tablets.
         tilesLayoutElement =
-                elements.declareView(
+                declareView(
                         viewSpec(withId(R.id.mv_tiles_layout)),
                         ViewElement.displayingAtLeastOption(1));
-        elements.declareEnterCondition(
+        declareEnterCondition(
                 new ViewHasChildrenCountCondition(tilesLayoutElement, mSiteSuggestions.size()));
-        super.declareElements(elements);
+
+        // Will call declareItems()
+        super.declareExtraElements();
     }
 
     @Override
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/RegularNewTabPageStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/RegularNewTabPageStation.java
index 7dc84bb..8d7ab94 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/RegularNewTabPageStation.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/RegularNewTabPageStation.java
@@ -13,7 +13,6 @@
 import android.view.View;
 
 import org.chromium.base.test.transit.Element;
-import org.chromium.base.test.transit.Elements;
 import org.chromium.base.test.transit.SimpleConditions;
 import org.chromium.base.test.transit.ViewElement;
 import org.chromium.chrome.R;
@@ -48,10 +47,10 @@
     }
 
     @Override
-    public void declareElements(Elements.Builder elements) {
-        super.declareElements(elements);
+    public void declareExtraElements() {
+        super.declareExtraElements();
 
-        elements.declareElementFactory(
+        declareElementFactory(
                 mActivityElement,
                 delayedElements -> {
                     if (mActivityElement.get().isTablet()) {
@@ -61,13 +60,13 @@
                     }
                 });
 
-        logoElement = elements.declareView(viewSpec(withId(R.id.search_provider_logo)));
-        searchBoxElement = elements.declareView(viewSpec(withId(R.id.search_box)));
+        logoElement = declareView(viewSpec(withId(R.id.search_provider_logo)));
+        searchBoxElement = declareView(viewSpec(withId(R.id.search_box)));
 
         nativePageElement =
-                elements.declareEnterConditionAsElement(
+                declareEnterConditionAsElement(
                         new NativePageCondition<>(NewTabPage.class, loadedTabElement));
-        elements.declareEnterCondition(
+        declareEnterCondition(
                 SimpleConditions.uiThreadCondition(
                         "Regular NTP is loaded",
                         nativePageElement,
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/omnibox/OmniboxEnteredTextFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/omnibox/OmniboxEnteredTextFacility.java
index 3eec4742..1749c87 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/omnibox/OmniboxEnteredTextFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/omnibox/OmniboxEnteredTextFacility.java
@@ -8,7 +8,6 @@
 
 import android.view.View;
 
-import org.chromium.base.test.transit.Elements;
 import org.chromium.base.test.transit.Facility;
 import org.chromium.base.test.transit.Station;
 import org.chromium.base.test.transit.ViewElement;
@@ -28,15 +27,11 @@
     public OmniboxEnteredTextFacility(OmniboxFacility omniboxFacility, String text) {
         mOmniboxFacility = omniboxFacility;
         mText = text;
-    }
 
-    @Override
-    public void declareElements(Elements.Builder elements) {
-        urlBarElement = elements.declareView(OmniboxFacility.URL_FIELD.and(withText(mText)));
+        urlBarElement = declareView(OmniboxFacility.URL_FIELD.and(withText(mText)));
         deleteButtonElement =
-                elements.declareView(
-                        OmniboxFacility.DELETE_BUTTON, ViewElement.displayingAtLeastOption(50));
-        elements.declareNoView(OmniboxFacility.MIC_BUTTON);
+                declareView(OmniboxFacility.DELETE_BUTTON, ViewElement.displayingAtLeastOption(50));
+        declareNoView(OmniboxFacility.MIC_BUTTON);
     }
 
     /** Enter text into the omnibox. */
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/omnibox/OmniboxFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/omnibox/OmniboxFacility.java
index d52506a..61c56d91 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/omnibox/OmniboxFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/omnibox/OmniboxFacility.java
@@ -14,7 +14,6 @@
 
 import androidx.test.espresso.Espresso;
 
-import org.chromium.base.test.transit.Elements;
 import org.chromium.base.test.transit.Facility;
 import org.chromium.base.test.transit.ViewElement;
 import org.chromium.base.test.transit.ViewSpec;
@@ -48,37 +47,36 @@
     }
 
     @Override
-    public void declareElements(Elements.Builder elements) {
-        elements.declareView(viewSpec(instanceOf(ScrimView.class)));
+    public void declareExtraElements() {
+        declareView(viewSpec(instanceOf(ScrimView.class)));
 
         // Unscoped elements exist in PageStations too.
         // Action buttons are 71% displayed in tablets (though the actual image is fully displayed).
         if (!mIncognito) {
             // Regular tab
-            statusIconElement = elements.declareView(STATUS_ICON, ViewElement.unscopedOption());
-            urlBarElement = elements.declareView(URL_FIELD, ViewElement.unscopedOption());
+            statusIconElement = declareView(STATUS_ICON, ViewElement.unscopedOption());
+            urlBarElement = declareView(URL_FIELD, ViewElement.unscopedOption());
             actionContainerElement =
-                    elements.declareView(
+                    declareView(
                             ACTION_CONTAINER,
                             ViewElement.newOptions().unscoped().displayingAtLeast(50).build());
-            micButtonElement =
-                    elements.declareView(MIC_BUTTON, ViewElement.displayingAtLeastOption(50));
-            elements.declareNoView(DELETE_BUTTON);
+            micButtonElement = declareView(MIC_BUTTON, ViewElement.displayingAtLeastOption(50));
+            declareNoView(DELETE_BUTTON);
         } else {
             if (mHostStation.getActivity().isTablet()) {
                 // Incognito tab in tablet
-                statusIconElement = elements.declareView(STATUS_ICON, ViewElement.unscopedOption());
-                urlBarElement = elements.declareView(URL_FIELD, ViewElement.unscopedOption());
-                elements.declareNoView(ACTION_CONTAINER);
-                elements.declareNoView(MIC_BUTTON);
-                elements.declareNoView(DELETE_BUTTON);
+                statusIconElement = declareView(STATUS_ICON, ViewElement.unscopedOption());
+                urlBarElement = declareView(URL_FIELD, ViewElement.unscopedOption());
+                declareNoView(ACTION_CONTAINER);
+                declareNoView(MIC_BUTTON);
+                declareNoView(DELETE_BUTTON);
             } else {
                 // Incognito tab in phone
-                elements.declareNoView(STATUS_ICON);
-                urlBarElement = elements.declareView(URL_FIELD, ViewElement.unscopedOption());
-                elements.declareNoView(ACTION_CONTAINER);
-                elements.declareNoView(MIC_BUTTON);
-                elements.declareNoView(DELETE_BUTTON);
+                declareNoView(STATUS_ICON);
+                urlBarElement = declareView(URL_FIELD, ViewElement.unscopedOption());
+                declareNoView(ACTION_CONTAINER);
+                declareNoView(MIC_BUTTON);
+                declareNoView(DELETE_BUTTON);
             }
         }
     }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/BasePageStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/BasePageStation.java
index 1835437..9ab1e5a 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/BasePageStation.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/BasePageStation.java
@@ -14,7 +14,6 @@
 import org.chromium.base.test.transit.ConditionStatusWithResult;
 import org.chromium.base.test.transit.ConditionWithResult;
 import org.chromium.base.test.transit.Element;
-import org.chromium.base.test.transit.Elements;
 import org.chromium.base.test.transit.Facility;
 import org.chromium.base.test.transit.Station;
 import org.chromium.base.test.transit.Transition;
@@ -198,26 +197,24 @@
     }
 
     @Override
-    public void declareElements(Elements.Builder elements) {
-        super.declareElements(elements);
+    public void declareExtraElements() {
+        super.declareExtraElements();
 
         if (mNumTabsBeingOpened > 0) {
-            elements.declareEnterCondition(
-                    new TabAddedCondition(mNumTabsBeingOpened, mActivityElement));
+            declareEnterCondition(new TabAddedCondition<>(mNumTabsBeingOpened, mActivityElement));
         }
 
         if (mIsEntryPoint) {
             // In entry points we just match the first ActivityTab we see, instead of waiting for
             // callbacks.
             activityTabElement =
-                    elements.declareEnterConditionAsElement(
-                            new AnyActivityTabCondition(mActivityElement));
+                    declareEnterConditionAsElement(new AnyActivityTabCondition<>(mActivityElement));
         } else {
             if (mNumTabsBeingSelected > 0) {
                 // The last tab of N opened is the Tab that mSelectedTabSupplier will supply.
-                TabSelectedCondition tabSelectedCondition =
-                        new TabSelectedCondition(mNumTabsBeingSelected, mActivityElement);
-                elements.declareEnterCondition(tabSelectedCondition);
+                TabSelectedCondition<HostActivity> tabSelectedCondition =
+                        new TabSelectedCondition<>(mNumTabsBeingSelected, mActivityElement);
+                declareEnterCondition(tabSelectedCondition);
                 mSelectedTabSupplier = tabSelectedCondition;
             } else {
                 // The Tab already created and provided to the constructor is the one that is
@@ -226,24 +223,23 @@
             }
             // Only returns the tab when it is the activityTab.
             activityTabElement =
-                    elements.declareEnterConditionAsElement(
-                            new CorrectActivityTabCondition(
+                    declareEnterConditionAsElement(
+                            new CorrectActivityTabCondition<>(
                                     mActivityElement, mSelectedTabSupplier));
         }
         loadedTabElement =
-                elements.declareEnterConditionAsElement(
+                declareEnterConditionAsElement(
                         new PageLoadedCondition(activityTabElement, mIncognito));
 
-        elements.declareEnterCondition(new PageInteractableOrHiddenCondition(loadedTabElement));
+        declareEnterCondition(new PageInteractableOrHiddenCondition(loadedTabElement));
 
         if (mExpectedUrlSubstring != null) {
-            elements.declareEnterCondition(
+            declareEnterCondition(
                     new PageUrlContainsCondition(mExpectedUrlSubstring, loadedTabElement));
         }
 
         if (mExpectedTitle != null) {
-            elements.declareEnterCondition(
-                    new PageTitleCondition(mExpectedTitle, loadedTabElement));
+            declareEnterCondition(new PageTitleCondition(mExpectedTitle, loadedTabElement));
         }
     }
 
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/CctPageStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/CctPageStation.java
index 145eef8..623940c60 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/CctPageStation.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/CctPageStation.java
@@ -5,7 +5,6 @@
 package org.chromium.chrome.test.transit.page;
 
 import org.chromium.base.test.transit.Element;
-import org.chromium.base.test.transit.Elements;
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
 import org.chromium.content_public.browser.WebContents;
 
@@ -44,14 +43,14 @@
     }
 
     @Override
-    public void declareElements(Elements.Builder elements) {
-        super.declareElements(elements);
+    public void declareExtraElements() {
+        super.declareExtraElements();
 
         if (!mIsNativePage) {
             webContentsElement =
-                    elements.declareEnterConditionAsElement(
+                    declareEnterConditionAsElement(
                             new WebContentsPresentCondition(loadedTabElement));
-            elements.declareEnterCondition(new FrameInfoUpdatedCondition(webContentsElement));
+            declareEnterCondition(new FrameInfoUpdatedCondition(webContentsElement));
         }
     }
 }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/PageAppMenuFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/PageAppMenuFacility.java
index 11a6bc51..2b733a8 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/PageAppMenuFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/PageAppMenuFacility.java
@@ -4,9 +4,6 @@
 
 package org.chromium.chrome.test.transit.page;
 
-import androidx.annotation.CallSuper;
-
-import org.chromium.base.test.transit.Elements;
 import org.chromium.base.test.transit.Station;
 import org.chromium.chrome.browser.tabbed_mode.TabbedAppMenuPropertiesDelegate;
 import org.chromium.chrome.test.transit.CtaAppMenuFacility;
@@ -39,15 +36,8 @@
     protected Item<SettingsStation> mSettings;
 
     @Override
-    @CallSuper
-    public void declareElements(Elements.Builder elements) {
-        super.declareElements(elements);
-
-        // TODO: Declare top buttons (forward, reload, bookmark, etc.).
-    }
-
-    @Override
     protected void declareItems(ItemsBuilder items) {
+        // TODO: Declare top buttons (forward, reload, bookmark, etc.).
         // TODO: Declare more common menu items
 
         mNewTab = declareMenuItemToStation(items, NEW_TAB_ID, this::createNewTabPageStation);
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/PageStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/PageStation.java
index 51d8407..08e41f3 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/PageStation.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/PageStation.java
@@ -16,7 +16,6 @@
 import androidx.test.platform.app.InstrumentationRegistry;
 
 import org.chromium.base.supplier.Supplier;
-import org.chromium.base.test.transit.Elements;
 import org.chromium.base.test.transit.Transition;
 import org.chromium.base.test.transit.Transition.Trigger;
 import org.chromium.base.test.transit.ViewElement;
@@ -78,10 +77,10 @@
     }
 
     @Override
-    public void declareElements(Elements.Builder elements) {
-        super.declareElements(elements);
+    public void declareExtraElements() {
+        super.declareExtraElements();
 
-        elements.declareEnterCondition(
+        declareEnterCondition(
                 new LayoutTypeVisibleCondition(mActivityElement, LayoutType.BROWSING));
 
         // TODO(crbug.com/41497463): These should be scoped, but for now they need to be unscoped
@@ -89,17 +88,17 @@
         // occluded by the tab switcher toolbar, but at least the tab_switcher_button is still
         // visible.
         toolbarElement =
-                elements.declareView(
+                declareView(
                         viewSpec(ToolbarControlContainer.class, withId(R.id.control_container)),
                         ViewElement.unscopedOption());
-        elements.declareView(
+        declareView(
                 viewSpec(HomeButton.class, withId(R.id.home_button)), ViewElement.unscopedOption());
         tabSwitcherButtonElement =
-                elements.declareView(
+                declareView(
                         viewSpec(ToggleTabStackButton.class, withId(R.id.tab_switcher_button)),
                         ViewElement.unscopedOption());
         menuButtonElement =
-                elements.declareView(
+                declareView(
                         viewSpec(ImageButton.class, withId(R.id.menu_button)),
                         ViewElement.unscopedOption());
     }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/PopupBlockedMessageFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/PopupBlockedMessageFacility.java
index 88e8782..e1c6990b 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/PopupBlockedMessageFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/PopupBlockedMessageFacility.java
@@ -6,7 +6,6 @@
 
 import android.view.View;
 
-import org.chromium.base.test.transit.Elements;
 import org.chromium.base.test.transit.ViewElement;
 import org.chromium.chrome.test.transit.MessageFacility;
 
@@ -17,26 +16,18 @@
  */
 public class PopupBlockedMessageFacility<HostStationT extends WebPageStation>
         extends MessageFacility<HostStationT> {
-    private final int mCount;
     public ViewElement<View> titleElement;
     public ViewElement<View> alwaysShowButtonElement;
 
     public PopupBlockedMessageFacility(int count) {
-        mCount = count;
-    }
-
-    @Override
-    public void declareElements(Elements.Builder elements) {
-        super.declareElements(elements);
-
         String title;
-        if (mCount == 1) {
+        if (count == 1) {
             title = "Pop-up blocked";
         } else {
-            title = String.format("%s pop-ups blocked", mCount);
+            title = String.format("%s pop-ups blocked", count);
         }
-        titleElement = elements.declareView(titleViewSpec(title));
-        alwaysShowButtonElement = elements.declareView(primaryButtonViewSpec("Always show"));
+        titleElement = declareView(titleViewSpec(title));
+        alwaysShowButtonElement = declareView(primaryButtonViewSpec("Always show"));
     }
 
     public WebPageStation clickAlwaysAllow() {
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/SwipingToTabFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/SwipingToTabFacility.java
index 95c9f6b2..a75f14e 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/SwipingToTabFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/SwipingToTabFacility.java
@@ -4,7 +4,6 @@
 
 package org.chromium.chrome.test.transit.page;
 
-import org.chromium.base.test.transit.Elements;
 import org.chromium.base.test.transit.Facility;
 import org.chromium.base.test.transit.Transition;
 import org.chromium.chrome.browser.layouts.LayoutType;
@@ -19,8 +18,8 @@
     }
 
     @Override
-    public void declareElements(Elements.Builder elements) {
-        elements.declareEnterCondition(
+    public void declareExtraElements() {
+        declareEnterCondition(
                 new LayoutTypeVisibleCondition(
                         mHostStation.getActivityElement(), LayoutType.TOOLBAR_SWIPE));
     }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/TabSwitcherActionMenuFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/TabSwitcherActionMenuFacility.java
index 6c5d624f..cbb15d25 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/TabSwitcherActionMenuFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/TabSwitcherActionMenuFacility.java
@@ -16,7 +16,6 @@
 import android.view.View;
 
 import org.chromium.base.test.transit.Condition;
-import org.chromium.base.test.transit.Elements;
 import org.chromium.base.test.transit.Facility;
 import org.chromium.base.test.transit.Station;
 import org.chromium.base.test.transit.Transition;
@@ -40,7 +39,7 @@
     public ViewElement<View> switchToIncognitoMenuItemElement;
 
     @Override
-    public void declareElements(Elements.Builder elements) {
+    public void declareExtraElements() {
         appMenuListElement = declareView(viewSpec(withId(R.id.app_menu_list)));
         closeTabMenuItemElement =
                 declareView(appMenuListElement.descendant(withText(R.string.close_tab)));
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/WebPageStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/WebPageStation.java
index fb44cc01..fb3e2c9 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/WebPageStation.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/WebPageStation.java
@@ -10,7 +10,6 @@
 import org.chromium.base.test.transit.Condition;
 import org.chromium.base.test.transit.ConditionStatus;
 import org.chromium.base.test.transit.Element;
-import org.chromium.base.test.transit.Elements;
 import org.chromium.base.test.transit.Transition;
 import org.chromium.base.test.transit.ViewElement;
 import org.chromium.chrome.browser.omnibox.UrlBar;
@@ -63,24 +62,22 @@
     }
 
     @Override
-    public void declareElements(Elements.Builder elements) {
-        super.declareElements(elements);
+    public void declareExtraElements() {
+        super.declareExtraElements();
 
         webContentsElement =
-                elements.declareEnterConditionAsElement(
-                        new WebContentsPresentCondition(loadedTabElement));
-        elements.declareEnterCondition(new FrameInfoUpdatedCondition(webContentsElement));
+                declareEnterConditionAsElement(new WebContentsPresentCondition(loadedTabElement));
+        declareEnterCondition(new FrameInfoUpdatedCondition(webContentsElement));
 
         if (!mIgnoreUrlBar) {
             // TODO(crbug.com/41497463): This should be shared, not unscoped, but the toolbar exists
             // in the tab switcher and it is not completely occluded.
-            urlBarElement = elements.declareView(URL_BAR, ViewElement.unscopedOption());
+            urlBarElement = declareView(URL_BAR, ViewElement.unscopedOption());
         }
 
         // Make sure that the new tab page is not considered a WebPageStation
         List<String> prohibitedUrls = List.of("chrome://newtab", "chrome-native://newtab");
-        elements.declareEnterCondition(
-                new PageUrlDoesNotMatchCondition(prohibitedUrls, loadedTabElement));
+        declareEnterCondition(new PageUrlDoesNotMatchCondition(prohibitedUrls, loadedTabElement));
     }
 
     /** Condition to check the page url does not match any of the prohibited urls. */
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/settings/PreferenceFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/settings/PreferenceFacility.java
index 106eb7cb..5336b99f 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/settings/PreferenceFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/settings/PreferenceFacility.java
@@ -8,23 +8,19 @@
 import static androidx.test.espresso.matcher.ViewMatchers.withParent;
 import static androidx.test.espresso.matcher.ViewMatchers.withText;
 
+import static org.hamcrest.Matchers.instanceOf;
+
 import static org.chromium.base.test.transit.ViewSpec.viewSpec;
 
 import android.view.View;
 
 import androidx.recyclerview.widget.RecyclerView;
 
-import org.hamcrest.Matchers;
-
-import org.chromium.base.test.transit.Elements;
 import org.chromium.base.test.transit.Facility;
 import org.chromium.base.test.transit.ViewElement;
-import org.chromium.base.test.transit.ViewSpec;
 
 /** The facility describing one setting preference item in the MainSettings fragment. */
 public class PreferenceFacility extends Facility<SettingsStation<?>> {
-    private final ViewSpec<View> mPrefViewSpec;
-
     public ViewElement<View> prefViewElement;
 
     /**
@@ -33,14 +29,10 @@
      * @param prefTitle The preference title. It's used to match the preference view on the screen.
      */
     public PreferenceFacility(String prefTitle) {
-        mPrefViewSpec =
-                viewSpec(
-                        hasDescendant(withText(prefTitle)),
-                        withParent(Matchers.instanceOf(RecyclerView.class)));
-    }
-
-    @Override
-    public void declareElements(Elements.Builder elements) {
-        prefViewElement = elements.declareView(mPrefViewSpec);
+        prefViewElement =
+                declareView(
+                        viewSpec(
+                                hasDescendant(withText(prefTitle)),
+                                withParent(instanceOf(RecyclerView.class))));
     }
 }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/settings/SettingsStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/settings/SettingsStation.java
index 420b7db9..009bdfc 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/settings/SettingsStation.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/settings/SettingsStation.java
@@ -4,7 +4,6 @@
 
 package org.chromium.chrome.test.transit.settings;
 
-import org.chromium.base.test.transit.Elements;
 import org.chromium.base.test.transit.FragmentElement;
 import org.chromium.base.test.transit.Station;
 import org.chromium.base.test.transit.Transition;
@@ -18,30 +17,22 @@
  */
 public class SettingsStation<FragmentT extends ChromeBaseSettingsFragment>
         extends Station<SettingsActivity> {
-    private final Class<FragmentT> mFragmentClass;
-    private FragmentElement<FragmentT, SettingsActivity> mFragmentElement;
+    public final FragmentElement<FragmentT, SettingsActivity> fragmentElement;
 
     public SettingsStation(Class<FragmentT> fragmentClass) {
         super(SettingsActivity.class);
-        mFragmentClass = fragmentClass;
-    }
-
-    @Override
-    public void declareElements(Elements.Builder elements) {
-        super.declareElements(elements);
-        mFragmentElement =
-                elements.declareElement(new FragmentElement<>(mFragmentClass, mActivityElement));
+        fragmentElement = declareElement(new FragmentElement<>(fragmentClass, mActivityElement));
     }
 
     public PreferenceFacility scrollToPref(String prefKey) {
         assertInPhase(Phase.ACTIVE);
-        String title = mFragmentElement.get().findPreference(prefKey).getTitle().toString();
+        String title = fragmentElement.get().findPreference(prefKey).getTitle().toString();
         return enterFacilitySync(
                 new PreferenceFacility(title),
                 Transition.newOptions()
                         .withPossiblyAlreadyFulfilled()
                         .withRunTriggerOnUiThread()
                         .build(),
-                () -> mFragmentElement.get().scrollToPreference(prefKey));
+                () -> fragmentElement.get().scrollToPreference(prefKey));
     }
 }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/tabmodel/TabGroupUiFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/tabmodel/TabGroupUiFacility.java
index b9ce49c4..f35315e8 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/tabmodel/TabGroupUiFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/tabmodel/TabGroupUiFacility.java
@@ -13,7 +13,6 @@
 import org.hamcrest.Matcher;
 
 import org.chromium.base.supplier.Supplier;
-import org.chromium.base.test.transit.Elements;
 import org.chromium.base.test.transit.Facility;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.tab_ui.R;
@@ -50,13 +49,13 @@
     }
 
     @Override
-    public void declareElements(Elements.Builder elements) {
+    public void declareExtraElements() {
         // Ensure the tab group UI is visible.
-        mTabGroupUiToolbarView = elements.declareView(viewSpec(BOTTOM_TAB_GROUP_LAYER));
+        mTabGroupUiToolbarView = declareView(viewSpec(BOTTOM_TAB_GROUP_LAYER));
 
         if (!mTabIds.isEmpty()) {
             // Ensure the number of tabs are in group.
-            elements.declareEnterCondition(
+            declareEnterCondition(
                     new TabGroupExistsCondition(
                             mHostStation.isIncognito(), mTabIds, mTabModelSelectorSupplier));
         }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/testhtmls/PopupOnClickPageStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/testhtmls/PopupOnClickPageStation.java
index 5ca68535..7267fc61 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/testhtmls/PopupOnClickPageStation.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/testhtmls/PopupOnClickPageStation.java
@@ -4,7 +4,6 @@
 
 package org.chromium.chrome.test.transit.testhtmls;
 
-import org.chromium.base.test.transit.Elements;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.chrome.test.transit.page.CctPageStation;
 import org.chromium.chrome.test.transit.page.PageStation;
@@ -20,11 +19,8 @@
 public class PopupOnClickPageStation extends WebPageStation {
     public static final String PATH = "/chrome/test/data/android/popup_on_click.html";
 
-    public static final HtmlElementSpec LINK_TO_POPUP = new HtmlElementSpec("link");
-    public static final HtmlElementSpec LINK_TO_POPUP_WITH_BOUNDS =
-            new HtmlElementSpec("link_with_bounds");
-    private HtmlElement mLinkToPopup;
-    private HtmlElement mLinkToPopupWithBounds;
+    public HtmlElement linkToPopup;
+    public HtmlElement linkToPopupWithBounds;
 
     protected <T extends PopupOnClickPageStation> PopupOnClickPageStation(Builder<T> builder) {
         super(builder);
@@ -40,13 +36,15 @@
     }
 
     @Override
-    public void declareElements(Elements.Builder elements) {
-        super.declareElements(elements);
+    public void declareExtraElements() {
+        super.declareExtraElements();
 
-        mLinkToPopup = elements.declareElement(new HtmlElement(LINK_TO_POPUP, webContentsElement));
-        mLinkToPopupWithBounds =
-                elements.declareElement(
-                        new HtmlElement(LINK_TO_POPUP_WITH_BOUNDS, webContentsElement));
+        linkToPopup =
+                declareElement(new HtmlElement(new HtmlElementSpec("link"), webContentsElement));
+        linkToPopupWithBounds =
+                declareElement(
+                        new HtmlElement(
+                                new HtmlElementSpec("link_with_bounds"), webContentsElement));
     }
 
     /** Opens the same page as a pop-up (in Android, this means in a new tab). */
@@ -57,7 +55,7 @@
                         .withIsOpeningTabs(1)
                         .withIsSelectingTabs(1)
                         .build();
-        return travelToSync(newPage, mLinkToPopup.getClickTrigger());
+        return travelToSync(newPage, linkToPopup.getClickTrigger());
     }
 
     /** Opens a sample page as a pop-up with bounds and expects a new window to open. */
@@ -68,7 +66,7 @@
                         .withExpectedUrlSubstring("simple.html")
                         .withExpectedTitle("Simple")
                         .build();
-        return spawnSync(newPage, mLinkToPopupWithBounds.getClickTrigger());
+        return spawnSync(newPage, linkToPopupWithBounds.getClickTrigger());
     }
 
     /**
@@ -78,6 +76,6 @@
     public PopupBlockedMessageFacility clickLinkAndExpectPopupBlockedMessage() {
         return enterFacilitySync(
                 new PopupBlockedMessageFacility<PopupOnClickPageStation>(1),
-                mLinkToPopup.getClickTrigger());
+                linkToPopup.getClickTrigger());
     }
 }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/testhtmls/TopBottomLinksPageStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/testhtmls/TopBottomLinksPageStation.java
index 4652b62..8ef3ff13 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/testhtmls/TopBottomLinksPageStation.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/testhtmls/TopBottomLinksPageStation.java
@@ -7,7 +7,6 @@
 import android.util.Pair;
 import android.view.View;
 
-import org.chromium.base.test.transit.Elements;
 import org.chromium.base.test.transit.Facility;
 import org.chromium.base.test.transit.Transition;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
@@ -69,19 +68,17 @@
 
     /** The page is scrolled to the top, and the top link is displayed. */
     public static class TopFacility extends Facility<TopBottomLinksPageStation> {
-        protected HtmlElement mTopElement;
+        public HtmlElement topElement;
 
         @Override
-        public void declareElements(Elements.Builder elements) {
-            mTopElement =
-                    elements.declareElement(
-                            new HtmlElement(TOP_LINK, mHostStation.webContentsElement));
+        public void declareExtraElements() {
+            topElement = declareElement(new HtmlElement(TOP_LINK, mHostStation.webContentsElement));
         }
 
         /** Open context menu on the top link. */
         public LinkContextMenuFacility openContextMenuOnTopLink() {
             return mHostStation.enterFacilitySync(
-                    new LinkContextMenuFacility(), mTopElement.getLongPressTrigger());
+                    new LinkContextMenuFacility(), topElement.getLongPressTrigger());
         }
 
         /** Scroll to the bottom of the page. */
@@ -93,21 +90,19 @@
 
     /** The page is scrolled to the bottom, and the bottom link is displayed. */
     public static class BottomFacility extends Facility<TopBottomLinksPageStation> {
-        protected HtmlElement mBottomElement;
+        public HtmlElement bottomElement;
 
         @Override
-        public void declareElements(Elements.Builder elements) {
-            mBottomElement =
-                    elements.declareElement(
-                            new HtmlElement(BOTTOM_LINK, mHostStation.webContentsElement));
-            elements.declareEnterCondition(
-                    new ScrollToBottomCondition(mHostStation.webContentsElement));
+        public void declareExtraElements() {
+            bottomElement =
+                    declareElement(new HtmlElement(BOTTOM_LINK, mHostStation.webContentsElement));
+            declareEnterCondition(new ScrollToBottomCondition(mHostStation.webContentsElement));
         }
 
         /** Open context menu on the bottom link. */
         public LinkContextMenuFacility openContextMenuOnBottomLink() {
             return mHostStation.enterFacilitySync(
-                    new LinkContextMenuFacility(), mBottomElement.getLongPressTrigger());
+                    new LinkContextMenuFacility(), bottomElement.getLongPressTrigger());
         }
     }
 }
diff --git a/chrome/test/base/test_browser_window.h b/chrome/test/base/test_browser_window.h
index b21bb15..da4c309 100644
--- a/chrome/test/base/test_browser_window.h
+++ b/chrome/test/base/test_browser_window.h
@@ -144,6 +144,8 @@
   void FocusWebContentsPane() override {}
   void ShowAppMenu() override {}
   bool PreHandleMouseEvent(const blink::WebMouseEvent& event) override;
+  void PreHandleDragUpdate(const content::DropData& drop_data,
+                           const gfx::PointF& point) override {}
   content::KeyboardEventProcessingResult PreHandleKeyboardEvent(
       const input::NativeWebKeyboardEvent& event) override;
   bool HandleKeyboardEvent(const input::NativeWebKeyboardEvent& event) override;
diff --git a/chrome/test/data/pdf/ink2_viewer_toolbar_test.ts b/chrome/test/data/pdf/ink2_viewer_toolbar_test.ts
index 6cf24f3..0a702cf 100644
--- a/chrome/test/data/pdf/ink2_viewer_toolbar_test.ts
+++ b/chrome/test/data/pdf/ink2_viewer_toolbar_test.ts
@@ -8,7 +8,7 @@
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {isMac} from 'chrome://resources/js/platform.js';
 import {keyDownOn} from 'chrome://webui-test/keyboard_mock_interactions.js';
-import {eventToPromise, microtasksFinished} from 'chrome://webui-test/test_util.js';
+import {eventToPromise, isVisible, microtasksFinished} from 'chrome://webui-test/test_util.js';
 
 import {assertCheckboxMenuButton, createTextBox, enterFullscreenWithUserGesture, finishInkStroke, getRequiredElement, openToolbarMenu, setupMockMetricsPrivate, setupTestMockPluginForInk} from './test_util.js';
 
@@ -422,13 +422,16 @@
     // Create a textbox. The undo button should now be disabled.
     const textBox = viewer.shadowRoot.querySelector('ink-text-box');
     assert(textBox);
+    chrome.test.assertFalse(isVisible(textBox));
     await createTextBoxAndWaitForStateChange(textBox);
+    chrome.test.assertTrue(isVisible(textBox));
     chrome.test.assertTrue(undoButton.disabled);
     chrome.test.assertTrue(redoButton.disabled);
 
     // Simulate closing the textbox with no changes. Now the undo button is
     // enabled again.
     await commitAnnotationAndWaitForStateChange(textBox);
+    chrome.test.assertFalse(isVisible(textBox));
     chrome.test.assertFalse(undoButton.disabled);
     chrome.test.assertTrue(redoButton.disabled);
 
@@ -445,6 +448,7 @@
     // Add a textbox. The redo button is disabled.
     mockPlugin.clearMessages();
     await createTextBoxAndWaitForStateChange(textBox);
+    chrome.test.assertTrue(isVisible(textBox));
     chrome.test.assertTrue(undoButton.disabled);
     chrome.test.assertTrue(redoButton.disabled);
 
@@ -459,6 +463,7 @@
     await whenStateChanged;
     await microtasksFinished();
     await commitAnnotationAndWaitForStateChange(textBox);
+    chrome.test.assertFalse(isVisible(textBox));
     chrome.test.assertFalse(undoButton.disabled);
     chrome.test.assertTrue(redoButton.disabled);
 
diff --git a/chrome/test/data/pdf/test_util.ts b/chrome/test/data/pdf/test_util.ts
index 2cb8643f..0556170 100644
--- a/chrome/test/data/pdf/test_util.ts
+++ b/chrome/test/data/pdf/test_util.ts
@@ -4,11 +4,12 @@
 
 // Utilities that are used in multiple tests.
 
+// clang-format off
 import type {Bookmark, DocumentDimensions, LayoutOptions, PdfViewerElement, ViewerToolbarElement} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
 import {resetForTesting as resetMetricsForTesting, UserAction, Viewport} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
 // <if expr="enable_pdf_ink2">
 import type {AnnotationBrush, BeforeUnloadProxy, InkBrushSelectorElement, InkColorSelectorElement, InkSizeSelectorElement, SelectableIconButtonElement, ViewerBottomToolbarDropdownElement} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
-import {AnnotationBrushType, BeforeUnloadProxyImpl, Ink2Manager, PluginController, PluginControllerEventType, SaveRequestType} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
+import {AnnotationBrushType, BeforeUnloadProxyImpl, DEFAULT_TEXTBOX_WIDTH, DEFAULT_TEXTBOX_HEIGHT, hexToColor, Ink2Manager, TEXT_COLORS, TextAlignment, TextStyle, PluginController, PluginControllerEventType, SaveRequestType} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
 // </if>
 import {assert} from 'chrome://resources/js/assert.js';
 import {CrLitElement, html} from 'chrome://resources/lit/v3_0/lit.rollup.js';
@@ -16,6 +17,7 @@
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 // </if>
 import {eventToPromise, isVisible, microtasksFinished} from 'chrome://webui-test/test_util.js';
+// clang-format on
 
 export class MockElement {
   dir: string = '';
@@ -713,10 +715,38 @@
   chrome.test.assertTrue(chrome.test.checkDeepEq(value1, value2));
 }
 
-// Simulates initializing a textbox with a click.
+// Simulates initializing a textbox. To make this usable from tests that do
+// not use a mock viewport, directly dispatch the event from the
+// Ink2Manager. Otherwise, the real viewport and page layout can vary, and
+// a textbox may not actually be created if a click event is simulated in a part
+// of the viewport that doesn't contain a page.
 export function createTextBox() {
-  PluginController.getInstance().getEventTarget().dispatchEvent(new CustomEvent(
-      PluginControllerEventType.PLUGIN_MESSAGE,
-      {detail: {type: 'sendClickEvent', x: 50, y: 50}}));
+  Ink2Manager.getInstance().dispatchEvent(
+      new CustomEvent('initialize-text-box', {
+        detail: {
+          annotation: {
+            text: '',
+            textAttributes: {
+              size: 12,
+              typeface: 'sans-serif',
+              styles: {
+                [TextStyle.BOLD]: false,
+                [TextStyle.ITALIC]: false,
+              },
+              alignment: TextAlignment.LEFT,
+              color: hexToColor(TEXT_COLORS[0]!.color),
+            },
+            textBoxRect: {
+              height: DEFAULT_TEXTBOX_HEIGHT,
+              locationX: 50,
+              locationY: 50,
+              width: DEFAULT_TEXTBOX_WIDTH,
+            },
+            id: 0,
+            pageNumber: 0,
+          },
+          pageCoordinates: {x: 10, y: 3},
+        },
+      }));
 }
 // </if>
diff --git a/chrome/test/data/web_apps/navigation_capture_expectations_AppAStandaloneAppBBrowser_CaptureOff.json b/chrome/test/data/web_apps/navigation_capture_expectations_AppAStandaloneAppBBrowser_CaptureOff.json
index 023d9aa5..ca50b3c8 100644
--- a/chrome/test/data/web_apps/navigation_capture_expectations_AppAStandaloneAppBBrowser_CaptureOff.json
+++ b/chrome/test/data/web_apps/navigation_capture_expectations_AppAStandaloneAppBBrowser_CaptureOff.json
@@ -322,7 +322,7 @@
       },
       "AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOff_AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -400,7 +400,7 @@
       },
       "AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOff_AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -662,7 +662,7 @@
       },
       "AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOff_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -740,7 +740,7 @@
       },
       "AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOff_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1538,7 +1538,7 @@
       },
       "FocusExisting_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_AppAStandaloneAppBBrowser_CaptureOff_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1607,7 +1607,7 @@
       },
       "FocusExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_AppAStandaloneAppBBrowser_CaptureOff_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1677,7 +1677,7 @@
       },
       "NavigateExisting_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_AppAStandaloneAppBBrowser_CaptureOff_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1746,7 +1746,7 @@
       },
       "NavigateExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_AppAStandaloneAppBBrowser_CaptureOff_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2028,7 +2028,7 @@
       },
       "Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOff_Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2089,7 +2089,7 @@
       },
       "Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOff_Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2297,7 +2297,7 @@
       },
       "Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOff_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2358,7 +2358,7 @@
       },
       "Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOff_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
diff --git a/chrome/test/data/web_apps/navigation_capture_expectations_AppAStandaloneAppBBrowser_CaptureOn.json b/chrome/test/data/web_apps/navigation_capture_expectations_AppAStandaloneAppBBrowser_CaptureOn.json
index 61a905b..aeeddbd 100644
--- a/chrome/test/data/web_apps/navigation_capture_expectations_AppAStandaloneAppBBrowser_CaptureOn.json
+++ b/chrome/test/data/web_apps/navigation_capture_expectations_AppAStandaloneAppBBrowser_CaptureOn.json
@@ -345,7 +345,7 @@
       },
       "AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOn_AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -424,7 +424,7 @@
       },
       "AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOn_AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -688,7 +688,7 @@
       },
       "AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOn_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -767,7 +767,7 @@
       },
       "AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOn_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1575,7 +1575,7 @@
       },
       "FocusExisting_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_AppAStandaloneAppBBrowser_CaptureOn_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1650,7 +1650,7 @@
       },
       "FocusExisting_Tab_ScopeA2A_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_AppAStandaloneAppBBrowser_CaptureOn_Tab_ScopeA2A_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1712,7 +1712,7 @@
       },
       "FocusExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_AppAStandaloneAppBBrowser_CaptureOn_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1783,7 +1783,7 @@
       },
       "NavigateExisting_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_AppAStandaloneAppBBrowser_CaptureOn_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1858,7 +1858,7 @@
       },
       "NavigateExisting_Tab_ScopeA2A_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_AppAStandaloneAppBBrowser_CaptureOn_Tab_ScopeA2A_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1920,7 +1920,7 @@
       },
       "NavigateExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_AppAStandaloneAppBBrowser_CaptureOn_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1987,7 +1987,7 @@
       },
       "Tab_ScopeA2A_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOn_Tab_ScopeA2A_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2126,7 +2126,7 @@
       },
       "Tab_ScopeA2A_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOn_Tab_ScopeA2A_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2265,7 +2265,7 @@
       },
       "Tab_ScopeA2A_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOn_Tab_ScopeA2A_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2403,7 +2403,7 @@
       },
       "Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOn_Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2465,7 +2465,7 @@
       },
       "Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOn_Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2675,7 +2675,7 @@
       },
       "Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOn_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2737,7 +2737,7 @@
       },
       "Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOn_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2947,7 +2947,7 @@
       },
       "Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOn_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -3081,7 +3081,7 @@
       },
       "Tab_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOn_Tab_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -3217,7 +3217,7 @@
       },
       "Tab_ScopeA2B_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOn_Tab_ScopeA2B_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -3350,7 +3350,7 @@
       },
       "Tab_ScopeA2X_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOn_Tab_ScopeA2X_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -3483,7 +3483,7 @@
       },
       "Tab_ScopeA2X_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOn_Tab_ScopeA2X_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -3616,7 +3616,7 @@
       },
       "Tab_ScopeA2X_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOn_Tab_ScopeA2X_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
diff --git a/chrome/test/data/web_apps/navigation_capture_expectations_AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture.json b/chrome/test/data/web_apps/navigation_capture_expectations_AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture.json
index 2452738e..f372b53 100644
--- a/chrome/test/data/web_apps/navigation_capture_expectations_AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture.json
+++ b/chrome/test/data/web_apps/navigation_capture_expectations_AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture.json
@@ -2,7 +2,7 @@
    "tests": {
       "AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithOpener_TargetBlank": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -44,12 +44,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -85,12 +87,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -127,12 +131,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppBrowserTab" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -168,12 +174,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithOpener_TargetBlank": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -209,12 +217,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled", "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -250,12 +260,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -292,12 +304,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppBrowserTab" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -333,12 +347,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "FocusExisting_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "FocusExisting_AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -375,12 +391,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppBrowserTab" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "FocusExisting_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -413,12 +431,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "FocusExisting_Tab_ScopeA2A_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "FocusExisting_AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -451,12 +471,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/destination.html?id-LINK-A_TO_B-BLANK-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppBrowserTab" ],
+            "redirection_result": [ "AppWindowOpened" ]
          }
       },
       "FocusExisting_Tab_ScopeA2A_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -476,12 +498,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "FocusExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "FocusExisting_AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -509,12 +533,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppBrowserTab" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "FocusExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -535,12 +561,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateExisting_AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -577,12 +605,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppBrowserTab" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "NavigateExisting_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -615,12 +645,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_Tab_ScopeA2A_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateExisting_AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -653,12 +685,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/destination.html?id-LINK-A_TO_B-BLANK-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppBrowserTab" ],
+            "redirection_result": [ "AppWindowOpened" ]
          }
       },
       "NavigateExisting_Tab_ScopeA2A_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -678,12 +712,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateExisting_AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -711,12 +747,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppBrowserTab" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "NavigateExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -737,12 +775,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2A_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -775,12 +815,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/destination.html?id-LINK-A_TO_A-BLANK-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "SameContext" ]
          }
       },
       "Tab_ScopeA2A_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -800,12 +842,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2A_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -838,12 +882,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/destination.html?id-LINK-A_TO_B-BLANK-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppBrowserTab" ],
+            "redirection_result": [ "AppWindowOpened" ]
          }
       },
       "Tab_ScopeA2A_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -869,12 +915,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2A_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -907,12 +955,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/destination.html?id-LINK-A_TO_X-BLANK-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible" ],
+            "redirection_result": [ "AppWindowOpened" ]
          }
       },
       "Tab_ScopeA2A_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -932,12 +982,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithOpener_TargetBlank": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -969,12 +1021,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1001,12 +1055,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_B-SELF-OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1034,12 +1090,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_B-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppBrowserTab" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1066,12 +1124,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithOpener_TargetBlank": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1098,12 +1158,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-BLANK-OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled", "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1130,12 +1192,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-SELF-OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1163,12 +1227,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppBrowserTab" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1195,12 +1261,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1228,12 +1296,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_A-BLANK-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "AppBrowserTabOpened" ]
          }
       },
       "Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1260,12 +1330,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_A-SELF-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1293,12 +1365,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-BLANK-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppBrowserTab" ],
+            "redirection_result": [ "SameContext" ]
          }
       },
       "Tab_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1325,12 +1399,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2B_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1358,12 +1434,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_X-BLANK-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible" ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2B_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1390,12 +1468,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_X-SELF-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2X_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1422,12 +1502,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "ReparentBrowserTabToBrowserTab" ]
          }
       },
       "Tab_ScopeA2X_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1447,12 +1529,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2X_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1479,12 +1563,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewAppBrowserTab" ],
+            "redirection_result": [ "SameContext" ]
          }
       },
       "Tab_ScopeA2X_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1510,12 +1596,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2X_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1542,12 +1630,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible" ],
+            "redirection_result": [ "SameContext" ]
          }
       },
       "Tab_ScopeA2X_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1567,7 +1657,9 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       }
    }
diff --git a/chrome/test/data/web_apps/navigation_capture_expectations_BothBrowser_CaptureOn.json b/chrome/test/data/web_apps/navigation_capture_expectations_BothBrowser_CaptureOn.json
index c7c970b..ce61ae9 100644
--- a/chrome/test/data/web_apps/navigation_capture_expectations_BothBrowser_CaptureOn.json
+++ b/chrome/test/data/web_apps/navigation_capture_expectations_BothBrowser_CaptureOn.json
@@ -37,7 +37,7 @@
       },
       "FocusExisting_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothBrowser_CaptureOn_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -99,7 +99,7 @@
       },
       "FocusExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothBrowser_CaptureOn_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -161,7 +161,7 @@
       },
       "NavigateExisting_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothBrowser_CaptureOn_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -223,7 +223,7 @@
       },
       "NavigateExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothBrowser_CaptureOn_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -285,7 +285,7 @@
       },
       "Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "BothBrowser_CaptureOn_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -385,7 +385,7 @@
       },
       "Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "BothBrowser_CaptureOn_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
diff --git a/chrome/test/data/web_apps/navigation_capture_expectations_BothBrowser_CaptureOnWithSelfLinkCapture.json b/chrome/test/data/web_apps/navigation_capture_expectations_BothBrowser_CaptureOnWithSelfLinkCapture.json
index 7955998..94ac17e5 100644
--- a/chrome/test/data/web_apps/navigation_capture_expectations_BothBrowser_CaptureOnWithSelfLinkCapture.json
+++ b/chrome/test/data/web_apps/navigation_capture_expectations_BothBrowser_CaptureOnWithSelfLinkCapture.json
@@ -2,7 +2,7 @@
    "tests": {
       "FocusExisting_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "FocusExisting_BothBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -30,12 +30,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/destination.html?id-LINK-A_TO_A-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppBrowserTab" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "FocusExisting_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -55,12 +57,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "FocusExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "FocusExisting_BothBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -88,12 +92,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppBrowserTab" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "FocusExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -114,12 +120,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateExisting_BothBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -147,12 +155,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/destination.html?id-LINK-A_TO_A-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppBrowserTab" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "NavigateExisting_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -172,12 +182,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateExisting_BothBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -205,12 +217,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppBrowserTab" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "NavigateExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -231,12 +245,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "BothBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -264,12 +280,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/destination.html?id-LINK-A_TO_A-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppBrowserTab" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "BothBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -289,12 +307,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "BothBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -322,12 +342,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppBrowserTab" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "BothBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -354,7 +376,9 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       }
    }
diff --git a/chrome/test/data/web_apps/navigation_capture_expectations_BothStandalone_CaptureForSpecifiedClientMode.json b/chrome/test/data/web_apps/navigation_capture_expectations_BothStandalone_CaptureForSpecifiedClientMode.json
index 36470f2..6c8fa0f 100644
--- a/chrome/test/data/web_apps/navigation_capture_expectations_BothStandalone_CaptureForSpecifiedClientMode.json
+++ b/chrome/test/data/web_apps/navigation_capture_expectations_BothStandalone_CaptureForSpecifiedClientMode.json
@@ -42,7 +42,7 @@
       },
       "NavigateNew_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateNew_BothStandalone_CaptureForSpecifiedClientMode_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -143,7 +143,7 @@
       },
       "Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "BothStandalone_CaptureForSpecifiedClientMode_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
diff --git a/chrome/test/data/web_apps/navigation_capture_expectations_BothStandalone_CaptureOff.json b/chrome/test/data/web_apps/navigation_capture_expectations_BothStandalone_CaptureOff.json
index 6e711f3..6c475b0 100644
--- a/chrome/test/data/web_apps/navigation_capture_expectations_BothStandalone_CaptureOff.json
+++ b/chrome/test/data/web_apps/navigation_capture_expectations_BothStandalone_CaptureOff.json
@@ -6359,7 +6359,7 @@
       },
       "FocusExisting_AppWnd_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOff_AppWnd_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6437,7 +6437,7 @@
       },
       "FocusExisting_AppWnd_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOff_AppWnd_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6515,7 +6515,7 @@
       },
       "FocusExisting_AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOff_AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6593,7 +6593,7 @@
       },
       "FocusExisting_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOff_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6662,7 +6662,7 @@
       },
       "FocusExisting_Tab_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOff_Tab_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6723,7 +6723,7 @@
       },
       "FocusExisting_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOff_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6784,7 +6784,7 @@
       },
       "FocusExisting_Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOff_Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6845,7 +6845,7 @@
       },
       "FocusExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOff_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6915,7 +6915,7 @@
       },
       "NavigateExisting_AppWnd_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOff_AppWnd_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6993,7 +6993,7 @@
       },
       "NavigateExisting_AppWnd_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOff_AppWnd_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7071,7 +7071,7 @@
       },
       "NavigateExisting_AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOff_AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7149,7 +7149,7 @@
       },
       "NavigateExisting_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOff_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7218,7 +7218,7 @@
       },
       "NavigateExisting_Tab_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOff_Tab_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7279,7 +7279,7 @@
       },
       "NavigateExisting_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOff_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7340,7 +7340,7 @@
       },
       "NavigateExisting_Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOff_Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7401,7 +7401,7 @@
       },
       "NavigateExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOff_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
diff --git a/chrome/test/data/web_apps/navigation_capture_expectations_BothStandalone_CaptureOn.json b/chrome/test/data/web_apps/navigation_capture_expectations_BothStandalone_CaptureOn.json
index 86f6b4a..5f37e15 100644
--- a/chrome/test/data/web_apps/navigation_capture_expectations_BothStandalone_CaptureOn.json
+++ b/chrome/test/data/web_apps/navigation_capture_expectations_BothStandalone_CaptureOn.json
@@ -7720,7 +7720,7 @@
       },
       "FocusExisting_AppWnd_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOn_AppWnd_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7804,7 +7804,7 @@
       },
       "FocusExisting_AppWnd_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOn_AppWnd_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7888,7 +7888,7 @@
       },
       "FocusExisting_AppWnd_ScopeA2A_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOn_AppWnd_ScopeA2A_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7972,7 +7972,7 @@
       },
       "FocusExisting_AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOn_AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -8056,7 +8056,7 @@
       },
       "FocusExisting_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOn_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -8131,7 +8131,7 @@
       },
       "FocusExisting_Tab_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOn_Tab_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -8198,7 +8198,7 @@
       },
       "FocusExisting_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOn_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -8265,7 +8265,7 @@
       },
       "FocusExisting_Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOn_Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -8332,7 +8332,7 @@
       },
       "FocusExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOn_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -8399,7 +8399,7 @@
       },
       "FocusExisting_Tab_ScopeA2B_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOn_Tab_ScopeA2B_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -8466,7 +8466,7 @@
       },
       "FocusExisting_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOn_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -8527,7 +8527,7 @@
       },
       "FocusExisting_Tab_ScopeA2X_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOn_Tab_ScopeA2X_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -8588,7 +8588,7 @@
       },
       "FocusExisting_Tab_ScopeA2X_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOn_Tab_ScopeA2X_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -8664,7 +8664,7 @@
       },
       "NavigateExisting_AppWnd_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOn_AppWnd_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -8748,7 +8748,7 @@
       },
       "NavigateExisting_AppWnd_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOn_AppWnd_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -8832,7 +8832,7 @@
       },
       "NavigateExisting_AppWnd_ScopeA2A_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOn_AppWnd_ScopeA2A_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -8916,7 +8916,7 @@
       },
       "NavigateExisting_AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOn_AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -9000,7 +9000,7 @@
       },
       "NavigateExisting_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOn_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -9075,7 +9075,7 @@
       },
       "NavigateExisting_Tab_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOn_Tab_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -9142,7 +9142,7 @@
       },
       "NavigateExisting_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOn_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -9209,7 +9209,7 @@
       },
       "NavigateExisting_Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOn_Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -9276,7 +9276,7 @@
       },
       "NavigateExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOn_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -9343,7 +9343,7 @@
       },
       "NavigateExisting_Tab_ScopeA2B_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOn_Tab_ScopeA2B_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -9410,7 +9410,7 @@
       },
       "NavigateExisting_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOn_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -9471,7 +9471,7 @@
       },
       "NavigateExisting_Tab_ScopeA2X_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOn_Tab_ScopeA2X_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -9532,7 +9532,7 @@
       },
       "NavigateExisting_Tab_ScopeA2X_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOn_Tab_ScopeA2X_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -9608,7 +9608,7 @@
       },
       "NavigateNew_AppWnd_ScopeA2B_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateNew_BothStandalone_CaptureOn_AppWnd_ScopeA2B_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -9692,7 +9692,7 @@
       },
       "NavigateNew_AppWnd_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateNew_BothStandalone_CaptureOn_AppWnd_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -9776,7 +9776,7 @@
       },
       "NavigateNew_AppWnd_ScopeA2B_ServerSideViaB_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateNew_BothStandalone_CaptureOn_AppWnd_ScopeA2B_ServerSideViaB_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -9860,7 +9860,7 @@
       },
       "NavigateNew_AppWnd_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateNew_BothStandalone_CaptureOn_AppWnd_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -9938,7 +9938,7 @@
       },
       "NavigateNew_AppWnd_ScopeA2X_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateNew_BothStandalone_CaptureOn_AppWnd_ScopeA2X_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -10016,7 +10016,7 @@
       },
       "NavigateNew_AppWnd_ScopeA2X_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateNew_BothStandalone_CaptureOn_AppWnd_ScopeA2X_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -10094,7 +10094,7 @@
       },
       "NavigateNew_AppWnd_ScopeA2X_ServerSideViaB_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateNew_BothStandalone_CaptureOn_AppWnd_ScopeA2X_ServerSideViaB_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -10172,7 +10172,7 @@
       },
       "NavigateNew_AppWnd_ScopeA2X_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateNew_BothStandalone_CaptureOn_AppWnd_ScopeA2X_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -10247,7 +10247,7 @@
       },
       "NavigateNew_Tab_ScopeA2B_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateNew_BothStandalone_CaptureOn_Tab_ScopeA2B_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -10314,7 +10314,7 @@
       },
       "NavigateNew_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateNew_BothStandalone_CaptureOn_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -10381,7 +10381,7 @@
       },
       "NavigateNew_Tab_ScopeA2B_ServerSideViaB_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateNew_BothStandalone_CaptureOn_Tab_ScopeA2B_ServerSideViaB_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -10448,7 +10448,7 @@
       },
       "NavigateNew_Tab_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateNew_BothStandalone_CaptureOn_Tab_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -10509,7 +10509,7 @@
       },
       "NavigateNew_Tab_ScopeA2X_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateNew_BothStandalone_CaptureOn_Tab_ScopeA2X_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -10570,7 +10570,7 @@
       },
       "NavigateNew_Tab_ScopeA2X_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateNew_BothStandalone_CaptureOn_Tab_ScopeA2X_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -10631,7 +10631,7 @@
       },
       "NavigateNew_Tab_ScopeA2X_ServerSideViaB_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateNew_BothStandalone_CaptureOn_Tab_ScopeA2X_ServerSideViaB_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -10692,7 +10692,7 @@
       },
       "NavigateNew_Tab_ScopeA2X_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateNew_BothStandalone_CaptureOn_Tab_ScopeA2X_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
diff --git a/chrome/test/data/web_apps/navigation_capture_expectations_BothStandalone_CaptureOnWithSelfLinkCapture.json b/chrome/test/data/web_apps/navigation_capture_expectations_BothStandalone_CaptureOnWithSelfLinkCapture.json
index 71985340..a78a90c 100644
--- a/chrome/test/data/web_apps/navigation_capture_expectations_BothStandalone_CaptureOnWithSelfLinkCapture.json
+++ b/chrome/test/data/web_apps/navigation_capture_expectations_BothStandalone_CaptureOnWithSelfLinkCapture.json
@@ -2,7 +2,7 @@
    "tests": {
       "AppWnd_ScopeA2A_Direct_ViaButton_LeftClick_WithOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2A_Direct_ViaButton_LeftClick_WithOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -43,12 +43,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/destination.html?id-BTN-A_TO_A-BLANK-OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled", "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2A_Direct_ViaButton_LeftClick_WithOpener_TargetFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2A_Direct_ViaButton_LeftClick_WithOpener_TargetFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -77,12 +79,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2A_Direct_ViaButton_LeftClick_WithOpener_TargetNoFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2A_Direct_ViaButton_LeftClick_WithOpener_TargetNoFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -123,12 +127,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/destination.html?id-BTN-A_TO_A-NO_FRAME-OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled", "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2A_Direct_ViaButton_LeftClick_WithOpener_TargetSelf": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2A_Direct_ViaButton_LeftClick_WithOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -156,12 +162,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -203,12 +211,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/destination.html?id-BTN-A_TO_A-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "AppWnd_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -237,12 +247,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetNoFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetNoFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -285,12 +297,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/destination.html?id-BTN-A_TO_A-NO_FRAME-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "AppWnd_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -318,12 +332,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2A_Direct_ViaLink_LeftClick_WithOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2A_Direct_ViaLink_LeftClick_WithOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -364,12 +380,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/destination.html?id-LINK-A_TO_A-BLANK-OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled", "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2A_Direct_ViaLink_LeftClick_WithOpener_TargetFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2A_Direct_ViaLink_LeftClick_WithOpener_TargetFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -398,12 +416,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2A_Direct_ViaLink_LeftClick_WithOpener_TargetNoFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2A_Direct_ViaLink_LeftClick_WithOpener_TargetNoFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -444,12 +464,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/destination.html?id-LINK-A_TO_A-NO_FRAME-OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled", "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2A_Direct_ViaLink_LeftClick_WithOpener_TargetSelf": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2A_Direct_ViaLink_LeftClick_WithOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -477,12 +499,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -524,12 +548,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/destination.html?id-LINK-A_TO_A-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "AppWnd_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -558,12 +584,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetNoFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetNoFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -606,12 +634,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/destination.html?id-LINK-A_TO_A-NO_FRAME-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "AppWnd_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -639,12 +669,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -686,12 +718,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithOpener_TargetFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithOpener_TargetFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -720,12 +754,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithOpener_TargetNoFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithOpener_TargetNoFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -768,12 +804,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithOpener_TargetSelf": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -814,12 +852,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_B-SELF-OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -861,12 +901,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_B-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -895,12 +937,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetNoFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetNoFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -943,12 +987,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_B-NO_FRAME-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -989,12 +1035,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaButton_MiddleClick_WithoutOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaButton_MiddleClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1030,12 +1078,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaButton_MiddleClick_WithoutOpener_TargetFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaButton_MiddleClick_WithoutOpener_TargetFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1064,12 +1114,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaButton_MiddleClick_WithoutOpener_TargetNoFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaButton_MiddleClick_WithoutOpener_TargetNoFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1106,12 +1158,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaButton_MiddleClick_WithoutOpener_TargetSelf": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaButton_MiddleClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1152,12 +1206,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_B-SELF-NO_OPENER&click=Middle" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaButton_ShiftClick_WithoutOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaButton_ShiftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1199,12 +1255,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_B-BLANK-NO_OPENER&click=Shift%20Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "ForcedContextAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaButton_ShiftClick_WithoutOpener_TargetFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaButton_ShiftClick_WithoutOpener_TargetFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1233,12 +1291,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaButton_ShiftClick_WithoutOpener_TargetNoFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaButton_ShiftClick_WithoutOpener_TargetNoFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1281,12 +1341,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_B-NO_FRAME-NO_OPENER&click=Shift%20Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "ForcedContextAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaButton_ShiftClick_WithoutOpener_TargetSelf": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaButton_ShiftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1327,12 +1389,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_B-SELF-NO_OPENER&click=Shift%20Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1373,12 +1437,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-BLANK-OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled", "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithOpener_TargetFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithOpener_TargetFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1407,12 +1473,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithOpener_TargetNoFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithOpener_TargetNoFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1453,12 +1521,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-NO_FRAME-OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled", "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithOpener_TargetSelf": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1499,12 +1569,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-SELF-OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1546,12 +1618,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1580,12 +1654,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetNoFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetNoFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1628,12 +1704,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-NO_FRAME-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1674,12 +1752,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaLink_MiddleClick_WithoutOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_MiddleClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1715,12 +1795,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaLink_MiddleClick_WithoutOpener_TargetFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_MiddleClick_WithoutOpener_TargetFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1756,12 +1838,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaLink_MiddleClick_WithoutOpener_TargetNoFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_MiddleClick_WithoutOpener_TargetNoFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1797,12 +1881,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaLink_MiddleClick_WithoutOpener_TargetSelf": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_MiddleClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1838,12 +1924,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaLink_ShiftClick_WithoutOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_ShiftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1885,12 +1973,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-BLANK-NO_OPENER&click=Shift%20Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "ForcedContextAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaLink_ShiftClick_WithoutOpener_TargetFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_ShiftClick_WithoutOpener_TargetFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1932,12 +2022,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-FRAME-NO_OPENER&click=Shift%20Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "ForcedContextAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaLink_ShiftClick_WithoutOpener_TargetNoFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_ShiftClick_WithoutOpener_TargetNoFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1979,12 +2071,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-NO_FRAME-NO_OPENER&click=Shift%20Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "ForcedContextAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "AppWnd_ScopeA2B_Direct_ViaLink_ShiftClick_WithoutOpener_TargetSelf": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_ShiftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2026,12 +2120,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-SELF-NO_OPENER&click=Shift%20Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "ForcedContextAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "AppWnd_ScopeA2X_Direct_ViaButton_LeftClick_WithOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2X_Direct_ViaButton_LeftClick_WithOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2073,12 +2169,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2X_Direct_ViaButton_LeftClick_WithOpener_TargetFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2X_Direct_ViaButton_LeftClick_WithOpener_TargetFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2107,12 +2205,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2X_Direct_ViaButton_LeftClick_WithOpener_TargetNoFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2X_Direct_ViaButton_LeftClick_WithOpener_TargetNoFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2155,12 +2255,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2X_Direct_ViaButton_LeftClick_WithOpener_TargetSelf": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2X_Direct_ViaButton_LeftClick_WithOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2188,12 +2290,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2X_Direct_ViaButton_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2X_Direct_ViaButton_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2229,12 +2333,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "AppWnd_ScopeA2X_Direct_ViaButton_LeftClick_WithoutOpener_TargetFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2X_Direct_ViaButton_LeftClick_WithoutOpener_TargetFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2263,12 +2369,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2X_Direct_ViaButton_LeftClick_WithoutOpener_TargetNoFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2X_Direct_ViaButton_LeftClick_WithoutOpener_TargetNoFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2305,12 +2413,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "AppWnd_ScopeA2X_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2X_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2338,12 +2448,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2X_Direct_ViaLink_LeftClick_WithOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2X_Direct_ViaLink_LeftClick_WithOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2380,12 +2492,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2X_Direct_ViaLink_LeftClick_WithOpener_TargetFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2X_Direct_ViaLink_LeftClick_WithOpener_TargetFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2414,12 +2528,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2X_Direct_ViaLink_LeftClick_WithOpener_TargetNoFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2X_Direct_ViaLink_LeftClick_WithOpener_TargetNoFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2457,12 +2573,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2X_Direct_ViaLink_LeftClick_WithOpener_TargetSelf": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2X_Direct_ViaLink_LeftClick_WithOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2490,12 +2608,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2X_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2X_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2531,12 +2651,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "AppWnd_ScopeA2X_Direct_ViaLink_LeftClick_WithoutOpener_TargetFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2X_Direct_ViaLink_LeftClick_WithoutOpener_TargetFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2565,12 +2687,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "AppWnd_ScopeA2X_Direct_ViaLink_LeftClick_WithoutOpener_TargetNoFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2X_Direct_ViaLink_LeftClick_WithoutOpener_TargetNoFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2607,12 +2731,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "AppWnd_ScopeA2X_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2X_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2640,12 +2766,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "FocusExisting_AppWnd_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2687,12 +2815,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/destination.html?id-BTN-A_TO_A-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "FocusExisting_AppWnd_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2720,12 +2850,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "FocusExisting_AppWnd_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2767,12 +2899,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/destination.html?id-LINK-A_TO_A-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "FocusExisting_AppWnd_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2800,12 +2934,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "FocusExisting_AppWnd_ScopeA2A_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2A_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2847,12 +2983,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/destination.html?id-LINK-A_TO_B-BLANK-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "AppWindowOpened" ]
          }
       },
       "FocusExisting_AppWnd_ScopeA2A_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2A_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2892,12 +3030,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "FocusExisting_AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2939,12 +3079,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_B-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "FocusExisting_AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -2985,12 +3127,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "FocusExisting_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -3032,12 +3176,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "FocusExisting_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -3078,12 +3224,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "FocusExisting_Tab_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -3116,12 +3264,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/destination.html?id-BTN-A_TO_A-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "FocusExisting_Tab_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -3141,12 +3291,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "FocusExisting_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -3179,12 +3331,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/destination.html?id-LINK-A_TO_A-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "FocusExisting_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -3204,12 +3358,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "FocusExisting_Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -3242,12 +3398,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_B-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "FocusExisting_Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -3279,12 +3437,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "FocusExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -3317,12 +3477,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "FocusExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -3354,12 +3516,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "FocusExisting_Tab_ScopeA2B_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -3392,12 +3556,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_A-BLANK-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "AppWindowOpened" ]
          }
       },
       "FocusExisting_Tab_ScopeA2B_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -3429,12 +3595,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_A-SELF-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "FocusExisting_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -3467,12 +3635,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_A-BLANK-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "AppWindowOpened" ]
          }
       },
       "FocusExisting_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -3504,12 +3674,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_A-SELF-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "FocusExisting_Tab_ScopeA2X_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -3536,12 +3708,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "ReparentBrowserTabToBrowserTab" ]
          }
       },
       "FocusExisting_Tab_ScopeA2X_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -3561,12 +3735,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "FocusExisting_Tab_ScopeA2X_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -3593,12 +3769,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "ReparentBrowserTabToBrowserTab" ]
          }
       },
       "FocusExisting_Tab_ScopeA2X_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -3618,12 +3796,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_AppWnd_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -3665,12 +3845,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/destination.html?id-BTN-A_TO_A-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "NavigateExisting_AppWnd_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -3698,12 +3880,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_AppWnd_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -3745,12 +3929,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/destination.html?id-LINK-A_TO_A-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "NavigateExisting_AppWnd_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -3778,12 +3964,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_AppWnd_ScopeA2A_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2A_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -3825,12 +4013,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/destination.html?id-LINK-A_TO_B-BLANK-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "AppWindowOpened" ]
          }
       },
       "NavigateExisting_AppWnd_ScopeA2A_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2A_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -3870,12 +4060,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -3917,12 +4109,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_B-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "NavigateExisting_AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -3963,12 +4157,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -4010,12 +4206,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "NavigateExisting_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -4056,12 +4254,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_Tab_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -4094,12 +4294,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/destination.html?id-BTN-A_TO_A-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "NavigateExisting_Tab_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -4119,12 +4321,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -4157,12 +4361,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/destination.html?id-LINK-A_TO_A-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "NavigateExisting_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -4182,12 +4388,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -4220,12 +4428,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_B-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "NavigateExisting_Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -4257,12 +4467,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -4295,12 +4507,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "NavigateExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -4332,12 +4546,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_Tab_ScopeA2B_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -4370,12 +4586,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_A-BLANK-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "AppWindowOpened" ]
          }
       },
       "NavigateExisting_Tab_ScopeA2B_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -4407,12 +4625,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_A-SELF-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -4445,12 +4665,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_A-BLANK-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "AppWindowOpened" ]
          }
       },
       "NavigateExisting_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -4482,12 +4704,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_A-SELF-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_Tab_ScopeA2X_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -4514,12 +4738,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "ReparentBrowserTabToBrowserTab" ]
          }
       },
       "NavigateExisting_Tab_ScopeA2X_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -4539,12 +4765,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_Tab_ScopeA2X_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -4571,12 +4799,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "ReparentBrowserTabToBrowserTab" ]
          }
       },
       "NavigateExisting_Tab_ScopeA2X_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -4596,12 +4826,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "NavigateNew_AppWnd_ScopeA2B_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateNew_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -4643,12 +4875,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_A-BLANK-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "AppWindowOpened" ]
          }
       },
       "NavigateNew_AppWnd_ScopeA2B_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateNew_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -4689,12 +4923,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_A-SELF-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateNew_AppWnd_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateNew_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -4736,12 +4972,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_A-BLANK-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "AppWindowOpened" ]
          }
       },
       "NavigateNew_AppWnd_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateNew_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -4782,12 +5020,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_A-SELF-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateNew_AppWnd_ScopeA2B_ServerSideViaB_ViaButton_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateNew_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_ServerSideViaB_ViaButton_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -4829,12 +5069,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_B-BLANK-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "SameContext" ]
          }
       },
       "NavigateNew_AppWnd_ScopeA2B_ServerSideViaB_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateNew_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_ServerSideViaB_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -4875,12 +5117,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateNew_AppWnd_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateNew_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -4922,12 +5166,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-BLANK-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "SameContext" ]
          }
       },
       "NavigateNew_AppWnd_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateNew_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -4968,12 +5214,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateNew_AppWnd_ScopeA2X_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateNew_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2X_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -5009,12 +5257,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "ReparentBrowserTabToBrowserTab" ]
          }
       },
       "NavigateNew_AppWnd_ScopeA2X_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateNew_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2X_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -5042,12 +5292,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "NavigateNew_AppWnd_ScopeA2X_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateNew_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2X_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -5083,12 +5335,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "ReparentBrowserTabToBrowserTab" ]
          }
       },
       "NavigateNew_AppWnd_ScopeA2X_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateNew_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2X_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -5116,12 +5370,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "NavigateNew_AppWnd_ScopeA2X_ServerSideViaB_ViaButton_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateNew_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2X_ServerSideViaB_ViaButton_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -5157,12 +5413,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "ReparentBrowserTabToBrowserTab" ]
          }
       },
       "NavigateNew_AppWnd_ScopeA2X_ServerSideViaB_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateNew_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2X_ServerSideViaB_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -5202,12 +5460,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateNew_AppWnd_ScopeA2X_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateNew_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2X_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -5243,12 +5503,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "ReparentBrowserTabToBrowserTab" ]
          }
       },
       "NavigateNew_AppWnd_ScopeA2X_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateNew_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2X_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -5288,12 +5550,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateNew_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateNew_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -5326,12 +5590,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "NavigateNew_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateNew_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -5363,12 +5629,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateNew_Tab_ScopeA2B_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateNew_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -5401,12 +5669,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_A-BLANK-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "AppWindowOpened" ]
          }
       },
       "NavigateNew_Tab_ScopeA2B_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateNew_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -5438,12 +5708,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_A-SELF-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateNew_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateNew_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -5476,12 +5748,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_A-BLANK-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "AppWindowOpened" ]
          }
       },
       "NavigateNew_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateNew_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -5513,12 +5787,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_A-SELF-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateNew_Tab_ScopeA2B_ServerSideViaB_ViaButton_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateNew_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaB_ViaButton_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -5551,12 +5827,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_B-BLANK-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "SameContext" ]
          }
       },
       "NavigateNew_Tab_ScopeA2B_ServerSideViaB_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateNew_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaB_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -5588,12 +5866,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateNew_Tab_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateNew_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -5626,12 +5906,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-BLANK-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "SameContext" ]
          }
       },
       "NavigateNew_Tab_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateNew_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -5663,12 +5945,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateNew_Tab_ScopeA2X_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateNew_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -5695,12 +5979,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "ReparentBrowserTabToBrowserTab" ]
          }
       },
       "NavigateNew_Tab_ScopeA2X_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateNew_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_ServerSideViaA_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -5720,12 +6006,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "NavigateNew_Tab_ScopeA2X_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateNew_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -5752,12 +6040,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "ReparentBrowserTabToBrowserTab" ]
          }
       },
       "NavigateNew_Tab_ScopeA2X_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateNew_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -5777,12 +6067,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "NavigateNew_Tab_ScopeA2X_ServerSideViaB_ViaButton_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateNew_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_ServerSideViaB_ViaButton_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -5809,12 +6101,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "ReparentBrowserTabToBrowserTab" ]
          }
       },
       "NavigateNew_Tab_ScopeA2X_ServerSideViaB_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateNew_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_ServerSideViaB_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -5845,12 +6139,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateNew_Tab_ScopeA2X_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateNew_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -5877,12 +6173,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "ReparentBrowserTabToBrowserTab" ]
          }
       },
       "NavigateNew_Tab_ScopeA2X_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateNew_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -5913,12 +6211,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2A_Direct_ViaButton_LeftClick_WithOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_Direct_ViaButton_LeftClick_WithOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -5950,12 +6250,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/destination.html?id-BTN-A_TO_A-BLANK-OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled", "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2A_Direct_ViaButton_LeftClick_WithOpener_TargetFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_Direct_ViaButton_LeftClick_WithOpener_TargetFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -5975,12 +6277,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2A_Direct_ViaButton_LeftClick_WithOpener_TargetNoFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_Direct_ViaButton_LeftClick_WithOpener_TargetNoFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6012,12 +6316,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/destination.html?id-BTN-A_TO_A-NO_FRAME-OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled", "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2A_Direct_ViaButton_LeftClick_WithOpener_TargetSelf": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_Direct_ViaButton_LeftClick_WithOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6037,12 +6343,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6075,12 +6383,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/destination.html?id-BTN-A_TO_A-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "Tab_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6100,12 +6410,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetNoFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetNoFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6139,12 +6451,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/destination.html?id-BTN-A_TO_A-NO_FRAME-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "Tab_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6164,12 +6478,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6201,12 +6517,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/destination.html?id-LINK-A_TO_A-BLANK-OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled", "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithOpener_TargetFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithOpener_TargetFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6226,12 +6544,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithOpener_TargetNoFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithOpener_TargetNoFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6263,12 +6583,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/destination.html?id-LINK-A_TO_A-NO_FRAME-OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled", "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithOpener_TargetSelf": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6288,12 +6610,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6326,12 +6650,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/destination.html?id-LINK-A_TO_A-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6351,12 +6677,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetNoFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetNoFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6390,12 +6718,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/destination.html?id-LINK-A_TO_A-NO_FRAME-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2A_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6415,12 +6745,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6452,12 +6784,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithOpener_TargetFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithOpener_TargetFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6477,12 +6811,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithOpener_TargetNoFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithOpener_TargetNoFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6515,12 +6851,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithOpener_TargetSelf": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6552,12 +6890,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_B-SELF-OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6590,12 +6930,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_B-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6615,12 +6957,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetNoFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetNoFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6654,12 +6998,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_B-NO_FRAME-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6691,12 +7037,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaButton_MiddleClick_WithoutOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaButton_MiddleClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6723,12 +7071,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaButton_MiddleClick_WithoutOpener_TargetFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaButton_MiddleClick_WithoutOpener_TargetFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6748,12 +7098,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaButton_MiddleClick_WithoutOpener_TargetNoFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaButton_MiddleClick_WithoutOpener_TargetNoFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6781,12 +7133,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaButton_MiddleClick_WithoutOpener_TargetSelf": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaButton_MiddleClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6818,12 +7172,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_B-SELF-NO_OPENER&click=Middle" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaButton_ShiftClick_WithoutOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaButton_ShiftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6854,12 +7210,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaButton_ShiftClick_WithoutOpener_TargetFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaButton_ShiftClick_WithoutOpener_TargetFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6879,12 +7237,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaButton_ShiftClick_WithoutOpener_TargetNoFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaButton_ShiftClick_WithoutOpener_TargetNoFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6916,12 +7276,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaButton_ShiftClick_WithoutOpener_TargetSelf": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaButton_ShiftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6953,12 +7315,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-BTN-A_TO_B-SELF-NO_OPENER&click=Shift%20Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -6990,12 +7354,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-BLANK-OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled", "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithOpener_TargetFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithOpener_TargetFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7015,12 +7381,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithOpener_TargetNoFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithOpener_TargetNoFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7052,12 +7420,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-NO_FRAME-OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled", "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithOpener_TargetSelf": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7089,12 +7459,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-SELF-OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7127,12 +7499,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7152,12 +7526,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetNoFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetNoFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7191,12 +7567,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-NO_FRAME-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7228,12 +7606,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaLink_MiddleClick_WithoutOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_MiddleClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7260,12 +7640,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaLink_MiddleClick_WithoutOpener_TargetFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_MiddleClick_WithoutOpener_TargetFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7292,12 +7674,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaLink_MiddleClick_WithoutOpener_TargetNoFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_MiddleClick_WithoutOpener_TargetNoFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7324,12 +7708,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaLink_MiddleClick_WithoutOpener_TargetSelf": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_MiddleClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7356,12 +7742,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaLink_ShiftClick_WithoutOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_ShiftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7392,12 +7780,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaLink_ShiftClick_WithoutOpener_TargetFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_ShiftClick_WithoutOpener_TargetFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7428,12 +7818,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaLink_ShiftClick_WithoutOpener_TargetNoFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_ShiftClick_WithoutOpener_TargetNoFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7464,12 +7856,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "Tab_ScopeA2B_Direct_ViaLink_ShiftClick_WithoutOpener_TargetSelf": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_ShiftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7500,12 +7894,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "Tab_ScopeA2X_Direct_ViaButton_LeftClick_WithOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_Direct_ViaButton_LeftClick_WithOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7537,12 +7933,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2X_Direct_ViaButton_LeftClick_WithOpener_TargetFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_Direct_ViaButton_LeftClick_WithOpener_TargetFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7562,12 +7960,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2X_Direct_ViaButton_LeftClick_WithOpener_TargetNoFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_Direct_ViaButton_LeftClick_WithOpener_TargetNoFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7600,12 +8000,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2X_Direct_ViaButton_LeftClick_WithOpener_TargetSelf": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_Direct_ViaButton_LeftClick_WithOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7625,12 +8027,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2X_Direct_ViaButton_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_Direct_ViaButton_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7657,12 +8061,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "Tab_ScopeA2X_Direct_ViaButton_LeftClick_WithoutOpener_TargetFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_Direct_ViaButton_LeftClick_WithoutOpener_TargetFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7682,12 +8088,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2X_Direct_ViaButton_LeftClick_WithoutOpener_TargetNoFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_Direct_ViaButton_LeftClick_WithoutOpener_TargetNoFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7715,12 +8123,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "Tab_ScopeA2X_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_Direct_ViaButton_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7740,12 +8150,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2X_Direct_ViaLink_LeftClick_WithOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_Direct_ViaLink_LeftClick_WithOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7773,12 +8185,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2X_Direct_ViaLink_LeftClick_WithOpener_TargetFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_Direct_ViaLink_LeftClick_WithOpener_TargetFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7798,12 +8212,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2X_Direct_ViaLink_LeftClick_WithOpener_TargetNoFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_Direct_ViaLink_LeftClick_WithOpener_TargetNoFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7832,12 +8248,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2X_Direct_ViaLink_LeftClick_WithOpener_TargetSelf": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_Direct_ViaLink_LeftClick_WithOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7857,12 +8275,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2X_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7889,12 +8309,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "Tab_ScopeA2X_Direct_ViaLink_LeftClick_WithoutOpener_TargetFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_Direct_ViaLink_LeftClick_WithoutOpener_TargetFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7914,12 +8336,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "Tab_ScopeA2X_Direct_ViaLink_LeftClick_WithoutOpener_TargetNoFrame": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_Direct_ViaLink_LeftClick_WithoutOpener_TargetNoFrame",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7947,12 +8371,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "Tab_ScopeA2X_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2X_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -7972,7 +8398,9 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       }
    }
diff --git a/chrome/test/data/web_apps/navigation_capture_expectations_with_b_launched_in_setup_AppAStandaloneAppBBrowser_CaptureOff.json b/chrome/test/data/web_apps/navigation_capture_expectations_with_b_launched_in_setup_AppAStandaloneAppBBrowser_CaptureOff.json
index 90c2a8f..5ff85ae 100644
--- a/chrome/test/data/web_apps/navigation_capture_expectations_with_b_launched_in_setup_AppAStandaloneAppBBrowser_CaptureOff.json
+++ b/chrome/test/data/web_apps/navigation_capture_expectations_with_b_launched_in_setup_AppAStandaloneAppBBrowser_CaptureOff.json
@@ -50,7 +50,7 @@
       },
       "FocusExisting_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_AppAStandaloneAppBBrowser_CaptureOff_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -133,7 +133,7 @@
       },
       "FocusExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_AppAStandaloneAppBBrowser_CaptureOff_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -217,7 +217,7 @@
       },
       "NavigateExisting_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_AppAStandaloneAppBBrowser_CaptureOff_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -300,7 +300,7 @@
       },
       "NavigateExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_AppAStandaloneAppBBrowser_CaptureOff_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
diff --git a/chrome/test/data/web_apps/navigation_capture_expectations_with_b_launched_in_setup_AppAStandaloneAppBBrowser_CaptureOn.json b/chrome/test/data/web_apps/navigation_capture_expectations_with_b_launched_in_setup_AppAStandaloneAppBBrowser_CaptureOn.json
index 3adada0..1c7afc3 100644
--- a/chrome/test/data/web_apps/navigation_capture_expectations_with_b_launched_in_setup_AppAStandaloneAppBBrowser_CaptureOn.json
+++ b/chrome/test/data/web_apps/navigation_capture_expectations_with_b_launched_in_setup_AppAStandaloneAppBBrowser_CaptureOn.json
@@ -42,7 +42,7 @@
       },
       "FocusExisting_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_AppAStandaloneAppBBrowser_CaptureOn_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -118,7 +118,7 @@
       },
       "FocusExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_AppAStandaloneAppBBrowser_CaptureOn_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -192,7 +192,7 @@
       },
       "FocusExisting_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_AppAStandaloneAppBBrowser_CaptureOn_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -273,7 +273,7 @@
       },
       "NavigateExisting_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_AppAStandaloneAppBBrowser_CaptureOn_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -353,7 +353,7 @@
       },
       "NavigateExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_AppAStandaloneAppBBrowser_CaptureOn_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -431,7 +431,7 @@
       },
       "NavigateExisting_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_AppAStandaloneAppBBrowser_CaptureOn_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
diff --git a/chrome/test/data/web_apps/navigation_capture_expectations_with_b_launched_in_setup_AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture.json b/chrome/test/data/web_apps/navigation_capture_expectations_with_b_launched_in_setup_AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture.json
index b4abccb..67cd386e 100644
--- a/chrome/test/data/web_apps/navigation_capture_expectations_with_b_launched_in_setup_AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture.json
+++ b/chrome/test/data/web_apps/navigation_capture_expectations_with_b_launched_in_setup_AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture.json
@@ -2,7 +2,7 @@
    "tests": {
       "FocusExisting_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "FocusExisting_AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -35,12 +35,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "FocusExistingAppBrowserTab" ],
+            "redirection_result": [  ]
          }
       },
       "FocusExisting_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -73,12 +75,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "FocusExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "FocusExisting_AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -108,12 +112,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "FocusExistingAppBrowserTab" ],
+            "redirection_result": [  ]
          }
       },
       "FocusExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -144,12 +150,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "FocusExisting_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "FocusExisting_AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -179,12 +187,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "FocusExistingAppBrowserTab" ]
          }
       },
       "FocusExisting_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -215,12 +225,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_A-SELF-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateExisting_AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -257,12 +269,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NavigateExistingAppBrowserTab" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "NavigateExisting_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -299,12 +313,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateExisting_AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -338,12 +354,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NavigateExistingAppBrowserTab" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "NavigateExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -374,12 +392,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateExisting_AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -413,12 +433,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow", "NotHandled" ],
+            "redirection_result": [ "NavigateExistingAppBrowserTab" ]
          }
       },
       "NavigateExisting_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_AppAStandaloneAppBBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -449,7 +471,9 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_A-SELF-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       }
    }
diff --git a/chrome/test/data/web_apps/navigation_capture_expectations_with_b_launched_in_setup_BothBrowser_CaptureOn.json b/chrome/test/data/web_apps/navigation_capture_expectations_with_b_launched_in_setup_BothBrowser_CaptureOn.json
index e2366be..fd2d065b 100644
--- a/chrome/test/data/web_apps/navigation_capture_expectations_with_b_launched_in_setup_BothBrowser_CaptureOn.json
+++ b/chrome/test/data/web_apps/navigation_capture_expectations_with_b_launched_in_setup_BothBrowser_CaptureOn.json
@@ -39,7 +39,7 @@
       },
       "FocusExisting_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothBrowser_CaptureOn_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -117,7 +117,7 @@
       },
       "NavigateExisting_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothBrowser_CaptureOn_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
diff --git a/chrome/test/data/web_apps/navigation_capture_expectations_with_b_launched_in_setup_BothBrowser_CaptureOnWithSelfLinkCapture.json b/chrome/test/data/web_apps/navigation_capture_expectations_with_b_launched_in_setup_BothBrowser_CaptureOnWithSelfLinkCapture.json
index a78c69d9..1ddf0372 100644
--- a/chrome/test/data/web_apps/navigation_capture_expectations_with_b_launched_in_setup_BothBrowser_CaptureOnWithSelfLinkCapture.json
+++ b/chrome/test/data/web_apps/navigation_capture_expectations_with_b_launched_in_setup_BothBrowser_CaptureOnWithSelfLinkCapture.json
@@ -2,7 +2,7 @@
    "tests": {
       "FocusExisting_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "FocusExisting_BothBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -32,12 +32,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppBrowserTab" ],
+            "redirection_result": [ "FocusExistingAppBrowserTab" ]
          }
       },
       "FocusExisting_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -68,12 +70,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_A-SELF-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateExisting_BothBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -107,12 +111,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppBrowserTab", "NotHandled" ],
+            "redirection_result": [ "NavigateExistingAppBrowserTab" ]
          }
       },
       "NavigateExisting_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothBrowser_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -143,7 +149,9 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_A-SELF-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       }
    }
diff --git a/chrome/test/data/web_apps/navigation_capture_expectations_with_b_launched_in_setup_BothStandalone_CaptureOn.json b/chrome/test/data/web_apps/navigation_capture_expectations_with_b_launched_in_setup_BothStandalone_CaptureOn.json
index 22c3d4c..32fe8cd7 100644
--- a/chrome/test/data/web_apps/navigation_capture_expectations_with_b_launched_in_setup_BothStandalone_CaptureOn.json
+++ b/chrome/test/data/web_apps/navigation_capture_expectations_with_b_launched_in_setup_BothStandalone_CaptureOn.json
@@ -50,7 +50,7 @@
       },
       "AppANavigateExistingAppBFocusExisting_AppWnd_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppANavigateExistingAppBFocusExisting_BothStandalone_CaptureOn_AppWnd_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -145,7 +145,7 @@
       },
       "AppANavigateExistingAppBFocusExisting_AppWnd_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppANavigateExistingAppBFocusExisting_BothStandalone_CaptureOn_AppWnd_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -240,7 +240,7 @@
       },
       "AppANavigateExistingAppBFocusExisting_AppWnd_ScopeA2B_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppANavigateExistingAppBFocusExisting_BothStandalone_CaptureOn_AppWnd_ScopeA2B_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -326,7 +326,7 @@
       },
       "AppANavigateExistingAppBFocusExisting_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppANavigateExistingAppBFocusExisting_BothStandalone_CaptureOn_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -404,7 +404,7 @@
       },
       "AppANavigateExistingAppBFocusExisting_Tab_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppANavigateExistingAppBFocusExisting_BothStandalone_CaptureOn_Tab_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -482,7 +482,7 @@
       },
       "AppANavigateExistingAppBFocusExisting_Tab_ScopeA2B_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppANavigateExistingAppBFocusExisting_BothStandalone_CaptureOn_Tab_ScopeA2B_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -569,7 +569,7 @@
       },
       "FocusExisting_AppWnd_ScopeA2B_Direct_ViaLink_RightClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOn_AppWnd_ScopeA2B_Direct_ViaLink_RightClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -656,7 +656,7 @@
       },
       "FocusExisting_Tab_ScopeA2B_Direct_ViaLink_RightClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOn_Tab_ScopeA2B_Direct_ViaLink_RightClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -747,7 +747,7 @@
       },
       "NavigateExisting_AppWnd_ScopeA2B_Direct_ViaLink_RightClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOn_AppWnd_ScopeA2B_Direct_ViaLink_RightClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -851,7 +851,7 @@
       },
       "NavigateExisting_AppWnd_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOn_AppWnd_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -950,7 +950,7 @@
       },
       "NavigateExisting_AppWnd_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOn_AppWnd_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1049,7 +1049,7 @@
       },
       "NavigateExisting_AppWnd_ScopeA2B_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOn_AppWnd_ScopeA2B_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1139,7 +1139,7 @@
       },
       "NavigateExisting_Tab_ScopeA2B_Direct_ViaLink_RightClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOn_Tab_ScopeA2B_Direct_ViaLink_RightClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1225,7 +1225,7 @@
       },
       "NavigateExisting_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOn_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1307,7 +1307,7 @@
       },
       "NavigateExisting_Tab_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOn_Tab_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1389,7 +1389,7 @@
       },
       "NavigateExisting_Tab_ScopeA2B_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOn_Tab_ScopeA2B_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
diff --git a/chrome/test/data/web_apps/navigation_capture_expectations_with_b_launched_in_setup_BothStandalone_CaptureOnWithSelfLinkCapture.json b/chrome/test/data/web_apps/navigation_capture_expectations_with_b_launched_in_setup_BothStandalone_CaptureOnWithSelfLinkCapture.json
index 478d494..9dd073a7 100644
--- a/chrome/test/data/web_apps/navigation_capture_expectations_with_b_launched_in_setup_BothStandalone_CaptureOnWithSelfLinkCapture.json
+++ b/chrome/test/data/web_apps/navigation_capture_expectations_with_b_launched_in_setup_BothStandalone_CaptureOnWithSelfLinkCapture.json
@@ -2,7 +2,7 @@
    "tests": {
       "AppANavigateExistingAppBFocusExisting_AppWnd_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "AppANavigateExistingAppBFocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -43,12 +43,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "FocusExistingAppWindow" ]
          }
       },
       "AppANavigateExistingAppBFocusExisting_AppWnd_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppANavigateExistingAppBFocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -89,12 +91,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "AppANavigateExistingAppBFocusExisting_AppWnd_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "AppANavigateExistingAppBFocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -135,12 +139,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "FocusExistingAppWindow" ],
+            "redirection_result": [  ]
          }
       },
       "AppANavigateExistingAppBFocusExisting_AppWnd_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppANavigateExistingAppBFocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -181,12 +187,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "AppANavigateExistingAppBFocusExisting_AppWnd_ScopeA2B_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "AppANavigateExistingAppBFocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -227,12 +235,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible" ],
+            "redirection_result": [ "FocusExistingAppWindow" ]
          }
       },
       "AppANavigateExistingAppBFocusExisting_AppWnd_ScopeA2B_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppANavigateExistingAppBFocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -273,12 +283,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "AppANavigateExistingAppBFocusExisting_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "AppANavigateExistingAppBFocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -310,12 +322,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?dont_redirect", "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_A-BLANK-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "FocusExistingAppWindow" ]
          }
       },
       "AppANavigateExistingAppBFocusExisting_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppANavigateExistingAppBFocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -347,12 +361,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?dont_redirect", "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_A-SELF-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "AppANavigateExistingAppBFocusExisting_Tab_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "AppANavigateExistingAppBFocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -384,12 +400,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?dont_redirect", "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "FocusExistingAppWindow" ],
+            "redirection_result": [  ]
          }
       },
       "AppANavigateExistingAppBFocusExisting_Tab_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppANavigateExistingAppBFocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -421,12 +439,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?dont_redirect", "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "AppANavigateExistingAppBFocusExisting_Tab_ScopeA2B_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "AppANavigateExistingAppBFocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -458,12 +478,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?dont_redirect", "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_X-BLANK-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible" ],
+            "redirection_result": [ "FocusExistingAppWindow" ]
          }
       },
       "AppANavigateExistingAppBFocusExisting_Tab_ScopeA2B_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "AppANavigateExistingAppBFocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -495,12 +517,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?dont_redirect", "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_X-SELF-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "FocusExisting_AppWnd_ScopeA2B_Direct_ViaLink_RightClick_WithoutOpener_TargetBlank": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_RightClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -541,12 +565,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromMenu" ]
+            "launch_metric_buckets": [ "kFromMenu" ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "FocusExisting_AppWnd_ScopeA2B_Direct_ViaLink_RightClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_RightClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -587,12 +613,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromMenu" ]
+            "launch_metric_buckets": [ "kFromMenu" ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "FocusExisting_Tab_ScopeA2B_Direct_ViaLink_RightClick_WithoutOpener_TargetBlank": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_RightClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -624,12 +652,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?dont_redirect", "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-BLANK-NO_OPENER&click=Right" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromMenu" ]
+            "launch_metric_buckets": [ "kFromMenu" ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "FocusExisting_Tab_ScopeA2B_Direct_ViaLink_RightClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_RightClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -661,12 +691,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?dont_redirect", "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-SELF-NO_OPENER&click=Right" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromMenu" ]
+            "launch_metric_buckets": [ "kFromMenu" ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_AppWnd_ScopeA2B_Direct_ViaLink_RightClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_RightClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -711,12 +743,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromMenu" ]
+            "launch_metric_buckets": [ "kFromMenu" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_AppWnd_ScopeA2B_Direct_ViaLink_RightClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_RightClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -761,12 +795,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromMenu" ]
+            "launch_metric_buckets": [ "kFromMenu" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_AppWnd_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -811,12 +847,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow", "NotHandled" ],
+            "redirection_result": [ "NavigateExistingAppWindow" ]
          }
       },
       "NavigateExisting_AppWnd_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -861,12 +899,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_AppWnd_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -911,12 +951,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NavigateExistingAppWindow" ],
+            "redirection_result": [ "SameContext" ]
          }
       },
       "NavigateExisting_AppWnd_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -961,12 +1003,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_AppWnd_ScopeA2B_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1011,12 +1055,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible", "NotHandled" ],
+            "redirection_result": [ "NavigateExistingAppWindow" ]
          }
       },
       "NavigateExisting_AppWnd_ScopeA2B_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1061,12 +1107,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_Tab_ScopeA2B_Direct_ViaLink_RightClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_RightClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1102,12 +1150,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-BLANK-NO_OPENER&click=Right" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromMenu" ]
+            "launch_metric_buckets": [ "kFromMenu" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_Tab_ScopeA2B_Direct_ViaLink_RightClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_RightClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1143,12 +1193,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-SELF-NO_OPENER&click=Right" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromMenu" ]
+            "launch_metric_buckets": [ "kFromMenu" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1184,12 +1236,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_A-BLANK-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewAppWindow", "NotHandled" ],
+            "redirection_result": [ "NavigateExistingAppWindow" ]
          }
       },
       "NavigateExisting_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaA_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1225,12 +1279,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_A-SELF-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_Tab_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1266,12 +1322,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-BLANK-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NavigateExistingAppWindow" ],
+            "redirection_result": [ "SameContext" ]
          }
       },
       "NavigateExisting_Tab_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1307,12 +1365,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_Tab_ScopeA2B_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1348,12 +1408,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_X-BLANK-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible", "NotHandled" ],
+            "redirection_result": [ "NavigateExistingAppWindow" ]
          }
       },
       "NavigateExisting_Tab_ScopeA2B_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_ServerSideViaX_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -1389,7 +1451,9 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_X-SELF-NO_OPENER&click=Left&did_redirect" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       }
    }
diff --git a/chrome/test/data/web_apps/navigation_capturing_no_browser_BothStandalone_CaptureOnWithSelfLinkCapture.json b/chrome/test/data/web_apps/navigation_capturing_no_browser_BothStandalone_CaptureOnWithSelfLinkCapture.json
index 4727617..3f7d2b11 100644
--- a/chrome/test/data/web_apps/navigation_capturing_no_browser_BothStandalone_CaptureOnWithSelfLinkCapture.json
+++ b/chrome/test/data/web_apps/navigation_capturing_no_browser_BothStandalone_CaptureOnWithSelfLinkCapture.json
@@ -2,7 +2,7 @@
    "tests": {
       "AppWnd_ScopeA2X_Direct_ViaButton_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2X_Direct_ViaButton_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "app_scope": "/banners/link_capturing/scope_a/",
@@ -32,12 +32,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "AppWnd_ScopeA2X_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2X_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "app_scope": "/banners/link_capturing/scope_a/",
@@ -67,12 +69,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "AppWnd_ScopeA2X_Direct_ViaServiceWorkerButton_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2X_Direct_ViaServiceWorkerButton_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "app_scope": "/banners/link_capturing/scope_a/",
@@ -102,12 +106,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewTabRedirectionEligible" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "AppWnd_ScopeA2X_ServerSideViaB_ViaButton_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2X_ServerSideViaB_ViaButton_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "app_scope": "/banners/link_capturing/scope_a/",
@@ -137,12 +143,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "ReparentBrowserTabToBrowserTab" ]
          }
       },
       "AppWnd_ScopeA2X_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2X_ServerSideViaB_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "app_scope": "/banners/link_capturing/scope_a/",
@@ -172,12 +180,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "ReparentBrowserTabToBrowserTab" ]
          }
       },
       "AppWnd_ScopeA2X_ServerSideViaB_ViaServiceWorkerButton_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2X_ServerSideViaB_ViaServiceWorkerButton_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "app_scope": "/banners/link_capturing/scope_a/",
@@ -207,7 +217,9 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [  ]
+            "launch_metric_buckets": [  ],
+            "navigation_capturing_result": [ "NewAppWindow" ],
+            "redirection_result": [ "ReparentBrowserTabToBrowserTab" ]
          }
       }
    }
diff --git a/chrome/test/data/web_apps/navigation_capturing_with_b_lauched_and_browser_tab_BothStandalone_CaptureOn.json b/chrome/test/data/web_apps/navigation_capturing_with_b_lauched_and_browser_tab_BothStandalone_CaptureOn.json
index 649fe95..3c7603b 100644
--- a/chrome/test/data/web_apps/navigation_capturing_with_b_lauched_and_browser_tab_BothStandalone_CaptureOn.json
+++ b/chrome/test/data/web_apps/navigation_capturing_with_b_lauched_and_browser_tab_BothStandalone_CaptureOn.json
@@ -53,7 +53,7 @@
       },
       "FocusExisting_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOn_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -151,7 +151,7 @@
       },
       "FocusExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOn_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -254,7 +254,7 @@
       },
       "NavigateExisting_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOn_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -356,7 +356,7 @@
       },
       "NavigateExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOn_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
diff --git a/chrome/test/data/web_apps/navigation_capturing_with_b_lauched_and_browser_tab_BothStandalone_CaptureOnWithSelfLinkCapture.json b/chrome/test/data/web_apps/navigation_capturing_with_b_lauched_and_browser_tab_BothStandalone_CaptureOnWithSelfLinkCapture.json
index 7e8e73e..7cfc0d7b 100644
--- a/chrome/test/data/web_apps/navigation_capturing_with_b_lauched_and_browser_tab_BothStandalone_CaptureOnWithSelfLinkCapture.json
+++ b/chrome/test/data/web_apps/navigation_capturing_with_b_lauched_and_browser_tab_BothStandalone_CaptureOnWithSelfLinkCapture.json
@@ -2,7 +2,7 @@
    "tests": {
       "FocusExisting_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -46,12 +46,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "FocusExistingAppWindow" ],
+            "redirection_result": [  ]
          }
       },
       "FocusExisting_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -95,12 +97,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "FocusExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -141,12 +145,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?dont_redirect", "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "FocusExistingAppWindow" ],
+            "redirection_result": [  ]
          }
       },
       "FocusExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -187,12 +193,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?dont_redirect", "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [  ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -240,12 +248,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NavigateExistingAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "NavigateExisting_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -293,12 +303,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -343,12 +355,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-BLANK-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NavigateExistingAppWindow" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "NavigateExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -393,7 +407,9 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       }
    }
diff --git a/chrome/test/data/web_apps/navigation_capturing_with_extra_browser_tab_b_BothStandalone_CaptureOnWithSelfLinkCapture.json b/chrome/test/data/web_apps/navigation_capturing_with_extra_browser_tab_b_BothStandalone_CaptureOnWithSelfLinkCapture.json
index b64903c..3dac6f1 100644
--- a/chrome/test/data/web_apps/navigation_capturing_with_extra_browser_tab_b_BothStandalone_CaptureOnWithSelfLinkCapture.json
+++ b/chrome/test/data/web_apps/navigation_capturing_with_extra_browser_tab_b_BothStandalone_CaptureOnWithSelfLinkCapture.json
@@ -2,7 +2,7 @@
    "tests": {
       "FocusExisting_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -35,12 +35,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "FocusExistingAppBrowserTab" ],
+            "redirection_result": [  ]
          }
       },
       "FocusExisting_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -84,12 +86,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "FocusExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -119,12 +123,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "FocusExistingAppBrowserTab" ],
+            "redirection_result": [  ]
          }
       },
       "FocusExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "FocusExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -165,12 +171,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -207,12 +215,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_a/start.html" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NavigateExistingAppBrowserTab" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "NavigateExisting_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_AppWnd_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -256,12 +266,14 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       },
       "NavigateExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetBlank",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -295,12 +307,14 @@
                   } ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromNavigationCapturing" ]
+            "launch_metric_buckets": [ "kFromNavigationCapturing" ],
+            "navigation_capturing_result": [ "NavigateExistingAppBrowserTab" ],
+            "redirection_result": [ "NotHandled" ]
          }
       },
       "NavigateExisting_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf": {
          "_params": "NavigateExisting_BothStandalone_CaptureOnWithSelfLinkCapture_Tab_ScopeA2B_Direct_ViaLink_LeftClick_WithoutOpener_TargetSelf",
-         "disabled": true,
+         "disabled": false,
          "expected_state": {
             "browsers": [ {
                "browser_type": "TYPE_NORMAL",
@@ -341,7 +355,9 @@
                   "launchParams": [ "/banners/link_capturing/scope_b/destination.html?id-LINK-A_TO_B-SELF-NO_OPENER&click=Left" ]
                } ]
             } ],
-            "launch_metric_buckets": [ "kFromLink" ]
+            "launch_metric_buckets": [ "kFromLink" ],
+            "navigation_capturing_result": [ "NotHandled" ],
+            "redirection_result": [  ]
          }
       }
    }
diff --git a/chrome/test/data/webui/chromeos/personalization_app/sea_pen_input_query_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/sea_pen_input_query_element_test.ts
index 87a7d21..2ce82d8 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/sea_pen_input_query_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/sea_pen_input_query_element_test.ts
@@ -485,11 +485,12 @@
 
     const inputElement =
         seaPenInputQueryElement.shadowRoot?.querySelector<CrTextareaElement>(
-            '#queryInput');
-    assertTrue(!!inputElement?.value, 'input should show text');
+            '#queryInput')!;
+    assertTrue(!!inputElement.value, 'input should show text');
     const action = await personalizationStore.waitForAction(
                        SeaPenActionName.BEGIN_SEARCH_SEA_PEN_THUMBNAILS) as
         BeginSearchSeaPenThumbnailsAction;
+    assertFalse(seaPenInputQueryElement.i18nExists(inputElement.value));
     assertEquals(
         inputElement?.value, action.query.textQuery,
         'search query should match input value');
diff --git a/chrome/test/data/webui/lens/lens_overlay_webui_browsertest.cc b/chrome/test/data/webui/lens/lens_overlay_webui_browsertest.cc
index 4b233966..8122340c 100644
--- a/chrome/test/data/webui/lens/lens_overlay_webui_browsertest.cc
+++ b/chrome/test/data/webui/lens/lens_overlay_webui_browsertest.cc
@@ -8,6 +8,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/lens/lens_overlay_controller.h"
 #include "chrome/browser/ui/lens/lens_search_controller.h"
+#include "chrome/browser/ui/lens/lens_searchbox_controller.h"
 #include "chrome/browser/ui/tabs/public/tab_features.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -94,7 +95,8 @@
 
     // Clean up (the searchbox handler will leave a dangling pointer if not
     // explicitly destroyed).
-    overlay_controller->ResetSidePanelSearchboxHandler();
+    search_controller->lens_searchbox_controller()
+        ->ResetSidePanelSearchboxHandler();
   }
 
   // Lens overlay takes a screenshot of the tab. In order to take a screenshot
diff --git a/chrome/test/data/webui/side_panel/read_anything/fake_reading_mode.ts b/chrome/test/data/webui/side_panel/read_anything/fake_reading_mode.ts
index 0bcbdd2..eab2a73 100644
--- a/chrome/test/data/webui/side_panel/read_anything/fake_reading_mode.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/fake_reading_mode.ts
@@ -390,6 +390,11 @@
   // Signal that the page language has changed.
   languageChanged(): void {}
 
+  // Gets the accessible text boundary for the given string
+  getAccessibleBoundary(_text: string, _maxSpeechLength: number): number {
+    return 0;
+  }
+
   // Requests the image in the form of bitmap. onImageDownloaded will be
   // called when the image has been downloaded.
   requestImageData(nodeId: number): void {
diff --git a/chrome/test/data/webui/side_panel/read_anything/speech_controller_test.ts b/chrome/test/data/webui/side_panel/read_anything/speech_controller_test.ts
index 10f96396..eafc3a5 100644
--- a/chrome/test/data/webui/side_panel/read_anything/speech_controller_test.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/speech_controller_test.ts
@@ -13,7 +13,7 @@
 suite('SpeechController', () => {
   let speech: TestSpeechBrowserProxy;
   let speechController: SpeechController;
-  let onPause: boolean;
+  let onStop: boolean;
   let isSpeechActiveChanged: boolean;
   let isAudioCurrentlyPlayingChanged: boolean;
   let onPreviewVoicePlaying: boolean;
@@ -31,12 +31,12 @@
     SpeechBrowserProxyImpl.setInstance(speech);
     isSpeechActiveChanged = false;
     isAudioCurrentlyPlayingChanged = false;
-    onPause = false;
+    onStop = false;
     onPreviewVoicePlaying = false;
     onEngineStateChange = false;
     const speechListener = {
-      onPause() {
-        onPause = true;
+      onStop() {
+        onStop = true;
       },
 
       onIsSpeechActiveChange() {
@@ -79,7 +79,7 @@
 
     assertTrue(isSpeechActiveChanged);
     assertTrue(isAudioCurrentlyPlayingChanged);
-    assertFalse(onPause);
+    assertFalse(onStop);
     assertNotEquals(state, speechController.getState());
     assertTrue(speechController.isSpeechActive());
     assertTrue(speechController.isSpeechTreeInitialized());
@@ -105,7 +105,7 @@
 
     assertTrue(isSpeechActiveChanged);
     assertTrue(isAudioCurrentlyPlayingChanged);
-    assertFalse(onPause);
+    assertFalse(onStop);
     assertFalse(speechController.isSpeechActive());
     assertFalse(speechController.isSpeechTreeInitialized());
     assertEquals(PauseActionSource.DEFAULT, speechController.getPauseSource());
@@ -302,7 +302,7 @@
 
     speechController.stopSpeech(source);
 
-    assertTrue(onPause);
+    assertTrue(onStop);
     assertFalse(speechController.isSpeechActive());
     assertFalse(speechController.isAudioCurrentlyPlaying());
     assertEquals(source, speechController.getPauseSource());
@@ -310,6 +310,19 @@
     assertEquals(0, speech.getCallCount('cancel'));
   });
 
+  test('stopSpeech with button click logs play session', () => {
+    const source = PauseActionSource.BUTTON_CLICK;
+    speechController.onPlay();
+    speechController.setIsSpeechActive(true);
+
+    speechController.stopSpeech(source);
+    assertEquals(1, metrics.getCallCount('recordSpeechPlaybackLength'));
+
+    // Calling it again without playing should not log again.
+    speechController.stopSpeech(source);
+    assertEquals(1, metrics.getCallCount('recordSpeechPlaybackLength'));
+  });
+
   test('stopSpeech without button click cancels', () => {
     const source = PauseActionSource.VOICE_SETTINGS_CHANGE;
     speechController.setIsSpeechActive(true);
@@ -317,12 +330,13 @@
 
     speechController.stopSpeech(source);
 
-    assertTrue(onPause);
+    assertTrue(onStop);
     assertFalse(speechController.isSpeechActive());
     assertFalse(speechController.isAudioCurrentlyPlaying());
     assertEquals(source, speechController.getPauseSource());
     assertEquals(0, speech.getCallCount('pause'));
     assertEquals(1, speech.getCallCount('cancel'));
+    assertEquals(0, metrics.getCallCount('recordSpeechPlaybackLength'));
   });
 
   test('onSpeechInterrupted while repositioning keeps playing speech', () => {
@@ -332,7 +346,7 @@
 
     speechController.onSpeechInterrupted();
 
-    assertFalse(onPause);
+    assertFalse(onStop);
     assertTrue(speechController.isAudioCurrentlyPlaying());
     assertTrue(speechController.isSpeechActive());
     assertTrue(speechController.isSpeechBeingRepositioned());
@@ -344,7 +358,7 @@
 
     speechController.onSpeechInterrupted();
 
-    assertTrue(onPause);
+    assertTrue(onStop);
     assertEquals(
         PauseActionSource.ENGINE_INTERRUPT, speechController.getPauseSource());
     assertFalse(speechController.isAudioCurrentlyPlaying());
@@ -354,4 +368,15 @@
         chrome.readingMode.engineInterruptStopSource,
         await metrics.whenCalled('recordSpeechStopSource'));
   });
+  test('onSpeechFinished', () => {
+    speechController.onPlay();
+    speechController.setIsSpeechActive(true);
+
+    speechController.onSpeechFinished();
+
+    assertTrue(onStop);
+    assertEquals(1, metrics.getCallCount('recordSpeechPlaybackLength'));
+    assertEquals(1, metrics.getCallCount('recordSpeechStopSource'));
+    assertFalse(speechController.isSpeechActive());
+  });
 });
diff --git a/chrome/test/data/webui/side_panel/read_anything/speech_test.ts b/chrome/test/data/webui/side_panel/read_anything/speech_test.ts
index d42be456..1d17b6e 100644
--- a/chrome/test/data/webui/side_panel/read_anything/speech_test.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/speech_test.ts
@@ -428,7 +428,8 @@
           selectedVoice: speech.getVoices()[0],
         },
       });
-      const accessibleTextLength = app.getAccessibleTextLength(longSentences);
+      const accessibleTextLength =
+          speechController.getUtteranceEndBoundary(longSentences, true);
       app.playSpeech();
       assertEquals(longSentences, getSpokenText());
       const utterance = speech.getArgs('speak')[0];
diff --git a/chrome/test/data/webui/side_panel/read_anything/speech_uses_max_text_length_test.ts b/chrome/test/data/webui/side_panel/read_anything/speech_uses_max_text_length_test.ts
index 0550b43..d4b52d3 100644
--- a/chrome/test/data/webui/side_panel/read_anything/speech_uses_max_text_length_test.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/speech_uses_max_text_length_test.ts
@@ -4,7 +4,7 @@
 import 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js';
 
 import type {AppElement} from 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js';
-import {MAX_SPEECH_LENGTH, SpeechBrowserProxyImpl} from 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js';
+import {MAX_SPEECH_LENGTH, SpeechBrowserProxyImpl, SpeechController} from 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js';
 import {assertEquals, assertGT, assertLT, assertNotEquals} from 'chrome-untrusted://webui-test/chai_assert.js';
 
 import {createApp} from './common.js';
@@ -14,6 +14,7 @@
   let app: AppElement;
   let maxSpeechLength: number;
   let speech: TestSpeechBrowserProxy;
+  let speechController: SpeechController;
 
   const shortSentence =
       'The snow glows white on the mountain tonight, not a footprint to be ' +
@@ -79,6 +80,8 @@
     chrome.readingMode.onConnected = () => {};
     speech = new TestSpeechBrowserProxy();
     SpeechBrowserProxyImpl.setInstance(speech);
+    speechController = new SpeechController();
+    SpeechController.setInstance(speechController);
 
     app = await createApp();
     maxSpeechLength = MAX_SPEECH_LENGTH;
@@ -103,7 +106,8 @@
 
   suite('on long sentence', () => {
     test('accessible text boundary is before max speech length', () => {
-      const firstBoundary = app.getAccessibleTextLength(longSentence);
+      const firstBoundary =
+          speechController.getUtteranceEndBoundary(longSentence, true);
       assertLT(firstBoundary, maxSpeechLength);
     });
 
@@ -122,7 +126,8 @@
     let firstBoundary: number;
 
     setup(() => {
-      firstBoundary = app.getAccessibleTextLength(longSentenceWithFewCommas);
+      firstBoundary = speechController.getUtteranceEndBoundary(
+          longSentenceWithFewCommas, true);
     });
 
 
@@ -134,7 +139,8 @@
     test('next accessible text boundary is before end of string', () => {
       const afterFirstBoundary = longSentenceWithFewCommas.substring(
           firstBoundary, longSentenceWithFewCommas.length);
-      const secondBoundary = app.getAccessibleTextLength(afterFirstBoundary);
+      const secondBoundary =
+          speechController.getUtteranceEndBoundary(afterFirstBoundary, true);
       const afterSecondBoundary = longSentenceWithFewCommas.substring(
           secondBoundary, longSentenceWithFewCommas.length);
 
@@ -150,13 +156,14 @@
 
     // When there are no other commas in a phrase, we don't splice on the
     // commas within numbers.
-    let boundary = app.getAccessibleTextLength(invalidCommaSplices);
+    let boundary =
+        speechController.getUtteranceEndBoundary(invalidCommaSplices, true);
     assertEquals(
         invalidCommaSplices, invalidCommaSplices.substring(0, boundary));
 
     // When there is a valid comma in a string, we splice on that instead of
     // on the commas within numbers
-    boundary = app.getAccessibleTextLength(validCommaSplice);
+    boundary = speechController.getUtteranceEndBoundary(validCommaSplice, true);
     assertEquals('525,600 minutes', validCommaSplice.substring(0, boundary));
   });
 
@@ -164,17 +171,20 @@
     let firstBoundary: number;
 
     setup(() => {
-      firstBoundary = app.getAccessibleTextLength(longSentenceWithLateComma);
+      firstBoundary = speechController.getUtteranceEndBoundary(
+          longSentenceWithLateComma, true);
     });
 
 
     test('commas after max speech length aren\'t used', () => {
       const afterFirstBoundary = longSentenceWithFewCommas.substring(
           firstBoundary, longSentenceWithFewCommas.length);
-      const secondBoundary = app.getAccessibleTextLength(afterFirstBoundary);
+      const secondBoundary =
+          speechController.getUtteranceEndBoundary(afterFirstBoundary, true);
       const afterSecondBoundary = longSentenceWithFewCommas.substring(
           firstBoundary, longSentenceWithFewCommas.length);
-      const thirdBoundary = app.getAccessibleTextLength(afterSecondBoundary);
+      const thirdBoundary =
+          speechController.getUtteranceEndBoundary(afterSecondBoundary, true);
 
       assertLT(firstBoundary, maxSpeechLength);
       assertLT(secondBoundary, maxSpeechLength);
diff --git a/chrome/updater/test/integration_tests.cc b/chrome/updater/test/integration_tests.cc
index a81b2e4..49635df 100644
--- a/chrome/updater/test/integration_tests.cc
+++ b/chrome/updater/test/integration_tests.cc
@@ -5098,19 +5098,31 @@
 }
 
 TEST_F(IntegrationTest, OfflineInstall) {
+  ScopedServer test_server(test_commands_);
+  ExpectInstallEvent(test_server, kUpdaterAppId);
   ASSERT_NO_FATAL_FAILURE(Install({kEnableCecaExperimentSwitch}));
   ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
+
+  ExpectInstallEvent(test_server, "{CDABE316-39CD-43BA-8440-6D1E0547AEE6}");
   ASSERT_NO_FATAL_FAILURE(RunOfflineInstall(/*is_legacy_install=*/false,
                                             /*is_silent_install=*/false));
+
+  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
   ASSERT_NO_FATAL_FAILURE(Uninstall());
 }
 
 TEST_F(IntegrationTest, OfflineInstallOsNotSupported) {
+  ScopedServer test_server(test_commands_);
+  ExpectInstallEvent(test_server, kUpdaterAppId);
   ASSERT_NO_FATAL_FAILURE(Install({kEnableCecaExperimentSwitch}));
   ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
+
+  // OS not supported is handled by the client, hence no ping.
   ASSERT_NO_FATAL_FAILURE(
       RunOfflineInstallOsNotSupported(/*is_legacy_install=*/false,
                                       /*is_silent_install=*/false));
+
+  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
   ASSERT_NO_FATAL_FAILURE(Uninstall());
 }
 
@@ -5135,44 +5147,71 @@
 }
 
 TEST_F(IntegrationTest, OfflineInstallSilent) {
+  ScopedServer test_server(test_commands_);
+  ExpectInstallEvent(test_server, kUpdaterAppId);
   ASSERT_NO_FATAL_FAILURE(Install({kEnableCecaExperimentSwitch}));
   ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
+
+  ExpectInstallEvent(test_server, "{CDABE316-39CD-43BA-8440-6D1E0547AEE6}");
   ASSERT_NO_FATAL_FAILURE(RunOfflineInstall(/*is_legacy_install=*/false,
                                             /*is_silent_install=*/true));
+
+  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
   ASSERT_NO_FATAL_FAILURE(Uninstall());
 }
 
 TEST_F(IntegrationTest, OfflineInstallOsNotSupportedSilent) {
+  ScopedServer test_server(test_commands_);
+  ExpectInstallEvent(test_server, kUpdaterAppId);
   ASSERT_NO_FATAL_FAILURE(Install({kEnableCecaExperimentSwitch}));
   ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
+
+  // OS not supported is handled by the client, hence no ping.
   ASSERT_NO_FATAL_FAILURE(
       RunOfflineInstallOsNotSupported(/*is_legacy_install=*/false,
                                       /*is_silent_install=*/true));
+
+  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
   ASSERT_NO_FATAL_FAILURE(Uninstall());
 }
 
 TEST_F(IntegrationTest, OfflineInstallSilentLegacy) {
+  ScopedServer test_server(test_commands_);
+  ExpectInstallEvent(test_server, kUpdaterAppId);
   ASSERT_NO_FATAL_FAILURE(Install({kEnableCecaExperimentSwitch}));
   ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
+
+  ExpectInstallEvent(test_server, "{CDABE316-39CD-43BA-8440-6D1E0547AEE6}");
   ASSERT_NO_FATAL_FAILURE(RunOfflineInstall(/*is_legacy_install=*/true,
                                             /*is_silent_install=*/true));
+
+  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
   ASSERT_NO_FATAL_FAILURE(Uninstall());
 }
 
 TEST_F(IntegrationTest, OfflineInstallOsNotSupportedSilentLegacy) {
+  ScopedServer test_server(test_commands_);
+  ExpectInstallEvent(test_server, kUpdaterAppId);
   ASSERT_NO_FATAL_FAILURE(Install({kEnableCecaExperimentSwitch}));
   ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
+
+  // OS not supported is handled by the client, hence no ping.
   ASSERT_NO_FATAL_FAILURE(
       RunOfflineInstallOsNotSupported(/*is_legacy_install=*/true,
                                       /*is_silent_install=*/true));
+
+  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
   ASSERT_NO_FATAL_FAILURE(Uninstall());
 }
 
 TEST_F(IntegrationTest, OfflineInstallEulaRequired) {
+  ScopedServer test_server(test_commands_);
   ASSERT_NO_FATAL_FAILURE(Install({kEulaRequiredSwitch}));
   ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
+
   ASSERT_NO_FATAL_FAILURE(RunOfflineInstall(/*is_legacy_install=*/false,
                                             /*is_silent_install=*/false));
+
   ASSERT_NO_FATAL_FAILURE(Uninstall());
 }
 
@@ -5185,10 +5224,16 @@
     ASSERT_NO_FATAL_FAILURE(ResetOemMode());
   };
 
+  ScopedServer test_server(test_commands_);
+  ExpectInstallEvent(test_server, kUpdaterAppId);
   ASSERT_NO_FATAL_FAILURE(Install({kOemSwitch}));
   ASSERT_NO_FATAL_FAILURE(ExpectInstalled());
+
+  ExpectInstallEvent(test_server, "{CDABE316-39CD-43BA-8440-6D1E0547AEE6}");
   ASSERT_NO_FATAL_FAILURE(RunOfflineInstall(/*is_legacy_install=*/false,
                                             /*is_silent_install=*/false));
+
+  ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
   ASSERT_NO_FATAL_FAILURE(Uninstall());
 }
 
diff --git a/chrome/updater/update_service_impl_impl.cc b/chrome/updater/update_service_impl_impl.cc
index d7c80d7..ec6a875 100644
--- a/chrome/updater/update_service_impl_impl.cc
+++ b/chrome/updater/update_service_impl_impl.cc
@@ -676,7 +676,7 @@
   VLOG(1) << "Starting an OTA installation of the enterprise companion app.";
   RegistrationRequest registration;
   registration.app_id = enterprise_companion::kCompanionAppId;
-  registration.version = base::Version("0.0.0.0");
+  registration.version = base::Version(kNullVersion);
   RegisterApp(
       registration,
       base::BindOnce([](int registration_result) {})
@@ -754,7 +754,7 @@
                          ->GetProductVersion(request.app_id)
                          .IsValid() &&
                     request.version.IsValid() &&
-                    request.version > base::Version("0") &&
+                    request.version > base::Version(kNullVersion) &&
                     !config_->GetUpdaterPersistedData()->GetEulaRequired();
   config_->GetUpdaterPersistedData()->RegisterApp(request);
   if (send_event) {
diff --git a/chromecast/app/BUILD.gn b/chromecast/app/BUILD.gn
index 4184134..65a3c1d 100644
--- a/chromecast/app/BUILD.gn
+++ b/chromecast/app/BUILD.gn
@@ -149,13 +149,11 @@
   ]
 }
 
-grit("chromecast_settings") {
+grit_strings("chromecast_settings") {
   source = "//chromecast/app/resources/chromecast_settings.grd"
 
   outputs = [ "grit/chromecast_settings.h" ]
-  foreach(locale, cast_locales) {
-    outputs += [ "chromecast_settings_${locale}.pak" ]
-  }
+  locales = cast_locales
 
   deps = [ ":verify_cast_locales" ]
 }
diff --git a/chromeos/strings/BUILD.gn b/chromeos/strings/BUILD.gn
index 2c09b71..fa307af 100644
--- a/chromeos/strings/BUILD.gn
+++ b/chromeos/strings/BUILD.gn
@@ -6,12 +6,10 @@
 import("//tools/grit/grit_rule.gni")
 import("//tools/grit/repack.gni")
 
-grit("strings") {
+grit_strings("strings") {
   source = "../chromeos_strings.grd"
-  outputs =
-      [ "grit/chromeos_strings.h" ] +
-      process_file_template(all_chrome_locales,
-                            [ "chromeos_strings_{{source_name_part}}.pak" ])
+  outputs = [ "grit/chromeos_strings.h" ]
+  output_prefix = "chromeos_strings_"
 }
 
 repack("chromeos_test_strings") {
diff --git a/clank b/clank
index 1881fd2..86901d8 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit 1881fd29e10d5d32e8b8f6ed892c860e43dded76
+Subproject commit 86901d8e9a33f78ccf07acfa57b63439e7cfa5f8
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 3c5bb6b..0d4b53b 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -34,6 +34,12 @@
   import("//ios/build/config.gni")
 }
 
+if (translate_genders) {
+  _gender_suffix = "_OTHER"
+} else {
+  _gender_suffix = ""
+}
+
 if (is_ios) {
   bundle_data("components_tests_pak_bundle_data") {
     testonly = true
@@ -916,11 +922,11 @@
     "$root_gen_dir/components/autofill/core/browser/geo/autofill_address_rewriter_resources.pak",
     "$root_gen_dir/components/components_resources.pak",
     "$root_gen_dir/components/metrics/metrics_server_urls.pak",
-    "$root_gen_dir/components/omnibox/resources/omnibox_pedal_synonyms_en-US.pak",
-    "$root_gen_dir/components/plus_addresses/resources/strings/plus_addresses_strings_en-US.pak",
-    "$root_gen_dir/components/strings/components_branded_strings_en-US.pak",
-    "$root_gen_dir/components/strings/components_locale_settings_en-US.pak",
-    "$root_gen_dir/components/strings/components_strings_en-US.pak",
+    "$root_gen_dir/components/omnibox/resources/omnibox_pedal_synonyms_en-US${_gender_suffix}.pak",
+    "$root_gen_dir/components/plus_addresses/resources/strings/plus_addresses_strings_en-US${_gender_suffix}.pak",
+    "$root_gen_dir/components/strings/components_branded_strings_en-US${_gender_suffix}.pak",
+    "$root_gen_dir/components/strings/components_locale_settings_en-US${_gender_suffix}.pak",
+    "$root_gen_dir/components/strings/components_strings_en-US${_gender_suffix}.pak",
   ]
 
   output = "$root_out_dir/components_tests_resources.pak"
diff --git a/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/BrowserControlsVisibilityDelegate.java b/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/BrowserControlsVisibilityDelegate.java
index a19a3ed..c1d0c42 100644
--- a/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/BrowserControlsVisibilityDelegate.java
+++ b/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/BrowserControlsVisibilityDelegate.java
@@ -4,7 +4,6 @@
 
 package org.chromium.components.browser_ui.util;
 
-import org.chromium.base.lifetime.Destroyable;
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.cc.input.BrowserControlsState;
@@ -12,7 +11,7 @@
 /** A delegate to determine visibility of the browser controls. */
 @NullMarked
 public class BrowserControlsVisibilityDelegate
-        extends ObservableSupplierImpl<@BrowserControlsState Integer> implements Destroyable {
+        extends ObservableSupplierImpl<@BrowserControlsState Integer> {
     /**
      * Constructs a delegate that controls the visibility of the browser controls.
      *
@@ -21,9 +20,4 @@
     public BrowserControlsVisibilityDelegate(@BrowserControlsState int initialValue) {
         super.set(initialValue);
     }
-
-    @Override
-    public void destroy() {
-        // This class has nothing to clean up, but derived classes may.
-    }
 }
diff --git a/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/ComposedBrowserControlsVisibilityDelegate.java b/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/ComposedBrowserControlsVisibilityDelegate.java
index 6a0b186b..bd3f4ef0 100644
--- a/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/ComposedBrowserControlsVisibilityDelegate.java
+++ b/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/ComposedBrowserControlsVisibilityDelegate.java
@@ -90,13 +90,6 @@
         super.set(value);
     }
 
-    @Override
-    public void destroy() {
-        for (BrowserControlsVisibilityDelegate delegate : mDelegates) {
-            delegate.destroy();
-        }
-    }
-
     private @BrowserControlsState int calculateVisibilityConstraints() {
         boolean shouldBeShown = false;
         for (BrowserControlsVisibilityDelegate delegate : mDelegates) {
diff --git a/components/cast_streaming/renderer/frame/frame_injecting_demuxer.cc b/components/cast_streaming/renderer/frame/frame_injecting_demuxer.cc
index 3aab391..2ba9135 100644
--- a/components/cast_streaming/renderer/frame/frame_injecting_demuxer.cc
+++ b/components/cast_streaming/renderer/frame/frame_injecting_demuxer.cc
@@ -496,23 +496,14 @@
 }
 
 // Not supported.
-void FrameInjectingDemuxer::OnEnabledAudioTracksChanged(
+void FrameInjectingDemuxer::OnTracksChanged(
+    media::DemuxerStream::Type track_type,
     const std::vector<media::MediaTrack::Id>& track_ids,
     base::TimeDelta curr_time,
     TrackChangeCB change_completed_cb) {
-  DLOG(WARNING) << "Track changes are not supported.";
-  std::vector<media::DemuxerStream*> streams;
-  std::move(change_completed_cb).Run(streams);
-}
-
-// Not supported.
-void FrameInjectingDemuxer::OnSelectedVideoTrackChanged(
-    const std::vector<media::MediaTrack::Id>& track_ids,
-    base::TimeDelta curr_time,
-    TrackChangeCB change_completed_cb) {
-  DLOG(WARNING) << "Track changes are not supported.";
-  std::vector<media::DemuxerStream*> streams;
-  std::move(change_completed_cb).Run(streams);
+  // TODO(crbug.com/416543891): Demuxers should have a way to report that they
+  // do not support exposing track switching to JS.
+  NOTREACHED();
 }
 
 }  // namespace cast_streaming
diff --git a/components/cast_streaming/renderer/frame/frame_injecting_demuxer.h b/components/cast_streaming/renderer/frame/frame_injecting_demuxer.h
index 5a6559a..8fed71f5 100644
--- a/components/cast_streaming/renderer/frame/frame_injecting_demuxer.h
+++ b/components/cast_streaming/renderer/frame/frame_injecting_demuxer.h
@@ -64,14 +64,10 @@
   int64_t GetMemoryUsage() const override;
   std::optional<media::container_names::MediaContainerName>
   GetContainerForMetrics() const override;
-  void OnEnabledAudioTracksChanged(
-      const std::vector<media::MediaTrack::Id>& track_ids,
-      base::TimeDelta curr_time,
-      TrackChangeCB change_completed_cb) override;
-  void OnSelectedVideoTrackChanged(
-      const std::vector<media::MediaTrack::Id>& track_ids,
-      base::TimeDelta curr_time,
-      TrackChangeCB change_completed_cb) override;
+  void OnTracksChanged(media::DemuxerStream::Type track_type,
+                       const std::vector<media::MediaTrack::Id>& track_ids,
+                       base::TimeDelta curr_time,
+                       TrackChangeCB change_completed_cb) override;
   void SetPlaybackRate(double rate) override {}
 
   // The number of initialized streams that have yet to call
diff --git a/components/collaboration/internal/collaboration_controller.cc b/components/collaboration/internal/collaboration_controller.cc
index 6c5bf8f0..2c5bec3 100644
--- a/components/collaboration/internal/collaboration_controller.cc
+++ b/components/collaboration/internal/collaboration_controller.cc
@@ -274,7 +274,7 @@
   void HandleError() override {
     ServiceStatus status =
         controller->collaboration_service()->GetServiceStatus();
-    if (status.signin_status == SigninStatus::kNotSignedIn) {
+    if (status.signin_status == SigninStatus::kSigninDisabled) {
       RecordJoinOrShareOrManageEvent(
           GetLogger(), controller->flow().type,
           CollaborationServiceJoinEvent::kDevicePolicyDisableSignin,
diff --git a/components/collaboration/internal/collaboration_controller_unittest.cc b/components/collaboration/internal/collaboration_controller_unittest.cc
index f34ce7de..7f59111 100644
--- a/components/collaboration/internal/collaboration_controller_unittest.cc
+++ b/components/collaboration/internal/collaboration_controller_unittest.cc
@@ -14,6 +14,7 @@
 #include "components/collaboration/internal/metrics.h"
 #include "components/collaboration/public/collaboration_controller_delegate.h"
 #include "components/collaboration/public/collaboration_flow_type.h"
+#include "components/collaboration/public/service_status.h"
 #include "components/collaboration/test_support/mock_collaboration_controller_delegate.h"
 #include "components/collaboration/test_support/mock_collaboration_service.h"
 #include "components/data_sharing/public/data_sharing_service.h"
@@ -66,6 +67,8 @@
     tab_group_sync_service_ =
         std::make_unique<tab_groups::MockTabGroupSyncService>();
     sync_service_ = std::make_unique<syncer::MockSyncService>();
+    EXPECT_CALL(*data_sharing_service_, GetLogger())
+        .Times(::testing::AnyNumber());
   }
 
   void TearDown() override {}
@@ -270,7 +273,7 @@
 
   // Simulate managed device.
   ServiceStatus status;
-  status.signin_status = SigninStatus::kNotSignedIn;
+  status.signin_status = SigninStatus::kSigninDisabled;
   status.sync_status = SyncStatus::kNotSyncing;
   status.collaboration_status = CollaborationStatus::kDisabledForPolicy;
   EXPECT_CALL(*collaboration_service_, GetServiceStatus())
@@ -327,7 +330,7 @@
 
   // Simulate managed account with info not ready.
   ServiceStatus status;
-  status.signin_status = SigninStatus::kNotSignedIn;
+  status.signin_status = SigninStatus::kSigninDisabled;
   status.sync_status = SyncStatus::kNotSyncing;
   status.collaboration_status = CollaborationStatus::kDisabledPending;
   EXPECT_CALL(*collaboration_service_, GetServiceStatus())
@@ -341,6 +344,7 @@
             StateId::kWaitingForPolicyUpdate);
 
   // The managed account info become available.
+  EXPECT_CALL(*collaboration_service_, RemoveObserver(_));
   EXPECT_CALL(*delegate_,
               ShowError(ErrorInfo(ErrorInfo::Type::kSigninDisabledByPolicy),
                         IsNotNullCallback()));
diff --git a/components/collaboration/internal/collaboration_service_impl.cc b/components/collaboration/internal/collaboration_service_impl.cc
index e3b350f..02f2b83 100644
--- a/components/collaboration/internal/collaboration_service_impl.cc
+++ b/components/collaboration/internal/collaboration_service_impl.cc
@@ -58,9 +58,14 @@
   current_status_.collaboration_status = GetCollaborationStatus();
 
   registrar_.Init(profile_prefs_);
-  registrar_.Add(prefs::kSharedTabGroupsManagedAccountSetting,
-                 base::BindRepeating(&CollaborationServiceImpl::OnPrefChanged,
-                                     base::Unretained(this)));
+  registrar_.Add(
+      prefs::kSharedTabGroupsManagedAccountSetting,
+      base::BindRepeating(&CollaborationServiceImpl::RefreshServiceStatus,
+                          base::Unretained(this)));
+  registrar_.Add(
+      ::prefs::kSigninAllowed,
+      base::BindRepeating(&CollaborationServiceImpl::RefreshServiceStatus,
+                          base::Unretained(this)));
 }
 
 CollaborationServiceImpl::~CollaborationServiceImpl() {
@@ -347,6 +352,8 @@
   } else if (identity_manager_->HasPrimaryAccount(
                  signin::ConsentLevel::kSignin)) {
     status = SigninStatus::kSignedInPaused;
+  } else if (!profile_prefs_->GetBoolean(::prefs::kSigninAllowed)) {
+    status = SigninStatus::kSigninDisabled;
   }
 
   return status;
@@ -354,7 +361,8 @@
 
 CollaborationStatus CollaborationServiceImpl::GetCollaborationStatus() {
   // Check if device policy allow signin.
-  if (!profile_prefs_->GetBoolean(::prefs::kSigninAllowed)) {
+  if (!profile_prefs_->GetBoolean(::prefs::kSigninAllowed) &&
+      profile_prefs_->IsManagedPreference(::prefs::kSigninAllowed)) {
     return CollaborationStatus::kDisabledForPolicy;
   }
 
@@ -500,8 +508,4 @@
   std::move(callback).Run(/*success=*/false);
 }
 
-void CollaborationServiceImpl::OnPrefChanged() {
-  RefreshServiceStatus();
-}
-
 }  // namespace collaboration
diff --git a/components/collaboration/internal/collaboration_service_impl.h b/components/collaboration/internal/collaboration_service_impl.h
index f80432d..22267ef9 100644
--- a/components/collaboration/internal/collaboration_service_impl.h
+++ b/components/collaboration/internal/collaboration_service_impl.h
@@ -116,7 +116,6 @@
       const data_sharing::GroupId& group_id,
       base::OnceCallback<void(bool)> callback,
       data_sharing::DataSharingService::PeopleGroupActionOutcome result);
-  void OnPrefChanged();
 
   ServiceStatus current_status_;
   base::ScopedObservation<syncer::SyncService, syncer::SyncServiceObserver>
diff --git a/components/collaboration/internal/collaboration_service_impl_unittest.cc b/components/collaboration/internal/collaboration_service_impl_unittest.cc
index 5601097..d835b6f 100644
--- a/components/collaboration/internal/collaboration_service_impl_unittest.cc
+++ b/components/collaboration/internal/collaboration_service_impl_unittest.cc
@@ -160,23 +160,21 @@
             CollaborationStatus::kEnabledCreateAndJoin);
 }
 
-TEST_F(CollaborationServiceImplTest, GetServiceStatus_ManagedDevice) {
+TEST_F(CollaborationServiceImplTest, GetServiceStatus_SigninDisabled) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(
       data_sharing::features::kDataSharingFeature);
 
-  // Set device policy to disable signin.
+  // Set signin preference to disable signin.
   pref_service_.SetBoolean(prefs::kSigninAllowed, false);
   InitService();
 
   EXPECT_EQ(service_->GetServiceStatus().signin_status,
-            SigninStatus::kNotSignedIn);
+            SigninStatus::kSigninDisabled);
   EXPECT_EQ(service_->GetServiceStatus().collaboration_status,
-            CollaborationStatus::kDisabledForPolicy);
-  identity_test_env_.MakePrimaryAccountAvailable(kConsumerUserEmail,
-                                                 signin::ConsentLevel::kSignin);
-  EXPECT_EQ(service_->GetServiceStatus().signin_status,
-            SigninStatus::kSignedIn);
+            CollaborationStatus::kEnabledCreateAndJoin);
+
+  pref_service_.SetManagedPref(prefs::kSigninAllowed, base::Value(false));
   EXPECT_EQ(service_->GetServiceStatus().collaboration_status,
             CollaborationStatus::kDisabledForPolicy);
 }
diff --git a/components/collaboration/internal/metrics.cc b/components/collaboration/internal/metrics.cc
index fef689d..eb3069fb 100644
--- a/components/collaboration/internal/metrics.cc
+++ b/components/collaboration/internal/metrics.cc
@@ -285,6 +285,7 @@
 
 void RecordJoinEvent(data_sharing::Logger* logger,
                      CollaborationServiceJoinEvent event) {
+  VLOG(1) << "RecordJoinEvent:" << (CreateJoinEventLogString(event));
   base::UmaHistogramEnumeration("CollaborationService.JoinFlow", event);
   DATA_SHARING_LOG(logger_common::mojom::LogSource::CollaborationService,
                    logger, CreateJoinEventLogString(event));
@@ -292,6 +293,8 @@
 
 void RecordShareOrManageEvent(data_sharing::Logger* logger,
                               CollaborationServiceShareOrManageEvent event) {
+  VLOG(1) << "RecordShareOrManageEvent:"
+          << (CreateShareOrManageEventLogString(event));
   base::UmaHistogramEnumeration("CollaborationService.ShareOrManageFlow",
                                 event);
   DATA_SHARING_LOG(logger_common::mojom::LogSource::CollaborationService,
diff --git a/components/collaboration/public/service_status.h b/components/collaboration/public/service_status.h
index 22302f4..0ef17d98 100644
--- a/components/collaboration/public/service_status.h
+++ b/components/collaboration/public/service_status.h
@@ -11,8 +11,10 @@
 //   org.chromium.components.collaboration)
 enum class SigninStatus {
   kNotSignedIn = 0,
-  kSignedInPaused = 1,
-  kSignedIn = 2
+  // Signin is disabled either in user setting or by enterprise policy.
+  kSigninDisabled = 1,
+  kSignedInPaused = 2,
+  kSignedIn = 3
 };
 
 // GENERATED_JAVA_ENUM_PACKAGE: (
diff --git a/components/collaboration_strings.grdp b/components/collaboration_strings.grdp
index 0d7229ac..740ffd43 100644
--- a/components/collaboration_strings.grdp
+++ b/components/collaboration_strings.grdp
@@ -158,8 +158,8 @@
   <message name="IDS_DATA_SHARING_RECENT_ACTIVITY_UNKNOWN_USER" desc="The user name to show on the recent activity bottom sheet row if the user info cannot be found, e.g. deleted their account.">
     Deleted account
   </message>
-  <message name ="IDS_DATA_SHARING_SHARED_TAB_GROUP_ACTIVITY" desc="The text on the button to show the full recent activity logs and manage screen menu options." formatter_data="android_java">
-    Shared tab group activity
+  <message name ="IDS_DATA_SHARING_SHARED_TAB_GROUPS_ACTIVITY" desc="The text on the button to show the full recent activity logs and manage screen menu options." formatter_data="android_java">
+    Shared tab groups activity
   </message>
   <!-- Recent Activity -->
 
diff --git a/components/collaboration_strings_grdp/IDS_DATA_SHARING_SHARED_TAB_GROUPS_ACTIVITY.png.sha1 b/components/collaboration_strings_grdp/IDS_DATA_SHARING_SHARED_TAB_GROUPS_ACTIVITY.png.sha1
new file mode 100644
index 0000000..9070107
--- /dev/null
+++ b/components/collaboration_strings_grdp/IDS_DATA_SHARING_SHARED_TAB_GROUPS_ACTIVITY.png.sha1
@@ -0,0 +1 @@
+f91992bb976e99cd15f326dfb6147fd952d2441a
\ No newline at end of file
diff --git a/components/collaboration_strings_grdp/IDS_DATA_SHARING_SHARED_TAB_GROUP_ACTIVITY.png.sha1 b/components/collaboration_strings_grdp/IDS_DATA_SHARING_SHARED_TAB_GROUP_ACTIVITY.png.sha1
deleted file mode 100644
index d61607d0..0000000
--- a/components/collaboration_strings_grdp/IDS_DATA_SHARING_SHARED_TAB_GROUP_ACTIVITY.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-cf9b27802baddb7c506b71dc00ba116852a36e3f
\ No newline at end of file
diff --git a/components/navigation_interception/intercept_navigation_delegate.cc b/components/navigation_interception/intercept_navigation_delegate.cc
index d9564672..4ecedcb 100644
--- a/components/navigation_interception/intercept_navigation_delegate.cc
+++ b/components/navigation_interception/intercept_navigation_delegate.cc
@@ -137,9 +137,8 @@
 }
 
 // static
-std::unique_ptr<content::NavigationThrottle>
-InterceptNavigationDelegate::MaybeCreateThrottleFor(
-    content::NavigationHandle* handle,
+void InterceptNavigationDelegate::MaybeCreateAndAdd(
+    content::NavigationThrottleRegistry& registry,
     navigation_interception::SynchronyMode mode) {
   // Navigations in a subframe or non-primary frame tree should not be
   // intercepted. As examples of a non-primary frame tree, a navigation
@@ -153,26 +152,28 @@
   // have been launched (without launching the intent). It's also not clear
   // what the right behavior for <portal> elements is.
   // https://crbug.com/1227659.
-  if (!handle->IsInPrimaryMainFrame())
-    return nullptr;
-
-  InterceptNavigationDelegate* intercept_navigation_delegate =
-      InterceptNavigationDelegate::Get(handle->GetWebContents());
-
-  if (!intercept_navigation_delegate) {
-    return std::make_unique<InterceptNavigationThrottle>(
-        handle, base::BindRepeating(&AllowNavigationToProceed), mode,
-        base::DoNothing());
+  if (!registry.GetNavigationHandle().IsInPrimaryMainFrame()) {
+    return;
   }
 
-  return std::make_unique<InterceptNavigationThrottle>(
-      handle,
+  InterceptNavigationDelegate* intercept_navigation_delegate =
+      InterceptNavigationDelegate::Get(
+          registry.GetNavigationHandle().GetWebContents());
+
+  if (!intercept_navigation_delegate) {
+    registry.AddThrottle(std::make_unique<InterceptNavigationThrottle>(
+        registry, base::BindRepeating(&AllowNavigationToProceed), mode,
+        base::DoNothing()));
+  } else {
+  registry.AddThrottle(std::make_unique<InterceptNavigationThrottle>(
+      registry,
       base::BindRepeating(&InterceptNavigationDelegate::ShouldIgnoreNavigation,
                           base::Unretained(intercept_navigation_delegate)),
       mode,
       base::BindRepeating(
           &InterceptNavigationDelegate::RequestFinishPendingShouldIgnoreCheck,
-          base::Unretained(intercept_navigation_delegate)));
+          base::Unretained(intercept_navigation_delegate))));
+  }
 }
 
 InterceptNavigationDelegate::InterceptNavigationDelegate(
@@ -278,21 +279,24 @@
   GURL escaped_url = escape_external_handler_value_
                          ? GURL(base::EscapeExternalHandlerValue(url.spec()))
                          : url;
-  if (!escaped_url.is_valid())
+  if (!escaped_url.is_valid()) {
     return;
+  }
 
   JNIEnv* env = base::android::AttachCurrentThread();
   ScopedJavaLocalRef<jobject> jdelegate = weak_jdelegate_.get(env);
 
-  if (jdelegate.is_null())
+  if (jdelegate.is_null()) {
     return;
+  }
   ScopedJavaLocalRef<jobject> j_gurl =
       Java_InterceptNavigationDelegate_handleSubframeExternalProtocol(
           env, jdelegate, url::GURLAndroid::FromNativeGURL(env, escaped_url),
           page_transition, has_user_gesture,
           initiating_origin ? initiating_origin->ToJavaObject(env) : nullptr);
-  if (j_gurl.is_null())
+  if (j_gurl.is_null()) {
     return;
+  }
   subframe_redirect_url_ =
       std::make_unique<GURL>(url::GURLAndroid::ToNativeGURL(env, j_gurl));
 
@@ -335,8 +339,9 @@
 void InterceptNavigationDelegate::OnResourceRequestWithGesture() {
   JNIEnv* env = base::android::AttachCurrentThread();
   ScopedJavaLocalRef<jobject> jdelegate = weak_jdelegate_.get(env);
-  if (jdelegate.is_null())
+  if (jdelegate.is_null()) {
     return;
+  }
   Java_InterceptNavigationDelegate_onResourceRequestWithGesture(env, jdelegate);
 }
 
diff --git a/components/navigation_interception/intercept_navigation_delegate.h b/components/navigation_interception/intercept_navigation_delegate.h
index 6a3d5e49..00dacc2 100644
--- a/components/navigation_interception/intercept_navigation_delegate.h
+++ b/components/navigation_interception/intercept_navigation_delegate.h
@@ -79,9 +79,8 @@
 
   // Creates a InterceptNavigationThrottle that will direct all callbacks to
   // the InterceptNavigationDelegate.
-  static std::unique_ptr<content::NavigationThrottle> MaybeCreateThrottleFor(
-      content::NavigationHandle* handle,
-      navigation_interception::SynchronyMode mode);
+  static void MaybeCreateAndAdd(content::NavigationThrottleRegistry& registry,
+                                navigation_interception::SynchronyMode mode);
 
   void ShouldIgnoreNavigation(
       content::NavigationHandle* navigation_handle,
diff --git a/components/navigation_interception/intercept_navigation_throttle.cc b/components/navigation_interception/intercept_navigation_throttle.cc
index 32dd931..1f7ed4fed 100644
--- a/components/navigation_interception/intercept_navigation_throttle.cc
+++ b/components/navigation_interception/intercept_navigation_throttle.cc
@@ -19,11 +19,11 @@
              base::FEATURE_ENABLED_BY_DEFAULT);
 
 InterceptNavigationThrottle::InterceptNavigationThrottle(
-    content::NavigationHandle* navigation_handle,
+    content::NavigationThrottleRegistry& registry,
     CheckCallback should_ignore_callback,
     SynchronyMode async_mode,
     std::optional<base::RepeatingClosure> request_finish_async_work_callback)
-    : content::NavigationThrottle(navigation_handle),
+    : content::NavigationThrottle(registry),
       should_ignore_callback_(should_ignore_callback),
       request_finish_async_work_callback_(request_finish_async_work_callback),
       ui_task_runner_(base::SingleThreadTaskRunner::GetCurrentDefault()),
diff --git a/components/navigation_interception/intercept_navigation_throttle.h b/components/navigation_interception/intercept_navigation_throttle.h
index bfc20ca..31b865d6 100644
--- a/components/navigation_interception/intercept_navigation_throttle.h
+++ b/components/navigation_interception/intercept_navigation_throttle.h
@@ -14,6 +14,7 @@
 
 namespace content {
 class NavigationHandle;
+class NavigationThrottleRegistry;
 }
 
 namespace navigation_interception {
@@ -39,7 +40,7 @@
       CheckCallback;
 
   InterceptNavigationThrottle(
-      content::NavigationHandle* navigation_handle,
+      content::NavigationThrottleRegistry& registry,
       CheckCallback should_ignore_callback,
       SynchronyMode async_mode,
       std::optional<base::RepeatingClosure> request_finish_async_work_callback);
diff --git a/components/navigation_interception/intercept_navigation_throttle_unittest.cc b/components/navigation_interception/intercept_navigation_throttle_unittest.cc
index 7ba9d18..8a4f61f 100644
--- a/components/navigation_interception/intercept_navigation_throttle_unittest.cc
+++ b/components/navigation_interception/intercept_navigation_throttle_unittest.cc
@@ -83,16 +83,17 @@
     }
   }
 
-  std::unique_ptr<content::NavigationThrottle> CreateThrottle(
+  void CreateAndAddThrottle(
       InterceptNavigationThrottle::CheckCallback callback,
       base::RepeatingClosure request_finish_closure,
-      content::NavigationHandle* handle) {
+      content::NavigationThrottleRegistry& registry) {
     std::unique_ptr<InterceptNavigationThrottle> throttle =
         std::make_unique<InterceptNavigationThrottle>(
-            handle, callback, navigation_interception::SynchronyMode::kAsync,
+            registry, callback,
+            navigation_interception::SynchronyMode::kAsync,
             request_finish_closure);
     throttle_ = throttle.get()->GetWeakPtrForTesting();
-    return throttle;
+    registry.AddThrottle(std::move(throttle));
   }
 
   std::unique_ptr<content::TestNavigationThrottleInserter>
@@ -100,7 +101,7 @@
     return std::make_unique<content::TestNavigationThrottleInserter>(
         web_contents(),
         base::BindRepeating(
-            &InterceptNavigationThrottleTest::CreateThrottle,
+            &InterceptNavigationThrottleTest::CreateAndAddThrottle,
             base::Unretained(this),
             base::BindRepeating(
                 &MockInterceptCallbackReceiver::ShouldIgnoreNavigation,
@@ -120,16 +121,19 @@
              NavigationThrottle::PROCEED;
     };
 
-    if (is_post)
+    if (is_post) {
       simulator->SetMethod("POST");
+    }
 
     simulator->Start();
-    if (failed(simulator.get()))
+    if (failed(simulator.get())) {
       return simulator->GetLastThrottleCheckResult();
+    }
     for (const GURL& redirect_url : redirect_chain) {
       simulator->Redirect(redirect_url);
-      if (failed(simulator.get()))
+      if (failed(simulator.get())) {
         return simulator->GetLastThrottleCheckResult();
+      }
     }
     simulator->Commit();
     return simulator->GetLastThrottleCheckResult();
diff --git a/components/omnibox/browser/autocomplete_result_unittest.cc b/components/omnibox/browser/autocomplete_result_unittest.cc
index c3fbe08..4b653e1 100644
--- a/components/omnibox/browser/autocomplete_result_unittest.cc
+++ b/components/omnibox/browser/autocomplete_result_unittest.cc
@@ -9,8 +9,6 @@
 #pragma allow_unsafe_buffers
 #endif
 
-#include "components/omnibox/browser/autocomplete_result.h"
-
 #include <stddef.h>
 
 #include <algorithm>
@@ -36,6 +34,7 @@
 #include "components/omnibox/browser/autocomplete_match_type.h"
 #include "components/omnibox/browser/autocomplete_provider.h"
 #include "components/omnibox/browser/autocomplete_provider_client.h"
+#include "components/omnibox/browser/autocomplete_result.h"
 #include "components/omnibox/browser/fake_autocomplete_provider.h"
 #include "components/omnibox/browser/fake_autocomplete_provider_client.h"
 #include "components/omnibox/browser/fake_tab_matcher.h"
diff --git a/components/omnibox/browser/clipboard_provider.cc b/components/omnibox/browser/clipboard_provider.cc
index c8cf52e..d1e1fa92 100644
--- a/components/omnibox/browser/clipboard_provider.cc
+++ b/components/omnibox/browser/clipboard_provider.cc
@@ -243,8 +243,7 @@
                                            matches_.empty(), match.type,
                                            clipboard_contents_age);
 
-  if (is_android &&
-      omnibox::IsNTPPage(input.current_page_classification())) {
+  if (is_android && omnibox::IsNTPPage(input.current_page_classification())) {
     // Assign the Clipboard to the PZPS group on NTP pages to improve the use
     // of the suggest space.
     match.suggestion_group_id = omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST;
diff --git a/components/omnibox/browser/contextual_search_provider.h b/components/omnibox/browser/contextual_search_provider.h
index 7a5c0a0..1b284974 100644
--- a/components/omnibox/browser/contextual_search_provider.h
+++ b/components/omnibox/browser/contextual_search_provider.h
@@ -48,7 +48,7 @@
 
   // Waits for the Lens suggest inputs to be ready and then sends the request to
   // the remote suggest server. If the inputs are already ready, the request is
-  //sent immediately.
+  // sent immediately.
   void StartSuggestRequest(AutocompleteInput input);
 
   // Attaches the lens suggest inputs to `input` and makes the suggest request.
diff --git a/components/omnibox/browser/document_provider_unittest.cc b/components/omnibox/browser/document_provider_unittest.cc
index 85501d6..69d5c89 100644
--- a/components/omnibox/browser/document_provider_unittest.cc
+++ b/components/omnibox/browser/document_provider_unittest.cc
@@ -124,7 +124,7 @@
   }
 
   base::test::SingleThreadTaskEnvironment task_environment_{
-    base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
   // Not enabled by default on mobile, so have to enable it explicitly.
   base::test::ScopedFeatureList feature_list_{omnibox::kDocumentProvider};
   std::unique_ptr<FakeAutocompleteProviderClient> client_;
@@ -1182,7 +1182,8 @@
 
   {
     omnibox_feature_configs::ScopedConfigForTesting<
-        omnibox_feature_configs::DocumentProvider> scoped_config;
+        omnibox_feature_configs::DocumentProvider>
+        scoped_config;
     scoped_config.Get().enabled = true;
     scoped_config.Get().scope_backoff_to_profile = false;
 
@@ -1199,7 +1200,8 @@
 
   {
     omnibox_feature_configs::ScopedConfigForTesting<
-        omnibox_feature_configs::DocumentProvider> scoped_config;
+        omnibox_feature_configs::DocumentProvider>
+        scoped_config;
     scoped_config.Get().enabled = true;
     scoped_config.Get().scope_backoff_to_profile = true;
     scoped_config.Get().backoff_duration = base::Minutes(30);
diff --git a/components/omnibox/browser/featured_search_provider_unittest.cc b/components/omnibox/browser/featured_search_provider_unittest.cc
index 4464f4ff..69eeed0 100644
--- a/components/omnibox/browser/featured_search_provider_unittest.cc
+++ b/components/omnibox/browser/featured_search_provider_unittest.cc
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-
 #include "components/omnibox/browser/featured_search_provider.h"
 
 #include <stddef.h>
diff --git a/components/omnibox/browser/most_visited_sites_provider_unittest.cc b/components/omnibox/browser/most_visited_sites_provider_unittest.cc
index 716e203..a773c12 100644
--- a/components/omnibox/browser/most_visited_sites_provider_unittest.cc
+++ b/components/omnibox/browser/most_visited_sites_provider_unittest.cc
@@ -99,10 +99,7 @@
 
 constexpr const auto* WEB_URL = u"https://example.com/";
 
-enum class ExpectedUiType {
-  kAggregateMatch,
-  kIndividualTiles
-};
+enum class ExpectedUiType { kAggregateMatch, kIndividualTiles };
 
 const std::vector<TestData> DefaultTestData() {
   return {{false, {GURL("http://www.a.art/"), u"A art"}},
diff --git a/components/omnibox/browser/zero_suggest_provider.h b/components/omnibox/browser/zero_suggest_provider.h
index a3a5dde..4051267 100644
--- a/components/omnibox/browser/zero_suggest_provider.h
+++ b/components/omnibox/browser/zero_suggest_provider.h
@@ -24,7 +24,6 @@
 class SimpleURLLoader;
 }
 
-
 // Autocomplete provider for searches based on the current URL.
 //
 // The controller will call Start() when the user focuses the omnibox. After
diff --git a/components/omnibox/resources/BUILD.gn b/components/omnibox/resources/BUILD.gn
index 668f4e1..4925760 100644
--- a/components/omnibox/resources/BUILD.gn
+++ b/components/omnibox/resources/BUILD.gn
@@ -5,10 +5,7 @@
 import("//build/config/locales.gni")
 import("//tools/grit/grit_rule.gni")
 
-grit("omnibox_pedal_synonyms") {
+grit_strings("omnibox_pedal_synonyms") {
   source = "omnibox_pedal_synonyms.grd"
   outputs = [ "grit/omnibox_pedal_synonyms.h" ]
-  foreach(locale, all_chrome_locales) {
-    outputs += [ "omnibox_pedal_synonyms_$locale.pak" ]
-  }
 }
diff --git a/components/optimization_guide/proto/features/common_quality_data.proto b/components/optimization_guide/proto/features/common_quality_data.proto
index eb82e0c8..d5b93712 100644
--- a/components/optimization_guide/proto/features/common_quality_data.proto
+++ b/components/optimization_guide/proto/features/common_quality_data.proto
@@ -311,6 +311,8 @@
   int32 label_for_dom_node_id = 25;
 
   // The role of this ContentNode derived from the aria-role attribute.
+  // NOTE: This is enabled only for the experimental version of
+  // ANNOTATED_PAGE_CONTENT_VERSION_ONLY_ACTIONABLE_ELEMENTS_1_0.
   AXRole aria_role = 26;
 
   reserved 5, 6, 7, 17, 18, 1, 22;
diff --git a/components/payments/content/android/java/res/layout/secure_payment_confirmation_authn_ui.xml b/components/payments/content/android/java/res/layout/secure_payment_confirmation_authn_ui.xml
index f48fc7fc..d5f1bb3e 100644
--- a/components/payments/content/android/java/res/layout/secure_payment_confirmation_authn_ui.xml
+++ b/components/payments/content/android/java/res/layout/secure_payment_confirmation_authn_ui.xml
@@ -203,10 +203,22 @@
                 android:textAppearance="@style/TextAppearance.TextSmall.Secondary"
                 android:text="@string/secure_payment_confirmation_opt_out_label"/>
 
+            <org.chromium.ui.widget.TextViewWithClickableSpans
+                android:id="@+id/secure_payment_confirmation_footnote"
+                android:layout_below="@id/secure_payment_confirmation_nocredmatch_opt_out"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_centerHorizontal="true"
+                android:gravity="center"
+                android:layout_marginHorizontal="@dimen/secure_payment_confirmation_ui_small_padding"
+                android:layout_marginBottom="@dimen/secure_payment_confirmation_ui_medium_padding"
+                android:textAppearance="@style/TextAppearance.TextSmall.Secondary"
+                android:text="@string/secure_payment_confirmation_footnote"/>
+
             <!-- "Continue" button -->
             <org.chromium.ui.widget.ButtonCompat
                 android:id="@+id/continue_button"
-                android:layout_below="@id/secure_payment_confirmation_nocredmatch_opt_out"
+                android:layout_below="@id/secure_payment_confirmation_footnote"
                 android:layout_height="wrap_content"
                 android:layout_width="match_parent"
                 tools:text="Verify"
diff --git a/components/payments/content/android/spc/java/src/org/chromium/components/payments/secure_payment_confirmation/SecurePaymentConfirmationAuthnController.java b/components/payments/content/android/spc/java/src/org/chromium/components/payments/secure_payment_confirmation/SecurePaymentConfirmationAuthnController.java
index d6954a42..1e222522 100644
--- a/components/payments/content/android/spc/java/src/org/chromium/components/payments/secure_payment_confirmation/SecurePaymentConfirmationAuthnController.java
+++ b/components/payments/content/android/spc/java/src/org/chromium/components/payments/secure_payment_confirmation/SecurePaymentConfirmationAuthnController.java
@@ -11,9 +11,11 @@
 import android.content.res.Resources;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.text.SpannableString;
 import android.util.Pair;
 import android.view.View;
 
+import androidx.annotation.IntDef;
 import androidx.annotation.StringRes;
 import androidx.annotation.VisibleForTesting;
 import androidx.core.content.res.ResourcesCompat;
@@ -39,8 +41,13 @@
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
+import org.chromium.ui.text.ChromeClickableSpan;
+import org.chromium.ui.text.SpanApplier;
+import org.chromium.ui.text.SpanApplier.SpanInfo;
 import org.chromium.url.Origin;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Locale;
 
 /**
@@ -51,15 +58,32 @@
  */
 @NullMarked
 public class SecurePaymentConfirmationAuthnController {
+    @IntDef({
+        SpcResponseStatus.UNKNOWN,
+        SpcResponseStatus.ACCEPT,
+        SpcResponseStatus.ANOTHER_WAY,
+        SpcResponseStatus.CANCEL
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SpcResponseStatus {
+        int UNKNOWN = 0;
+        int ACCEPT = 1;
+        int ANOTHER_WAY = 2;
+        int CANCEL = 3;
+        int OPT_OUT = 4;
+    }
+
     private final WebContents mWebContents;
     private @Nullable Runnable mHider;
 
-    private @Nullable Callback<Boolean> mResponseCallback;
+    private @Nullable Callback<Integer> mResponseCallback;
 
     private @Nullable Runnable mOptOutCallback;
 
     private @Nullable SecurePaymentConfirmationAuthnView mView;
 
+    private @Nullable Boolean mInformOnly;
+
     private InputProtector mInputProtector = new InputProtector();
 
     private final BottomSheetObserver mBottomSheetObserver =
@@ -169,7 +193,7 @@
      * @param paymentIcon The icon of the payment instrument.
      * @param paymentInstrumentLabel The label to display for the payment instrument.
      * @param total The total amount of the transaction.
-     * @param responseCallback The function to call on sheet dismiss; false if it failed.
+     * @param responseCallback The function to call on sheet dismiss; called with SpcResponseStatus.
      * @param optOutCallback The function to call on user opt out.
      * @param payeeName The name of the payee, or null if not specified.
      * @param payeeOrigin The origin of the payee, or null if not specified.
@@ -183,7 +207,7 @@
             Drawable paymentIcon,
             String paymentInstrumentLabel,
             PaymentItem total,
-            Callback<Boolean> responseCallback,
+            Callback<Integer> responseCallback,
             Runnable optOutCallback,
             @Nullable String payeeName,
             @Nullable Origin payeeOrigin,
@@ -195,6 +219,7 @@
         assert !informOnly
                 || PaymentFeatureList.isEnabledOrExperimentalFeaturesEnabled(
                         PaymentFeatureList.SECURE_PAYMENT_CONFIRMATION_FALLBACK);
+        mInformOnly = informOnly;
 
         if (mHider != null) return false;
 
@@ -232,6 +257,26 @@
             showsIssuerNetworkIcons = true;
         }
 
+        SpannableString footnote = null;
+        if (!mInformOnly
+                && PaymentFeatureList.isEnabledOrExperimentalFeaturesEnabled(
+                        PaymentFeatureList.SECURE_PAYMENT_CONFIRMATION_FALLBACK)) {
+            footnote =
+                    SpanApplier.applySpans(
+                            context.getString(R.string.secure_payment_confirmation_footnote),
+                            new SpanInfo(
+                                    "BEGIN_LINK",
+                                    "END_LINK",
+                                    new ChromeClickableSpan(
+                                            context,
+                                            (widget) -> {
+                                                hide();
+                                                assumeNonNull(mResponseCallback);
+                                                mResponseCallback.onResult(
+                                                        SpcResponseStatus.ANOTHER_WAY);
+                                            })));
+        }
+
         PropertyModel model =
                 new PropertyModel.Builder(SecurePaymentConfirmationAuthnProperties.ALL_KEYS)
                         .with(
@@ -263,7 +308,7 @@
                         .with(SecurePaymentConfirmationAuthnProperties.NETWORK_ICON, networkIcon)
                         .with(
                                 SecurePaymentConfirmationAuthnProperties.TITLE,
-                                informOnly
+                                mInformOnly
                                         ? context.getString(
                                                 R.string
                                                         .secure_payment_confirmation_inform_only_title)
@@ -272,9 +317,10 @@
                                                         .secure_payment_confirmation_verify_purchase))
                         .with(
                                 SecurePaymentConfirmationAuthnProperties.CONTINUE_BUTTON_LABEL,
-                                informOnly
+                                mInformOnly
                                         ? context.getString(R.string.payments_confirm_button)
                                         : context.getString(R.string.payments_continue_button))
+                        .with(SecurePaymentConfirmationAuthnProperties.FOOTNOTE, footnote)
                         .build();
 
         bottomSheet.addObserver(mBottomSheetObserver);
@@ -347,8 +393,13 @@
 
     private void onConfirm() {
         hide();
+        assumeNonNull(mInformOnly);
         assumeNonNull(mResponseCallback);
-        mResponseCallback.onResult(true);
+        if (mInformOnly) {
+            mResponseCallback.onResult(SpcResponseStatus.ANOTHER_WAY);
+        } else {
+            mResponseCallback.onResult(SpcResponseStatus.ACCEPT);
+        }
     }
 
     private void onConfirmPressed() {
@@ -358,7 +409,12 @@
     private void onCancel() {
         hide();
         assumeNonNull(mResponseCallback);
-        mResponseCallback.onResult(false);
+        if (PaymentFeatureList.isEnabledOrExperimentalFeaturesEnabled(
+                PaymentFeatureList.SECURE_PAYMENT_CONFIRMATION_FALLBACK)) {
+            mResponseCallback.onResult(SpcResponseStatus.CANCEL);
+        } else {
+            mResponseCallback.onResult(SpcResponseStatus.ANOTHER_WAY);
+        }
     }
 
     private void onCancelPressed() {
diff --git a/components/payments/content/android/spc/java/src/org/chromium/components/payments/secure_payment_confirmation/SecurePaymentConfirmationAuthnProperties.java b/components/payments/content/android/spc/java/src/org/chromium/components/payments/secure_payment_confirmation/SecurePaymentConfirmationAuthnProperties.java
index 17a02555..6050767 100644
--- a/components/payments/content/android/spc/java/src/org/chromium/components/payments/secure_payment_confirmation/SecurePaymentConfirmationAuthnProperties.java
+++ b/components/payments/content/android/spc/java/src/org/chromium/components/payments/secure_payment_confirmation/SecurePaymentConfirmationAuthnProperties.java
@@ -5,6 +5,7 @@
 package org.chromium.components.payments.secure_payment_confirmation;
 
 import android.graphics.drawable.Drawable;
+import android.text.SpannableString;
 import android.util.Pair;
 
 import org.chromium.build.annotations.NullMarked;
@@ -77,6 +78,10 @@
     /* package */ static final ReadableObjectPropertyKey<String> CONTINUE_BUTTON_LABEL =
             new ReadableObjectPropertyKey<>();
 
+    /** The footnote for the UI. */
+    /* package */ static final ReadableObjectPropertyKey<SpannableString> FOOTNOTE =
+            new ReadableObjectPropertyKey<>();
+
     /* package */ static final PropertyKey[] ALL_KEYS =
             new PropertyKey[] {
                 STORE_LABEL,
@@ -92,6 +97,7 @@
                 NETWORK_ICON,
                 TITLE,
                 CONTINUE_BUTTON_LABEL,
+                FOOTNOTE,
             };
 
     // Prevent instantiation.
diff --git a/components/payments/content/android/spc/java/src/org/chromium/components/payments/secure_payment_confirmation/SecurePaymentConfirmationAuthnView.java b/components/payments/content/android/spc/java/src/org/chromium/components/payments/secure_payment_confirmation/SecurePaymentConfirmationAuthnView.java
index f3b97d10..ba9e9e7 100644
--- a/components/payments/content/android/spc/java/src/org/chromium/components/payments/secure_payment_confirmation/SecurePaymentConfirmationAuthnView.java
+++ b/components/payments/content/android/spc/java/src/org/chromium/components/payments/secure_payment_confirmation/SecurePaymentConfirmationAuthnView.java
@@ -60,6 +60,7 @@
     /* package */ final Button mContinueButton;
     /* package */ final Button mCancelButton;
     /* package */ final TextViewWithClickableSpans mOptOutText;
+    /* package */ final TextViewWithClickableSpans mFootnote;
 
     /* package */ SecurePaymentConfirmationAuthnView(Context context) {
         mContentView =
@@ -86,9 +87,13 @@
                 (TextViewWithClickableSpans)
                         mContentView.findViewById(
                                 R.id.secure_payment_confirmation_nocredmatch_opt_out);
+        mFootnote =
+                (TextViewWithClickableSpans)
+                        mContentView.findViewById(R.id.secure_payment_confirmation_footnote);
 
         mHeaderImage.setImageResource(R.drawable.save_card);
         mOptOutText.setMovementMethod(LinkMovementMethod.getInstance());
+        mFootnote.setMovementMethod(LinkMovementMethod.getInstance());
     }
 
     /* package */ View getContentView() {
diff --git a/components/payments/content/android/spc/java/src/org/chromium/components/payments/secure_payment_confirmation/SecurePaymentConfirmationAuthnViewBinder.java b/components/payments/content/android/spc/java/src/org/chromium/components/payments/secure_payment_confirmation/SecurePaymentConfirmationAuthnViewBinder.java
index 445a579..85b8a9b 100644
--- a/components/payments/content/android/spc/java/src/org/chromium/components/payments/secure_payment_confirmation/SecurePaymentConfirmationAuthnViewBinder.java
+++ b/components/payments/content/android/spc/java/src/org/chromium/components/payments/secure_payment_confirmation/SecurePaymentConfirmationAuthnViewBinder.java
@@ -89,6 +89,14 @@
         } else if (SecurePaymentConfirmationAuthnProperties.CONTINUE_BUTTON_LABEL == propertyKey) {
             view.mContinueButton.setText(
                     model.get(SecurePaymentConfirmationAuthnProperties.CONTINUE_BUTTON_LABEL));
+        } else if (SecurePaymentConfirmationAuthnProperties.FOOTNOTE == propertyKey) {
+            if (model.get(SecurePaymentConfirmationAuthnProperties.FOOTNOTE) == null) {
+                view.mFootnote.setVisibility(View.GONE);
+            } else {
+                view.mFootnote.setVisibility(View.VISIBLE);
+                view.mFootnote.setText(
+                        model.get(SecurePaymentConfirmationAuthnProperties.FOOTNOTE));
+            }
         }
     }
 
diff --git a/components/payments/content/android/spc/junit/src/org/chromium/components/payments/secure_payment_confirmation/SecurePaymentConfirmationAuthnTest.java b/components/payments/content/android/spc/junit/src/org/chromium/components/payments/secure_payment_confirmation/SecurePaymentConfirmationAuthnTest.java
index 70e1b8ce..b864892 100644
--- a/components/payments/content/android/spc/junit/src/org/chromium/components/payments/secure_payment_confirmation/SecurePaymentConfirmationAuthnTest.java
+++ b/components/payments/content/android/spc/junit/src/org/chromium/components/payments/secure_payment_confirmation/SecurePaymentConfirmationAuthnTest.java
@@ -39,6 +39,7 @@
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetControllerProvider;
 import org.chromium.components.payments.PaymentFeatureList;
 import org.chromium.components.payments.R;
+import org.chromium.components.payments.secure_payment_confirmation.SecurePaymentConfirmationAuthnController.SpcResponseStatus;
 import org.chromium.components.payments.test_support.DefaultPaymentFeatureConfig;
 import org.chromium.components.payments.ui.CurrencyFormatter;
 import org.chromium.components.payments.ui.CurrencyFormatterJni;
@@ -58,7 +59,11 @@
 @Config(
         manifest = Config.NONE,
         shadows = {SecurePaymentConfirmationAuthnTest.ShadowBottomSheetControllerProvider.class})
-@EnableFeatures(BlinkFeatures.SECURE_PAYMENT_CONFIRMATION_NETWORK_AND_ISSUER_ICONS)
+@EnableFeatures({
+    BlinkFeatures.SECURE_PAYMENT_CONFIRMATION_NETWORK_AND_ISSUER_ICONS,
+    PaymentFeatureList.SECURE_PAYMENT_CONFIRMATION_FALLBACK
+})
+@DisableFeatures(PaymentFeatureList.WEB_PAYMENTS_EXPERIMENTAL_FEATURES)
 public class SecurePaymentConfirmationAuthnTest {
     private static final long IGNORED_INPUT_DELAY =
             InputProtector.POTENTIALLY_UNINTENDED_INPUT_THRESHOLD - 100;
@@ -70,10 +75,8 @@
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private WebContents mWebContents;
 
-    private boolean mIsPaymentConfirmed;
-    private boolean mIsPaymentCancelled;
     private boolean mIsPaymentOptOut;
-    private Callback<Boolean> mResponseCallback;
+    private Callback<Integer> mResponseCallback;
     private Runnable mOptOutCallback;
 
     private String mPayeeName;
@@ -84,6 +87,7 @@
     private Drawable mNetworkIcon;
     private SecurePaymentConfirmationAuthnController mAuthnController;
     private FakeClock mClock = new FakeClock();
+    private @SpcResponseStatus int mResponseStatus = SpcResponseStatus.UNKNOWN;
 
     /** The shadow of BottomSheetControllerProvider. Not to use outside the test. */
     @Implements(BottomSheetControllerProvider.class)
@@ -152,12 +156,8 @@
                                 /* height= */ 1,
                                 Bitmap.Config.ARGB_8888));
         mResponseCallback =
-                (response) -> {
-                    if (response) {
-                        mIsPaymentConfirmed = true;
-                    } else {
-                        mIsPaymentCancelled = true;
-                    }
+                (responseStatus) -> {
+                    mResponseStatus = responseStatus;
                 };
         mOptOutCallback =
                 () -> {
@@ -213,8 +213,6 @@
             String payeeName, Origin payeeOrigin, boolean enableOptOut, boolean informOnly) {
         if (mAuthnController == null) return false;
 
-        mIsPaymentConfirmed = false;
-        mIsPaymentCancelled = false;
         mIsPaymentOptOut = false;
 
         String paymentInstrumentLabel = "My Card";
@@ -249,8 +247,10 @@
         createAuthnController();
         show();
         mClock.advanceCurrentTimeMillis(SAFE_INPUT_DELAY);
+
         mAuthnController.getView().mContinueButton.performClick();
-        Assert.assertTrue(mIsPaymentConfirmed);
+
+        Assert.assertEquals(SpcResponseStatus.ACCEPT, mResponseStatus);
         Assert.assertTrue(mAuthnController.isHidden());
     }
 
@@ -260,8 +260,24 @@
         createAuthnController();
         show();
         mClock.advanceCurrentTimeMillis(SAFE_INPUT_DELAY);
+
         mAuthnController.getView().mCancelButton.performClick();
-        Assert.assertTrue(mIsPaymentCancelled);
+
+        Assert.assertEquals(SpcResponseStatus.CANCEL, mResponseStatus);
+        Assert.assertTrue(mAuthnController.isHidden());
+    }
+
+    @Test
+    @Feature({"Payments"})
+    @DisableFeatures(PaymentFeatureList.SECURE_PAYMENT_CONFIRMATION_FALLBACK)
+    public void testOnAuthnCancellationFeatureDisabled() {
+        createAuthnController();
+        show();
+        mClock.advanceCurrentTimeMillis(SAFE_INPUT_DELAY);
+
+        mAuthnController.getView().mCancelButton.performClick();
+
+        Assert.assertEquals(SpcResponseStatus.ANOTHER_WAY, mResponseStatus);
         Assert.assertTrue(mAuthnController.isHidden());
     }
 
@@ -271,8 +287,10 @@
         createAuthnController();
         showWithOptOut();
         mClock.advanceCurrentTimeMillis(SAFE_INPUT_DELAY);
+
         SecurePaymentConfirmationAuthnView authnView = mAuthnController.getView();
         authnView.mOptOutText.getClickableSpans()[0].onClick(authnView.mOptOutText);
+
         Assert.assertTrue(mIsPaymentOptOut);
         Assert.assertTrue(mAuthnController.isHidden());
     }
@@ -285,11 +303,11 @@
 
         // Clicking immediately is prevented.
         mAuthnController.getView().mContinueButton.performClick();
-        Assert.assertFalse(mIsPaymentConfirmed);
+        Assert.assertEquals(SpcResponseStatus.UNKNOWN, mResponseStatus);
         Assert.assertFalse(mAuthnController.isHidden());
 
         mAuthnController.getView().mCancelButton.performClick();
-        Assert.assertFalse(mIsPaymentCancelled);
+        Assert.assertEquals(SpcResponseStatus.UNKNOWN, mResponseStatus);
         Assert.assertFalse(mAuthnController.isHidden());
 
         SecurePaymentConfirmationAuthnView authnView = mAuthnController.getView();
@@ -301,11 +319,11 @@
         mClock.advanceCurrentTimeMillis(IGNORED_INPUT_DELAY);
 
         mAuthnController.getView().mContinueButton.performClick();
-        Assert.assertFalse(mIsPaymentConfirmed);
+        Assert.assertEquals(SpcResponseStatus.UNKNOWN, mResponseStatus);
         Assert.assertFalse(mAuthnController.isHidden());
 
         mAuthnController.getView().mCancelButton.performClick();
-        Assert.assertFalse(mIsPaymentCancelled);
+        Assert.assertEquals(SpcResponseStatus.UNKNOWN, mResponseStatus);
         Assert.assertFalse(mAuthnController.isHidden());
 
         authnView.mOptOutText.getClickableSpans()[0].onClick(authnView.mOptOutText);
@@ -315,12 +333,39 @@
         // Clicking confirm after the threshold is no longer prevented and confirms the dialog.
         mClock.advanceCurrentTimeMillis(SAFE_INPUT_DELAY);
         mAuthnController.getView().mContinueButton.performClick();
-        Assert.assertTrue(mIsPaymentConfirmed);
+        Assert.assertEquals(SpcResponseStatus.ACCEPT, mResponseStatus);
         Assert.assertTrue(mAuthnController.isHidden());
     }
 
     @Test
     @Feature({"Payments"})
+    public void testOnVerifyAnotherWay() {
+        createAuthnController();
+        show();
+        mClock.advanceCurrentTimeMillis(SAFE_INPUT_DELAY);
+
+        SecurePaymentConfirmationAuthnView authnView = mAuthnController.getView();
+        Assert.assertEquals(View.VISIBLE, authnView.mFootnote.getVisibility());
+        authnView.mFootnote.getClickableSpans()[0].onClick(authnView.mFootnote);
+
+        Assert.assertEquals(SpcResponseStatus.ANOTHER_WAY, mResponseStatus);
+        Assert.assertTrue(mAuthnController.isHidden());
+    }
+
+    @Test
+    @Feature({"Payments"})
+    @DisableFeatures(PaymentFeatureList.SECURE_PAYMENT_CONFIRMATION_FALLBACK)
+    public void testOnVerifyAnotherWayFeatureDisabled() {
+        createAuthnController();
+
+        show();
+        SecurePaymentConfirmationAuthnView authnView = mAuthnController.getView();
+
+        Assert.assertEquals(View.GONE, authnView.mFootnote.getVisibility());
+    }
+
+    @Test
+    @Feature({"Payments"})
     public void testHide() {
         createAuthnController();
         show();
@@ -533,9 +578,9 @@
 
     @Test
     @Feature({"Payments"})
-    @EnableFeatures({PaymentFeatureList.SECURE_PAYMENT_CONFIRMATION_FALLBACK})
     public void testShowInformOnly() {
         createAuthnController();
+
         Assert.assertTrue(showInformOnly());
 
         SecurePaymentConfirmationAuthnView view = mAuthnController.getView();
@@ -547,5 +592,40 @@
         Assert.assertEquals(
                 view.mContinueButton.getText(),
                 context.getString(R.string.payments_confirm_button));
+        Assert.assertEquals(View.GONE, view.mFootnote.getVisibility());
+    }
+
+    @Test
+    @Feature({"Payments"})
+    @DisableFeatures(PaymentFeatureList.SECURE_PAYMENT_CONFIRMATION_FALLBACK)
+    public void testShowInformOnlyFeatureIsDisabled() {
+        createAuthnController();
+        Assert.assertThrows(AssertionError.class, this::showInformOnly);
+    }
+
+    @Test
+    @Feature({"Payments"})
+    public void testShowInformOnlyOnConfirmation() {
+        createAuthnController();
+        showInformOnly();
+        mClock.advanceCurrentTimeMillis(SAFE_INPUT_DELAY);
+
+        mAuthnController.getView().mContinueButton.performClick();
+
+        Assert.assertEquals(SpcResponseStatus.ANOTHER_WAY, mResponseStatus);
+        Assert.assertTrue(mAuthnController.isHidden());
+    }
+
+    @Test
+    @Feature({"Payments"})
+    public void testShowInformOnlyOnCancellation() {
+        createAuthnController();
+        showInformOnly();
+        mClock.advanceCurrentTimeMillis(SAFE_INPUT_DELAY);
+
+        mAuthnController.getView().mCancelButton.performClick();
+
+        Assert.assertEquals(SpcResponseStatus.CANCEL, mResponseStatus);
+        Assert.assertTrue(mAuthnController.isHidden());
     }
 }
diff --git a/components/payments_strings.grdp b/components/payments_strings.grdp
index ac08265..af8dabf 100644
--- a/components/payments_strings.grdp
+++ b/components/payments_strings.grdp
@@ -552,6 +552,9 @@
     <message name="IDS_NO_MATCHING_CREDENTIAL_DESCRIPTION" desc="Description on a dialog that appears when there are no matching payment credentials to authenticate. Informs users that additional steps are needed to verify the payment." formatter_data="android_java">
         <ph name="URL">%1$s<ex>merchant.com</ex></ph> may need to take additional steps to verify your payment
     </message>
+    <message name="IDS_SECURE_PAYMENT_CONFIRMATION_FOOTNOTE" desc="Footnote on a secure payment confirmation dialog that explains what will happen next and provides a link to verify by another way." formatter_data="android_java">
+        Unlock your screen in the next step, typically using your fingerprint. Or you can <ph name="BEGIN_LINK">BEGIN_LINK</ph>verify another way<ph name="END_LINK">END_LINK</ph>.
+    </message>
   </if>
   <if expr="not is_android">
     <message name="IDS_NO_MATCHING_CREDENTIAL_DESCRIPTION" desc="Description on a dialog that appears when there are no matching payment credentials to authenticate. Informs users that additional steps are needed to verify the payment.">
diff --git a/components/payments_strings_grdp/IDS_SECURE_PAYMENT_CONFIRMATION_FOOTNOTE.png.sha1 b/components/payments_strings_grdp/IDS_SECURE_PAYMENT_CONFIRMATION_FOOTNOTE.png.sha1
new file mode 100644
index 0000000..84076a1a
--- /dev/null
+++ b/components/payments_strings_grdp/IDS_SECURE_PAYMENT_CONFIRMATION_FOOTNOTE.png.sha1
@@ -0,0 +1 @@
+bbb184bb399483cf53f83bfba6a3140893b8f9a2
\ No newline at end of file
diff --git a/components/plus_addresses/resources/strings/BUILD.gn b/components/plus_addresses/resources/strings/BUILD.gn
index 413fbc52..de5d899 100644
--- a/components/plus_addresses/resources/strings/BUILD.gn
+++ b/components/plus_addresses/resources/strings/BUILD.gn
@@ -10,20 +10,20 @@
   import("//build/config/android/rules.gni")
 }
 
-grit("strings") {
+grit_strings("strings") {
   if (is_chrome_branded) {
     source = "../internal/strings/plus_addresses_internal_strings.grd"
   } else {
     source = "plus_addresses_strings.grd"
   }
 
-  outputs = [ "../../grit/plus_addresses_strings.h" ] +
-            process_file_template(
-                all_chrome_locales,
-                [ "plus_addresses_strings_{{source_name_part}}.pak" ])
+  outputs = [ "../../grit/plus_addresses_strings.h" ]
+
   if (is_android) {
     create_android_resources = true
   }
+
+  output_prefix = "plus_addresses_strings_"
 }
 
 if (is_android) {
diff --git a/components/policy/content/policy_blocklist_navigation_throttle.cc b/components/policy/content/policy_blocklist_navigation_throttle.cc
index cbce3a66..509afeaa 100644
--- a/components/policy/content/policy_blocklist_navigation_throttle.cc
+++ b/components/policy/content/policy_blocklist_navigation_throttle.cc
@@ -27,19 +27,19 @@
 // callback is safe because this object owns safe_sites_navigation_throttle_,
 // which runs the callback from within the object.
 PolicyBlocklistNavigationThrottle::PolicyBlocklistNavigationThrottle(
-    content::NavigationHandle* navigation_handle,
+    content::NavigationThrottleRegistry& registry,
     content::BrowserContext* context)
-    : content::NavigationThrottle(navigation_handle),
+    : content::NavigationThrottle(registry),
       blocklist_service_(PolicyBlocklistFactory::GetForBrowserContext(context)),
       prefs_(user_prefs::UserPrefs::Get(context)) {
   DCHECK(prefs_);
   auto safe_sites_navigation_throttle =
-      std::make_unique<SafeSitesNavigationThrottle>(navigation_handle, context);
+      std::make_unique<SafeSitesNavigationThrottle>(registry, context);
   if (base::FeatureList::IsEnabled(
           policy::features::kPolicyBlocklistProceedUntilResponse)) {
     safe_sites_navigation_throttle_ =
         std::make_unique<ProceedUntilResponseNavigationThrottle>(
-            navigation_handle, std::move(safe_sites_navigation_throttle),
+            registry, std::move(safe_sites_navigation_throttle),
             base::BindRepeating(
                 &PolicyBlocklistNavigationThrottle::OnDeferredSafeSitesResult,
                 base::Unretained(this)));
diff --git a/components/policy/content/policy_blocklist_navigation_throttle.h b/components/policy/content/policy_blocklist_navigation_throttle.h
index 17502e3..0fc522e 100644
--- a/components/policy/content/policy_blocklist_navigation_throttle.h
+++ b/components/policy/content/policy_blocklist_navigation_throttle.h
@@ -15,6 +15,7 @@
 
 namespace content {
 class BrowserContext;
+class NavigationThrottleRegistry;
 }  // namespace content
 
 // PolicyBlocklistNavigationThrottle provides a simple way to block a navigation
@@ -26,7 +27,7 @@
 class PolicyBlocklistNavigationThrottle : public content::NavigationThrottle {
  public:
   PolicyBlocklistNavigationThrottle(
-      content::NavigationHandle* navigation_handle,
+      content::NavigationThrottleRegistry& registry,
       content::BrowserContext* context);
   PolicyBlocklistNavigationThrottle(const PolicyBlocklistNavigationThrottle&) =
       delete;
diff --git a/components/policy/content/policy_blocklist_navigation_throttle_unittest.cc b/components/policy/content/policy_blocklist_navigation_throttle_unittest.cc
index cdbf515a..b182953 100644
--- a/components/policy/content/policy_blocklist_navigation_throttle_unittest.cc
+++ b/components/policy/content/policy_blocklist_navigation_throttle_unittest.cc
@@ -29,6 +29,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/test/navigation_simulator.h"
+#include "content/public/test/test_navigation_throttle_inserter.h"
 #include "content/public/test/test_renderer_host.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
@@ -44,8 +45,7 @@
 // TODO(crbug.com/40156526): Break out the tests into separate files. The
 // SafeSites tests should be parameterized to run the same tests on both types.
 class SafeSitesNavigationThrottleTest
-    : public content::RenderViewHostTestHarness,
-      public content::WebContentsObserver {
+    : public content::RenderViewHostTestHarness {
  public:
   SafeSitesNavigationThrottleTest() = default;
   SafeSitesNavigationThrottleTest(const SafeSitesNavigationThrottleTest&) =
@@ -69,10 +69,7 @@
     SafeSearchFactory::GetInstance()
         ->GetForBrowserContext(browser_context())
         ->SetSafeSearchURLCheckerForTest(
-            stub_url_checker_.BuildURLChecker(kCacheSize));
-
-    // Observe the WebContents to add the throttle.
-    Observe(RenderViewHostTestHarness::web_contents());
+            stub_url_checker().BuildURLChecker(kCacheSize));
   }
 
   void TearDown() override {
@@ -82,20 +79,17 @@
   }
 
  protected:
-  // content::WebContentsObserver:
-  void DidStartNavigation(
-      content::NavigationHandle* navigation_handle) override {
-    auto throttle = std::make_unique<SafeSitesNavigationThrottle>(
-        navigation_handle, browser_context());
-
-    navigation_handle->RegisterThrottleForTesting(std::move(throttle));
-  }
-
   std::unique_ptr<content::NavigationSimulator> StartNavigation(
       const GURL& first_url) {
     auto navigation_simulator =
         content::NavigationSimulator::CreateRendererInitiated(first_url,
                                                               main_rfh());
+    auto throttle_inserter =
+        std::make_unique<content::TestNavigationThrottleInserter>(
+            web_contents(),
+            base::BindRepeating(
+                &SafeSitesNavigationThrottleTest::CreateAndAddThrottle,
+                base::Unretained(this)));
     navigation_simulator->SetAutoAdvance(false);
     navigation_simulator->Start();
     return navigation_simulator;
@@ -116,6 +110,16 @@
   void TestSafeSitesCachedSites(const char* expected_error_page_content,
                                 bool is_proceed_until_response_enabled = false);
 
+  safe_search_api::StubURLChecker& stub_url_checker() {
+    return stub_url_checker_;
+  }
+
+ private:
+  virtual void CreateAndAddThrottle(content::NavigationThrottleRegistry& registry) {
+    registry.AddThrottle(std::make_unique<SafeSitesNavigationThrottle>(
+        registry, browser_context()));
+  }
+
   safe_search_api::StubURLChecker stub_url_checker_;
 };
 
@@ -124,13 +128,10 @@
  protected:
   static const char kErrorPageContent[];
 
-  // content::WebContentsObserver:
-  void DidStartNavigation(
-      content::NavigationHandle* navigation_handle) override {
-    auto throttle = std::make_unique<SafeSitesNavigationThrottle>(
-        navigation_handle, browser_context(), kErrorPageContent);
-
-    navigation_handle->RegisterThrottleForTesting(std::move(throttle));
+  // SafeSitesNavigationThrottleTest:
+  void CreateAndAddThrottle(content::NavigationThrottleRegistry& registry) override {
+    registry.AddThrottle(std::make_unique<SafeSitesNavigationThrottle>(
+        registry, browser_context(), kErrorPageContent));
   }
 };
 
@@ -159,13 +160,10 @@
   }
 
  protected:
-  // content::WebContentsObserver:
-  void DidStartNavigation(
-      content::NavigationHandle* navigation_handle) override {
-    auto throttle = std::make_unique<PolicyBlocklistNavigationThrottle>(
-        navigation_handle, browser_context());
-
-    navigation_handle->RegisterThrottleForTesting(std::move(throttle));
+  // SafeSitesNavigationThrottleTest:
+  void CreateAndAddThrottle(content::NavigationThrottleRegistry& registry) override {
+    registry.AddThrottle(std::make_unique<PolicyBlocklistNavigationThrottle>(
+        registry, browser_context()));
   }
 
   void SetBlocklistUrlPattern(const std::string& pattern) {
@@ -235,7 +233,7 @@
   base::HistogramTester histogram_tester;
 
   SetSafeSitesFilterBehavior(SafeSitesFilterBehavior::kSafeSitesFilterEnabled);
-  stub_url_checker_.SetUpValidResponse(false /* is_porn */);
+  stub_url_checker().SetUpValidResponse(false /* is_porn */);
 
   const GURL url = GURL("http://example.com/");
   auto navigation_simulator = StartNavigation(url);
@@ -260,7 +258,7 @@
   base::HistogramTester histogram_tester;
 
   SetSafeSitesFilterBehavior(SafeSitesFilterBehavior::kSafeSitesFilterEnabled);
-  stub_url_checker_.SetUpValidResponse(true /* is_porn */);
+  stub_url_checker().SetUpValidResponse(true /* is_porn */);
 
   // Defer, then cancel a porn site.
   const GURL url = GURL("http://example.com/");
@@ -284,7 +282,7 @@
 TEST_P(PolicyBlocklistNavigationThrottleTest, SafeSites_Allowlisted) {
   SetAllowlistUrlPattern("example.com");
   SetSafeSitesFilterBehavior(SafeSitesFilterBehavior::kSafeSitesFilterEnabled);
-  stub_url_checker_.SetUpValidResponse(true /* is_porn */);
+  stub_url_checker().SetUpValidResponse(true /* is_porn */);
 
   // Even with SafeSites enabled, a allowlisted site is immediately allowed.
   auto navigation_simulator = StartNavigation(GURL("http://example.com/"));
@@ -295,7 +293,7 @@
 
 TEST_P(PolicyBlocklistNavigationThrottleTest, SafeSites_Schemes) {
   SetSafeSitesFilterBehavior(SafeSitesFilterBehavior::kSafeSitesFilterEnabled);
-  stub_url_checker_.SetUpValidResponse(true /* is_porn */);
+  stub_url_checker().SetUpValidResponse(true /* is_porn */);
 
   // The safe sites filter is only used for http(s) URLs. This test uses
   // browser-initiated navigation, since renderer-initiated navigations to
@@ -311,7 +309,7 @@
 }
 
 TEST_P(PolicyBlocklistNavigationThrottleTest, SafeSites_PolicyChange) {
-  stub_url_checker_.SetUpValidResponse(true /* is_porn */);
+  stub_url_checker().SetUpValidResponse(true /* is_porn */);
 
   // The safe sites filter is initially disabled.
   {
@@ -349,7 +347,7 @@
 
 TEST_P(PolicyBlocklistNavigationThrottleTest, SafeSites_Failure) {
   SetSafeSitesFilterBehavior(SafeSitesFilterBehavior::kSafeSitesFilterEnabled);
-  stub_url_checker_.SetUpFailedResponse();
+  stub_url_checker().SetUpFailedResponse();
 
   // If the Safe Search API request fails, the navigation is allowed.
   auto navigation_simulator = StartNavigation(GURL("http://example.com/"));
@@ -379,7 +377,7 @@
   const GURL safe_site = GURL("http://example.com/");
   const GURL porn_site = GURL("http://example2.com/");
 
-  stub_url_checker_.SetUpValidResponse(false /* is_porn */);
+  stub_url_checker().SetUpValidResponse(false /* is_porn */);
   {
     auto navigation_simulator = StartNavigation(safe_site);
     if (is_proceed_until_response_enabled) {
@@ -396,7 +394,7 @@
                      .error_page_content());
   }
 
-  stub_url_checker_.SetUpValidResponse(true /* is_porn */);
+  stub_url_checker().SetUpValidResponse(true /* is_porn */);
   {
     auto navigation_simulator = StartNavigation(porn_site);
     if (is_proceed_until_response_enabled) {
@@ -420,7 +418,7 @@
     }
   }
 
-  stub_url_checker_.ClearResponses();
+  stub_url_checker().ClearResponses();
   {
     // This check is synchronous since the site is in the cache.
     auto navigation_simulator = StartNavigation(safe_site);
@@ -470,7 +468,7 @@
   const GURL safe_site = GURL("http://example.com/");
   const GURL porn_site = GURL("http://example2.com/");
 
-  stub_url_checker_.SetUpValidResponse(false /* is_porn */);
+  stub_url_checker().SetUpValidResponse(false /* is_porn */);
   {
     auto navigation_simulator = StartNavigation(safe_site);
     if (is_proceed_until_response_enabled) {
@@ -486,7 +484,7 @@
     EXPECT_FALSE(navigation_simulator->GetLastThrottleCheckResult()
                      .error_page_content());
 
-    stub_url_checker_.SetUpValidResponse(true /* is_porn */);
+    stub_url_checker().SetUpValidResponse(true /* is_porn */);
     navigation_simulator->Redirect(porn_site);
     if (is_proceed_until_response_enabled) {
       // Proceed with running a background check, will defer on the subsequent
@@ -509,7 +507,7 @@
     }
   }
 
-  stub_url_checker_.ClearResponses();
+  stub_url_checker().ClearResponses();
   {
     // This check is synchronous since the site is in the cache.
     auto navigation_simulator = StartNavigation(safe_site);
diff --git a/components/policy/content/proceed_until_response_navigation_throttle.cc b/components/policy/content/proceed_until_response_navigation_throttle.cc
index 892f2b6..5e32781 100644
--- a/components/policy/content/proceed_until_response_navigation_throttle.cc
+++ b/components/policy/content/proceed_until_response_navigation_throttle.cc
@@ -8,14 +8,14 @@
 #include "base/strings/strcat.h"
 
 ProceedUntilResponseNavigationThrottle::Client::Client(
-    content::NavigationHandle* navigation_handle)
-    : NavigationThrottle(navigation_handle) {}
+    content::NavigationThrottleRegistry& registry)
+    : NavigationThrottle(registry) {}
 
 ProceedUntilResponseNavigationThrottle::ProceedUntilResponseNavigationThrottle(
-    content::NavigationHandle* navigation_handle,
+    content::NavigationThrottleRegistry& registry,
     std::unique_ptr<Client> client,
     std::optional<DeferredResultCallback> deferred_result_callback)
-    : NavigationThrottle(navigation_handle),
+    : NavigationThrottle(registry),
       name_(base::StrCat({"ProceedUntilResponseNavigationThrottle::",
                           client ? client->GetNameForLogging() : ""})),
       client_(std::move(client)),
diff --git a/components/policy/content/proceed_until_response_navigation_throttle.h b/components/policy/content/proceed_until_response_navigation_throttle.h
index dd870ba..333b075 100644
--- a/components/policy/content/proceed_until_response_navigation_throttle.h
+++ b/components/policy/content/proceed_until_response_navigation_throttle.h
@@ -11,10 +11,6 @@
 #include "base/functional/callback_forward.h"
 #include "content/public/browser/navigation_throttle.h"
 
-namespace content {
-class NavigationHandle;
-}  // namespace content
-
 // This class can be used to wrap internal throttles that trigger asynchronous
 // tasks on WillStartRequest or WillRedirectRequest and need to make a final
 // decision based on the result of the asynchronous tasks, but are actually OK
@@ -36,12 +32,12 @@
   // asynchronous tasks are done.
   class Client : public content::NavigationThrottle {
    public:
-    explicit Client(content::NavigationHandle* navigation_handle);
+    explicit Client(content::NavigationThrottleRegistry& registry);
     virtual void SetDeferredResultCallback(
         const DeferredResultCallback& deferred_result_callback) = 0;
   };
   ProceedUntilResponseNavigationThrottle(
-      content::NavigationHandle* navigation_handle,
+      content::NavigationThrottleRegistry& registry,
       std::unique_ptr<Client> client,
       std::optional<DeferredResultCallback> deferred_result_callback);
   ProceedUntilResponseNavigationThrottle(
diff --git a/components/policy/content/proceed_until_response_navigation_throttle_unittest.cc b/components/policy/content/proceed_until_response_navigation_throttle_unittest.cc
index ef1ef32..7e7067f2 100644
--- a/components/policy/content/proceed_until_response_navigation_throttle_unittest.cc
+++ b/components/policy/content/proceed_until_response_navigation_throttle_unittest.cc
@@ -10,6 +10,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/test/navigation_simulator.h"
+#include "content/public/test/test_navigation_throttle_inserter.h"
 #include "content/public/test/test_renderer_host.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -97,9 +98,9 @@
     : public ProceedUntilResponseNavigationThrottle::Client {
  public:
   EventMonitorNavigationThrottleClient(
-      content::NavigationHandle* navigation_handle,
+      content::NavigationThrottleRegistry& registry,
       raw_ptr<EventExpectation> event_expectation)
-      : ProceedUntilResponseNavigationThrottle::Client(navigation_handle),
+      : ProceedUntilResponseNavigationThrottle::Client(registry),
         event_expectation_(event_expectation) {}
 
  private:
@@ -130,8 +131,7 @@
 }  // namespace
 
 class ProceedUntilResponseNavigationThrottleTest
-    : public content::RenderViewHostTestHarness,
-      public content::WebContentsObserver {
+    : public content::RenderViewHostTestHarness {
  public:
   ProceedUntilResponseNavigationThrottleTest() = default;
   ProceedUntilResponseNavigationThrottleTest(
@@ -145,6 +145,12 @@
       const GURL& url) {
     auto navigation_simulator =
         content::NavigationSimulator::CreateRendererInitiated(url, main_rfh());
+    auto throttle_inserter = std::make_unique<
+        content::TestNavigationThrottleInserter>(
+        web_contents(),
+        base::BindRepeating(
+            &ProceedUntilResponseNavigationThrottleTest::CreateAndAddThrottle,
+            base::Unretained(this)));
     navigation_simulator->SetAutoAdvance(false);
     navigation_simulator->Start();
     return navigation_simulator;
@@ -156,20 +162,16 @@
   // content::RenderViewHostTestHarness implementation:
   void SetUp() override {
     content::RenderViewHostTestHarness::SetUp();
-    Observe(content::RenderViewHostTestHarness::web_contents());
   }
 
-  // content::WebContentsObserver implementation:
-  void DidStartNavigation(
-      content::NavigationHandle* navigation_handle) override {
+  void CreateAndAddThrottle(content::NavigationThrottleRegistry& registry) {
     std::unique_ptr<ProceedUntilResponseNavigationThrottle::Client>
         monitor_throttle =
             std::make_unique<EventMonitorNavigationThrottleClient>(
-                navigation_handle, &event_expectation_);
-    auto throttle = std::make_unique<ProceedUntilResponseNavigationThrottle>(
-        navigation_handle, std::move(monitor_throttle), std::nullopt);
-
-    navigation_handle->RegisterThrottleForTesting(std::move(throttle));
+                registry, &event_expectation_);
+    registry.AddThrottle(
+        std::make_unique<ProceedUntilResponseNavigationThrottle>(
+            registry, std::move(monitor_throttle), std::nullopt));
   }
 
   EventExpectation event_expectation_;
diff --git a/components/policy/content/safe_sites_navigation_throttle.cc b/components/policy/content/safe_sites_navigation_throttle.cc
index daf12af..1a86b8b 100644
--- a/components/policy/content/safe_sites_navigation_throttle.cc
+++ b/components/policy/content/safe_sites_navigation_throttle.cc
@@ -13,10 +13,10 @@
 // Use of Unretained for is safe because it is called synchronously from this
 // object.
 SafeSitesNavigationThrottle::SafeSitesNavigationThrottle(
-    content::NavigationHandle* navigation_handle,
+    content::NavigationThrottleRegistry& registry,
     content::BrowserContext* context,
     std::optional<std::string_view> safe_sites_error_page_content)
-    : Client(navigation_handle),
+    : Client(registry),
       safe_search_service_(SafeSearchFactory::GetForBrowserContext(context)),
       safe_sites_error_page_content_(std::move(safe_sites_error_page_content)) {
   SetDeferredResultCallback(base::BindRepeating(
diff --git a/components/policy/content/safe_sites_navigation_throttle.h b/components/policy/content/safe_sites_navigation_throttle.h
index 265b7b08..e8b821f0 100644
--- a/components/policy/content/safe_sites_navigation_throttle.h
+++ b/components/policy/content/safe_sites_navigation_throttle.h
@@ -18,7 +18,7 @@
 
 namespace content {
 class BrowserContext;
-class NavigationHandle;
+class NavigationThrottleRegistry;
 }  // namespace content
 
 // SafeSitesNavigationThrottle provides a simple way to block a navigation
@@ -28,7 +28,7 @@
 class SafeSitesNavigationThrottle
     : public ProceedUntilResponseNavigationThrottle::Client {
  public:
-  SafeSitesNavigationThrottle(content::NavigationHandle* navigation_handle,
+  SafeSitesNavigationThrottle(content::NavigationThrottleRegistry& registry,
                               content::BrowserContext* context,
                               std::optional<std::string_view>
                                   safe_sites_error_page_content = std::nullopt);
diff --git a/components/saved_tab_groups/internal/BUILD.gn b/components/saved_tab_groups/internal/BUILD.gn
index 5c775a7..04789e90 100644
--- a/components/saved_tab_groups/internal/BUILD.gn
+++ b/components/saved_tab_groups/internal/BUILD.gn
@@ -185,6 +185,7 @@
     "//components/saved_tab_groups/proto",
     "//components/saved_tab_groups/public",
     "//components/saved_tab_groups/test_support",
+    "//components/saved_tab_groups/test_support:test_support_proto",
     "//components/signin/public/identity_manager:test_support",
     "//components/sync",
     "//components/sync:test_support",
diff --git a/components/saved_tab_groups/internal/shared_tab_group_data_sync_bridge.cc b/components/saved_tab_groups/internal/shared_tab_group_data_sync_bridge.cc
index 19ef708..226db09 100644
--- a/components/saved_tab_groups/internal/shared_tab_group_data_sync_bridge.cc
+++ b/components/saved_tab_groups/internal/shared_tab_group_data_sync_bridge.cc
@@ -906,9 +906,43 @@
 SharedTabGroupDataSyncBridge::TrimAllSupportedFieldsFromRemoteSpecifics(
     const sync_pb::EntitySpecifics& entity_specifics) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  NOTIMPLEMENTED();
-  return DataTypeSyncBridge::TrimAllSupportedFieldsFromRemoteSpecifics(
-      entity_specifics);
+
+  // LINT.IfChange(TrimAllSupportedFieldsFromRemoteSpecifics)
+  sync_pb::SharedTabGroupDataSpecifics trimmed_specifics =
+      entity_specifics.shared_tab_group_data();
+  trimmed_specifics.clear_guid();
+  trimmed_specifics.clear_update_time_windows_epoch_micros();
+
+  if (trimmed_specifics.has_tab()) {
+    sync_pb::SharedTab* tab = trimmed_specifics.mutable_tab();
+    tab->clear_url();
+    tab->clear_title();
+    tab->clear_shared_tab_group_guid();
+    tab->clear_unique_position();
+
+    if (tab->ByteSizeLong() == 0) {
+      trimmed_specifics.clear_tab();
+    }
+  }
+
+  if (trimmed_specifics.has_tab_group()) {
+    sync_pb::SharedTabGroup* tab_group = trimmed_specifics.mutable_tab_group();
+    tab_group->clear_title();
+    tab_group->clear_color();
+    tab_group->clear_originating_tab_group_guid();
+
+    if (tab_group->ByteSizeLong() == 0) {
+      trimmed_specifics.clear_tab_group();
+    }
+  }
+  // LINT.ThenChange(//components/sync/protocol/shared_tab_group_data_specifics.proto:SharedTabGroupDataSpecifics)
+
+  sync_pb::EntitySpecifics trimmed_entity_specifics;
+  if (trimmed_specifics.ByteSizeLong() > 0) {
+    *trimmed_entity_specifics.mutable_shared_tab_group_data() =
+        std::move(trimmed_specifics);
+  }
+  return trimmed_entity_specifics;
 }
 
 bool SharedTabGroupDataSyncBridge::IsEntityDataValid(
diff --git a/components/saved_tab_groups/internal/shared_tab_group_data_sync_bridge_unittest.cc b/components/saved_tab_groups/internal/shared_tab_group_data_sync_bridge_unittest.cc
index 902bff7e..fc39f72 100644
--- a/components/saved_tab_groups/internal/shared_tab_group_data_sync_bridge_unittest.cc
+++ b/components/saved_tab_groups/internal/shared_tab_group_data_sync_bridge_unittest.cc
@@ -32,6 +32,7 @@
 #include "components/saved_tab_groups/public/saved_tab_group_tab.h"
 #include "components/saved_tab_groups/public/types.h"
 #include "components/saved_tab_groups/public/utils.h"
+#include "components/saved_tab_groups/test_support/extended_shared_tab_group_data_specifics.pb.h"
 #include "components/saved_tab_groups/test_support/saved_tab_group_test_utils.h"
 #include "components/sync/base/client_tag_hash.h"
 #include "components/sync/base/data_type.h"
@@ -72,6 +73,7 @@
 using testing::InvokeWithoutArgs;
 using testing::IsEmpty;
 using testing::IsNull;
+using testing::Not;
 using testing::NotNull;
 using testing::Pointee;
 using testing::Return;
@@ -2369,6 +2371,117 @@
               ElementsAre(HasTabMetadata("tab title", "http://google.com/1")));
 }
 
+TEST_F(SharedTabGroupDataSyncBridgeTest,
+       ShouldTrimAllSupportedFieldsFromRemoteTabGroupSpecifics) {
+  ASSERT_TRUE(InitializeBridgeAndModel());
+
+  sync_pb::EntitySpecifics remote_tab_group_specifics;
+  sync_pb::SharedTabGroupDataSpecifics* tab_group_specifics =
+      remote_tab_group_specifics.mutable_shared_tab_group_data();
+  tab_group_specifics->set_guid("guid");
+  tab_group_specifics->set_update_time_windows_epoch_micros(1234567890);
+  tab_group_specifics->mutable_tab_group()->set_title("title");
+  tab_group_specifics->mutable_tab_group()->set_color(
+      sync_pb::SharedTabGroup::BLUE);
+  tab_group_specifics->mutable_tab_group()->set_originating_tab_group_guid(
+      "originating_guid");
+
+  EXPECT_THAT(bridge()->TrimAllSupportedFieldsFromRemoteSpecifics(
+                  remote_tab_group_specifics),
+              EqualsProto(sync_pb::EntitySpecifics()));
+}
+
+TEST_F(SharedTabGroupDataSyncBridgeTest,
+       ShouldKeepUnknownFieldsFromRemoteTabGroupSpecifics) {
+  ASSERT_TRUE(InitializeBridgeAndModel());
+
+  sync_pb::test_utils::SharedTabGroupDataSpecifics extended_tab_group_specifics;
+  extended_tab_group_specifics.set_guid("guid");
+  extended_tab_group_specifics.set_update_time_windows_epoch_micros(1234567890);
+  extended_tab_group_specifics.mutable_tab_group()->set_title("title");
+  extended_tab_group_specifics.mutable_tab_group()->set_color(
+      sync_pb::test_utils::SharedTabGroup::UNSPECIFIED);
+  extended_tab_group_specifics.mutable_tab_group()
+      ->set_originating_tab_group_guid("originating_guid");
+  extended_tab_group_specifics.mutable_tab_group()->set_extra_field_for_testing(
+      "extra_field_for_testing");
+
+  // Serialize and deserialize the proto to get unknown fields.
+  sync_pb::EntitySpecifics remote_tab_group_specifics;
+  ASSERT_TRUE(
+      remote_tab_group_specifics.mutable_shared_tab_group_data()
+          ->ParseFromString(extended_tab_group_specifics.SerializeAsString()));
+
+  sync_pb::EntitySpecifics trimmed_specifics =
+      bridge()->TrimAllSupportedFieldsFromRemoteSpecifics(
+          remote_tab_group_specifics);
+  EXPECT_THAT(trimmed_specifics, Not(EqualsProto(sync_pb::EntitySpecifics())));
+
+  // Verify that deserialized proto keeps unknown fields.
+  sync_pb::test_utils::SharedTabGroupDataSpecifics
+      deserialized_extended_specifics;
+  ASSERT_TRUE(deserialized_extended_specifics.ParseFromString(
+      trimmed_specifics.shared_tab_group_data().SerializeAsString()));
+  EXPECT_EQ(
+      deserialized_extended_specifics.tab_group().extra_field_for_testing(),
+      "extra_field_for_testing");
+}
+
+TEST_F(SharedTabGroupDataSyncBridgeTest,
+       ShouldTrimAllSupportedFieldsFromRemoteTabSpecifics) {
+  ASSERT_TRUE(InitializeBridgeAndModel());
+
+  sync_pb::EntitySpecifics remote_tab_specifics;
+  sync_pb::SharedTabGroupDataSpecifics* tab_specifics =
+      remote_tab_specifics.mutable_shared_tab_group_data();
+  tab_specifics->set_guid("guid");
+  tab_specifics->set_update_time_windows_epoch_micros(1234567890);
+  tab_specifics->mutable_tab()->set_url("http://google.com/1");
+  tab_specifics->mutable_tab()->set_title("title");
+  tab_specifics->mutable_tab()->set_shared_tab_group_guid("group_guid");
+  *tab_specifics->mutable_tab()->mutable_unique_position() =
+      GenerateRandomUniquePosition().ToProto();
+
+  EXPECT_THAT(
+      bridge()->TrimAllSupportedFieldsFromRemoteSpecifics(remote_tab_specifics),
+      EqualsProto(sync_pb::EntitySpecifics()));
+}
+
+TEST_F(SharedTabGroupDataSyncBridgeTest,
+       ShouldKeepUnknownFieldsFromRemoteTabSpecifics) {
+  ASSERT_TRUE(InitializeBridgeAndModel());
+
+  sync_pb::test_utils::SharedTabGroupDataSpecifics extended_tab_specifics;
+  extended_tab_specifics.set_guid("guid");
+  extended_tab_specifics.set_update_time_windows_epoch_micros(1234567890);
+  extended_tab_specifics.mutable_tab()->set_url("http://google.com/1");
+  extended_tab_specifics.mutable_tab()->set_title("title");
+  extended_tab_specifics.mutable_tab()->set_shared_tab_group_guid("group_guid");
+  *extended_tab_specifics.mutable_tab()->mutable_unique_position() =
+      GenerateRandomUniquePosition().ToProto();
+  extended_tab_specifics.mutable_tab()->set_extra_field_for_testing(
+      "extra_field_for_testing");
+
+  // Serialize and deserialize the proto to get unknown fields.
+  sync_pb::EntitySpecifics remote_tab_specifics;
+  ASSERT_TRUE(
+      remote_tab_specifics.mutable_shared_tab_group_data()->ParseFromString(
+          extended_tab_specifics.SerializeAsString()));
+
+  sync_pb::EntitySpecifics trimmed_specifics =
+      bridge()->TrimAllSupportedFieldsFromRemoteSpecifics(remote_tab_specifics);
+
+  EXPECT_THAT(trimmed_specifics, Not(EqualsProto(sync_pb::EntitySpecifics())));
+
+  // Verify that deserialized proto keeps unknown fields.
+  sync_pb::test_utils::SharedTabGroupDataSpecifics
+      deserialized_extended_specifics;
+  ASSERT_TRUE(deserialized_extended_specifics.ParseFromString(
+      trimmed_specifics.shared_tab_group_data().SerializeAsString()));
+  EXPECT_EQ(deserialized_extended_specifics.tab().extra_field_for_testing(),
+            "extra_field_for_testing");
+}
+
 // The number of tabs to test the correct ordering of remote updates.
 constexpr size_t kNumTabsForOrderTest = 5;
 
diff --git a/components/saved_tab_groups/test_support/BUILD.gn b/components/saved_tab_groups/test_support/BUILD.gn
index 574983d..f3bc3835 100644
--- a/components/saved_tab_groups/test_support/BUILD.gn
+++ b/components/saved_tab_groups/test_support/BUILD.gn
@@ -3,6 +3,18 @@
 # found in the LICENSE file.
 
 import("//build/buildflag_header.gni")
+import("//third_party/protobuf/proto_library.gni")
+
+proto_library("test_support_proto") {
+  testonly = true
+  proto_in_dir = "//"
+  sources = [
+    "extended_shared_tab_group_data_specifics.proto",
+  ]
+  deps = [
+    "//components/sync/protocol",
+  ]
+}
 
 source_set("test_support") {
   testonly = true
diff --git a/components/saved_tab_groups/test_support/extended_shared_tab_group_data_specifics.proto b/components/saved_tab_groups/test_support/extended_shared_tab_group_data_specifics.proto
new file mode 100644
index 0000000..291d282
--- /dev/null
+++ b/components/saved_tab_groups/test_support/extended_shared_tab_group_data_specifics.proto
@@ -0,0 +1,43 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This file is used for testing only. It contains a subset of fields from
+// components/sync/protocol/shared_tab_group_data_specifics.proto with
+// additional fields for testing purposes.
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package sync_pb.test_utils;
+
+import "components/sync/protocol/unique_position.proto";
+
+message SharedTabGroupDataSpecifics {
+  optional string guid = 1;
+  oneof entity {
+    SharedTabGroup tab_group = 3;
+    SharedTab tab = 4;
+  }
+  optional int64 update_time_windows_epoch_micros = 5;
+}
+
+message SharedTabGroup {
+  optional string title = 1;
+  enum Color {
+    UNSPECIFIED = 0;
+  }
+  optional Color color = 2;
+  optional string originating_tab_group_guid = 3;
+
+  optional string extra_field_for_testing = 10000;
+}
+
+message SharedTab {
+  optional string url = 1;
+  optional string title = 2;
+  optional string shared_tab_group_guid = 4;
+  optional UniquePosition unique_position = 5;
+
+  optional string extra_field_for_testing = 10000;
+}
diff --git a/components/strings/BUILD.gn b/components/strings/BUILD.gn
index dd44c6a..774b976 100644
--- a/components/strings/BUILD.gn
+++ b/components/strings/BUILD.gn
@@ -25,7 +25,7 @@
   ]
 }
 
-grit("components_strings") {
+grit_strings("components_strings") {
   source = "../components_strings.grd"
   defines = [
     "enable_arcore=$enable_arcore",
@@ -40,10 +40,6 @@
   ]
 
   outputs = [ "grit/components_strings.h" ]
-  foreach(locale, all_chrome_locales) {
-    outputs += [ "components_strings_$locale.pak" ]
-  }
-
   if (is_android) {
     create_android_resources = true
   }
@@ -55,32 +51,23 @@
   }
 }
 
-grit("components_branded_strings") {
+grit_strings("components_branded_strings") {
   source = "../components_${branding_path_product}_strings.grd"
   outputs = [ "grit/components_branded_strings.h" ]
-  foreach(locale, all_chrome_locales) {
-    outputs += [ "components_branded_strings_$locale.pak" ]
-  }
 }
 
-grit("components_locale_settings") {
+grit_strings("components_locale_settings") {
   source = "../components_locale_settings.grd"
   outputs = [ "grit/components_locale_settings.h" ]
-  foreach(locale, all_chrome_locales) {
-    outputs += [ "components_locale_settings_$locale.pak" ]
-  }
 
   if (is_android) {
     create_android_resources = true
   }
 }
 
-grit("search_engine_descriptions_strings") {
+grit_strings("search_engine_descriptions_strings") {
   source = "../search_engine_descriptions_strings.grd"
   outputs = [ "grit/search_engine_descriptions_strings.h" ]
-  foreach(locale, all_chrome_locales) {
-    outputs += [ "search_engine_descriptions_strings_$locale.pak" ]
-  }
 }
 
 if (is_android) {
@@ -89,12 +76,9 @@
   }
 }
 
-grit("privacy_sandbox_strings") {
+grit_strings("privacy_sandbox_strings") {
   source = "../privacy_sandbox_strings.grd"
   outputs = [ "grit/privacy_sandbox_strings.h" ]
-  foreach(locale, all_chrome_locales) {
-    outputs += [ "privacy_sandbox_strings_$locale.pak" ]
-  }
 
   if (is_android) {
     create_android_resources = true
diff --git a/components/sync/BUILD.gn b/components/sync/BUILD.gn
index f003393..11f1385 100644
--- a/components/sync/BUILD.gn
+++ b/components/sync/BUILD.gn
@@ -20,6 +20,8 @@
   sources = [
     "test/bookmark_entity_builder.cc",
     "test/bookmark_entity_builder.h",
+    "test/collaboration_group_util.cc",
+    "test/collaboration_group_util.h",
     "test/data_type_manager_mock.cc",
     "test/data_type_manager_mock.h",
     "test/data_type_store_test_util.cc",
diff --git a/components/sync/protocol/shared_tab_group_data_specifics.proto b/components/sync/protocol/shared_tab_group_data_specifics.proto
index 7b769f1..ba0ada9 100644
--- a/components/sync/protocol/shared_tab_group_data_specifics.proto
+++ b/components/sync/protocol/shared_tab_group_data_specifics.proto
@@ -18,6 +18,7 @@
 
 import "components/sync/protocol/unique_position.proto";
 
+// LINT.IfChange(SharedTabGroupDataSpecifics)
 message SharedTabGroupDataSpecifics {
   // Unique identifier for this entity.
   optional string guid = 1;
@@ -70,3 +71,4 @@
   reserved 3;
   reserved "favicon_url";
 }
+// LINT.ThenChange(//components/saved_tab_groups/internal/shared_tab_group_data_sync_bridge.cc:TrimAllSupportedFieldsFromRemoteSpecifics)
diff --git a/components/sync/test/collaboration_group_util.cc b/components/sync/test/collaboration_group_util.cc
new file mode 100644
index 0000000..ef9cb25f
--- /dev/null
+++ b/components/sync/test/collaboration_group_util.cc
@@ -0,0 +1,56 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/sync/test/collaboration_group_util.h"
+
+#include "components/sync/base/time.h"
+#include "components/sync/model/entity_change.h"
+#include "components/sync/protocol/collaboration_group_specifics.pb.h"
+#include "components/sync/protocol/entity_data.h"
+#include "components/sync/protocol/entity_specifics.pb.h"
+#include "components/sync/test/entity_builder_factory.h"
+#include "components/sync/test/fake_server.h"
+
+namespace collaboration_group_utils {
+
+sync_pb::CollaborationGroupSpecifics MakeCollaborationGroupSpecifics(
+    const std::string& group_id) {
+  sync_pb::CollaborationGroupSpecifics result;
+  result.set_collaboration_id(group_id);
+
+  base::Time now = base::Time::Now();
+  result.set_changed_at_timestamp_millis_since_unix_epoch(
+      now.InMillisecondsSinceUnixEpoch());
+  result.set_consistency_token(
+      base::NumberToString(now.InMillisecondsSinceUnixEpoch()));
+  return result;
+}
+
+syncer::EntityData EntityDataFromSpecifics(
+    const sync_pb::CollaborationGroupSpecifics& specifics) {
+  syncer::EntityData entity_data;
+  *entity_data.specifics.mutable_collaboration_group() = specifics;
+  entity_data.name = specifics.collaboration_id();
+  return entity_data;
+}
+
+std::unique_ptr<syncer::EntityChange> EntityChangeAddFromSpecifics(
+    const sync_pb::CollaborationGroupSpecifics& specifics) {
+  return syncer::EntityChange::CreateAdd(specifics.collaboration_id(),
+                                         EntityDataFromSpecifics(specifics));
+}
+
+std::unique_ptr<syncer::EntityChange> EntityChangeUpdateFromSpecifics(
+    const sync_pb::CollaborationGroupSpecifics& specifics) {
+  return syncer::EntityChange::CreateUpdate(specifics.collaboration_id(),
+                                            EntityDataFromSpecifics(specifics));
+}
+
+std::unique_ptr<syncer::EntityChange> EntityChangeDeleteFromSpecifics(
+    const sync_pb::CollaborationGroupSpecifics& specifics) {
+  return syncer::EntityChange::CreateDelete(specifics.collaboration_id(),
+                                            syncer::EntityData());
+}
+
+}  // namespace collaboration_group_utils
diff --git a/components/sync/test/collaboration_group_util.h b/components/sync/test/collaboration_group_util.h
new file mode 100644
index 0000000..6b3ac64
--- /dev/null
+++ b/components/sync/test/collaboration_group_util.h
@@ -0,0 +1,38 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SYNC_TEST_COLLABORATION_GROUP_UTIL_H_
+#define COMPONENTS_SYNC_TEST_COLLABORATION_GROUP_UTIL_H_
+
+#include "components/sync/base/time.h"
+#include "components/sync/model/entity_change.h"
+#include "components/sync/protocol/collaboration_group_specifics.pb.h"
+#include "components/sync/protocol/entity_data.h"
+#include "components/sync/protocol/entity_specifics.pb.h"
+#include "components/sync/test/entity_builder_factory.h"
+#include "components/sync/test/fake_server.h"
+
+// Util class with methods to facilitate writing unit tests for
+// injecting/updating entities for collaboration group in fake
+// sync server.
+namespace collaboration_group_utils {
+
+sync_pb::CollaborationGroupSpecifics MakeCollaborationGroupSpecifics(
+    const std::string& id);
+
+syncer::EntityData EntityDataFromSpecifics(
+    const sync_pb::CollaborationGroupSpecifics& specifics);
+
+std::unique_ptr<syncer::EntityChange> EntityChangeAddFromSpecifics(
+    const sync_pb::CollaborationGroupSpecifics& specifics);
+
+std::unique_ptr<syncer::EntityChange> EntityChangeUpdateFromSpecifics(
+    const sync_pb::CollaborationGroupSpecifics& specifics);
+
+std::unique_ptr<syncer::EntityChange> EntityChangeDeleteFromSpecifics(
+    const sync_pb::CollaborationGroupSpecifics& specifics);
+
+}  // namespace collaboration_group_utils
+
+#endif  // COMPONENTS_SYNC_TEST_COLLABORATION_GROUP_UTIL_H_
diff --git a/components/user_education/custom-help-bubbles.md b/components/user_education/custom-help-bubbles.md
index 80c817b5..9a0c70a 100644
--- a/components/user_education/custom-help-bubbles.md
+++ b/components/user_education/custom-help-bubbles.md
@@ -117,9 +117,9 @@
 4. Use `DECLARE_TOP_CHROME_WEBUI_CONFIG(ClassName)` to automatically create a
    config for your WebUI.
 5. Call `.AddWebUIConfig(ClassNameConfig)` in
-   [this file](//chrome/browser/ui/webui/chrome_web_ui_configs.cc).
+   [this file](/chrome/browser/ui/webui/chrome_web_ui_configs.cc).
 6. Call or add to the appropriate `RegisterWebUIControllerInterfaceBinder()` in
-   [this file](//chrome/browser/chrome_browser_interface_binders_webui.cc).
+   [this file](/chrome/browser/chrome_browser_interface_binders_webui.cc).
    - This should include `CustomHelpBubbleHandlerFactory` and any additional
      mojo bindings you need.
 
diff --git a/content/browser/aggregation_service/aggregation_service_storage_sql.cc b/content/browser/aggregation_service/aggregation_service_storage_sql.cc
index 3cb7a97..42604da 100644
--- a/content/browser/aggregation_service/aggregation_service_storage_sql.cc
+++ b/content/browser/aggregation_service/aggregation_service_storage_sql.cc
@@ -241,9 +241,6 @@
   CHECK(network::IsUrlPotentiallyTrustworthy(url));
   CHECK_LE(keyset.keys.size(), PublicKeyset::kMaxNumberKeys);
 
-  // TODO(crbug.com/40190806): Add an allowlist for helper server urls and
-  // validate the url.
-
   // Force the creation of the database if it doesn't exist, as we need to
   // persist the public keys.
   if (!EnsureDatabaseOpen(DbCreationPolicy::kCreateIfAbsent)) {
diff --git a/content/browser/data_decoder_browsertest.cc b/content/browser/data_decoder_browsertest.cc
index 0d70114..6745b6d 100644
--- a/content/browser/data_decoder_browsertest.cc
+++ b/content/browser/data_decoder_browsertest.cc
@@ -23,7 +23,6 @@
 #include "services/data_decoder/public/cpp/decode_image.h"
 #include "services/data_decoder/public/mojom/data_decoder_service.mojom.h"
 #include "services/data_decoder/public/mojom/image_decoder.mojom.h"
-#include "services/data_decoder/public/mojom/json_parser.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 using ::testing::Pair;
@@ -129,25 +128,27 @@
   ServiceProcessObserver observer;
 
   // Verifies that separate DataDecoder client objects will launch separate
-  // service processes. We also bind a JsonParser interface to ensure that the
-  // instances don't go idle.
+  // service processes. We also bind an ImageDecoder interface to ensure that
+  // the instances don't go idle.
   data_decoder::DataDecoder decoder1;
-  mojo::Remote<data_decoder::mojom::JsonParser> parser1;
-  decoder1.GetService()->BindJsonParser(parser1.BindNewPipeAndPassReceiver());
+  mojo::Remote<data_decoder::mojom::ImageDecoder> image_decoder1;
+  decoder1.GetService()->BindImageDecoder(
+      image_decoder1.BindNewPipeAndPassReceiver());
   observer.WaitForNextLaunch();
   EXPECT_EQ(1, observer.instances_started());
 
   data_decoder::DataDecoder decoder2;
-  mojo::Remote<data_decoder::mojom::JsonParser> parser2;
-  decoder2.GetService()->BindJsonParser(parser2.BindNewPipeAndPassReceiver());
+  mojo::Remote<data_decoder::mojom::ImageDecoder> image_decoder2;
+  decoder2.GetService()->BindImageDecoder(
+      image_decoder2.BindNewPipeAndPassReceiver());
   observer.WaitForNextLaunch();
   EXPECT_EQ(2, observer.instances_started());
 
   // Both interfaces should be connected end-to-end.
-  parser1.FlushForTesting();
-  parser2.FlushForTesting();
-  EXPECT_TRUE(parser1.is_connected());
-  EXPECT_TRUE(parser2.is_connected());
+  image_decoder1.FlushForTesting();
+  image_decoder2.FlushForTesting();
+  EXPECT_TRUE(image_decoder1.is_connected());
+  EXPECT_TRUE(image_decoder2.is_connected());
 }
 
 IN_PROC_BROWSER_TEST_F(DataDecoderBrowserTest, DecodeImageIsolated) {
diff --git a/content/browser/preloading/preloading_config.cc b/content/browser/preloading/preloading_config.cc
index dac0689..9014b99 100644
--- a/content/browser/preloading/preloading_config.cc
+++ b/content/browser/preloading/preloading_config.cc
@@ -32,31 +32,31 @@
 [{
   "preloading_type": "NoStatePrefetch",
   "preloading_predictor": "LinkRel",
-  "sampling_likelihood": 0.006667
+  "sampling_likelihood": 0.005689
 }, {
   "preloading_type": "Preconnect",
   "preloading_predictor": "PointerDownOnAnchor",
-  "sampling_likelihood": 0.000113
+  "sampling_likelihood": 0.000100
 }, {
   "preloading_type": "Prefetch",
   "preloading_predictor": "DefaultSearchEngine",
-  "sampling_likelihood": 0.006505
+  "sampling_likelihood": 0.005785
 }, {
   "preloading_type": "Prefetch",
   "preloading_predictor": "OmniboxMousePredictor",
-  "sampling_likelihood": 1.000000
+  "sampling_likelihood": 0.357184
 }, {
   "preloading_type": "Prefetch",
   "preloading_predictor": "OmniboxSearchPredictor",
-  "sampling_likelihood": 1.000000
+  "sampling_likelihood": 0.670228
 }, {
   "preloading_type": "Prefetch",
   "preloading_predictor": "OmniboxTouchDownPredirector",
-  "sampling_likelihood": 0.011505
+  "sampling_likelihood": 0.010079
 }, {
   "preloading_type": "Prefetch",
   "preloading_predictor": "SpeculationRules",
-  "sampling_likelihood": 0.001356
+  "sampling_likelihood": 0.000892
 }, {
   "preloading_type": "Prefetch",
   "preloading_predictor": "SpeculationRulesFromIsolatedWorld",
@@ -64,43 +64,47 @@
 }, {
   "preloading_type": "Prefetch",
   "preloading_predictor": "UrlPointerDownOnAnchor",
-  "sampling_likelihood": 0.027996
+  "sampling_likelihood": 0.002821
 }, {
   "preloading_type": "Prefetch",
   "preloading_predictor": "UrlPointerHoverOnAnchor",
-  "sampling_likelihood": 0.029942
+  "sampling_likelihood": 0.033897
+}, {
+  "preloading_type": "Prefetch",
+  "preloading_predictor": "ViewportHeuristic",
+  "sampling_likelihood": 1.000000
 }, {
   "preloading_type": "Prerender",
   "preloading_predictor": "BackButtonHover",
-  "sampling_likelihood": 0.007192
+  "sampling_likelihood": 0.006551
 }, {
   "preloading_type": "Prerender",
   "preloading_predictor": "BackGestureNavigation",
-  "sampling_likelihood": 0.232383
+  "sampling_likelihood": 0.209279
 }, {
   "preloading_type": "Prerender",
   "preloading_predictor": "DefaultSearchEngine",
-  "sampling_likelihood": 0.006344
+  "sampling_likelihood": 0.005771
 }, {
   "preloading_type": "Prerender",
   "preloading_predictor": "MouseBackButton",
-  "sampling_likelihood": 0.076308
+  "sampling_likelihood": 0.070962
 }, {
   "preloading_type": "Prerender",
   "preloading_predictor": "MouseHoverOrMouseDownOnBookmarkBar",
-  "sampling_likelihood": 0.023874
+  "sampling_likelihood": 0.022187
 }, {
   "preloading_type": "Prerender",
   "preloading_predictor": "MouseHoverOrMouseDownOnNewTabPage",
-  "sampling_likelihood": 0.033195
+  "sampling_likelihood": 0.030196
 }, {
   "preloading_type": "Prerender",
   "preloading_predictor": "OmniboxDirectURLInput",
-  "sampling_likelihood": 0.005746
+  "sampling_likelihood": 0.005348
 }, {
   "preloading_type": "Prerender",
   "preloading_predictor": "SpeculationRules",
-  "sampling_likelihood": 0.067464
+  "sampling_likelihood": 0.046348
 }, {
   "preloading_type": "Prerender",
   "preloading_predictor": "SpeculationRulesFromIsolatedWorld",
@@ -108,15 +112,19 @@
 }, {
   "preloading_type": "Prerender",
   "preloading_predictor": "TouchOnNewTabPage",
-  "sampling_likelihood": 0.034646
+  "sampling_likelihood": 0.028625
 }, {
   "preloading_type": "Prerender",
   "preloading_predictor": "UrlPointerDownOnAnchor",
-  "sampling_likelihood": 0.194379
+  "sampling_likelihood": 0.132505
 }, {
   "preloading_type": "Prerender",
   "preloading_predictor": "UrlPointerHoverOnAnchor",
-  "sampling_likelihood": 0.788890
+  "sampling_likelihood": 0.525988
+}, {
+  "preloading_type": "Prerender",
+  "preloading_predictor": "ViewportHeuristic",
+  "sampling_likelihood": 1.000000
 }]
 )"};
 
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
index dafc516..775c1d3 100644
--- a/content/browser/renderer_host/navigation_request.cc
+++ b/content/browser/renderer_host/navigation_request.cc
@@ -8406,15 +8406,7 @@
         did_receive_early_hints_before_cross_origin_redirect_);
   }
 
-  // Calculate origin in which this navigation would commit so it can be
-  // compared with what is generated by the renderer process and the browser
-  // process at commit time.
-  // TODO(crbug.com/40092527): Consider using this cached value everywhere
-  // we currently call `GetOriginToCommit()`, to prevent nonce mismatches.
-  browser_side_origin_to_commit_with_debug_info_ =
-      GetOriginToCommitWithDebugInfo();
-  std::optional<url::Origin> origin_to_commit =
-      browser_side_origin_to_commit_with_debug_info_.first;
+  std::optional<url::Origin> origin_to_commit = GetOriginToCommit();
   same_origin_ = (previous_render_frame_host->GetLastCommittedOrigin() ==
                   origin_to_commit);
 
@@ -8501,26 +8493,13 @@
 }
 
 std::optional<url::Origin> NavigationRequest::GetOriginToCommit() {
-  return GetOriginToCommitWithDebugInfo().first;
-}
-
-std::pair<std::optional<url::Origin>, std::string>
-NavigationRequest::GetOriginToCommitWithDebugInfo() {
-  return GetOriginForURLLoaderFactoryAfterResponseWithDebugInfo();
+  return GetOriginForURLLoaderFactoryAfterResponse();
 }
 
 url::Origin NavigationRequest::GetOriginForURLLoaderFactoryBeforeResponse(
     network::mojom::WebSandboxFlags sandbox_flags) {
-  return GetOriginForURLLoaderFactoryBeforeResponseWithDebugInfo(sandbox_flags)
-      .first;
-}
-
-std::pair<url::Origin, std::string>
-NavigationRequest::GetOriginForURLLoaderFactoryBeforeResponseWithDebugInfo(
-    network::mojom::WebSandboxFlags sandbox_flags) {
   // Calculate an approximation of the origin. The sandbox/csp are ignored.
-  std::pair<url::Origin, std::string> origin_and_debug_info =
-      GetOriginForURLLoaderFactoryUncheckedWithDebugInfo();
+  url::Origin origin = GetOriginForURLLoaderFactoryUnchecked();
 
   // Apply sandbox flags.
   // See https://html.spec.whatwg.org/#sandboxed-origin-browsing-context-flag
@@ -8536,17 +8515,10 @@
       (sandbox_flags & network::mojom::WebSandboxFlags::kOrigin) ==
       network::mojom::WebSandboxFlags::kOrigin;
   if (use_opaque_origin) {
-    origin_and_debug_info =
-        std::pair(origin_and_debug_info.first.DeriveNewOpaqueOrigin(),
-                  origin_and_debug_info.second + ", sandbox_flags");
+    origin = origin.DeriveNewOpaqueOrigin();
   }
 
-  return origin_and_debug_info;
-}
-
-std::optional<url::Origin>
-NavigationRequest::GetOriginForURLLoaderFactoryAfterResponse() {
-  return GetOriginForURLLoaderFactoryAfterResponseWithDebugInfo().first;
+  return origin;
 }
 
 // TODO(crbug.com/40065692): Remove.
@@ -8584,8 +8556,8 @@
   return "other";
 }
 
-std::pair<std::optional<url::Origin>, std::string>
-NavigationRequest::GetOriginForURLLoaderFactoryAfterResponseWithDebugInfo() {
+std::optional<url::Origin>
+NavigationRequest::GetOriginForURLLoaderFactoryAfterResponse() {
   // The origin to commit is not known until we get the final network response.
   DCHECK_GE(state_, WILL_PROCESS_RESPONSE);
 
@@ -8593,25 +8565,16 @@
   // commit in (and therefore there is no origin that will get committed and we
   // indicate this by returning `nullopt`).
   if (!response_should_be_rendered_) {
-    return std::make_pair(std::nullopt, "no_commit");
+    return std::nullopt;
   }
 
   if (IsSameDocument() || IsPageActivation()) {
     CHECK(HasRenderFrameHost());
-    return std::make_pair(GetRenderFrameHost()->GetLastCommittedOrigin(),
-                          "same_doc_or_page_activation");
+    return GetRenderFrameHost()->GetLastCommittedOrigin();
   }
 
-  std::pair<url::Origin, std::string> origin_with_debug_info =
-      GetOriginForURLLoaderFactoryBeforeResponseWithDebugInfo(
-          SandboxFlagsToCommit());
-
-  // Add the crash keys for debugging navigation crashes with data URL.
-  // TODO(crbug.com/40065692): Remove.
-  SCOPED_CRASH_KEY_STRING256("Bug1454273", "calculated_origin",
-                             origin_with_debug_info.first.GetDebugString());
-  SCOPED_CRASH_KEY_STRING256("Bug1454273", "origin_debug_info",
-                             origin_with_debug_info.second);
+  url::Origin origin =
+      GetOriginForURLLoaderFactoryBeforeResponse(SandboxFlagsToCommit());
 
   SCOPED_CRASH_KEY_BOOL("Bug1454273", "is_in_main_frame", IsInMainFrame());
   SCOPED_CRASH_KEY_STRING256(
@@ -8649,8 +8612,7 @@
   // MHTML documents should commit as an opaque origin. They should not be able
   // to make network request on behalf of the real origin.
   // TODO(crbug.com/370979008): Migrate to CHECK.
-  DUMP_WILL_BE_CHECK(!IsMhtmlOrSubframe() ||
-                     origin_with_debug_info.first.opaque());
+  DUMP_WILL_BE_CHECK(!IsMhtmlOrSubframe() || origin.opaque());
 
   // If the target of this navigation will be rendered in a RenderFrameHost,
   // then verify that the chosen origin is allowed to be accessed from that
@@ -8673,11 +8635,11 @@
     int process_id = GetRenderFrameHost()->GetProcess()->GetDeprecatedID();
     auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
     CHECK(policy->CanAccessOrigin(
-        process_id, origin_with_debug_info.first,
+        process_id, origin,
         ChildProcessSecurityPolicyImpl::AccessType::kCanCommitNewOrigin));
   }
 
-  return origin_with_debug_info;
+  return origin;
 }
 
 void NavigationRequest::WriteIntoTrace(
@@ -11033,17 +10995,14 @@
   }
 }
 
-std::pair<url::Origin, std::string>
-NavigationRequest::GetOriginForURLLoaderFactoryUncheckedWithDebugInfo() {
+url::Origin NavigationRequest::GetOriginForURLLoaderFactoryUnchecked() {
   if (DidEncounterError()) {
     // Error pages commit in an opaque origin in the renderer process. If this
     // NavigationRequest resulted in committing an error page, return an
     // opaque origin that has precursor information consistent with the URL
     // being requested.  Note: this is intentionally done first; cases like
     // errors in srcdoc frames need not inherit the parent's origin for errors.
-    return std::make_pair(
-        url::Origin::Create(common_params().url).DeriveNewOpaqueOrigin(),
-        "error");
+    return url::Origin::Create(common_params().url).DeriveNewOpaqueOrigin();
   }
 
   // Check if this is loadDataWithBaseUrl (which needs special treatment).
@@ -11069,9 +11028,7 @@
     // opaque origin), but commits that URL as if it came from
     // |common_params.base_url_for_data_url|.  See also
     // https://crbug.com/976253.
-    return std::make_pair(
-        url::Origin::Create(common_params().base_url_for_data_url),
-        "load_data_with_base_url");
+    return url::Origin::Create(common_params().base_url_for_data_url);
   }
 
   // Use the cached tentative data origin to commit in the data: URL case. When
@@ -11080,8 +11037,7 @@
   // us uniquely identify the correct data: SiteInstance.
   if (common_params().url.SchemeIs(url::kDataScheme) &&
       tentative_data_origin_to_commit_.has_value()) {
-    return std::make_pair(tentative_data_origin_to_commit_.value(),
-                          "data: URL");
+    return tentative_data_origin_to_commit_.value();
   }
 
   // Srcdoc subframes need to inherit their origin from their parent frame.
@@ -11089,7 +11045,7 @@
     RenderFrameHostImpl* parent = frame_tree_node()->parent();
 
     if (parent) {
-      return std::make_pair(parent->GetLastCommittedOrigin(), "about_srcdoc");
+      return parent->GetLastCommittedOrigin();
     } else {
       // The only path for `parent` to be missing for a srcdoc navigation is if
       // a mainframe renderer executes `location = "about:srcdoc` instead of
@@ -11099,9 +11055,7 @@
       // encounters a COOP header. In that case we return the origin of the
       // page that executed the script, knowing that the navigation will fail
       // anyways.
-      return std::make_pair(
-          frame_tree_node()->current_frame_host()->GetLastCommittedOrigin(),
-          "about_srcdoc, no-parent");
+      return frame_tree_node()->current_frame_host()->GetLastCommittedOrigin();
     }
   }
 
@@ -11112,15 +11066,12 @@
     if (HasRenderFrameHost() && GetRenderFrameHost()->GetProcess()) {
       target_rph_id = GetRenderFrameHost()->GetProcess()->GetDeprecatedID();
     }
-    return std::make_pair(
-        static_cast<StoragePartitionImpl*>(
-            GetStoragePartitionWithCurrentSiteInfo())
-            ->GetBlobUrlRegistry()
-            ->GetOriginForNavigation(
-                GetURL(),
-                common_params().initiator_origin.value_or(url::Origin()),
-                target_rph_id),
-        "blob");
+    return static_cast<StoragePartitionImpl*>(
+               GetStoragePartitionWithCurrentSiteInfo())
+        ->GetBlobUrlRegistry()
+        ->GetOriginForNavigation(
+            GetURL(), common_params().initiator_origin.value_or(url::Origin()),
+            target_rph_id);
   }
 
   // In cases not covered above, URLLoaderFactory should be associated with the
@@ -11134,11 +11085,7 @@
     tentative_data_origin_to_commit_ = resolved_origin;
   }
 
-  return std::make_pair(resolved_origin, "url_or_initiator");
-}
-
-url::Origin NavigationRequest::GetOriginForURLLoaderFactoryUnchecked() {
-  return GetOriginForURLLoaderFactoryUncheckedWithDebugInfo().first;
+  return resolved_origin;
 }
 
 bool NavigationRequest::HasLoader() const {
diff --git a/content/browser/renderer_host/navigation_request.h b/content/browser/renderer_host/navigation_request.h
index 0f9dc9f..92dabdb 100644
--- a/content/browser/renderer_host/navigation_request.h
+++ b/content/browser/renderer_host/navigation_request.h
@@ -975,14 +975,6 @@
   // possible.
   url::Origin GetTentativeOriginAtRequestTime();
 
-  // Same as `GetOriginToCommit()`, except that includes information about how
-  // the origin gets calculated, to help debug if the browser-side calculated
-  // origin for this navigation differs from the origin calculated on the
-  // renderer side.
-  // TODO(crbug.com/40772732): Remove this.
-  std::pair<std::optional<url::Origin>, std::string>
-  GetOriginToCommitWithDebugInfo();
-
   // If this navigation fails with net::ERR_BLOCKED_BY_CLIENT, act as if it were
   // cancelled by the user and do not commit an error page.
   void SetSilentlyIgnoreBlockedByClient() {
@@ -1258,11 +1250,6 @@
     return navigation_or_document_handle_;
   }
 
-  const std::pair<std::optional<url::Origin>, std::string>&
-  browser_side_origin_to_commit_with_debug_info() {
-    return browser_side_origin_to_commit_with_debug_info_;
-  }
-
   // Initializes state which is passed from the old Document to the new Document
   // for a ViewTransition.
   void SetViewTransitionState(
@@ -2262,17 +2249,6 @@
   // after the final response is received or ready.
   std::optional<url::Origin> GetOriginForURLLoaderFactoryAfterResponse();
 
-  // These functions are the same as their non-WithDebugInfo counterparts,
-  // except that they include information about how the origin gets calculated,
-  // to help debug if the browser-side calculated origin for this navigation
-  // differs from the origin calculated on the renderer side.
-  // TODO(crbug.com/40772732): Remove this.
-  std::pair<url::Origin, std::string>
-  GetOriginForURLLoaderFactoryBeforeResponseWithDebugInfo(
-      network::mojom::WebSandboxFlags sandbox_flags);
-  std::pair<std::optional<url::Origin>, std::string>
-  GetOriginForURLLoaderFactoryAfterResponseWithDebugInfo();
-
   // Computes the CrossOriginIsolationKey to use for committing the navigation.
   // A nullopt result means that either the cross-origin isolation status of the
   // request cannot be determined because we do not have final headers for the
@@ -2354,8 +2330,6 @@
   void RecordEarlyRenderFrameHostSwapMetrics();
 
   // Helpers for GetTentativeOriginAtRequestTime and GetOriginToCommit.
-  std::pair<url::Origin, std::string>
-  GetOriginForURLLoaderFactoryUncheckedWithDebugInfo();
   url::Origin GetOriginForURLLoaderFactoryUnchecked();
 
   void MaybeRecordTraceEventsAndHistograms();
@@ -2435,11 +2409,6 @@
   blink::mojom::BeginNavigationParamsPtr begin_params_;
   blink::mojom::CommitNavigationParamsPtr commit_params_;
   bool same_origin_ = false;
-  // This member is calculated at ReadyToCommit time. It is used to compare
-  // against renderer calculated origin and browser calculated one at commit
-  // time.
-  std::pair<std::optional<url::Origin>, std::string>
-      browser_side_origin_to_commit_with_debug_info_;
 
   // Stores the NavigationUIData for this navigation until the NavigationHandle
   // is created. This can be null if the embedded did not provide a
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index f5534e1b..75e4229 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -766,117 +766,6 @@
          network::mojom::WebSandboxFlags::kNone;
 }
 
-// Verify that |browser_side_origin| and |renderer_side_origin| match.  See also
-// https://crbug.com/888079.
-void VerifyThatBrowserAndRendererCalculatedOriginsToCommitMatch(
-    NavigationRequest* navigation_request,
-    const mojom::DidCommitProvisionalLoadParams& params) {
-  DCHECK(navigation_request);
-
-  // This should be called only when a new document is created. Navigations in
-  // the same document and page activations do not create a new document.
-  DCHECK(!navigation_request->IsSameDocument());
-  DCHECK(!navigation_request->IsPageActivation());
-
-  // Ignore for now cases where the NavigationRequest is in an unexpectedly
-  // early state. Triggered by the following tests:
-  // NavigationBrowserTest.OpenerNavigation_DownloadPolicy,
-  // WebContentsImplBrowserTest.NewNamedWindow.
-  if (navigation_request->state() < NavigationRequest::WILL_PROCESS_RESPONSE)
-    return;
-
-  const url::Origin& renderer_side_origin = params.origin;
-  std::pair<std::optional<url::Origin>, std::string>
-      browser_side_origin_and_debug_info =
-          navigation_request->browser_side_origin_to_commit_with_debug_info();
-
-  // For non-opaque origins, we say the browser and renderer calculated origins
-  // match if they are exactly the same.
-  bool origins_match = (browser_side_origin_and_debug_info.first.value() ==
-                        renderer_side_origin);
-
-  // For opaque origins, we can check for equality if the opaque origin is not
-  // newly created in the renderer. If the opaque origin can be known by the
-  // browser,  e.g. if the opaque origin is inherited/copied from another
-  // document, or is from the browser-sent `origin_to_commit`, then the browser
-  // calculated origin must match the one used by the renderer in the end. On
-  // the other hand, if the opaque origin is newly created, e.g. a new sandboxed
-  // opaque origin, we can only match the precursor origin. The renderer will
-  // tell us if the origin is newly created in the renderer or not through
-  // appending "is_newly_created" in the end of `origin_calculation_debug_info`.
-  // See also `DocumentLoader::CalculateOrigin()`.
-  // TODO(crbug.com/40092527): Consider adding a separate boolean that
-  // tracks this instead of piggybacking `origin_calculation_debug_info`.
-  if (renderer_side_origin.opaque() &&
-      browser_side_origin_and_debug_info.first->opaque() &&
-      params.origin_calculation_debug_info.ends_with("is_newly_created")) {
-    origins_match = (renderer_side_origin.GetTupleOrPrecursorTupleIfOpaque() ==
-                     browser_side_origin_and_debug_info.first
-                         ->GetTupleOrPrecursorTupleIfOpaque());
-  }
-
-  // For Blob URLs, it's possible that the renderer thinks the origin is opaque
-  // while the browser thinks it's not opaque if the Blob URL origin is
-  // registered in the BlobURLNullOriginMap by the document that the navigation
-  // is replacing, causing the origin to be de-registered just before the new
-  // document commits. In this case the browser actually has the correct origin,
-  // so just compare the precursor origin of the renderer side.
-  if (params.url.SchemeIsBlob() && renderer_side_origin.opaque() &&
-      params.origin_calculation_debug_info.ends_with("is_newly_created") &&
-      navigation_request->GetRenderFrameHost()
-          ->ShouldChangeRenderFrameHostOnSameSiteNavigation()) {
-    origins_match = (renderer_side_origin.GetTupleOrPrecursorTupleIfOpaque() ==
-                     browser_side_origin_and_debug_info.first
-                         ->GetTupleOrPrecursorTupleIfOpaque());
-  }
-
-  // TODO(crbug.com/40092527): Remove the DumpWithoutCrashing below, once
-  // we are sure that the `browser_side_origin` is always the same as the
-  // `renderer_side_origin`.
-  if (!origins_match) {
-    NavigationRequest::ScopedCrashKeys navigation_request_crash_keys(
-        *navigation_request);
-    SCOPED_CRASH_KEY_STRING256(
-        "", "browser_origin",
-        browser_side_origin_and_debug_info.first->GetDebugString());
-    SCOPED_CRASH_KEY_STRING256("", "browser_debug_info",
-                               browser_side_origin_and_debug_info.second);
-    auto* parent_rfh = navigation_request->GetRenderFrameHost()->GetParent();
-    SCOPED_CRASH_KEY_STRING256(
-        "", "parent_rfh_origin",
-        parent_rfh ? parent_rfh->GetLastCommittedOrigin().GetDebugString()
-                   : "");
-    SCOPED_CRASH_KEY_STRING256("", "parent_rs_origin",
-                               parent_rfh ? parent_rfh->browsing_context_state()
-                                                ->current_replication_state()
-                                                .origin.GetDebugString()
-                                          : "");
-
-    SCOPED_CRASH_KEY_STRING256(
-        "", "browser_ready_to_commit_origin",
-        navigation_request->browser_side_origin_to_commit_with_debug_info()
-            .first->GetDebugString());
-    SCOPED_CRASH_KEY_STRING256(
-        "", "browser_ready_to_commit_debug_info",
-        navigation_request->browser_side_origin_to_commit_with_debug_info()
-            .second);
-
-    SCOPED_CRASH_KEY_STRING256("", "renderer_origin",
-                               renderer_side_origin.GetDebugString());
-    SCOPED_CRASH_KEY_STRING256("", "renderer_debug_info",
-                               params.origin_calculation_debug_info);
-    CaptureTraceForNavigationDebugScenario(
-        DebugScenario::kDebugBrowserVsRendererOriginToCommit);
-    base::debug::DumpWithoutCrashing();
-    DCHECK_EQ(browser_side_origin_and_debug_info.first.value(),
-              renderer_side_origin)
-        << "; navigation_request->GetURL() = " << navigation_request->GetURL();
-    return;
-  }
-
-  return;
-}
-
 // A simplified version of Blink's WebFrameLoadType, used to simulate renderer
 // calculations. See CalculateRendererLoadType() further below.
 // TODO(crbug.com/40150370): This should only be here temporarily.
@@ -12233,12 +12122,11 @@
   auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
   const ProcessLock process_lock =
       ProcessLock::FromSiteInfo(GetSiteInstance()->GetSiteInfo());
-  auto browser_calc_origin_to_commit =
-      navigation_request->GetOriginToCommitWithDebugInfo();
+  auto browser_calc_origin_to_commit = navigation_request->GetOriginToCommit();
   if (!process_lock.is_error_page() && !is_mhtml_subframe &&
       !policy->CanAccessOrigin(
           GetProcess()->GetDeprecatedID(),
-          browser_calc_origin_to_commit.first.value(),
+          browser_calc_origin_to_commit.value(),
           ChildProcessSecurityPolicyImpl::AccessType::kCanCommitNewOrigin)) {
     SCOPED_CRASH_KEY_STRING64("CommitNavigation", "lock_url",
                               process_lock.ToString());
@@ -12247,9 +12135,7 @@
         common_params->url.DeprecatedGetOriginAsURL().spec());
     SCOPED_CRASH_KEY_STRING64(
         "CommitNavigation", "browser_calc_origin",
-        browser_calc_origin_to_commit.first.value().GetDebugString());
-    SCOPED_CRASH_KEY_STRING64("CommitNavigation", "origin_debug_info",
-                              browser_calc_origin_to_commit.second);
+        browser_calc_origin_to_commit.value().GetDebugString());
     SCOPED_CRASH_KEY_BOOL("CommitNavigation", "is_main_frame", is_main_frame());
     // The reason this isn't is_outermost_main_frame is so that the full name of
     // the key does not exceed the 40 character limit.
@@ -14775,8 +14661,7 @@
 
   if (!bypass_checks_for_error_page &&
       !ValidateURLAndOrigin(params->url, params->origin,
-                            is_same_document_navigation, navigation_request,
-                            params->origin_calculation_debug_info)) {
+                            is_same_document_navigation, navigation_request)) {
     return false;
   }
 
@@ -14914,8 +14799,7 @@
     const GURL& url,
     const url::Origin& origin,
     bool is_same_document_navigation,
-    NavigationRequest* navigation_request,
-    std::string origin_calculation_debug_info) {
+    NavigationRequest* navigation_request) {
   // WebView's allow_universal_access_from_file_urls setting allows file origins
   // to access any other origin and bypass normal commit checks. If new
   // documents in the same process and origin may also bypass these checks after
@@ -14988,8 +14872,7 @@
                   << " lock '" << process->GetProcessLock().ToString() << "'";
       VLOG(1) << "Blocked URL " << url.spec();
       LogCannotCommitUrlCrashKeys(url, origin, is_same_document_navigation,
-                                  navigation_request,
-                                  origin_calculation_debug_info);
+                                  navigation_request);
 
       // Kills the process.
       bad_message::ReceivedBadMessage(process,
@@ -15171,9 +15054,9 @@
     //   DidCommitSameDocumentNavigation).
     // TODO(crbug.com/40150370): Make this a CHECK instead once we're
     // sure we never hit this case.
-    LogCannotCommitUrlCrashKeys(
-        params->url, params->origin, is_same_document_navigation,
-        navigation_request.get(), params->origin_calculation_debug_info);
+    LogCannotCommitUrlCrashKeys(params->url, params->origin,
+                                is_same_document_navigation,
+                                navigation_request.get());
     base::debug::DumpWithoutCrashing();
   }
 
@@ -15196,9 +15079,9 @@
           params->url, frame_tree_node_->is_on_initial_empty_document());
   if (!navigation_request && !is_synchronous_about_blank_commit &&
       !is_same_document_navigation) {
-    LogCannotCommitUrlCrashKeys(
-        params->url, params->origin, is_same_document_navigation,
-        navigation_request.get(), params->origin_calculation_debug_info);
+    LogCannotCommitUrlCrashKeys(params->url, params->origin,
+                                is_same_document_navigation,
+                                navigation_request.get());
 
     bad_message::ReceivedBadMessage(
         GetProcess(),
@@ -16045,9 +15928,6 @@
 
   base::ElapsedTimer timer;
   DCHECK_EQ(net::OK, navigation_request->GetNetErrorCode());
-  CHECK_EQ(commit_params->origin_to_commit,
-           navigation_request->browser_side_origin_to_commit_with_debug_info()
-               .first.value());
   IncreaseCommitNavigationCounter();
   mojo::PendingRemote<blink::mojom::CodeCacheHost> code_cache_host;
   mojo::PendingRemote<blink::mojom::CodeCacheHost>
@@ -16222,9 +16102,6 @@
         subresource_loader_factories,
     const blink::DocumentToken& document_token,
     blink::mojom::PolicyContainerPtr policy_container) {
-  CHECK_EQ(commit_params->origin_to_commit,
-           navigation_request->browser_side_origin_to_commit_with_debug_info()
-               .first.value());
   DCHECK(navigation_client && navigation_request);
   DCHECK_NE(GURL(), common_params->url);
   DCHECK_NE(net::OK, error_code);
@@ -16660,8 +16537,7 @@
     const GURL& url,
     const url::Origin& origin,
     bool is_same_document_navigation,
-    NavigationRequest* navigation_request,
-    std::string& origin_calculation_debug_info) {
+    NavigationRequest* navigation_request) {
   LogRendererKillCrashKeys(GetSiteInstance()->GetSiteInfo());
 
   // Temporary instrumentation to debug the root cause of renderer process
@@ -16789,12 +16665,6 @@
       is_on_initial_empty_document_key,
       base::ToString(frame_tree_node_->is_on_initial_empty_document()));
 
-  static auto* const origin_calculation_debug_info_key =
-      base::debug::AllocateCrashKeyString("origin_calculation_debug_info",
-                                          base::debug::CrashKeySize::Size256);
-  base::debug::SetCrashKeyString(origin_calculation_debug_info_key,
-                                 origin_calculation_debug_info);
-
   if (navigation_request && navigation_request->IsNavigationStarted()) {
     static auto* const is_renderer_initiated_key =
         base::debug::AllocateCrashKeyString("is_renderer_initiated",
@@ -17444,10 +17314,6 @@
                                                      params.transition));
   DCHECK_EQ(browser_history_list_was_cleared, params.history_list_was_cleared);
 
-  // TODO(crbug.com/40092527): The origin computed from the browser must
-  // match the one reported from the renderer process.
-  VerifyThatBrowserAndRendererCalculatedOriginsToCommitMatch(request, params);
-
   if (!everything_except_origin_matches) {
     // It's possible to get here when everything except the origin matches.
     // If the origin doesn't match, we would do a DumpWithoutCrashing above.
diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h
index f053f85f..1eb87a93 100644
--- a/content/browser/renderer_host/render_frame_host_impl.h
+++ b/content/browser/renderer_host/render_frame_host_impl.h
@@ -3953,12 +3953,10 @@
   // Validates whether we can commit |url| and |origin| for a navigation or a
   // document.open() URL update.
   // A return value of true means that the URL & origin can be committed.
-  bool ValidateURLAndOrigin(
-      const GURL& url,
-      const url::Origin& origin,
-      bool is_same_document_navigation,
-      NavigationRequest* navigation_request,
-      std::string origin_calculation_debug_info = std::string());
+  bool ValidateURLAndOrigin(const GURL& url,
+                            const url::Origin& origin,
+                            bool is_same_document_navigation,
+                            NavigationRequest* navigation_request);
 
   // The actual implementation of committing a navigation in the browser
   // process. Called by the DidCommitProvisionalLoad IPC handler.
@@ -4129,8 +4127,7 @@
   void LogCannotCommitUrlCrashKeys(const GURL& url,
                                    const url::Origin& origin,
                                    bool is_same_document_navigation,
-                                   NavigationRequest* navigation_request,
-                                   std::string& origin_calculation_debug_info);
+                                   NavigationRequest* navigation_request);
   void LogCannotCommitOriginCrashKeys(const GURL& url,
                                       const url::Origin& origin,
                                       const ProcessLock& process_lock,
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 6d39dcc1..55efc47d 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -4228,6 +4228,13 @@
   return delegate_ ? delegate_->PreHandleMouseEvent(this, event) : false;
 }
 
+void WebContentsImpl::PreHandleDragUpdate(const DropData& drop_data,
+                                          const gfx::PointF& client_pt) {
+  if (delegate_) {
+    delegate_->PreHandleDragUpdate(drop_data, client_pt);
+  }
+}
+
 KeyboardEventProcessingResult WebContentsImpl::PreHandleKeyboardEvent(
     const input::NativeWebKeyboardEvent& event) {
   OPTIONAL_TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("content.verbose"),
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 98fdc7f7..c6eafe5 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -1118,6 +1118,8 @@
   double GetPendingZoomLevel(RenderWidgetHostImpl* rwh) override;
 
   bool PreHandleMouseEvent(const blink::WebMouseEvent& event) override;
+  void PreHandleDragUpdate(const DropData& drop_data,
+                           const gfx::PointF& client_pt);
   KeyboardEventProcessingResult PreHandleKeyboardEvent(
       const input::NativeWebKeyboardEvent& event) override;
   bool HandleMouseEvent(const blink::WebMouseEvent& event) override;
diff --git a/content/browser/web_contents/web_contents_view_aura.cc b/content/browser/web_contents/web_contents_view_aura.cc
index 330d2fe..90615f5 100644
--- a/content/browser/web_contents/web_contents_view_aura.cc
+++ b/content/browser/web_contents/web_contents_view_aura.cc
@@ -1447,6 +1447,11 @@
   if (!target) {
     return;
   }
+
+  if (transformed_pt.has_value()) {
+    web_contents_->PreHandleDragUpdate(*drop_data, transformed_pt.value());
+  }
+
   RenderWidgetHostImpl* target_rwh =
       RenderWidgetHostImpl::From(target->GetRenderWidgetHost());
   if (!drag_security_info_.IsValidDragTarget(target_rwh)) {
diff --git a/content/browser/web_contents/web_drag_dest_mac.mm b/content/browser/web_contents/web_drag_dest_mac.mm
index b92e06b..495aa05 100644
--- a/content/browser/web_contents/web_drag_dest_mac.mm
+++ b/content/browser/web_contents/web_drag_dest_mac.mm
@@ -308,6 +308,9 @@
     return NSDragOperationNone;
   }
 
+  _webContents->PreHandleDragUpdate(*_dropDataUnfiltered,
+                                    info->location_in_view);
+
   gfx::PointF transformedPt;
   content::RenderWidgetHostImpl* targetRWH =
       [self GetRenderWidgetHostAtPoint:info->location_in_view
diff --git a/content/common/navigation_client.mojom b/content/common/navigation_client.mojom
index 1670df1..50602e7 100644
--- a/content/common/navigation_client.mojom
+++ b/content/common/navigation_client.mojom
@@ -119,11 +119,6 @@
   // removed, then this will no longer be needed.
   url.mojom.Url? initiator_base_url;
 
-  // Information about how `origin` was calculated, to help debug if it differs
-  // from the origin calculated on the browser side.
-  // TODO(crbug.com/40772732): Remove this.
-  string origin_calculation_debug_info;
-
   // The 'Permissions-Policy' headers applied to the document.
   // https://w3c.github.io/webappsec-permissions-policy/#permissions-policy-http-header-field
   // Note: For backward compatibility, this field also contains
diff --git a/content/public/browser/web_contents_delegate.h b/content/public/browser/web_contents_delegate.h
index 0125c481..503ab27 100644
--- a/content/public/browser/web_contents_delegate.h
+++ b/content/public/browser/web_contents_delegate.h
@@ -308,6 +308,8 @@
   // false.
   virtual bool PreHandleMouseEvent(WebContents* source,
                                    const blink::WebMouseEvent& event);
+  virtual void PreHandleDragUpdate(const DropData& drop_data,
+                                   const gfx::PointF& client_pt) {}
 
   // Allows delegates to handle keyboard events before sending to the renderer.
   // See enum for description of return values.
diff --git a/content/public/test/navigation_simulator.h b/content/public/test/navigation_simulator.h
index 3ab4b6777..441423c1 100644
--- a/content/public/test/navigation_simulator.h
+++ b/content/public/test/navigation_simulator.h
@@ -30,6 +30,7 @@
 
 class NavigationController;
 class NavigationHandle;
+class NavigationThrottleRegistry;
 class RenderFrameHost;
 class WebContents;
 struct GlobalRequestID;
@@ -346,6 +347,12 @@
   // navigation has finished (successfully or not).
   virtual NavigationHandle* GetNavigationHandle() = 0;
 
+  // Returns the NavigationThrottleRegistry associated with the navigation
+  // being simulated. It is an error to call this before Start() or after the
+  // navigation has finished (successfully or not) as the runner does not
+  // outlive the NavigationHandle.
+  virtual NavigationThrottleRegistry& GetNavigationThrottleRegistry() = 0;
+
   // Returns the GlobalRequestID for the simulated navigation request. Can be
   // invoked after the navigation has completed. It is an error to call this
   // before the simulated navigation has completed its WillProcessResponse
diff --git a/content/public/test/test_navigation_throttle_inserter.cc b/content/public/test/test_navigation_throttle_inserter.cc
index f4cab537..465f04e 100644
--- a/content/public/test/test_navigation_throttle_inserter.cc
+++ b/content/public/test/test_navigation_throttle_inserter.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "content/browser/renderer_host/navigation_request.h"
 #include "content/public/browser/navigation_handle.h"
 
 namespace content {
@@ -15,13 +16,24 @@
     ThrottleInsertionCallback callback)
     : WebContentsObserver(web_contents), callback_(std::move(callback)) {}
 
+TestNavigationThrottleInserter::TestNavigationThrottleInserter(
+    WebContents* web_contents,
+    NewThrottleInsertionCallback callback)
+    : WebContentsObserver(web_contents), new_callback_(std::move(callback)) {}
+
 TestNavigationThrottleInserter::~TestNavigationThrottleInserter() = default;
 
 void TestNavigationThrottleInserter::DidStartNavigation(
     NavigationHandle* navigation_handle) {
-  if (std::unique_ptr<NavigationThrottle> throttle =
-          callback_.Run(navigation_handle)) {
-    navigation_handle->RegisterThrottleForTesting(std::move(throttle));
+  if (callback_) {
+    if (std::unique_ptr<NavigationThrottle> throttle =
+            callback_.Run(navigation_handle)) {
+      navigation_handle->RegisterThrottleForTesting(std::move(throttle));
+    }
+  }
+  if (new_callback_) {
+    new_callback_.Run(*NavigationRequest::From(navigation_handle)
+                           ->GetNavigationThrottleRunnerForTesting());
   }
 }
 
diff --git a/content/public/test/test_navigation_throttle_inserter.h b/content/public/test/test_navigation_throttle_inserter.h
index 4dcb841..65bbee0 100644
--- a/content/public/test/test_navigation_throttle_inserter.h
+++ b/content/public/test/test_navigation_throttle_inserter.h
@@ -13,20 +13,29 @@
 namespace content {
 
 class NavigationThrottle;
+class NavigationThrottleRegistry;
 class WebContents;
 
+// TODO(https://crbug.com/412524375): Remove old callback type.
 using ThrottleInsertionCallback =
     base::RepeatingCallback<std::unique_ptr<NavigationThrottle>(
         NavigationHandle*)>;
 
+using NewThrottleInsertionCallback =
+    base::RepeatingCallback<void(NavigationThrottleRegistry& registry)>;
+
 // This class is instantiated with a NavigationThrottle factory callback, and
 //  - Calls the callback in every DidStartNavigation.
 //  - If the throttle is successfully created, registers it with the given
 //    navigation.
 class TestNavigationThrottleInserter : public WebContentsObserver {
  public:
+  // TODO(https://crbug.com/412524375): Remove old constructor with a legacy
+  // callback type.
   TestNavigationThrottleInserter(WebContents* web_contents,
                                  ThrottleInsertionCallback callback);
+  TestNavigationThrottleInserter(WebContents* web_contents,
+                                 NewThrottleInsertionCallback callback);
 
   TestNavigationThrottleInserter(const TestNavigationThrottleInserter&) =
       delete;
@@ -40,6 +49,7 @@
 
  private:
   ThrottleInsertionCallback callback_;
+  NewThrottleInsertionCallback new_callback_;
 };
 
 }  // namespace content
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 4d40964a..2ec398b 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -5001,8 +5001,6 @@
   params->method = "GET";
   params->post_id = -1;
   params->embedding_token = embedding_token;
-  params->origin_calculation_debug_info =
-      document_loader->OriginCalculationDebugInfo().Utf8();
 
   // Pass the navigation token back to the browser process, or generate a new
   // one if this navigation is committing without the browser process asking for
diff --git a/content/shell/BUILD.gn b/content/shell/BUILD.gn
index 9d41c83a..86e25ae8 100644
--- a/content/shell/BUILD.gn
+++ b/content/shell/BUILD.gn
@@ -38,6 +38,12 @@
   import("//content/shell/app/ios/extensions.gni")
 }
 
+if (translate_genders) {
+  _gender_suffix = "_OTHER"
+} else {
+  _gender_suffix = ""
+}
+
 # TODO(crbug.com/1336055, spang): Investigate using shell_views with cast builds as
 # true.
 shell_use_toolkit_views = toolkit_views && !is_castos
@@ -533,14 +539,14 @@
     "$root_gen_dir/third_party/blink/public/resources/blink_resources.pak",
     "$root_gen_dir/third_party/blink/public/resources/blink_scaled_resources_100_percent.pak",
     "$root_gen_dir/third_party/blink/public/resources/inspector_overlay_resources.pak",
-    "$root_gen_dir/third_party/blink/public/strings/blink_strings_en-US.pak",
+    "$root_gen_dir/third_party/blink/public/strings/blink_strings_en-US${_gender_suffix}.pak",
     "$root_gen_dir/third_party/blink/public/strings/permission_element_generated_strings.pak",
-    "$root_gen_dir/third_party/blink/public/strings/permission_element_strings_en-US.pak",
+    "$root_gen_dir/third_party/blink/public/strings/permission_element_strings_en-US${_gender_suffix}.pak",
     "$root_gen_dir/ui/resources/ui_resources_100_percent.pak",
-    "$root_gen_dir/ui/strings/app_locale_settings_en-US.pak",
-    "$root_gen_dir/ui/strings/auto_image_annotation_strings_en-US.pak",
-    "$root_gen_dir/ui/strings/ax_strings_en-US.pak",
-    "$root_gen_dir/ui/strings/ui_strings_en-US.pak",
+    "$root_gen_dir/ui/strings/app_locale_settings_en-US${_gender_suffix}.pak",
+    "$root_gen_dir/ui/strings/auto_image_annotation_strings_en-US${_gender_suffix}.pak",
+    "$root_gen_dir/ui/strings/ax_strings_en-US${_gender_suffix}.pak",
+    "$root_gen_dir/ui/strings/ui_strings_en-US${_gender_suffix}.pak",
     "$root_gen_dir/ui/webui/resources/webui_resources.pak",
   ]
 
diff --git a/content/test/navigation_simulator_impl.cc b/content/test/navigation_simulator_impl.cc
index 68518db..0ffffbc 100644
--- a/content/test/navigation_simulator_impl.cc
+++ b/content/test/navigation_simulator_impl.cc
@@ -1130,6 +1130,11 @@
   return request_;
 }
 
+NavigationThrottleRegistry&
+NavigationSimulatorImpl::GetNavigationThrottleRegistry() {
+  return *GetNavigationHandle()->GetNavigationThrottleRunnerForTesting();
+}
+
 content::GlobalRequestID NavigationSimulatorImpl::GetGlobalRequestID() {
   CHECK_GT(state_, STARTED) << "The GlobalRequestID is not available until "
                                "after the navigation has completed "
@@ -1593,9 +1598,7 @@
   if (same_document) {
     params->origin = current_rfh->GetLastCommittedOrigin();
   } else {
-    params->origin = origin_.value_or(
-        request_->browser_side_origin_to_commit_with_debug_info()
-            .first.value());
+    params->origin = origin_.value_or(request_->GetOriginToCommit().value());
   }
 
   if (same_document) {
diff --git a/content/test/navigation_simulator_impl.h b/content/test/navigation_simulator_impl.h
index 30616905..2634ff1 100644
--- a/content/test/navigation_simulator_impl.h
+++ b/content/test/navigation_simulator_impl.h
@@ -115,6 +115,7 @@
 
   NavigationThrottle::ThrottleCheckResult GetLastThrottleCheckResult() override;
   NavigationRequest* GetNavigationHandle() override;
+  NavigationThrottleRegistry& GetNavigationThrottleRegistry() override;
   content::GlobalRequestID GetGlobalRequestID() override;
 
   void SetKeepLoading(bool keep_loading) override;
diff --git a/crypto/obsolete/md5.cc b/crypto/obsolete/md5.cc
index 4a01856..2eac8cb 100644
--- a/crypto/obsolete/md5.cc
+++ b/crypto/obsolete/md5.cc
@@ -22,8 +22,35 @@
   CHECK(EVP_DigestInit(ctx_.get(), EVP_md5()));
 }
 
+Md5::Md5(const Md5& other) {
+  *this = other;
+}
+
+Md5::Md5(Md5&& other) {
+  *this = other;
+}
+
+Md5& Md5::operator=(const Md5& other) {
+  CHECK(EVP_MD_CTX_copy_ex(ctx_.get(), other.ctx_.get()));
+  return *this;
+}
+
+Md5& Md5::operator=(Md5&& other) {
+  ctx_ = std::move(other.ctx_);
+  return *this;
+}
+
 Md5::~Md5() = default;
 
+Md5 Md5::MakeMd5HasherForTesting() {
+  return {};
+}
+
+std::array<uint8_t, Md5::kSize> Md5::HashForTesting(
+    base::span<const uint8_t> data) {
+  return Hash(data);
+}
+
 void Md5::Update(base::span<const uint8_t> data) {
   CHECK(EVP_DigestUpdate(ctx_.get(), data.data(), data.size()));
 }
diff --git a/crypto/obsolete/md5.h b/crypto/obsolete/md5.h
index e9bb5cde1..c136797 100644
--- a/crypto/obsolete/md5.h
+++ b/crypto/obsolete/md5.h
@@ -16,6 +16,10 @@
 class Md5;
 }
 
+namespace extensions::image_writer {
+crypto::obsolete::Md5 MakeMd5HasherForImageWriter();
+}
+
 namespace policy {
 crypto::obsolete::Md5 MakeMd5HasherForPolicyEventId();
 }
@@ -35,6 +39,10 @@
  public:
   static constexpr size_t kSize = 16;
 
+  Md5(const Md5& other);
+  Md5(Md5&& other);
+  Md5& operator=(const Md5& other);
+  Md5& operator=(Md5&& other);
   ~Md5();
 
   void Update(base::span<const uint8_t> data);
@@ -42,12 +50,17 @@
   void Finish(base::span<uint8_t, kSize> result);
   std::array<uint8_t, kSize> Finish();
 
+  Md5 MakeMd5HasherForTesting();
+  static std::array<uint8_t, kSize> HashForTesting(
+      base::span<const uint8_t> data);
+
  private:
   FRIEND_TEST_ALL_PREFIXES(Md5Test, KnownAnswer);
 
   // The friends listed here are the areas required to continue using MD5 for
   // compatibility with existing specs, on-disk data, or similar.
   friend Md5 policy::MakeMd5HasherForPolicyEventId();
+  friend Md5 extensions::image_writer::MakeMd5HasherForImageWriter();
 
   // TODO(https://crbug.com/416304903): get rid of this.
   friend Md5 web_app::internals::MakeMd5HasherForWebAppShortcutIcon();
diff --git a/device/bluetooth/strings/BUILD.gn b/device/bluetooth/strings/BUILD.gn
index e73c0b5..d8bdeb82 100644
--- a/device/bluetooth/strings/BUILD.gn
+++ b/device/bluetooth/strings/BUILD.gn
@@ -6,17 +6,19 @@
 import("//tools/grit/grit_rule.gni")
 import("//tools/grit/repack.gni")
 
-grit("strings") {
+grit_strings("strings") {
   source = "../bluetooth_strings.grd"
-  outputs =
-      [ "grit/bluetooth_strings.h" ] +
-      process_file_template(all_chrome_locales,
-                            [ "bluetooth_strings_{{source_name_part}}.pak" ])
+  outputs = [ "grit/bluetooth_strings.h" ]
+  output_prefix = "bluetooth_strings_"
 }
 
 repack("bluetooth_test_strings") {
-  sources =
-      [ "$root_gen_dir/device/bluetooth/strings/bluetooth_strings_en-US.pak" ]
+  if (translate_genders) {
+    sources = [ "$root_gen_dir/device/bluetooth/strings/bluetooth_strings_en-US_OTHER.pak" ]
+  } else {
+    sources =
+        [ "$root_gen_dir/device/bluetooth/strings/bluetooth_strings_en-US.pak" ]
+  }
   output = "$root_out_dir/bluetooth_test_strings.pak"
   deps = [ ":strings" ]
 }
diff --git a/device/fido/strings/BUILD.gn b/device/fido/strings/BUILD.gn
index d033e97..e3437bf 100644
--- a/device/fido/strings/BUILD.gn
+++ b/device/fido/strings/BUILD.gn
@@ -6,16 +6,19 @@
 import("//tools/grit/grit_rule.gni")
 import("//tools/grit/repack.gni")
 
-grit("strings") {
+grit_strings("strings") {
   source = "../fido_strings.grd"
   outputs = [ "grit/fido_strings.h" ]
-  foreach(locale, all_chrome_locales) {
-    outputs += [ "fido_strings_$locale.pak" ]
-  }
+  output_prefix = "fido_strings_"
 }
 
 repack("fido_test_strings") {
-  sources = [ "$root_gen_dir/device/fido/strings/fido_strings_en-US.pak" ]
+  if (translate_genders) {
+    sources =
+        [ "$root_gen_dir/device/fido/strings/fido_strings_en-US_OTHER.pak" ]
+  } else {
+    sources = [ "$root_gen_dir/device/fido/strings/fido_strings_en-US.pak" ]
+  }
   output = "$root_out_dir/fido_test_strings.pak"
   deps = [ ":strings" ]
 }
diff --git a/docs/adding_to_third_party.md b/docs/adding_to_third_party.md
index 0b34789..6b40029 100644
--- a/docs/adding_to_third_party.md
+++ b/docs/adding_to_third_party.md
@@ -4,11 +4,10 @@
 
 Using third party code can save time and is consistent with our values - no need
 to reinvent the wheel! We put all code that isn't written by Chromium developers
-into `//third_party` (even if you end up modifying just a few functions). We do
-this to make it easy to track license compliance, security patches, and supply
-the right credit and attributions. It also makes it a lot easier for other
-projects that embed our code to track what is Chromium licensed and what is
-covered by other licenses.
+into `//third_party`. We do this to make it easy to track license compliance,
+security patches, and supply the right credit and attributions. It also makes it
+a lot easier for other projects that embed our code to track what is Chromium
+licensed and what is covered by other licenses.
 
 ## Put the code in //third_party
 
@@ -37,6 +36,9 @@
 other directory with third_party in the name it's okay to put new things
 there.
 
+Regardless of where you add a third party dependency, you should use the
+[recommended directory structure](#standard-dep-structure).
+
 ## Before you start
 
 To make sure the inclusion of a new third_party project makes sense for the
@@ -171,12 +173,10 @@
 to the deps entry so that developers on other platforms don't pull in things
 they don't need.
 
-As for specifying the path where the library is fetched, a path like
-`//third_party/<project_name>/src` is highly recommended so that you can put
-the file like OWNERS or README.chromium at `//third_party/<project_name>`. If
-you have a wrong path in DEPS and want to change the path of the existing
-library in DEPS, please ask the infrastructure team before committing the
-change.
+Follow the [standard directory structure](#standard-dep-structure) when creating
+a directory for your dependency. If you have a wrong path in
+DEPS and want to change the path of the existing library in DEPS, please ask the
+infrastructure team before committing the change.
 
 Lastly, add the new directory to Chromium's `//third_party/.gitignore`, so that
 it won't show up as untracked files when you run `git status` on the main
@@ -184,14 +184,15 @@
 
 ### Checking in the code directly
 
-If you are checking in a snapshot, please describe the source in the
-README.chromium file, described below.  For security reasons, please retrieve
-the code as securely as you can, using HTTPS and GPG signatures if available.
+If you are checking in a snapshot, you should follow the [standard directory structure](#standard-dep-structure).
+For security reasons, please retrieve the code as securely as you can, using
+HTTPS and GPG signatures if available.
 If retrieving a tarball, please do not check the tarball itself into the tree,
 but do list the source and the SHA-512 hash (for verification) in the
 README.chromium and Change List. The SHA-512 hash can be computed via
 `sha512sum` or `openssl dgst -sha512`.  If retrieving from a git
-repository, please list the revision that the code was pulled from.
+repository, please list the upstream URL and revision that the code was pulled
+from.
 
 If you are checking the files in directly, you do not need an entry in DEPS
 and do not need to modify `//third_party/.gitignore`.
@@ -203,6 +204,52 @@
 
 See [Moving large files to Google Storage](https://goto.google.com/checking-in-large-files)
 
+## Standard directory structure for dependencies {standard-dep-structure}
+
+Regardless of how you import a dependency, you should use the following
+directory structure. This folder layout enforces separation between first and
+third party code, making it easier to manage updates and dependency hygiene
+long term.
+
+Any first party code or files you need for dependency management or
+interoperability should be added to the top level dependency directory, and the
+dependency source imported into the child src directory.
+
+**Recommended directory structure:**
+```
+❯ //third_party/<dependency-name>
+├── BUILD.gn
+├── README.chromium
+├── OWNERS
+├── src <-- import third party code here
+│   ├── LICENSE
+│   ├── a.h
+│   └── b.cc
+```
+
+**What constitutes a dependency:**
+
+* A dependency should be sourced from a single upstream location. Putting code
+  from multiple upstream sources in a single `//third_party` directory makes it
+  difficult to reason about the origin of files and perform automated updates.
+* If your dependency has its own vendored dependencies, it's not necessary to
+  split these into additional directories.
+
+**Formatting:**
+
+Do not reformat or apply Chromium-style formatting to any code within the
+dependency `src` directory. Maintaining the original formatting is essential
+for generating clean diffs against upstream versions. This simplifies
+reviewing upstream changes, applying security patches, and performing updates.
+
+If you experience issues with submitting a CL due to Chromium formatting
+requirements which need to be disabled, or you need to format first party code
+in your top level dependency folder, you can add a language appropriate
+formatting config (e.g [.clang-format-ignore](https://clang.llvm.org/docs/ClangFormat.html#clang-format-ignore))
+to your top level dependency directory. Ensure it does not format the third
+party code.
+
+
 ## Document the code's context
 
 ### Add OWNERS
@@ -279,9 +326,18 @@
 
 
 **Multiple packages**
-Each package should have its own README.chromium. However, if this is not
-possible and the information for multiple packages must be placed in a single
-README.chromium, use the below line to separate the data for each package:
+Adding multiple packages in a single third party directory is not recommended,
+because it does not follow the best practices for [third party dependency structure](#standard-dep-structure)
+and complicates vulnerability scanning.
+
+Each dependency should have its own third party directory with a few very
+limited exceptions:
+* A package manager is used to manage dependencies in the directory via a lockfile.
+* Your third party dependency has its own vendored transitive dependencies
+
+If your dependency is covered by one of the above exceptions and the information
+for multiple packages must be placed in a single README.chromium, use the below
+line to separate the data for each package:
 ```
 -------------------- DEPENDENCY DIVIDER --------------------
 ```
diff --git a/docs/fuchsia/telemetry.md b/docs/fuchsia/telemetry.md
index 74b7f42..7ab4d3a 100644
--- a/docs/fuchsia/telemetry.md
+++ b/docs/fuchsia/telemetry.md
@@ -5,7 +5,7 @@
 General instruction on running and debugging benchmarks can be found in the
 [`tools/perf/README.md`](../../tools/perf/README.md).
 
-Fuchsia uses [web_engine_shell](../../fuchsia_web/webengine/test/README.md) to run
+Fuchsia uses [web_engine_shell](../../fuchsia_web/shell/README.md) to run
 integration tests. Be sure to build any components you wish to deploy on your
 device, along with `web_engine_shell`.
 
diff --git a/extensions/strings/BUILD.gn b/extensions/strings/BUILD.gn
index e456ee9..6bd619d 100644
--- a/extensions/strings/BUILD.gn
+++ b/extensions/strings/BUILD.gn
@@ -8,10 +8,8 @@
 
 assert(enable_extensions_core)
 
-grit("strings") {
+grit_strings("strings") {
   source = "extensions_strings.grd"
-  outputs =
-      [ "grit/extensions_strings.h" ] +
-      process_file_template(all_chrome_locales,
-                            [ "extensions_strings_{{source_name_part}}.pak" ])
+  outputs = [ "grit/extensions_strings.h" ]
+  output_prefix = "extensions_strings_"
 }
diff --git a/fuchsia_web/BUILD.gn b/fuchsia_web/BUILD.gn
index cb8bca6..d0acd7d 100644
--- a/fuchsia_web/BUILD.gn
+++ b/fuchsia_web/BUILD.gn
@@ -15,5 +15,6 @@
     "runners:cast_runner",
     "shell",
     "webengine:web_engine",
+    "//build/fuchsia/test:component_storage_test",
   ]
 }
diff --git a/fuchsia_web/webengine/browser/web_engine_content_browser_client.cc b/fuchsia_web/webengine/browser/web_engine_content_browser_client.cc
index 6f1812c..13cbc42 100644
--- a/fuchsia_web/webengine/browser/web_engine_content_browser_client.cc
+++ b/fuchsia_web/webengine/browser/web_engine_content_browser_client.cc
@@ -326,7 +326,7 @@
 
   if (explicit_sites_filter_error_page) {
     registry.AddThrottle(std::make_unique<SafeSitesNavigationThrottle>(
-        &navigation_handle,
+        registry,
         navigation_handle.GetWebContents()->GetBrowserContext(),
         *explicit_sites_filter_error_page));
   }
diff --git a/gpu/command_buffer/service/shared_image/iosurface_image_backing.h b/gpu/command_buffer/service/shared_image/iosurface_image_backing.h
index 3f939ce..0ad7beca 100644
--- a/gpu/command_buffer/service/shared_image/iosurface_image_backing.h
+++ b/gpu/command_buffer/service/shared_image/iosurface_image_backing.h
@@ -19,6 +19,8 @@
 #include "ui/gl/gl_fence.h"
 #include "ui/gl/gl_surface.h"
 
+@protocol MTLDevice;
+
 namespace gl {
 class ScopedEGLSurfaceIOSurface;
 }  // namespace gl
@@ -141,15 +143,21 @@
 
   void AddWGPUDeviceWithPendingCommands(wgpu::Device device)
       EXCLUSIVE_LOCKS_REQUIRED(lock_);
-  void WaitForDawnCommandsToBeScheduled(const wgpu::Device& device_to_exclude)
-      EXCLUSIVE_LOCKS_REQUIRED(lock_);
 
   void AddEGLDisplayWithPendingCommands(gl::GLDisplayEGL* display)
       EXCLUSIVE_LOCKS_REQUIRED(lock_);
-  void WaitForANGLECommandsToBeScheduled() EXCLUSIVE_LOCKS_REQUIRED(lock_);
   void ClearEGLDisplaysWithPendingCommands(gl::GLDisplayEGL* display_to_keep)
       EXCLUSIVE_LOCKS_REQUIRED(lock_);
 
+  // Wait for commands to be scheduled on every WGPUDevice or EGLDisplay that's
+  // pending a flush except those using the same MTLDevice as `waiting_device`.
+  // This is needed in two cases: 1) handing off the IOSurface to CoreAnimation
+  // since there's no other synchronization mechanism, and 2) accessing the
+  // IOSurface on different GPUs/MTLDevices since there could be shadow copies
+  // performed by the kernel.
+  void WaitForCommandsToBeScheduled(id<MTLDevice> waiting_device = nil)
+      EXCLUSIVE_LOCKS_REQUIRED(lock_);
+
  private:
   class GLTextureIRepresentation;
   class DawnRepresentation;
diff --git a/gpu/command_buffer/service/shared_image/iosurface_image_backing.mm b/gpu/command_buffer/service/shared_image/iosurface_image_backing.mm
index 577e739..62110a3 100644
--- a/gpu/command_buffer/service/shared_image/iosurface_image_backing.mm
+++ b/gpu/command_buffer/service/shared_image/iosurface_image_backing.mm
@@ -10,8 +10,10 @@
 #include "gpu/command_buffer/service/shared_image/iosurface_image_backing.h"
 
 #include <EGL/egl.h>
+#include <EGL/eglext.h>
 #import <Metal/Metal.h>
 #include <dawn/native/MetalBackend.h>
+#include <dawn/webgpu_cpp.h>
 
 #include "base/apple/scoped_cftyperef.h"
 #include "base/apple/scoped_nsobject.h"
@@ -32,6 +34,7 @@
 #include "gpu/command_buffer/service/shared_image/skia_graphite_dawn_image_representation.h"
 #include "gpu/command_buffer/service/skia_utils.h"
 #include "gpu/config/gpu_finch_features.h"
+#include "third_party/angle/include/EGL/eglext_angle.h"
 #include "third_party/skia/include/core/SkColorSpace.h"
 #include "third_party/skia/include/gpu/ganesh/GrContextThreadSafeProxy.h"
 #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
@@ -186,6 +189,24 @@
 }
 #endif
 
+id<MTLDevice> QueryMetalDeviceFromANGLE(gl::GLDisplayEGL* display) {
+  id<MTLDevice> metal_device = nil;
+  if (gl::GetANGLEImplementation() == gl::ANGLEImplementation::kMetal) {
+    EGLAttrib angle_device_attrib = 0;
+    if (eglQueryDisplayAttribEXT(display->GetDisplay(), EGL_DEVICE_EXT,
+                                 &angle_device_attrib)) {
+      EGLDeviceEXT angle_device =
+          reinterpret_cast<EGLDeviceEXT>(angle_device_attrib);
+      EGLAttrib metal_device_attrib = 0;
+      if (eglQueryDeviceAttribEXT(angle_device, EGL_METAL_DEVICE_ANGLE,
+                                  &metal_device_attrib)) {
+        metal_device = (__bridge id)(void*)metal_device_attrib;
+      }
+    }
+  }
+  return metal_device;
+}
+
 class BackpressureMetalSharedEventImpl final
     : public BackpressureMetalSharedEvent {
  public:
@@ -697,13 +718,8 @@
     return false;
   }
 
-  // This will transition the image to be accessed by CoreAnimation. So
-  // WaitForANGLECommandsToBeScheduled() call is required.
-  iosurface_backing->WaitForANGLECommandsToBeScheduled();
-
-  // Likewise do the same for Dawn's commands.
-  iosurface_backing->WaitForDawnCommandsToBeScheduled(
-      /*device_to_exclude=*/nullptr);
+  // This will transition the image to be accessed by CoreAnimation.
+  iosurface_backing->WaitForCommandsToBeScheduled();
 
   gl::GLContext* context = gl::GLContext::GetCurrent();
   if (context) {
@@ -808,17 +824,6 @@
     return {};
   }
 
-  // IOSurface might be written on a different GPU. We need to wait for
-  // previous Dawn and ANGLE commands to be scheduled first.
-  // Note: we don't need to wait for the commands from the same wgpu::Device to
-  // be scheduled, but we do need it on different devices since they could wrap
-  // the same IOSurface in different MTLTextures and the kernel needs to be told
-  // about the pending update to the IOSurface before we use it on another Metal
-  // command queue and the way to do that is waitUntilScheduled.
-  iosurface_backing->WaitForANGLECommandsToBeScheduled();
-  iosurface_backing->WaitForDawnCommandsToBeScheduled(
-      /*device_to_exclude=*/device_);
-
   usage_ = wgpu_texture_usage;
   internal_usage_ = internal_usage;
 
@@ -846,6 +851,11 @@
     return texture_;
   }
 
+  // IOSurface might be written on a different GPU. We need to wait for previous
+  // Dawn and ANGLE commands to be scheduled first.
+  iosurface_backing->WaitForCommandsToBeScheduled(
+      dawn::native::metal::GetMTLDevice(device_.Get()));
+
   bool is_cleared = iosurface_backing->IsClearedInternal();
   wgpu::SharedTextureMemoryBeginAccessDescriptor begin_access_desc = {};
   begin_access_desc.initialized = is_cleared;
@@ -857,26 +867,20 @@
   std::vector<wgpu::SharedFence> shared_fences;
   std::vector<uint64_t> signaled_values;
 
-  // Synchronize with all of the MTLSharedEvents that have been
-  // stored in the backing as a consequence of earlier BeginAccess/
-  // EndAccess calls against other representations.
-  if (gl::GetANGLEImplementation() == gl::ANGLEImplementation::kMetal) {
-    // Not possible to reach this with any other type of backing.
-    DCHECK_EQ(backing()->GetType(), SharedImageBackingType::kIOSurface);
+  // Synchronize with all of the MTLSharedEvents that have been stored in the
+  // backing as a consequence of earlier BeginAccess/EndAccess calls against
+  // other representations.
+  iosurface_backing->ProcessSharedEventsForBeginAccess(
+      readonly, [&](id<MTLSharedEvent> shared_event, uint64_t signaled_value) {
+        wgpu::SharedFenceMTLSharedEventDescriptor shared_event_desc;
+        shared_event_desc.sharedEvent = shared_event;
 
-    iosurface_backing->ProcessSharedEventsForBeginAccess(
-        readonly,
-        [&](id<MTLSharedEvent> shared_event, uint64_t signaled_value) {
-          wgpu::SharedFenceMTLSharedEventDescriptor shared_event_desc;
-          shared_event_desc.sharedEvent = shared_event;
+        wgpu::SharedFenceDescriptor fence_desc;
+        fence_desc.nextInChain = &shared_event_desc;
 
-          wgpu::SharedFenceDescriptor fence_desc;
-          fence_desc.nextInChain = &shared_event_desc;
-
-          shared_fences.push_back(device_.ImportSharedFence(&fence_desc));
-          signaled_values.push_back(signaled_value);
-        });
-  }
+        shared_fences.push_back(device_.ImportSharedFence(&fence_desc));
+        signaled_values.push_back(signaled_value);
+      });
 
   // Populate `begin_access_desc` with the fence data.
   CHECK(shared_fences.size() == signaled_values.size());
@@ -1116,8 +1120,7 @@
   CHECK_LE(pixmaps.size(), 3u);
 
   // Make sure any pending ANGLE EGLDisplays and Dawn devices are flushed.
-  WaitForANGLECommandsToBeScheduled();
-  WaitForDawnCommandsToBeScheduled(/*device_to_exclude=*/nullptr);
+  WaitForCommandsToBeScheduled();
 
   ScopedIOSurfaceLock io_surface_lock(io_surface_.get(), /*options=*/0);
 
@@ -1168,8 +1171,7 @@
   CHECK_LE(pixmaps.size(), 3u);
 
   // Make sure any pending ANGLE EGLDisplays and Dawn devices are flushed.
-  WaitForANGLECommandsToBeScheduled();
-  WaitForDawnCommandsToBeScheduled(/*device_to_exclude=*/nullptr);
+  WaitForCommandsToBeScheduled();
 
   ScopedIOSurfaceLock io_surface_lock(io_surface_.get(), /*options=*/0);
 
@@ -1386,23 +1388,38 @@
   wgpu_devices_pending_flush_.insert(std::move(device));
 }
 
-void IOSurfaceImageBacking::WaitForDawnCommandsToBeScheduled(
-    const wgpu::Device& device_to_exclude) {
+void IOSurfaceImageBacking::WaitForCommandsToBeScheduled(
+    id<MTLDevice> waiting_device) {
   AssertLockAcquired();
-  TRACE_EVENT0("gpu",
-               "IOSurfaceImageBacking::WaitForDawnCommandsToBeScheduled");
-  bool excluded_device_was_pending_flush = false;
+  TRACE_EVENT0("gpu", "IOSurfaceImageBacking::WaitForCommandsToBeScheduled");
+
+  std::vector<wgpu::Device> wgpu_devices_to_keep;
   for (const auto& device : std::move(wgpu_devices_pending_flush_)) {
-    if (device.Get() == device_to_exclude.Get()) {
-      excluded_device_was_pending_flush = true;
+    // Only Metal backed devices are added to `wgpu_devices_pending_flush_`.
+    id<MTLDevice> mtl_device = dawn::native::metal::GetMTLDevice(device.Get());
+    if (mtl_device && mtl_device == waiting_device) {
+      wgpu_devices_to_keep.push_back(device);
       continue;
     }
+    TRACE_EVENT0("gpu",
+                 "IOSurfaceImageBacking::WaitForCommandsToBeScheduled::Dawn");
     dawn::native::metal::WaitForCommandsToBeScheduled(device.Get());
   }
-  if (excluded_device_was_pending_flush) {
-    // This device wasn't flushed, so we need to add it to the list again.
-    wgpu_devices_pending_flush_.insert(device_to_exclude);
+  wgpu_devices_pending_flush_ = std::move(wgpu_devices_to_keep);
+
+  std::vector<gl::GLDisplayEGL*> egl_displays_to_keep;
+  for (auto* display : std::move(egl_displays_pending_flush_)) {
+    // Always flush work for any ANGLE-OpenGL EGLDisplays.
+    if (gl::GetANGLEImplementation() == gl::ANGLEImplementation::kMetal &&
+        QueryMetalDeviceFromANGLE(display) == waiting_device) {
+      egl_displays_to_keep.push_back(display);
+      continue;
+    }
+    TRACE_EVENT0("gpu",
+                 "IOSurfaceImageBacking::WaitForCommandsToBeScheduled::ANGLE");
+    eglWaitUntilWorkScheduledANGLE(display->GetDisplay());
   }
+  egl_displays_pending_flush_ = std::move(egl_displays_to_keep);
 }
 
 void IOSurfaceImageBacking::AddEGLDisplayWithPendingCommands(
@@ -1411,16 +1428,6 @@
   egl_displays_pending_flush_.insert(display);
 }
 
-void IOSurfaceImageBacking::WaitForANGLECommandsToBeScheduled() {
-  AssertLockAcquired();
-  TRACE_EVENT0("gpu",
-               "IOSurfaceImageBacking::WaitForANGLECommandsToBeScheduled");
-
-  for (auto* display : std::move(egl_displays_pending_flush_)) {
-    eglWaitUntilWorkScheduledANGLE(display->GetDisplay());
-  }
-}
-
 void IOSurfaceImageBacking::ClearEGLDisplaysWithPendingCommands(
     gl::GLDisplayEGL* display_to_keep) {
   AssertLockAcquired();
@@ -1742,16 +1749,16 @@
   CHECK(display);
   CHECK_EQ(display->GetDisplay(), egl_state->egl_display_);
 
-  // IOSurface might be written on a different queue. So we have to wait for the
-  // previous Dawn and ANGLE commands to be scheduled first so that the kernel
-  // knows about the pending update to the IOSurface.
-  WaitForDawnCommandsToBeScheduled(/*device_to_exclude=*/nullptr);
-
-  // Note that we don't need to call WaitForANGLECommandsToBeScheduled for other
+  // Note that we don't need to call WaitForCommandsToBeScheduled for other
   // EGLDisplays because it is already done when the previous GL context is made
   // uncurrent. We can simply remove the other EGLDisplays from the list.
   ClearEGLDisplaysWithPendingCommands(/*display_to_keep=*/display);
 
+  // IOSurface might be written on a different queue. So we have to wait for the
+  // previous Dawn and ANGLE commands to be scheduled first so that the kernel
+  // knows about the pending update to the IOSurface.
+  WaitForCommandsToBeScheduled(QueryMetalDeviceFromANGLE(display));
+
   if (gl::GetANGLEImplementation() == gl::ANGLEImplementation::kMetal) {
     // If this image could potentially be shared with another Metal device,
     // it's necessary to synchronize between the two devices. If any Metal
@@ -1853,7 +1860,9 @@
   CHECK(display);
   CHECK_EQ(display->GetDisplay(), egl_state->egl_display_);
 
-  if (gl::GetANGLEImplementation() == gl::ANGLEImplementation::kMetal) {
+  const bool is_angle_metal =
+      gl::GetANGLEImplementation() == gl::ANGLEImplementation::kMetal;
+  if (is_angle_metal) {
     id<MTLSharedEvent> shared_event = nil;
     uint64_t signal_value = 0;
     if (display->CreateMetalSharedEvent(&shared_event, &signal_value)) {
@@ -1885,10 +1894,18 @@
   // serialized with respect to reads (so that the end of a write always
   // triggers a release and copy). By design, IOSurfaceImageBackingFactory
   // enforces this property for this use case.
+  //
+  // For ANGLE Metal, we need to rebind the texture for two reasons:
+  // 1) ReleaseTexImage flushes the command buffer which contains the shared
+  //    event signal above, otherwise the shared event might never be signaled
+  //    since we skip calling eglWaitUntilWorkScheduledANGLE in single GPU case.
+  // 2) BindTexImage adds a synchronization dependency on the command buffer
+  //    which contains the shared event wait before the next ANGLE access.
+  //    Otherwise, ANGLE might skip waiting on the command buffer and hence the
+  //    shared event and do a CPU readback from the IOSurface in some cases.
   const bool is_swangle =
       gl::GetANGLEImplementation() == gl::ANGLEImplementation::kSwiftShader;
-
-  if (is_swangle && egl_state->num_ongoing_accesses_ == 0) {
+  if ((is_swangle || is_angle_metal) && egl_state->num_ongoing_accesses_ == 0) {
     CHECK_EQ(static_cast<int>(egl_state->gl_textures_.size()),
              format().NumberOfPlanes());
     CHECK_EQ(static_cast<int>(egl_state->egl_surfaces_.size()),
diff --git a/headless/lib/browser/headless_content_browser_client.cc b/headless/lib/browser/headless_content_browser_client.cc
index 0865a115..bf1cebd 100644
--- a/headless/lib/browser/headless_content_browser_client.cc
+++ b/headless/lib/browser/headless_content_browser_client.cc
@@ -460,7 +460,7 @@
   content::NavigationHandle& handle = registry.GetNavigationHandle();
   if (browser_->GetPrefs()) {
     registry.AddThrottle(std::make_unique<PolicyBlocklistNavigationThrottle>(
-        &handle, handle.GetWebContents()->GetBrowserContext()));
+        registry, handle.GetWebContents()->GetBrowserContext()));
   }
 }
 #endif  // defined(HEADLESS_USE_POLICY)
diff --git "a/infra/config/generated/builders/ci/Mac Builder \050dbg\051/targets/chromium.mac.json" "b/infra/config/generated/builders/ci/Mac Builder \050dbg\051/targets/chromium.mac.json"
index cdccbe07..556b0765 100644
--- "a/infra/config/generated/builders/ci/Mac Builder \050dbg\051/targets/chromium.mac.json"
+++ "b/infra/config/generated/builders/ci/Mac Builder \050dbg\051/targets/chromium.mac.json"
@@ -182,25 +182,6 @@
       },
       {
         "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mac.mac-rel.browser_tests.filter"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "browser_tests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "os": "Mac-15"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 20
-        },
-        "test": "browser_tests",
-        "test_id_prefix": "ninja://chrome/test:browser_tests/"
-      },
-      {
-        "args": [
           "--gtest_filter=-*UsingRealWebcam*"
         ],
         "merge": {
diff --git a/infra/config/generated/builders/ci/ToTFuchsia x64/targets/chromium.clang.json b/infra/config/generated/builders/ci/ToTFuchsia x64/targets/chromium.clang.json
index 0fbeee8a..938bccd 100644
--- a/infra/config/generated/builders/ci/ToTFuchsia x64/targets/chromium.clang.json
+++ b/infra/config/generated/builders/ci/ToTFuchsia x64/targets/chromium.clang.json
@@ -106,6 +106,21 @@
         },
         "test": "blink_wpt_tests",
         "test_id_prefix": "ninja://:blink_wpt_tests/"
+      },
+      {
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "component_storage_test",
+        "swarming": {
+          "dimensions": {
+            "kvm": "1",
+            "os": "Ubuntu-22.04"
+          },
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "component_storage_test",
+        "test_id_prefix": "ninja://build/fuchsia/test:component_storage_test/"
       }
     ]
   }
diff --git a/infra/config/generated/builders/ci/fuchsia-fyi-arm64-dbg/targets/chromium.fuchsia.fyi.json b/infra/config/generated/builders/ci/fuchsia-fyi-arm64-dbg/targets/chromium.fuchsia.fyi.json
index 0d2e098e4..ab226f82 100644
--- a/infra/config/generated/builders/ci/fuchsia-fyi-arm64-dbg/targets/chromium.fuchsia.fyi.json
+++ b/infra/config/generated/builders/ci/fuchsia-fyi-arm64-dbg/targets/chromium.fuchsia.fyi.json
@@ -1418,6 +1418,26 @@
         },
         "test": "angle_unittests",
         "test_id_prefix": "ninja://third_party/angle/src/tests:angle_unittests/"
+      },
+      {
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "component_storage_test",
+        "resultdb": {
+          "enable": true,
+          "inv_extended_properties_dir": "${ISOLATED_OUTDIR}/invocations"
+        },
+        "swarming": {
+          "dimensions": {
+            "cpu": "arm64",
+            "inside_docker": "1",
+            "os": "Ubuntu-22.04|Ubuntu-20.04"
+          },
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "component_storage_test",
+        "test_id_prefix": "ninja://build/fuchsia/test:component_storage_test/"
       }
     ]
   }
diff --git a/infra/config/generated/builders/ci/fuchsia-fyi-x64-asan/targets/chromium.fuchsia.fyi.json b/infra/config/generated/builders/ci/fuchsia-fyi-x64-asan/targets/chromium.fuchsia.fyi.json
index 77d7168..4787c7c 100644
--- a/infra/config/generated/builders/ci/fuchsia-fyi-x64-asan/targets/chromium.fuchsia.fyi.json
+++ b/infra/config/generated/builders/ci/fuchsia-fyi-x64-asan/targets/chromium.fuchsia.fyi.json
@@ -1383,6 +1383,25 @@
         "test_id_prefix": "ninja://:blink_wpt_tests/"
       },
       {
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "component_storage_test",
+        "resultdb": {
+          "enable": true,
+          "inv_extended_properties_dir": "${ISOLATED_OUTDIR}/invocations"
+        },
+        "swarming": {
+          "dimensions": {
+            "kvm": "1",
+            "os": "Ubuntu-22.04"
+          },
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "component_storage_test",
+        "test_id_prefix": "ninja://build/fuchsia/test:component_storage_test/"
+      },
+      {
         "args": [
           "context_lost",
           "--show-stdout",
diff --git a/infra/config/generated/builders/ci/fuchsia-netstack2-x64-cast-receiver-rel/targets/chromium.fuchsia.fyi.json b/infra/config/generated/builders/ci/fuchsia-netstack2-x64-cast-receiver-rel/targets/chromium.fuchsia.fyi.json
index 6a20bac..a940e69 100644
--- a/infra/config/generated/builders/ci/fuchsia-netstack2-x64-cast-receiver-rel/targets/chromium.fuchsia.fyi.json
+++ b/infra/config/generated/builders/ci/fuchsia-netstack2-x64-cast-receiver-rel/targets/chromium.fuchsia.fyi.json
@@ -1721,6 +1721,30 @@
       },
       {
         "args": [
+          "--device-spec=x64-emu-large",
+          "--product=terminal_with_netstack2.x64"
+        ],
+        "isolate_profile_data": true,
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "component_storage_test",
+        "resultdb": {
+          "enable": true,
+          "inv_extended_properties_dir": "${ISOLATED_OUTDIR}/invocations"
+        },
+        "swarming": {
+          "dimensions": {
+            "kvm": "1",
+            "os": "Ubuntu-22.04"
+          },
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "component_storage_test",
+        "test_id_prefix": "ninja://build/fuchsia/test:component_storage_test/"
+      },
+      {
+        "args": [
           "context_lost",
           "--show-stdout",
           "--browser=web-engine-shell",
diff --git a/infra/config/generated/builders/ci/fuchsia-x64-cast-receiver-dbg/targets/chromium.fuchsia.json b/infra/config/generated/builders/ci/fuchsia-x64-cast-receiver-dbg/targets/chromium.fuchsia.json
index 2ea880b..f001bb4c 100644
--- a/infra/config/generated/builders/ci/fuchsia-x64-cast-receiver-dbg/targets/chromium.fuchsia.json
+++ b/infra/config/generated/builders/ci/fuchsia-x64-cast-receiver-dbg/targets/chromium.fuchsia.json
@@ -1465,6 +1465,26 @@
         "test_id_prefix": "ninja://:blink_wpt_tests/"
       },
       {
+        "isolate_profile_data": true,
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "component_storage_test",
+        "resultdb": {
+          "enable": true,
+          "inv_extended_properties_dir": "${ISOLATED_OUTDIR}/invocations"
+        },
+        "swarming": {
+          "dimensions": {
+            "kvm": "1",
+            "os": "Ubuntu-22.04"
+          },
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "component_storage_test",
+        "test_id_prefix": "ninja://build/fuchsia/test:component_storage_test/"
+      },
+      {
         "args": [
           "context_lost",
           "--show-stdout",
diff --git a/infra/config/generated/builders/ci/fuchsia-x64-cast-receiver-rel/targets/chromium.fuchsia.json b/infra/config/generated/builders/ci/fuchsia-x64-cast-receiver-rel/targets/chromium.fuchsia.json
index 65c8acf..b1722ed 100644
--- a/infra/config/generated/builders/ci/fuchsia-x64-cast-receiver-rel/targets/chromium.fuchsia.json
+++ b/infra/config/generated/builders/ci/fuchsia-x64-cast-receiver-rel/targets/chromium.fuchsia.json
@@ -1655,6 +1655,29 @@
       },
       {
         "args": [
+          "--device-spec=x64-emu-large"
+        ],
+        "isolate_profile_data": true,
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "component_storage_test",
+        "resultdb": {
+          "enable": true,
+          "inv_extended_properties_dir": "${ISOLATED_OUTDIR}/invocations"
+        },
+        "swarming": {
+          "dimensions": {
+            "kvm": "1",
+            "os": "Ubuntu-22.04"
+          },
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "component_storage_test",
+        "test_id_prefix": "ninja://build/fuchsia/test:component_storage_test/"
+      },
+      {
+        "args": [
           "context_lost",
           "--show-stdout",
           "--browser=web-engine-shell",
diff --git a/infra/config/generated/builders/ci/fuchsia-x64-perf-cast-receiver-rel/targets/chromium.fuchsia.fyi.json b/infra/config/generated/builders/ci/fuchsia-x64-perf-cast-receiver-rel/targets/chromium.fuchsia.fyi.json
index 8597731..a4810f5 100644
--- a/infra/config/generated/builders/ci/fuchsia-x64-perf-cast-receiver-rel/targets/chromium.fuchsia.fyi.json
+++ b/infra/config/generated/builders/ci/fuchsia-x64-perf-cast-receiver-rel/targets/chromium.fuchsia.fyi.json
@@ -1652,6 +1652,29 @@
       },
       {
         "args": [
+          "--device-spec=x64-emu-large"
+        ],
+        "isolate_profile_data": true,
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "component_storage_test",
+        "resultdb": {
+          "enable": true,
+          "inv_extended_properties_dir": "${ISOLATED_OUTDIR}/invocations"
+        },
+        "swarming": {
+          "dimensions": {
+            "kvm": "1",
+            "os": "Ubuntu-22.04"
+          },
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "component_storage_test",
+        "test_id_prefix": "ninja://build/fuchsia/test:component_storage_test/"
+      },
+      {
+        "args": [
           "context_lost",
           "--show-stdout",
           "--browser=web-engine-shell",
diff --git a/infra/config/generated/builders/ci/mac15-tests-dbg/targets/chromium.mac.json b/infra/config/generated/builders/ci/mac15-tests-dbg/targets/chromium.mac.json
index f5f6c1c..94cab17 100644
--- a/infra/config/generated/builders/ci/mac15-tests-dbg/targets/chromium.mac.json
+++ b/infra/config/generated/builders/ci/mac15-tests-dbg/targets/chromium.mac.json
@@ -177,25 +177,6 @@
       },
       {
         "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mac.mac-rel.browser_tests.filter"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "browser_tests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "os": "Mac-15"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 20
-        },
-        "test": "browser_tests",
-        "test_id_prefix": "ninja://chrome/test:browser_tests/"
-      },
-      {
-        "args": [
           "--gtest_filter=-*UsingRealWebcam*"
         ],
         "merge": {
diff --git a/infra/config/generated/builders/try/fuchsia-fyi-arm64-dbg/targets/chromium.fuchsia.fyi.json b/infra/config/generated/builders/try/fuchsia-fyi-arm64-dbg/targets/chromium.fuchsia.fyi.json
index 0d2e098e4..ab226f82 100644
--- a/infra/config/generated/builders/try/fuchsia-fyi-arm64-dbg/targets/chromium.fuchsia.fyi.json
+++ b/infra/config/generated/builders/try/fuchsia-fyi-arm64-dbg/targets/chromium.fuchsia.fyi.json
@@ -1418,6 +1418,26 @@
         },
         "test": "angle_unittests",
         "test_id_prefix": "ninja://third_party/angle/src/tests:angle_unittests/"
+      },
+      {
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "component_storage_test",
+        "resultdb": {
+          "enable": true,
+          "inv_extended_properties_dir": "${ISOLATED_OUTDIR}/invocations"
+        },
+        "swarming": {
+          "dimensions": {
+            "cpu": "arm64",
+            "inside_docker": "1",
+            "os": "Ubuntu-22.04|Ubuntu-20.04"
+          },
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "component_storage_test",
+        "test_id_prefix": "ninja://build/fuchsia/test:component_storage_test/"
       }
     ]
   }
diff --git a/infra/config/generated/builders/try/fuchsia-fyi-x64-asan/targets/chromium.fuchsia.fyi.json b/infra/config/generated/builders/try/fuchsia-fyi-x64-asan/targets/chromium.fuchsia.fyi.json
index 77d7168..4787c7c 100644
--- a/infra/config/generated/builders/try/fuchsia-fyi-x64-asan/targets/chromium.fuchsia.fyi.json
+++ b/infra/config/generated/builders/try/fuchsia-fyi-x64-asan/targets/chromium.fuchsia.fyi.json
@@ -1383,6 +1383,25 @@
         "test_id_prefix": "ninja://:blink_wpt_tests/"
       },
       {
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "component_storage_test",
+        "resultdb": {
+          "enable": true,
+          "inv_extended_properties_dir": "${ISOLATED_OUTDIR}/invocations"
+        },
+        "swarming": {
+          "dimensions": {
+            "kvm": "1",
+            "os": "Ubuntu-22.04"
+          },
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "component_storage_test",
+        "test_id_prefix": "ninja://build/fuchsia/test:component_storage_test/"
+      },
+      {
         "args": [
           "context_lost",
           "--show-stdout",
diff --git a/infra/config/generated/builders/try/fuchsia-netstack2-x64-cast-receiver-rel/targets/chromium.fuchsia.fyi.json b/infra/config/generated/builders/try/fuchsia-netstack2-x64-cast-receiver-rel/targets/chromium.fuchsia.fyi.json
index 6a20bac..a940e69 100644
--- a/infra/config/generated/builders/try/fuchsia-netstack2-x64-cast-receiver-rel/targets/chromium.fuchsia.fyi.json
+++ b/infra/config/generated/builders/try/fuchsia-netstack2-x64-cast-receiver-rel/targets/chromium.fuchsia.fyi.json
@@ -1721,6 +1721,30 @@
       },
       {
         "args": [
+          "--device-spec=x64-emu-large",
+          "--product=terminal_with_netstack2.x64"
+        ],
+        "isolate_profile_data": true,
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "component_storage_test",
+        "resultdb": {
+          "enable": true,
+          "inv_extended_properties_dir": "${ISOLATED_OUTDIR}/invocations"
+        },
+        "swarming": {
+          "dimensions": {
+            "kvm": "1",
+            "os": "Ubuntu-22.04"
+          },
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "component_storage_test",
+        "test_id_prefix": "ninja://build/fuchsia/test:component_storage_test/"
+      },
+      {
+        "args": [
           "context_lost",
           "--show-stdout",
           "--browser=web-engine-shell",
diff --git a/infra/config/generated/builders/try/fuchsia-x64-cast-receiver-dbg-compile/targets/chromium.fuchsia.json b/infra/config/generated/builders/try/fuchsia-x64-cast-receiver-dbg-compile/targets/chromium.fuchsia.json
index 2ea880b..f001bb4c 100644
--- a/infra/config/generated/builders/try/fuchsia-x64-cast-receiver-dbg-compile/targets/chromium.fuchsia.json
+++ b/infra/config/generated/builders/try/fuchsia-x64-cast-receiver-dbg-compile/targets/chromium.fuchsia.json
@@ -1465,6 +1465,26 @@
         "test_id_prefix": "ninja://:blink_wpt_tests/"
       },
       {
+        "isolate_profile_data": true,
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "component_storage_test",
+        "resultdb": {
+          "enable": true,
+          "inv_extended_properties_dir": "${ISOLATED_OUTDIR}/invocations"
+        },
+        "swarming": {
+          "dimensions": {
+            "kvm": "1",
+            "os": "Ubuntu-22.04"
+          },
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "component_storage_test",
+        "test_id_prefix": "ninja://build/fuchsia/test:component_storage_test/"
+      },
+      {
         "args": [
           "context_lost",
           "--show-stdout",
diff --git a/infra/config/generated/builders/try/fuchsia-x64-cast-receiver-dbg/targets/chromium.fuchsia.json b/infra/config/generated/builders/try/fuchsia-x64-cast-receiver-dbg/targets/chromium.fuchsia.json
index 2ea880b..f001bb4c 100644
--- a/infra/config/generated/builders/try/fuchsia-x64-cast-receiver-dbg/targets/chromium.fuchsia.json
+++ b/infra/config/generated/builders/try/fuchsia-x64-cast-receiver-dbg/targets/chromium.fuchsia.json
@@ -1465,6 +1465,26 @@
         "test_id_prefix": "ninja://:blink_wpt_tests/"
       },
       {
+        "isolate_profile_data": true,
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "component_storage_test",
+        "resultdb": {
+          "enable": true,
+          "inv_extended_properties_dir": "${ISOLATED_OUTDIR}/invocations"
+        },
+        "swarming": {
+          "dimensions": {
+            "kvm": "1",
+            "os": "Ubuntu-22.04"
+          },
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "component_storage_test",
+        "test_id_prefix": "ninja://build/fuchsia/test:component_storage_test/"
+      },
+      {
         "args": [
           "context_lost",
           "--show-stdout",
diff --git a/infra/config/generated/builders/try/fuchsia-x64-cast-receiver-rel/targets/chromium.fuchsia.json b/infra/config/generated/builders/try/fuchsia-x64-cast-receiver-rel/targets/chromium.fuchsia.json
index 65c8acf..b1722ed 100644
--- a/infra/config/generated/builders/try/fuchsia-x64-cast-receiver-rel/targets/chromium.fuchsia.json
+++ b/infra/config/generated/builders/try/fuchsia-x64-cast-receiver-rel/targets/chromium.fuchsia.json
@@ -1655,6 +1655,29 @@
       },
       {
         "args": [
+          "--device-spec=x64-emu-large"
+        ],
+        "isolate_profile_data": true,
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "component_storage_test",
+        "resultdb": {
+          "enable": true,
+          "inv_extended_properties_dir": "${ISOLATED_OUTDIR}/invocations"
+        },
+        "swarming": {
+          "dimensions": {
+            "kvm": "1",
+            "os": "Ubuntu-22.04"
+          },
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "component_storage_test",
+        "test_id_prefix": "ninja://build/fuchsia/test:component_storage_test/"
+      },
+      {
+        "args": [
           "context_lost",
           "--show-stdout",
           "--browser=web-engine-shell",
diff --git a/infra/config/generated/builders/try/fuchsia-x64-perf-cast-receiver-rel/targets/chromium.fuchsia.fyi.json b/infra/config/generated/builders/try/fuchsia-x64-perf-cast-receiver-rel/targets/chromium.fuchsia.fyi.json
index 8597731..a4810f5 100644
--- a/infra/config/generated/builders/try/fuchsia-x64-perf-cast-receiver-rel/targets/chromium.fuchsia.fyi.json
+++ b/infra/config/generated/builders/try/fuchsia-x64-perf-cast-receiver-rel/targets/chromium.fuchsia.fyi.json
@@ -1652,6 +1652,29 @@
       },
       {
         "args": [
+          "--device-spec=x64-emu-large"
+        ],
+        "isolate_profile_data": true,
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "component_storage_test",
+        "resultdb": {
+          "enable": true,
+          "inv_extended_properties_dir": "${ISOLATED_OUTDIR}/invocations"
+        },
+        "swarming": {
+          "dimensions": {
+            "kvm": "1",
+            "os": "Ubuntu-22.04"
+          },
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "component_storage_test",
+        "test_id_prefix": "ninja://build/fuchsia/test:component_storage_test/"
+      },
+      {
+        "args": [
           "context_lost",
           "--show-stdout",
           "--browser=web-engine-shell",
diff --git a/infra/config/generated/builders/try/mac_chromium_compile_dbg_ng/targets/chromium.mac.json b/infra/config/generated/builders/try/mac_chromium_compile_dbg_ng/targets/chromium.mac.json
index cdccbe07..556b0765 100644
--- a/infra/config/generated/builders/try/mac_chromium_compile_dbg_ng/targets/chromium.mac.json
+++ b/infra/config/generated/builders/try/mac_chromium_compile_dbg_ng/targets/chromium.mac.json
@@ -182,25 +182,6 @@
       },
       {
         "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mac.mac-rel.browser_tests.filter"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "browser_tests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "os": "Mac-15"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 20
-        },
-        "test": "browser_tests",
-        "test_id_prefix": "ninja://chrome/test:browser_tests/"
-      },
-      {
-        "args": [
           "--gtest_filter=-*UsingRealWebcam*"
         ],
         "merge": {
diff --git a/infra/config/generated/builders/try/mac_chromium_dbg_ng/targets/chromium.mac.json b/infra/config/generated/builders/try/mac_chromium_dbg_ng/targets/chromium.mac.json
index cdccbe07..556b0765 100644
--- a/infra/config/generated/builders/try/mac_chromium_dbg_ng/targets/chromium.mac.json
+++ b/infra/config/generated/builders/try/mac_chromium_dbg_ng/targets/chromium.mac.json
@@ -182,25 +182,6 @@
       },
       {
         "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mac.mac-rel.browser_tests.filter"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "browser_tests",
-        "swarming": {
-          "dimensions": {
-            "cpu": "x86-64",
-            "os": "Mac-15"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 20
-        },
-        "test": "browser_tests",
-        "test_id_prefix": "ninja://chrome/test:browser_tests/"
-      },
-      {
-        "args": [
           "--gtest_filter=-*UsingRealWebcam*"
         ],
         "merge": {
diff --git a/infra/config/generated/testing/gn_isolate_map.pyl b/infra/config/generated/testing/gn_isolate_map.pyl
index 2d4ff3a..d6553f0 100644
--- a/infra/config/generated/testing/gn_isolate_map.pyl
+++ b/infra/config/generated/testing/gn_isolate_map.pyl
@@ -628,6 +628,12 @@
       "src/third_party/android_sdk/public/platform-tools/adb",
     ],
   },
+  "component_storage_test": {
+    "label": "//build/fuchsia/test:component_storage_test",
+    "type": "script",
+    "script": "//build/fuchsia/test/component_storage_test.py",
+    "skip_usage_check": True,
+  },
   "components/media_router/common/providers/cast/certificate": {
     "label": "//components/media_router/common/providers/cast/certificate",
     "type": "additional_compile_target",
diff --git a/infra/config/subprojects/chromium/ci/chromium.mac.star b/infra/config/subprojects/chromium/ci/chromium.mac.star
index 6cb9c26..8b6dfd7 100644
--- a/infra/config/subprojects/chromium/ci/chromium.mac.star
+++ b/infra/config/subprojects/chromium/ci/chromium.mac.star
@@ -959,13 +959,8 @@
                     shards = 8,
                 ),
             ),
-            "browser_tests": targets.mixin(
-                args = [
-                    "--test-launcher-filter-file=../../testing/buildbot/filters/mac.mac-rel.browser_tests.filter",
-                ],
-                swarming = targets.swarming(
-                    shards = 20,
-                ),
+            "browser_tests": targets.remove(
+                reason = "https://crbug.com/1406364",
             ),
             "content_browsertests": targets.mixin(
                 # https://crbug.com/1279504
diff --git a/infra/config/targets/binaries.star b/infra/config/targets/binaries.star
index 7cadf023..a9c18b92 100644
--- a/infra/config/targets/binaries.star
+++ b/infra/config/targets/binaries.star
@@ -715,6 +715,13 @@
     label = "//components:components_unittests",
 )
 
+targets.binaries.script(
+    name = "component_storage_test",
+    label = "//build/fuchsia/test:component_storage_test",
+    script = "//build/fuchsia/test/component_storage_test.py",
+    skip_usage_check = True,
+)
+
 targets.binaries.windowed_test_launcher(
     name = "compositor_unittests",
     label = "//ui/compositor:compositor_unittests",
diff --git a/infra/config/targets/bundles.star b/infra/config/targets/bundles.star
index a9b4dc1c..0c5a62c1 100644
--- a/infra/config/targets/bundles.star
+++ b/infra/config/targets/bundles.star
@@ -3018,6 +3018,7 @@
     name = "fuchsia_isolated_scripts",
     targets = [
         "chromium_webkit_isolated_scripts",
+        "component_storage_test",
         # TODO(crbug.com/40821367): Enable content_shell_crash_test
         "gpu_angle_fuchsia_unittests_isolated_scripts",
     ],
diff --git a/infra/config/targets/tests.star b/infra/config/targets/tests.star
index 68e386cf..8793c1c 100644
--- a/infra/config/targets/tests.star
+++ b/infra/config/targets/tests.star
@@ -715,6 +715,10 @@
     name = "components_unittests",
 )
 
+targets.tests.isolated_script_test(
+    name = "component_storage_test",
+)
+
 targets.tests.gtest_test(
     name = "compositor_unittests",
 )
diff --git a/internal b/internal
index 5de18f0..7a2d2c5a 160000
--- a/internal
+++ b/internal
@@ -1 +1 @@
-Subproject commit 5de18f0aacc3865198ffcb45372a83f6b0b9b27b
+Subproject commit 7a2d2c5a328e293e0a9849b8d1e17e59d56a3b14
diff --git a/ios/chrome/app/strings/BUILD.gn b/ios/chrome/app/strings/BUILD.gn
index 7a9ed667..b4cacc8 100644
--- a/ios/chrome/app/strings/BUILD.gn
+++ b/ios/chrome/app/strings/BUILD.gn
@@ -13,20 +13,15 @@
   ]
 }
 
-grit("ios_strings") {
+grit_strings("ios_strings") {
   source = "ios_strings.grd"
   output_dir = "$root_gen_dir/ios/chrome"
   outputs = [ "grit/ios_strings.h" ]
-  foreach(locale, locales_with_pseudolocales) {
-    outputs += [ "ios_strings_$locale.pak" ]
-  }
+  locales = locales_with_pseudolocales
 }
 
-grit("ios_branded_strings") {
+grit_strings("ios_branded_strings") {
   source = "ios_${branding_path_product}_strings.grd"
   output_dir = "$root_gen_dir/ios/chrome"
   outputs = [ "grit/ios_branded_strings.h" ]
-  foreach(locale, all_chrome_locales) {
-    outputs += [ "ios_branded_strings_$locale.pak" ]
-  }
 }
diff --git a/ios/chrome/browser/collaboration/model/ios_collaboration_controller_delegate.mm b/ios/chrome/browser/collaboration/model/ios_collaboration_controller_delegate.mm
index 19c6c3a8f..a7d31ac 100644
--- a/ios/chrome/browser/collaboration/model/ios_collaboration_controller_delegate.mm
+++ b/ios/chrome/browser/collaboration/model/ios_collaboration_controller_delegate.mm
@@ -178,6 +178,10 @@
       operation = AuthenticationOperation::kSheetSigninAndHistorySync;
       break;
 
+    case SigninStatus::kSigninDisabled:
+      // TODO(crbug.com/390153810): Handle the sign in disabled case.
+      NOTREACHED();
+
     case SigninStatus::kSignedInPaused:
       // TODO(crbug.com/390153810): Handle the sign in paused.
       NOTREACHED();
diff --git a/ios/chrome/browser/home_customization/coordinator/BUILD.gn b/ios/chrome/browser/home_customization/coordinator/BUILD.gn
index 6c69e630..fd8c565f 100644
--- a/ios/chrome/browser/home_customization/coordinator/BUILD.gn
+++ b/ios/chrome/browser/home_customization/coordinator/BUILD.gn
@@ -4,6 +4,8 @@
 
 source_set("coordinator") {
   sources = [
+    "home_customization_background_color_picker_mediator.h",
+    "home_customization_background_color_picker_mediator.mm",
     "home_customization_background_picker_action_sheet_coordinator.h",
     "home_customization_background_picker_action_sheet_coordinator.mm",
     "home_customization_coordinator+Testing.h",
@@ -32,9 +34,13 @@
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/features",
+    "//ios/chrome/browser/shared/ui/util",
     "//ios/chrome/browser/url_loading/model",
     "//ios/public/provider/chrome/browser/ui_utils:ui_utils_api",
+    "//skia",
+    "//third_party/material_color_utilities",
     "//ui/base",
+    "//ui/color/dynamic_color",
     "//url",
   ]
   frameworks = [ "UIKit.framework" ]
diff --git a/ios/chrome/browser/home_customization/coordinator/DEPS b/ios/chrome/browser/home_customization/coordinator/DEPS
index 06f22bd..a95c87a 100644
--- a/ios/chrome/browser/home_customization/coordinator/DEPS
+++ b/ios/chrome/browser/home_customization/coordinator/DEPS
@@ -3,5 +3,6 @@
   "+ios/chrome/browser/url_loading/model",
   "+ios/chrome/browser/parcel_tracking",
   "+ios/chrome/browser/content_suggestions/ui_bundled/set_up_list",
-  "+ios/chrome/browser/ntp/ui_bundled"
+  "+ios/chrome/browser/ntp/ui_bundled",
+  "+third_party/material_color_utilities/src/cpp"
 ]
diff --git a/ios/chrome/browser/home_customization/coordinator/home_customization_background_color_picker_mediator.h b/ios/chrome/browser/home_customization/coordinator/home_customization_background_color_picker_mediator.h
new file mode 100644
index 0000000..479541c5
--- /dev/null
+++ b/ios/chrome/browser/home_customization/coordinator/home_customization_background_color_picker_mediator.h
@@ -0,0 +1,28 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_HOME_CUSTOMIZATION_COORDINATOR_HOME_CUSTOMIZATION_BACKGROUND_COLOR_PICKER_MEDIATOR_H_
+#define IOS_CHROME_BROWSER_HOME_CUSTOMIZATION_COORDINATOR_HOME_CUSTOMIZATION_BACKGROUND_COLOR_PICKER_MEDIATOR_H_
+
+#import <UIKit/UIKit.h>
+
+#import "ios/chrome/browser/home_customization/ui/home_customization_background_color_picker_mutator.h"
+@protocol HomeCustomizationBackgroundColorPickerConsumer;
+
+// A mediator that generates and configures background color palettes
+// for the Home customization screen, and communicates them to a consumer.
+@interface HomeCustomizationBackgroundColorPickerMediator
+    : NSObject <HomeCustomizationBackgroundColorPickerMutator>
+
+// The consumer that receives the generated color palette configurations.
+@property(nonatomic, weak) id<HomeCustomizationBackgroundColorPickerConsumer>
+    consumer;
+
+// Generates a predefined set of color palettes and provides them to the
+// consumer.
+- (void)configureColorPalettes;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_HOME_CUSTOMIZATION_COORDINATOR_HOME_CUSTOMIZATION_BACKGROUND_COLOR_PICKER_MEDIATOR_H_
diff --git a/ios/chrome/browser/home_customization/coordinator/home_customization_background_color_picker_mediator.mm b/ios/chrome/browser/home_customization/coordinator/home_customization_background_color_picker_mediator.mm
new file mode 100644
index 0000000..66302fe
--- /dev/null
+++ b/ios/chrome/browser/home_customization/coordinator/home_customization_background_color_picker_mediator.mm
@@ -0,0 +1,119 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/home_customization/coordinator/home_customization_background_color_picker_mediator.h"
+
+#import <Foundation/Foundation.h>
+
+#import "ios/chrome/browser/home_customization/ui/home_customization_background_color_picker_consumer.h"
+#import "skia/ext/skia_utils_ios.h"
+#import "third_party/material_color_utilities/src/cpp/cam/hct.h"
+#import "third_party/material_color_utilities/src/cpp/palettes/core.h"
+#import "third_party/material_color_utilities/src/cpp/utils/utils.h"
+#import "ui/color/dynamic_color/palette_factory.h"
+
+// Define constants within the namespace
+namespace {
+
+// A block type that provides a dynamic color based on the current trait
+// collection.
+typedef UIColor* (^DynamicColorProviderBlock)(UITraitCollection* traits);
+
+// Represents a pair of tone values for a given color tone,
+// with separate values for light and dark UI modes.
+struct ToneSet {
+  // The tone value to use in light mode.
+  int light_mode;
+
+  // The tone value to use in dark mode.
+  int dark_mode;
+};
+
+// The tone value used for generating a light variant of the seed color.
+const ToneSet kLightTone = {
+    /*light_mode=*/90,
+    /*dark_mode=*/30,
+};
+
+// The tone value used for generating a medium variant of the seed color.
+const ToneSet kMediumTone = {
+    /*light_mode=*/80,
+    /*dark_mode=*/50,
+};
+
+// The tone value used for generating a dark variant of the seed color.
+const ToneSet kDarkTone = {
+    /*light_mode=*/40,
+    /*dark_mode=*/80,
+};
+
+// Returns a dynamic `UIColor` that adapts to the system's light or dark
+// appearance using tones derived from the given `TonalPalette`.
+DynamicColorProviderBlock GetDynamicProviderForPrimary(
+    const ui::TonalPalette& primary,
+    const ToneSet& toneSet) {
+  uint32_t lightARGB = primary.get(toneSet.light_mode);
+  uint32_t darkARGB = primary.get(toneSet.dark_mode);
+
+  return ^UIColor*(UITraitCollection* traits) {
+    BOOL isDark = (traits.userInterfaceStyle == UIUserInterfaceStyleDark);
+    return skia::UIColorFromSkColor(isDark ? darkARGB : lightARGB);
+  };
+}
+
+}  // namespace
+
+@implementation HomeCustomizationBackgroundColorPickerMediator
+
+- (void)configureColorPalettes {
+  NSMutableArray* configs = [NSMutableArray array];
+
+  // TODO(crbug.com/408243803):Add the UIColor seeds that will be used by Monet
+  // to generate the color palettes.
+  [@[
+    [UIColor redColor], [UIColor blueColor], [UIColor greenColor],
+    [UIColor orangeColor], [UIColor purpleColor]
+  ] enumerateObjectsUsingBlock:^(UIColor* seedColor, NSUInteger index,
+                                 BOOL* stop) {
+    [configs addObject:[self configurationForSeedColor:seedColor]];
+  }];
+
+  [_consumer setColorPaletteConfigurations:configs];
+}
+
+#pragma mark - Private
+
+// Creates and returns a color palette configuration from a seed color.
+- (HomeCustomizationColorPaletteConfiguration*)configurationForSeedColor:
+    (UIColor*)seedColor {
+  HomeCustomizationColorPaletteConfiguration* config =
+      [[HomeCustomizationColorPaletteConfiguration alloc] init];
+
+  CGFloat red = 0.0;
+  CGFloat green = 0.0;
+  CGFloat blue = 0.0;
+  CGFloat alpha = 0.0;
+  [seedColor getRed:&red green:&green blue:&blue alpha:&alpha];
+
+  SkColor skColor =
+      SkColorSetARGB(alpha * 255.0, red * 255.0, green * 255.0, blue * 255.0);
+
+  std::unique_ptr<ui::Palette> palette = ui::GeneratePalette(
+      skColor, ui::ColorProviderKey::SchemeVariant::kTonalSpot);
+  ui::TonalPalette primary = palette->primary();
+
+  config.seedColor = seedColor;
+  config.lightColor =
+      [UIColor colorWithDynamicProvider:GetDynamicProviderForPrimary(
+                                            primary, kLightTone)];
+  config.mediumColor =
+      [UIColor colorWithDynamicProvider:GetDynamicProviderForPrimary(
+                                            primary, kMediumTone)];
+  config.darkColor =
+      [UIColor colorWithDynamicProvider:GetDynamicProviderForPrimary(
+                                            primary, kDarkTone)];
+  return config;
+}
+
+@end
diff --git a/ios/chrome/browser/home_customization/coordinator/home_customization_background_picker_action_sheet_coordinator.mm b/ios/chrome/browser/home_customization/coordinator/home_customization_background_picker_action_sheet_coordinator.mm
index 3d6c48d..ac61d8c 100644
--- a/ios/chrome/browser/home_customization/coordinator/home_customization_background_picker_action_sheet_coordinator.mm
+++ b/ios/chrome/browser/home_customization/coordinator/home_customization_background_picker_action_sheet_coordinator.mm
@@ -4,12 +4,21 @@
 
 #import "ios/chrome/browser/home_customization/coordinator/home_customization_background_picker_action_sheet_coordinator.h"
 
+#import "ios/chrome/browser/home_customization/coordinator/home_customization_background_color_picker_mediator.h"
 #import "ios/chrome/browser/home_customization/ui/home_customization_background_color_picker_view_controller.h"
 #import "ios/chrome/browser/home_customization/ui/home_customization_background_photo_library_picker_view_controller.h"
 #import "ios/chrome/browser/home_customization/ui/home_customization_background_preset_gallery_picker_view_controller.h"
 #import "ios/chrome/grit/ios_strings.h"
 #import "ui/base/l10n/l10n_util_mac.h"
 
+@interface HomeCustomizationBackgroundPickerActionSheetCoordinator () {
+  // The mediator for the color picker.
+  HomeCustomizationBackgroundColorPickerMediator*
+      _backgroundColorPickerMediator;
+}
+
+@end
+
 @implementation HomeCustomizationBackgroundPickerActionSheetCoordinator
 
 - (instancetype)initWithBaseViewController:(UIViewController*)viewController
@@ -24,6 +33,8 @@
 
 - (void)start {
   __weak __typeof(self) weakSelf = self;
+  _backgroundColorPickerMediator =
+      [[HomeCustomizationBackgroundColorPickerMediator alloc] init];
 
   [self
       addItemWithTitle:
@@ -56,6 +67,7 @@
 
 - (void)stop {
   [self.baseViewController dismissViewControllerAnimated:YES completion:nil];
+  _backgroundColorPickerMediator.consumer = nil;
   [super stop];
 }
 
@@ -63,8 +75,13 @@
 
 // Presents the view controller for picking a solid background color.
 - (void)presentBackgroundColorPicker {
-  UIViewController* mainViewController =
+  HomeCustomizationBackgroundColorPickerViewController* mainViewController =
       [[HomeCustomizationBackgroundColorPickerViewController alloc] init];
+
+  mainViewController.mutator = _backgroundColorPickerMediator;
+  _backgroundColorPickerMediator.consumer = mainViewController;
+  [_backgroundColorPickerMediator configureColorPalettes];
+
   mainViewController.modalPresentationStyle = UIModalPresentationFormSheet;
   UINavigationController* navigationController = [[UINavigationController alloc]
       initWithRootViewController:mainViewController];
diff --git a/ios/chrome/browser/home_customization/ui/BUILD.gn b/ios/chrome/browser/home_customization/ui/BUILD.gn
index 08bf71d3e..9170ff6b 100644
--- a/ios/chrome/browser/home_customization/ui/BUILD.gn
+++ b/ios/chrome/browser/home_customization/ui/BUILD.gn
@@ -7,6 +7,8 @@
     "home_customization_background_cell+subclassing.h",
     "home_customization_background_cell.h",
     "home_customization_background_cell.mm",
+    "home_customization_background_color_picker_consumer.h",
+    "home_customization_background_color_picker_mutator.h",
     "home_customization_background_color_picker_view_controller.h",
     "home_customization_background_color_picker_view_controller.mm",
     "home_customization_background_photo_library_picker_view_controller.h",
@@ -18,6 +20,8 @@
     "home_customization_background_preset_gallery_picker_view_controller.mm",
     "home_customization_collection_configurator.h",
     "home_customization_collection_configurator.mm",
+    "home_customization_color_palette_configuration.h",
+    "home_customization_color_palette_configuration.mm",
     "home_customization_discover_consumer.h",
     "home_customization_discover_view_controller.h",
     "home_customization_discover_view_controller.mm",
@@ -36,6 +40,8 @@
     "home_customization_toggle_cell.h",
     "home_customization_toggle_cell.mm",
     "home_customization_view_controller_protocol.h",
+    "home_cutomization_color_palette_cell.h",
+    "home_cutomization_color_palette_cell.mm",
   ]
   deps = [
     "//ios/chrome/app/strings:ios_strings",
diff --git a/ios/chrome/browser/home_customization/ui/home_customization_background_color_picker_consumer.h b/ios/chrome/browser/home_customization/ui/home_customization_background_color_picker_consumer.h
new file mode 100644
index 0000000..783769b
--- /dev/null
+++ b/ios/chrome/browser/home_customization/ui/home_customization_background_color_picker_consumer.h
@@ -0,0 +1,22 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_HOME_CUSTOMIZATION_UI_HOME_CUSTOMIZATION_BACKGROUND_COLOR_PICKER_CONSUMER_H_
+#define IOS_CHROME_BROWSER_HOME_CUSTOMIZATION_UI_HOME_CUSTOMIZATION_BACKGROUND_COLOR_PICKER_CONSUMER_H_
+
+#import <Foundation/Foundation.h>
+
+#import "ios/chrome/browser/home_customization/ui/home_customization_color_palette_configuration.h"
+
+// A consumer protocol that receives updates about background color palettes.
+@protocol HomeCustomizationBackgroundColorPickerConsumer
+
+// Sets the available background color palette configurations, indexed by seed
+// color index.
+- (void)setColorPaletteConfigurations:
+    (NSArray<HomeCustomizationColorPaletteConfiguration*>*)
+        colorPaletteConfigurations;
+@end
+
+#endif  // IOS_CHROME_BROWSER_HOME_CUSTOMIZATION_UI_HOME_CUSTOMIZATION_BACKGROUND_COLOR_PICKER_CONSUMER_H_
diff --git a/ios/chrome/browser/home_customization/ui/home_customization_background_color_picker_mutator.h b/ios/chrome/browser/home_customization/ui/home_customization_background_color_picker_mutator.h
new file mode 100644
index 0000000..3574a6d
--- /dev/null
+++ b/ios/chrome/browser/home_customization/ui/home_customization_background_color_picker_mutator.h
@@ -0,0 +1,15 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_HOME_CUSTOMIZATION_UI_HOME_CUSTOMIZATION_BACKGROUND_COLOR_PICKER_MUTATOR_H_
+#define IOS_CHROME_BROWSER_HOME_CUSTOMIZATION_UI_HOME_CUSTOMIZATION_BACKGROUND_COLOR_PICKER_MUTATOR_H_
+
+// A mutator protocol used to communicate with the
+// `HomeCustomizationBackgroundColorPickerMediator`.
+@protocol HomeCustomizationBackgroundColorPickerMutator
+// TODO(crbug.com/408243803): Add the mutator implementation when the cells are
+// tapped.
+@end
+
+#endif  // IOS_CHROME_BROWSER_HOME_CUSTOMIZATION_UI_HOME_CUSTOMIZATION_BACKGROUND_COLOR_PICKER_MUTATOR_H_
diff --git a/ios/chrome/browser/home_customization/ui/home_customization_background_color_picker_view_controller.h b/ios/chrome/browser/home_customization/ui/home_customization_background_color_picker_view_controller.h
index 9fba0329..e74a43e 100644
--- a/ios/chrome/browser/home_customization/ui/home_customization_background_color_picker_view_controller.h
+++ b/ios/chrome/browser/home_customization/ui/home_customization_background_color_picker_view_controller.h
@@ -7,11 +7,21 @@
 
 #import <UIKit/UIKit.h>
 
+#import "ios/chrome/browser/home_customization/ui/home_customization_background_color_picker_consumer.h"
+
+@protocol HomeCustomizationBackgroundColorPickerMutator;
+
 // View controller responsible for displaying and managing the background color
 // picker in the Home customization flow. Implements collection view delegate
 // and data source to handle color options.
 @interface HomeCustomizationBackgroundColorPickerViewController
-    : UIViewController <UICollectionViewDelegate, UICollectionViewDataSource>
+    : UIViewController <HomeCustomizationBackgroundColorPickerConsumer,
+                        UICollectionViewDataSource>
+
+// Mutator for communicating with the
+// `HomeCustomizationBackgroundColorPickerMediator`.
+@property(nonatomic, weak) id<HomeCustomizationBackgroundColorPickerMutator>
+    mutator;
 
 @end
 
diff --git a/ios/chrome/browser/home_customization/ui/home_customization_background_color_picker_view_controller.mm b/ios/chrome/browser/home_customization/ui/home_customization_background_color_picker_view_controller.mm
index c2f486b..8250613 100644
--- a/ios/chrome/browser/home_customization/ui/home_customization_background_color_picker_view_controller.mm
+++ b/ios/chrome/browser/home_customization/ui/home_customization_background_color_picker_view_controller.mm
@@ -4,10 +4,45 @@
 
 #import "ios/chrome/browser/home_customization/ui/home_customization_background_color_picker_view_controller.h"
 
+#import "ios/chrome/browser/home_customization/ui/home_customization_color_palette_configuration.h"
+#import "ios/chrome/browser/home_customization/ui/home_cutomization_color_palette_cell.h"
 #import "ios/chrome/browser/home_customization/utils/home_customization_constants.h"
 #import "ios/chrome/grit/ios_strings.h"
 #import "ui/base/l10n/l10n_util_mac.h"
 
+// Define constants within the namespace
+namespace {
+// The width and height of each color palette cell in the collection view.
+const CGFloat kColorCellSize = 48.0;
+
+// The vertical spacing between rows of cells.
+const CGFloat kLineSpacing = 28.0;
+
+// The horizontal spacing between cells in the same row.
+const CGFloat kItemSpacing = 20.0;
+
+// The top padding for the section in the collection view.
+const CGFloat kSectionInsetTop = 20.0;
+
+// The left and right padding for the section in the collection view.
+const CGFloat kSectionInsetSides = 27.0;
+
+// The bottom padding for the section in the collection view.
+const CGFloat kSectionInsetBottom = 20.0;
+}  // namespace
+
+@interface HomeCustomizationBackgroundColorPickerViewController () {
+  // An array storing the available color palette configurations,
+  // ordered by their index in the palette.
+  NSArray<HomeCustomizationColorPaletteConfiguration*>*
+      _colorPaletteConfigurations;
+
+  // The `UICollectionViewCellRegistration` for registering  and configuring the
+  // `HomeCustomizationColorPaletteCell` in the collection view.
+  UICollectionViewCellRegistration* _colorCellRegistration;
+}
+@end
+
 @implementation HomeCustomizationBackgroundColorPickerViewController
 
 - (void)viewDidLoad {
@@ -15,6 +50,7 @@
 
   self.title = l10n_util::GetNSStringWithFixup(
       IDS_IOS_HOME_CUSTOMIZATION_BACKGROUND_PICKER_COLOR_TITLE);
+
   self.view.backgroundColor = [UIColor systemBackgroundColor];
 
   UIBarButtonItem* dismissButton = [[UIBarButtonItem alloc]
@@ -28,17 +64,73 @@
   self.navigationItem.backBarButtonItem.accessibilityIdentifier =
       kNavigationBarBackButtonIdentifier;
   [self.navigationItem setHidesBackButton:YES];
+
+  UICollectionViewFlowLayout* layout =
+      [[UICollectionViewFlowLayout alloc] init];
+
+  layout.itemSize = CGSizeMake(kColorCellSize, kColorCellSize);
+  layout.minimumLineSpacing = kLineSpacing;
+  layout.minimumInteritemSpacing = kItemSpacing;
+  layout.sectionInset =
+      UIEdgeInsetsMake(kSectionInsetTop, kSectionInsetSides,
+                       kSectionInsetBottom, kSectionInsetSides);
+
+  _colorCellRegistration = [UICollectionViewCellRegistration
+      registrationWithCellClass:[HomeCustomizationColorPaletteCell class]
+           configurationHandler:^(
+               HomeCustomizationColorPaletteCell* cell, NSIndexPath* indexPath,
+               HomeCustomizationColorPaletteConfiguration* configuration) {
+             cell.configuration = configuration;
+           }];
+
+  UICollectionView* collectionView =
+      [[UICollectionView alloc] initWithFrame:CGRectZero
+                         collectionViewLayout:layout];
+  collectionView.dataSource = self;
+
+  collectionView.translatesAutoresizingMaskIntoConstraints = NO;
+  [self.view addSubview:collectionView];
+
+  [NSLayoutConstraint activateConstraints:@[
+    [collectionView.topAnchor constraintEqualToAnchor:self.view.topAnchor],
+    [collectionView.leadingAnchor
+        constraintEqualToAnchor:self.view.leadingAnchor],
+    [collectionView.trailingAnchor
+        constraintEqualToAnchor:self.view.trailingAnchor],
+    [collectionView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor]
+  ]];
+}
+
+#pragma mark - HomeCustomizationBackgroundColorPickerConsumer
+
+- (void)setColorPaletteConfigurations:
+    (NSArray<HomeCustomizationColorPaletteConfiguration*>*)
+        colorPaletteConfigurations {
+  _colorPaletteConfigurations = colorPaletteConfigurations;
 }
 
 #pragma mark - UICollectionViewDelegate
 
 - (NSInteger)collectionView:(UICollectionView*)collectionView
      numberOfItemsInSection:(NSInteger)section {
-  return 0;
+  return _colorPaletteConfigurations.count;
 }
 
 - (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView
                  cellForItemAtIndexPath:(NSIndexPath*)indexPath {
+  HomeCustomizationColorPaletteConfiguration* configuration =
+      _colorPaletteConfigurations[indexPath.item];
+
+  if (indexPath.item >= 0) {
+    std::size_t index = static_cast<std::size_t>(indexPath.item);
+    if (index < _colorPaletteConfigurations.count) {
+      return [collectionView
+          dequeueConfiguredReusableCellWithRegistration:_colorCellRegistration
+                                           forIndexPath:indexPath
+                                                   item:configuration];
+    }
+  }
+
   return nil;
 }
 
diff --git a/ios/chrome/browser/home_customization/ui/home_customization_color_palette_configuration.h b/ios/chrome/browser/home_customization/ui/home_customization_color_palette_configuration.h
new file mode 100644
index 0000000..2eeb26b
--- /dev/null
+++ b/ios/chrome/browser/home_customization/ui/home_customization_color_palette_configuration.h
@@ -0,0 +1,28 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_HOME_CUSTOMIZATION_UI_HOME_CUSTOMIZATION_COLOR_PALETTE_CONFIGURATION_H_
+#define IOS_CHROME_BROWSER_HOME_CUSTOMIZATION_UI_HOME_CUSTOMIZATION_COLOR_PALETTE_CONFIGURATION_H_
+
+#import <UIKit/UIKit.h>
+
+// A model object that defines a background color palette, including
+// light, medium, and dark variants derived from a seed color.
+@interface HomeCustomizationColorPaletteConfiguration : NSObject
+
+// A lighter tone variant of the seed color.
+@property(nonatomic, strong) UIColor* lightColor;
+
+// A medium tone variant of the seed color.
+@property(nonatomic, strong) UIColor* mediumColor;
+
+// A darker tone variant of the seed color.
+@property(nonatomic, strong) UIColor* darkColor;
+
+// The original seed color used to generate the palette.
+@property(nonatomic, strong) UIColor* seedColor;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_HOME_CUSTOMIZATION_UI_HOME_CUSTOMIZATION_COLOR_PALETTE_CONFIGURATION_H_
diff --git a/ios/chrome/browser/home_customization/ui/home_customization_color_palette_configuration.mm b/ios/chrome/browser/home_customization/ui/home_customization_color_palette_configuration.mm
new file mode 100644
index 0000000..325e850
--- /dev/null
+++ b/ios/chrome/browser/home_customization/ui/home_customization_color_palette_configuration.mm
@@ -0,0 +1,9 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/home_customization/ui/home_customization_color_palette_configuration.h"
+
+@implementation HomeCustomizationColorPaletteConfiguration
+
+@end
diff --git a/ios/chrome/browser/home_customization/ui/home_cutomization_color_palette_cell.h b/ios/chrome/browser/home_customization/ui/home_cutomization_color_palette_cell.h
new file mode 100644
index 0000000..9e36ce3
--- /dev/null
+++ b/ios/chrome/browser/home_customization/ui/home_cutomization_color_palette_cell.h
@@ -0,0 +1,23 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_HOME_CUSTOMIZATION_UI_HOME_CUTOMIZATION_COLOR_PALETTE_CELL_H_
+#define IOS_CHROME_BROWSER_HOME_CUSTOMIZATION_UI_HOME_CUTOMIZATION_COLOR_PALETTE_CELL_H_
+
+#import <UIKit/UIKit.h>
+
+// Configuration used to populate the color palette cell.
+@class HomeCustomizationColorPaletteConfiguration;
+
+// A collection view cell that displays a single background color palette option
+// for the background customization.
+@interface HomeCustomizationColorPaletteCell : UICollectionViewCell
+
+// The configuration model that specifies the colors shown in the cell.
+@property(nonatomic, strong)
+    HomeCustomizationColorPaletteConfiguration* configuration;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_HOME_CUSTOMIZATION_UI_HOME_CUTOMIZATION_COLOR_PALETTE_CELL_H_
diff --git a/ios/chrome/browser/home_customization/ui/home_cutomization_color_palette_cell.mm b/ios/chrome/browser/home_customization/ui/home_cutomization_color_palette_cell.mm
new file mode 100644
index 0000000..883de23
--- /dev/null
+++ b/ios/chrome/browser/home_customization/ui/home_cutomization_color_palette_cell.mm
@@ -0,0 +1,87 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/home_customization/ui/home_cutomization_color_palette_cell.h"
+
+#import <UIKit/UIKit.h>
+
+#import "base/check.h"
+#import "ios/chrome/browser/home_customization/ui/home_customization_color_palette_configuration.h"
+
+@implementation HomeCustomizationColorPaletteCell {
+  // View representing the light tone color.
+  UIView* _lightColorView;
+
+  // View representing the dark tone color.
+  UIView* _darkColorView;
+
+  // View representing the medium tone color.
+  UIView* _mediumColorView;
+}
+
+- (instancetype)initWithFrame:(CGRect)frame {
+  self = [super initWithFrame:frame];
+  if (self) {
+    self.backgroundColor = UIColor.clearColor;
+
+    _lightColorView = [[UIView alloc] init];
+    _darkColorView = [[UIView alloc] init];
+    _mediumColorView = [[UIView alloc] init];
+
+    _lightColorView.translatesAutoresizingMaskIntoConstraints = NO;
+    _darkColorView.translatesAutoresizingMaskIntoConstraints = NO;
+    _mediumColorView.translatesAutoresizingMaskIntoConstraints = NO;
+
+    [self addSubview:_lightColorView];
+    [self addSubview:_darkColorView];
+    [self addSubview:_mediumColorView];
+
+    [NSLayoutConstraint activateConstraints:@[
+      // Top half (light color).
+      [_lightColorView.topAnchor constraintEqualToAnchor:self.topAnchor],
+      [_lightColorView.leadingAnchor
+          constraintEqualToAnchor:self.leadingAnchor],
+      [_lightColorView.trailingAnchor
+          constraintEqualToAnchor:self.trailingAnchor],
+      [_lightColorView.heightAnchor constraintEqualToAnchor:self.heightAnchor
+                                                 multiplier:0.5],
+
+      // Bottom left quarter (dark color).
+      [_darkColorView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor],
+      [_darkColorView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor],
+      [_darkColorView.widthAnchor constraintEqualToAnchor:self.widthAnchor
+                                               multiplier:0.5],
+      [_darkColorView.heightAnchor constraintEqualToAnchor:self.heightAnchor
+                                                multiplier:0.5],
+
+      // Bottom right quarter (medium color).
+      [_mediumColorView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor],
+      [_mediumColorView.trailingAnchor
+          constraintEqualToAnchor:self.trailingAnchor],
+      [_mediumColorView.widthAnchor constraintEqualToAnchor:self.widthAnchor
+                                                 multiplier:0.5],
+      [_mediumColorView.heightAnchor constraintEqualToAnchor:self.heightAnchor
+                                                  multiplier:0.5],
+    ]];
+  }
+  return self;
+}
+
+- (void)layoutSubviews {
+  [super layoutSubviews];
+
+  // Circle shape by rounding the corners
+  self.layer.cornerRadius = self.bounds.size.width / 2.0;
+  self.clipsToBounds = YES;
+}
+
+- (void)setConfiguration:
+    (HomeCustomizationColorPaletteConfiguration*)configuration {
+  _configuration = configuration;
+  _lightColorView.backgroundColor = configuration.lightColor;
+  _darkColorView.backgroundColor = configuration.darkColor;
+  _mediumColorView.backgroundColor = configuration.mediumColor;
+}
+
+@end
diff --git a/ios/chrome/browser/web/model/chrome_web_client.h b/ios/chrome/browser/web/model/chrome_web_client.h
index fc79652..032ad05 100644
--- a/ios/chrome/browser/web/model/chrome_web_client.h
+++ b/ios/chrome/browser/web/model/chrome_web_client.h
@@ -33,6 +33,7 @@
   std::string GetApplicationLocale() const override;
   bool IsAppSpecificURL(const GURL& url) const override;
   std::string GetUserAgent(web::UserAgentType type) const override;
+  std::string GetMainThreadName() const override;
   std::u16string GetLocalizedString(int message_id) const override;
   std::string_view GetDataResource(
       int resource_id,
diff --git a/ios/chrome/browser/web/model/chrome_web_client.mm b/ios/chrome/browser/web/model/chrome_web_client.mm
index cc2c8f2..2da89db 100644
--- a/ios/chrome/browser/web/model/chrome_web_client.mm
+++ b/ios/chrome/browser/web/model/chrome_web_client.mm
@@ -363,6 +363,10 @@
   return web::BuildMobileUserAgent(GetMobileProduct());
 }
 
+std::string ChromeWebClient::GetMainThreadName() const {
+  return "CrWebMain";
+}
+
 std::u16string ChromeWebClient::GetLocalizedString(int message_id) const {
   return l10n_util::GetStringUTF16(message_id);
 }
diff --git a/ios/chrome/browser/whats_new/ui/strings/BUILD.gn b/ios/chrome/browser/whats_new/ui/strings/BUILD.gn
index 040aa86..9fab4a96 100644
--- a/ios/chrome/browser/whats_new/ui/strings/BUILD.gn
+++ b/ios/chrome/browser/whats_new/ui/strings/BUILD.gn
@@ -5,11 +5,9 @@
 import("//build/config/locales.gni")
 import("//tools/grit/grit_rule.gni")
 
-grit("strings") {
+grit_strings("strings") {
   source = "ios_whats_new_strings.grd"
   output_dir = "$root_gen_dir/ios/chrome"
   outputs = [ "grit/ios_whats_new_strings.h" ]
-  foreach(locale, all_chrome_locales) {
-    outputs += [ "ios_whats_new_strings_$locale.pak" ]
-  }
+  output_prefix = "ios_whats_new_strings_"
 }
diff --git a/ios/chrome/content_widget_extension/strings/BUILD.gn b/ios/chrome/content_widget_extension/strings/BUILD.gn
index 8dff629..3c79ab3a 100644
--- a/ios/chrome/content_widget_extension/strings/BUILD.gn
+++ b/ios/chrome/content_widget_extension/strings/BUILD.gn
@@ -6,11 +6,9 @@
 import("//build/config/locales.gni")
 import("//tools/grit/grit_rule.gni")
 
-grit("strings") {
+grit_strings("strings") {
   source = "ios_content_widget_extension_${branding_path_product}_strings.grd"
   output_dir = "$root_gen_dir/ios/content_widget_extension"
   outputs = [ "grit/ios_content_widget_extension_branded_strings.h" ]
-  foreach(locale, all_chrome_locales) {
-    outputs += [ "ios_content_widget_extension_branded_strings_$locale.pak" ]
-  }
+  output_prefix = "ios_content_widget_extension_branded_strings_"
 }
diff --git a/ios/chrome/credential_provider_extension/strings/BUILD.gn b/ios/chrome/credential_provider_extension/strings/BUILD.gn
index b118fd37..f5e5b959 100644
--- a/ios/chrome/credential_provider_extension/strings/BUILD.gn
+++ b/ios/chrome/credential_provider_extension/strings/BUILD.gn
@@ -5,11 +5,9 @@
 import("//build/config/locales.gni")
 import("//tools/grit/grit_rule.gni")
 
-grit("strings") {
+grit_strings("strings") {
   source = "ios_credential_provider_extension_strings.grd"
   output_dir = "$root_gen_dir/ios/credential_provider_extension"
   outputs = [ "grit/ios_credential_provider_extension_strings.h" ]
-  foreach(locale, all_chrome_locales) {
-    outputs += [ "ios_credential_provider_extension_strings_$locale.pak" ]
-  }
+  output_prefix = "ios_credential_provider_extension_strings_"
 }
diff --git a/ios/chrome/open_extension/strings/BUILD.gn b/ios/chrome/open_extension/strings/BUILD.gn
index 5df449b..dbea06db 100644
--- a/ios/chrome/open_extension/strings/BUILD.gn
+++ b/ios/chrome/open_extension/strings/BUILD.gn
@@ -5,11 +5,9 @@
 import("//build/config/locales.gni")
 import("//tools/grit/grit_rule.gni")
 
-grit("strings") {
+grit_strings("strings") {
   source = "ios_open_extension_${branding_path_product}_strings.grd"
   output_dir = "$root_gen_dir/ios/open_extension"
   outputs = [ "grit/ios_open_extension_branded_strings.h" ]
-  foreach(locale, all_chrome_locales) {
-    outputs += [ "ios_open_extension_branded_strings_$locale.pak" ]
-  }
+  output_prefix = "ios_open_extension_branded_strings_"
 }
diff --git a/ios/chrome/search_widget_extension/strings/BUILD.gn b/ios/chrome/search_widget_extension/strings/BUILD.gn
index 00df3f3..d214bf5f 100644
--- a/ios/chrome/search_widget_extension/strings/BUILD.gn
+++ b/ios/chrome/search_widget_extension/strings/BUILD.gn
@@ -6,11 +6,9 @@
 import("//build/config/locales.gni")
 import("//tools/grit/grit_rule.gni")
 
-grit("strings") {
+grit_strings("strings") {
   source = "ios_search_widget_extension_${branding_path_product}_strings.grd"
   output_dir = "$root_gen_dir/ios/search_widget_extension"
   outputs = [ "grit/ios_search_widget_extension_branded_strings.h" ]
-  foreach(locale, all_chrome_locales) {
-    outputs += [ "ios_search_widget_extension_branded_strings_$locale.pak" ]
-  }
+  output_prefix = "ios_search_widget_extension_branded_strings_"
 }
diff --git a/ios/chrome/share_extension/strings/BUILD.gn b/ios/chrome/share_extension/strings/BUILD.gn
index fc08eb2b..eabdec1 100644
--- a/ios/chrome/share_extension/strings/BUILD.gn
+++ b/ios/chrome/share_extension/strings/BUILD.gn
@@ -5,11 +5,9 @@
 import("//build/config/locales.gni")
 import("//tools/grit/grit_rule.gni")
 
-grit("strings") {
+grit_strings("strings") {
   source = "ios_share_extension_strings.grd"
   output_dir = "$root_gen_dir/ios/share_extension"
   outputs = [ "grit/ios_share_extension_strings.h" ]
-  foreach(locale, all_chrome_locales) {
-    outputs += [ "ios_share_extension_strings_$locale.pak" ]
-  }
+  output_prefix = "ios_share_extension_strings_"
 }
diff --git a/ios/chrome/widget_kit_extension/strings/BUILD.gn b/ios/chrome/widget_kit_extension/strings/BUILD.gn
index c780b02..eae97db 100644
--- a/ios/chrome/widget_kit_extension/strings/BUILD.gn
+++ b/ios/chrome/widget_kit_extension/strings/BUILD.gn
@@ -5,11 +5,9 @@
 import("//build/config/locales.gni")
 import("//tools/grit/grit_rule.gni")
 
-grit("strings") {
+grit_strings("strings") {
   source = "ios_widget_kit_extension_strings.grd"
   output_dir = "$root_gen_dir/ios/widget_kit_extension"
   outputs = [ "grit/ios_widget_kit_extension_strings.h" ]
-  foreach(locale, all_chrome_locales) {
-    outputs += [ "ios_widget_kit_extension_strings_$locale.pak" ]
-  }
+  output_prefix = "ios_widget_kit_extension_strings_"
 }
diff --git a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1
index 6b612ac0..6fd93bd7 100644
--- a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-e3edaf782e8f7149c4c2492e3e7a55533b5638ad
\ No newline at end of file
+e83b6b238ea10cb67a1cad02fef0ddf20bd701cd
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1
index d3b92eb3..03e91d6f 100644
--- a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@
-5a24acfa661c59bd8eb53e20d89ba10a23de757e
\ No newline at end of file
+1c7a5d05d7bc15a38ec5d5dc957f9a6ab279760e
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
index 0cead64..f63a419 100644
--- a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-4e7143699ba681818cab8c98ec0d39998d43cc87
\ No newline at end of file
+3e545bdb1a460666466b7e75f1ea1530ac1ab4d6
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1
index a626e5df..367f0fa 100644
--- a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@
-e13e0ffcbdd1b91cfb251441cf1e3eff6f255322
\ No newline at end of file
+7edca93a5f3da84d9b3bec5c61a12dd2751fb93e
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
index e176586..8f0b7d53 100644
--- a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-54f71374da89594dfdbc25e1f90e897ed3fbbeaa
\ No newline at end of file
+2fa4ad0f4b2baeae4f167a5585de05d0c5da5b98
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1
index 7f66ac3..bb3e8464 100644
--- a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@
-a2caf961234900394a8039c7b2c1e74ed7562b1b
\ No newline at end of file
+6c3293d383cb2b3b75ba7f6f1140df3a6c7cab87
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1
index 803649ae..125e24b 100644
--- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-4036b5db59d8c153da0c24ad91a780e6061dcaf0
\ No newline at end of file
+d3596ab6811c6440dc812ab45c033e5cf46c955d
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1
index 0660c05..ad51185 100644
--- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-76bc5701495778b9db3f446869591ec9f45734e6
\ No newline at end of file
+aa827e01c365ddc93ed796459b370fccc6995ca3
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1
index 4ab2859..51bc465 100644
--- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-cfe872b9fc93298b235d7d423d68ca900e38ead5
\ No newline at end of file
+b86d0c3678c1b54f388833057c5200e25972db4d
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1
index 047e6e9c..374ef34 100644
--- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-9f334152f35499b5f00f36a8906b28dda901da44
\ No newline at end of file
+43de3ae7ad6b09bc744a1955399c9a066bec1f3d
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
index ad9288ae..23afe6c 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-b6a85dbe1fd2d7d17ee714acd1e9303c682b1c02
\ No newline at end of file
+da94702d642d621dbf91498157ed258562872bd9
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1
index 4ffa20c..e0c95fba 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-f625ed4638257c969b975d98f137717dc71a76af
\ No newline at end of file
+0233896bc1ad8255f5f9109a08f57d55404e2dc9
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
index 9152f79f..3ec7e1c 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-94db3fa3a60e48af158c566cba149af100116b88
\ No newline at end of file
+bc66a15eb8325005648820cdc2f10f62593afa77
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1
index eda7b36..8806ab94 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-0e3aff95dd9f2f827ab1ae47dbd4dcd51cf3086c
\ No newline at end of file
+0163bdeaab93259b50af8b1c8277f7a08c5cac68
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
index 91dc2a6..9e2baa2 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-e0332d07cfc93647eb21f7af0553d8a4449d72ab
\ No newline at end of file
+77598fd63598aebae733780e9d96fd9239e77712
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1
index 5c4ec80c..bf9a9e87 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-cd52aeb6ddf2ea57d809fc7a2ebdd46095b4b7b8
\ No newline at end of file
+5e51373abe3f2278a86540f6bb4ad979a25d103b
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
index 9c7f715..27eeb3f3 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-52deadc27942090c40f04674ec314aa856569a02
\ No newline at end of file
+78045c87916304e5f0b76783f054096693d783fe
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1
index 3ef9efe..784ccec 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-362f24d16d131448bf0f2ec87ee5e9d3d0df694c
\ No newline at end of file
+e6c2d37215b80f879647274eb8dfceb1fc224275
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
index d714b0b..b93e2db 100644
--- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-8e61156591295d7e7216a3bb15d6d3ef31b1858b
\ No newline at end of file
+05daaf5aa23786597cdb7628504b4bbf81503f29
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
index 6fb827c..7a74712 100644
--- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-5f397699b0b9d48a8dccfafa637991051a737b7b
\ No newline at end of file
+32ecf69c38f73d6800ded374daee594368cfbc3a
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
index 3c10a54..d29b3231 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-30cfdb6241dfe222a5796a2608824ccd9c238f54
\ No newline at end of file
+341108f8c614b31666f30f246216eb4c47e888db
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1
index 8c916e3..64c9e67 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-8dbc98f17be818d6f35848a9f3d63f7039f2f20f
\ No newline at end of file
+48ba6deb3edc7f8666f71cd129eab53797c20be5
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
index 9adb7d4..d569c3a 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-9ff82179f542204d5776257bceeaa0d16d42ad3f
\ No newline at end of file
+dcf5e7830335049925329161209933bdadb4a543
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1
index 5977791..2616ad8 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-76dc804eaaedb9bd7d6051cf98c2cf9aef1b826b
\ No newline at end of file
+aad36b4d3e972fd3b8aeb3465f18afcac8862a6b
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
index d0208d6..265caeec 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-0accc764651e6aa91a7f91e10b5176cacbc7bba8
\ No newline at end of file
+aa79f83ae88173dddd2d8bc32208d267bdb0e06e
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1
index 2265367..98bbc8b 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-fa2a6fc90c741236ee1c3e1d239d3a4a4fb6aa8c
\ No newline at end of file
+2971b968bd37726d7a7b47d196ad672e87b4254c
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
index 751f790d..ba6cff35 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-e7b6e5963970afbf8266e6490ce6e9df4d4e718b
\ No newline at end of file
+a4214754898c718d6128b859c07895d0af2f6e24
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1
index 5809b26..e3790816 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-8c2470ab343dd081ee28eb7196da7b85a6b2996e
\ No newline at end of file
+d2e78755f2f8f398c56ebd2749adbb71f69d6b9a
\ No newline at end of file
diff --git a/ios/web/init/web_main_loop.mm b/ios/web/init/web_main_loop.mm
index 68139e4..bcb8687 100644
--- a/ios/web/init/web_main_loop.mm
+++ b/ios/web/init/web_main_loop.mm
@@ -212,7 +212,10 @@
 }
 
 void WebMainLoop::InitializeMainThread() {
-  base::PlatformThread::SetName("CrWebMain");
+  const std::string name = web::GetWebClient()->GetMainThreadName();
+  if (!name.empty()) {
+    base::PlatformThread::SetName(name);
+  }
 
   // Register the main thread by instantiating it, but don't call any methods.
   DCHECK(base::SingleThreadTaskRunner::HasCurrentDefault());
diff --git a/ios/web/public/web_client.h b/ios/web/public/web_client.h
index 4b82c7bf..27bb22e 100644
--- a/ios/web/public/web_client.h
+++ b/ios/web/public/web_client.h
@@ -94,6 +94,11 @@
   // Returns the user agent string for the specified type.
   virtual std::string GetUserAgent(UserAgentType type) const;
 
+  // Returns the name of the main thread. If the returned string is empty,
+  // the main thread name will not be set. The default implementation returns
+  // an empty string and does not rename the main thread.
+  virtual std::string GetMainThreadName() const;
+
   // Returns a string resource given its id.
   virtual std::u16string GetLocalizedString(int message_id) const;
 
diff --git a/ios/web/web_client.mm b/ios/web/web_client.mm
index 3dfb34e..fb2a8e7 100644
--- a/ios/web/web_client.mm
+++ b/ios/web/web_client.mm
@@ -47,6 +47,10 @@
   return std::string();
 }
 
+std::string WebClient::GetMainThreadName() const {
+  return std::string();
+}
+
 std::u16string WebClient::GetLocalizedString(int message_id) const {
   return std::u16string();
 }
diff --git a/media/base/demuxer.h b/media/base/demuxer.h
index 0c3f0b03..6d45124 100644
--- a/media/base/demuxer.h
+++ b/media/base/demuxer.h
@@ -173,15 +173,17 @@
   // all tracks should be disabled. |change_completed_cb| is fired after the
   // demuxer streams are disabled, however this callback should then notify
   // the appropriate renderer in order for tracks to be switched fully.
-  virtual void OnEnabledAudioTracksChanged(
-      const std::vector<MediaTrack::Id>& track_ids,
-      base::TimeDelta curr_time,
-      TrackChangeCB change_completed_cb) = 0;
 
-  virtual void OnSelectedVideoTrackChanged(
-      const std::vector<MediaTrack::Id>& track_ids,
-      base::TimeDelta curr_time,
-      TrackChangeCB change_completed_cb) = 0;
+  // TODO(crbug.com/41393620): No more than one video track may be enabled at
+  // once, per the VideoTrack w3c spec. Due to our renderer implementation, only
+  // one audio track is supported, but this restriction isn't necessarily a
+  // permanent one. We should either decide to always stick with one audio
+  // track and switch `track_ids` to an std::optional container, or to support
+  // multiple audio tracks.
+  virtual void OnTracksChanged(DemuxerStream::Type track_type,
+                               const std::vector<MediaTrack::Id>& track_ids,
+                               base::TimeDelta curr_time,
+                               TrackChangeCB change_completed_cb) = 0;
 
   // Allows a demuxer to change behavior based on the playback rate, including
   // but not limited to changing the amount of buffer space.
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index 358bd89..52e5c568 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -1691,12 +1691,6 @@
              "RenderMutedAudio",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-// Serves as killswitch for rolling out Mappable SharedImage mojom support.
-// TODO(crbug.com/40263579): Eliminate post safe rollout.
-BASE_FEATURE(kSupportMappableSharedImageOverMojo,
-             "SupportMappableSharedImageOverMojo",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 // Controls headless Live Caption experiment, which is likely unstable.
 BASE_FEATURE(kHeadlessLiveCaption,
              "HeadlessLiveCaption",
diff --git a/media/base/media_switches.h b/media/base/media_switches.h
index 628fd50..ce8e748 100644
--- a/media/base/media_switches.h
+++ b/media/base/media_switches.h
@@ -533,10 +533,6 @@
 
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kRenderMutedAudio);
 
-// Serves as killswitch for rolling out Mappable SharedImage mojom support.
-// TODO(crbug.com/40263579): Eliminate post safe rollout.
-MEDIA_EXPORT BASE_DECLARE_FEATURE(kSupportMappableSharedImageOverMojo);
-
 // Enable experimental headless captions.
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kHeadlessLiveCaption);
 
diff --git a/media/base/mock_filters.h b/media/base/mock_filters.h
index 50636b4..dd99ed3 100644
--- a/media/base/mock_filters.h
+++ b/media/base/mock_filters.h
@@ -193,14 +193,9 @@
               (),
               (const, override));
   MOCK_METHOD(void,
-              OnEnabledAudioTracksChanged,
-              (const std::vector<MediaTrack::Id>&,
-               base::TimeDelta,
-               TrackChangeCB),
-              (override));
-  MOCK_METHOD(void,
-              OnSelectedVideoTrackChanged,
-              (const std::vector<MediaTrack::Id>&,
+              OnTracksChanged,
+              (DemuxerStream::Type,
+               const std::vector<MediaTrack::Id>&,
                base::TimeDelta,
                TrackChangeCB),
               (override));
diff --git a/media/base/pipeline_impl.cc b/media/base/pipeline_impl.cc
index 12590103..00c415f 100644
--- a/media/base/pipeline_impl.cc
+++ b/media/base/pipeline_impl.cc
@@ -101,16 +101,10 @@
   PipelineStatistics GetStatistics() const;
   void SetCdm(CdmContext* cdm_context, CdmAttachedCB cdm_attached_cb);
 
-  // |enabled_track_ids| contains track ids of enabled audio tracks.
-  void OnEnabledAudioTracksChanged(
-      const std::vector<MediaTrack::Id>& enabled_track_ids,
-      base::OnceClosure change_completed_cb);
-
-  // |selected_track_id| is either empty, which means no video track is
-  // selected, or contains the selected video track id.
-  void OnSelectedVideoTrackChanged(
-      std::optional<MediaTrack::Id> selected_track_id,
-      base::OnceClosure change_completed_cb);
+  // Handles asynchronous track changing for the demuxer and renderer.
+  void OnTracksChanged(DemuxerStream::Type track_type,
+                       std::vector<MediaTrack::Id> enabled_track_ids,
+                       base::OnceClosure change_completed_cb);
 
   void OnExternalVideoFrameRequest();
 
@@ -770,13 +764,32 @@
   media_task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(
-          &RendererWrapper::OnEnabledAudioTracksChanged,
-          base::Unretained(renderer_wrapper_.get()), enabled_track_ids,
+          &RendererWrapper::OnTracksChanged,
+          base::Unretained(renderer_wrapper_.get()), DemuxerStream::AUDIO,
+          std::move(enabled_track_ids),
           base::BindPostTaskToCurrentDefault(std::move(change_completed_cb))));
 }
 
-void PipelineImpl::RendererWrapper::OnEnabledAudioTracksChanged(
-    const std::vector<MediaTrack::Id>& enabled_track_ids,
+void PipelineImpl::OnSelectedVideoTrackChanged(
+    std::optional<MediaTrack::Id> selected_track_id,
+    base::OnceClosure change_completed_cb) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  std::vector<MediaTrack::Id> tracks;
+  if (selected_track_id) {
+    tracks.push_back(*selected_track_id);
+  }
+
+  media_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&RendererWrapper::OnTracksChanged,
+                                base::Unretained(renderer_wrapper_.get()),
+                                DemuxerStream::VIDEO, std::move(tracks),
+                                base::BindPostTaskToCurrentDefault(
+                                    std::move(change_completed_cb))));
+}
+
+void PipelineImpl::RendererWrapper::OnTracksChanged(
+    DemuxerStream::Type track_type,
+    std::vector<MediaTrack::Id> enabled_track_ids,
     base::OnceClosure change_completed_cb) {
   DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
 
@@ -797,74 +810,14 @@
     std::move(change_completed_cb).Run();
     return;
   }
-  demuxer_->OnEnabledAudioTracksChanged(
-      enabled_track_ids, GetCurrentTimestamp(),
+
+  demuxer_->OnTracksChanged(
+      track_type, std::move(enabled_track_ids), GetCurrentTimestamp(),
       base::BindOnce(&RendererWrapper::OnDemuxerCompletedTrackChange,
-                     weak_factory_.GetWeakPtr(), DemuxerStream::AUDIO,
+                     weak_factory_.GetWeakPtr(), track_type,
                      std::move(change_completed_cb)));
 }
 
-void PipelineImpl::OnSelectedVideoTrackChanged(
-    std::optional<MediaTrack::Id> selected_track_id,
-    base::OnceClosure change_completed_cb) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  media_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(
-          &RendererWrapper::OnSelectedVideoTrackChanged,
-          base::Unretained(renderer_wrapper_.get()), selected_track_id,
-          base::BindPostTaskToCurrentDefault(std::move(change_completed_cb))));
-}
-
-void PipelineImpl::RendererWrapper::OnSelectedVideoTrackChanged(
-    std::optional<MediaTrack::Id> selected_track_id,
-    base::OnceClosure change_completed_cb) {
-  DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
-
-  // See RenderWrapper::OnEnabledAudioTracksChanged.
-  if (state_ == State::kCreated) {
-    DCHECK(!demuxer_);
-    std::move(change_completed_cb).Run();
-    return;
-  }
-
-  if (state_ == State::kStopping || state_ == State::kStopped) {
-    std::move(change_completed_cb).Run();
-    return;
-  }
-
-  std::vector<MediaTrack::Id> tracks;
-  if (selected_track_id)
-    tracks.push_back(*selected_track_id);
-
-  demuxer_->OnSelectedVideoTrackChanged(
-      tracks, GetCurrentTimestamp(),
-      base::BindOnce(&RendererWrapper::OnDemuxerCompletedTrackChange,
-                     weak_factory_.GetWeakPtr(), DemuxerStream::VIDEO,
-                     std::move(change_completed_cb)));
-}
-
-void PipelineImpl::OnExternalVideoFrameRequest() {
-  // This function is currently a no-op unless we're on a Windows build with
-  // Media Foundation for Clear running.
-  DCHECK(thread_checker_.CalledOnValidThread());
-  if (!external_video_frame_request_signaled_) {
-    external_video_frame_request_signaled_ = true;
-    media_task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(&RendererWrapper::OnExternalVideoFrameRequest,
-                                  base::Unretained(renderer_wrapper_.get())));
-  }
-}
-
-void PipelineImpl::RendererWrapper::OnExternalVideoFrameRequest() {
-  DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
-  if (!shared_state_.renderer) {
-    return;
-  }
-
-  shared_state_.renderer->OnExternalVideoFrameRequest();
-}
-
 void PipelineImpl::RendererWrapper::OnDemuxerCompletedTrackChange(
     DemuxerStream::Type stream_type,
     base::OnceClosure change_completed_cb,
@@ -890,6 +843,27 @@
   }
 }
 
+void PipelineImpl::OnExternalVideoFrameRequest() {
+  // This function is currently a no-op unless we're on a Windows build with
+  // Media Foundation for Clear running.
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (!external_video_frame_request_signaled_) {
+    external_video_frame_request_signaled_ = true;
+    media_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&RendererWrapper::OnExternalVideoFrameRequest,
+                                  base::Unretained(renderer_wrapper_.get())));
+  }
+}
+
+void PipelineImpl::RendererWrapper::OnExternalVideoFrameRequest() {
+  DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
+  if (!shared_state_.renderer) {
+    return;
+  }
+
+  shared_state_.renderer->OnExternalVideoFrameRequest();
+}
+
 void PipelineImpl::RendererWrapper::OnStatisticsUpdate(
     const PipelineStatistics& stats) {
   DVLOG(3) << __func__;
diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc
index c63d1b7..d9fcbe78 100644
--- a/media/filters/chunk_demuxer.cc
+++ b/media/filters/chunk_demuxer.cc
@@ -875,11 +875,10 @@
   return itr->second->GetHighestPresentationTimestamp();
 }
 
-void ChunkDemuxer::FindAndEnableProperTracks(
-    const std::vector<MediaTrack::Id>& track_ids,
-    base::TimeDelta curr_time,
-    DemuxerStream::Type track_type,
-    TrackChangeCB change_completed_cb) {
+void ChunkDemuxer::OnTracksChanged(DemuxerStream::Type track_type,
+                                   const std::vector<MediaTrack::Id>& track_ids,
+                                   base::TimeDelta curr_time,
+                                   TrackChangeCB change_completed_cb) {
   base::AutoLock auto_lock(lock_);
 
   std::set<ChunkDemuxerStream*> enabled_streams;
@@ -914,22 +913,6 @@
   std::move(change_completed_cb).Run(streams);
 }
 
-void ChunkDemuxer::OnEnabledAudioTracksChanged(
-    const std::vector<MediaTrack::Id>& track_ids,
-    base::TimeDelta curr_time,
-    TrackChangeCB change_completed_cb) {
-  FindAndEnableProperTracks(track_ids, curr_time, DemuxerStream::AUDIO,
-                            std::move(change_completed_cb));
-}
-
-void ChunkDemuxer::OnSelectedVideoTrackChanged(
-    const std::vector<MediaTrack::Id>& track_ids,
-    base::TimeDelta curr_time,
-    TrackChangeCB change_completed_cb) {
-  FindAndEnableProperTracks(track_ids, curr_time, DemuxerStream::VIDEO,
-                            std::move(change_completed_cb));
-}
-
 void ChunkDemuxer::DisableCanChangeType() {
   supports_change_type_ = false;
 }
diff --git a/media/filters/chunk_demuxer.h b/media/filters/chunk_demuxer.h
index 1c78069..1143a31 100644
--- a/media/filters/chunk_demuxer.h
+++ b/media/filters/chunk_demuxer.h
@@ -306,13 +306,10 @@
   // buffered, returns base::TimeDelta().
   base::TimeDelta GetHighestPresentationTimestamp(const std::string& id) const;
 
-  void OnEnabledAudioTracksChanged(const std::vector<MediaTrack::Id>& track_ids,
-                                   base::TimeDelta curr_time,
-                                   TrackChangeCB change_completed_cb) override;
-
-  void OnSelectedVideoTrackChanged(const std::vector<MediaTrack::Id>& track_ids,
-                                   base::TimeDelta curr_time,
-                                   TrackChangeCB change_completed_cb) override;
+  void OnTracksChanged(DemuxerStream::Type track_type,
+                       const std::vector<MediaTrack::Id>& track_ids,
+                       base::TimeDelta curr_time,
+                       TrackChangeCB change_completed_cb) override;
 
   void SetPlaybackRate(double rate) override {}
 
@@ -478,14 +475,6 @@
       std::unique_ptr<media::StreamParser> stream_parser,
       std::optional<std::string_view> expected_codecs);
 
-  // Helper for video and audio track changing. For the `track_type`, enables
-  // tracks associated with `track_ids` and disables the rest. Fires
-  // `change_completed_cb` when the operation is completed.
-  void FindAndEnableProperTracks(const std::vector<MediaTrack::Id>& track_ids,
-                                 base::TimeDelta curr_time,
-                                 DemuxerStream::Type track_type,
-                                 TrackChangeCB change_completed_cb);
-
   void ChangeState_Locked(State new_state);
 
   // Reports an error and puts the demuxer in a state where it won't accept more
diff --git a/media/filters/chunk_demuxer_unittest.cc b/media/filters/chunk_demuxer_unittest.cc
index bfde410..58676b6 100644
--- a/media/filters/chunk_demuxer_unittest.cc
+++ b/media/filters/chunk_demuxer_unittest.cc
@@ -4613,28 +4613,28 @@
   std::vector<MediaTrack::Id> video_tracks;
 
   base::RunLoop disable_video;
-  demuxer->OnSelectedVideoTrackChanged(
-      video_tracks, base::TimeDelta(),
+  demuxer->OnTracksChanged(
+      DemuxerStream::VIDEO, video_tracks, base::TimeDelta(),
       base::BindOnce(QuitLoop, disable_video.QuitClosure()));
   disable_video.Run();
 
   base::RunLoop disable_audio;
-  demuxer->OnEnabledAudioTracksChanged(
-      audio_tracks, base::TimeDelta(),
+  demuxer->OnTracksChanged(
+      DemuxerStream::AUDIO, audio_tracks, base::TimeDelta(),
       base::BindOnce(QuitLoop, disable_audio.QuitClosure()));
   disable_audio.Run();
 
   base::RunLoop enable_video;
   video_tracks.push_back(MediaTrack::Id("1"));
-  demuxer->OnSelectedVideoTrackChanged(
-      video_tracks, base::TimeDelta(),
+  demuxer->OnTracksChanged(
+      DemuxerStream::VIDEO, video_tracks, base::TimeDelta(),
       base::BindOnce(QuitLoop, enable_video.QuitClosure()));
   enable_video.Run();
 
   base::RunLoop enable_audio;
   audio_tracks.push_back(MediaTrack::Id("2"));
-  demuxer->OnEnabledAudioTracksChanged(
-      audio_tracks, base::TimeDelta(),
+  demuxer->OnTracksChanged(
+      DemuxerStream::AUDIO, audio_tracks, base::TimeDelta(),
       base::BindOnce(QuitLoop, enable_audio.QuitClosure()));
   enable_audio.Run();
 
diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc
index ff199ef..8cb1707 100644
--- a/media/filters/ffmpeg_demuxer.cc
+++ b/media/filters/ffmpeg_demuxer.cc
@@ -1782,10 +1782,22 @@
   RunPendingSeekCB(PIPELINE_OK);
 }
 
-void FFmpegDemuxer::FindAndEnableProperTracks(
+void FFmpegDemuxer::OnTrackChangeSeekComplete(
+    base::OnceClosure cb,
+    std::vector<FFmpegDemuxerStream*> needs_flush,
+    int seek_status) {
+  for (const auto& stream : needs_flush) {
+    CHECK(stream->IsEnabled());
+    stream->FlushBuffers(true);
+  }
+  // TODO(crbug.com/41393620): Report seek failures for track changes too.
+  std::move(cb).Run();
+}
+
+void FFmpegDemuxer::OnTracksChanged(
+    DemuxerStream::Type track_type,
     const std::vector<MediaTrack::Id>& track_ids,
     base::TimeDelta curr_time,
-    DemuxerStream::Type track_type,
     TrackChangeCB change_completed_cb) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
 
@@ -1841,34 +1853,6 @@
   }
 }
 
-void FFmpegDemuxer::OnTrackChangeSeekComplete(
-    base::OnceClosure cb,
-    std::vector<FFmpegDemuxerStream*> needs_flush,
-    int seek_status) {
-  for (const auto& stream : needs_flush) {
-    CHECK(stream->IsEnabled());
-    stream->FlushBuffers(true);
-  }
-  // TODO(crbug.com/40898124): Report seek failures for track changes too.
-  std::move(cb).Run();
-}
-
-void FFmpegDemuxer::OnEnabledAudioTracksChanged(
-    const std::vector<MediaTrack::Id>& track_ids,
-    base::TimeDelta curr_time,
-    TrackChangeCB change_completed_cb) {
-  FindAndEnableProperTracks(track_ids, curr_time, DemuxerStream::AUDIO,
-                            std::move(change_completed_cb));
-}
-
-void FFmpegDemuxer::OnSelectedVideoTrackChanged(
-    const std::vector<MediaTrack::Id>& track_ids,
-    base::TimeDelta curr_time,
-    TrackChangeCB change_completed_cb) {
-  FindAndEnableProperTracks(track_ids, curr_time, DemuxerStream::VIDEO,
-                            std::move(change_completed_cb));
-}
-
 void FFmpegDemuxer::ReadFrameIfNeeded() {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
 
diff --git a/media/filters/ffmpeg_demuxer.h b/media/filters/ffmpeg_demuxer.h
index a7aa7028..e096166c 100644
--- a/media/filters/ffmpeg_demuxer.h
+++ b/media/filters/ffmpeg_demuxer.h
@@ -250,13 +250,10 @@
   // Allow FFmpegDemxuerStream to notify us about an error.
   void NotifyDemuxerError(PipelineStatus error);
 
-  void OnEnabledAudioTracksChanged(const std::vector<MediaTrack::Id>& track_ids,
-                                   base::TimeDelta curr_time,
-                                   TrackChangeCB change_completed_cb) override;
-
-  void OnSelectedVideoTrackChanged(const std::vector<MediaTrack::Id>& track_ids,
-                                   base::TimeDelta curr_time,
-                                   TrackChangeCB change_completed_cb) override;
+  void OnTracksChanged(DemuxerStream::Type track_type,
+                       const std::vector<MediaTrack::Id>& track_ids,
+                       base::TimeDelta curr_time,
+                       TrackChangeCB change_completed_cb) override;
   void SetPlaybackRate(double rate) override {}
 
   // The lowest demuxed timestamp.  If negative, DemuxerStreams must use this to
@@ -278,14 +275,6 @@
   // To allow tests access to privates.
   friend class FFmpegDemuxerTest;
 
-  // Helper for video and audio track changing. For the `track_type`, enables
-  // tracks associated with `track_ids` and disables the rest. Fires
-  // `change_completed_cb` when the operation is completed.
-  void FindAndEnableProperTracks(const std::vector<MediaTrack::Id>& track_ids,
-                                 base::TimeDelta curr_time,
-                                 DemuxerStream::Type track_type,
-                                 TrackChangeCB change_completed_cb);
-
   // FFmpeg callbacks during initialization.
   void OnOpenContextDone(bool result);
   void OnFindStreamInfoDone(int result);
diff --git a/media/filters/ffmpeg_demuxer_unittest.cc b/media/filters/ffmpeg_demuxer_unittest.cc
index fba6aead..a122d68 100644
--- a/media/filters/ffmpeg_demuxer_unittest.cc
+++ b/media/filters/ffmpeg_demuxer_unittest.cc
@@ -1769,28 +1769,28 @@
   std::vector<MediaTrack::Id> video_tracks;
 
   base::RunLoop disable_video;
-  demuxer->OnSelectedVideoTrackChanged(
-      video_tracks, base::TimeDelta(),
+  demuxer->OnTracksChanged(
+      DemuxerStream::VIDEO, video_tracks, base::TimeDelta(),
       base::BindOnce(QuitLoop, disable_video.QuitClosure()));
   disable_video.Run();
 
   base::RunLoop disable_audio;
-  demuxer->OnEnabledAudioTracksChanged(
-      audio_tracks, base::TimeDelta(),
+  demuxer->OnTracksChanged(
+      DemuxerStream::AUDIO, audio_tracks, base::TimeDelta(),
       base::BindOnce(QuitLoop, disable_audio.QuitClosure()));
   disable_audio.Run();
 
   base::RunLoop enable_video;
   video_tracks.push_back(MediaTrack::Id("1"));
-  demuxer->OnSelectedVideoTrackChanged(
-      video_tracks, base::TimeDelta(),
+  demuxer->OnTracksChanged(
+      DemuxerStream::VIDEO, video_tracks, base::TimeDelta(),
       base::BindOnce(QuitLoop, enable_video.QuitClosure()));
   enable_video.Run();
 
   base::RunLoop enable_audio;
   audio_tracks.push_back(MediaTrack::Id("2"));
-  demuxer->OnEnabledAudioTracksChanged(
-      audio_tracks, base::TimeDelta(),
+  demuxer->OnTracksChanged(
+      DemuxerStream::AUDIO, audio_tracks, base::TimeDelta(),
       base::BindOnce(QuitLoop, enable_audio.QuitClosure()));
   enable_audio.Run();
 
diff --git a/media/filters/manifest_demuxer.cc b/media/filters/manifest_demuxer.cc
index 6535467..ef7f191 100644
--- a/media/filters/manifest_demuxer.cc
+++ b/media/filters/manifest_demuxer.cc
@@ -294,44 +294,27 @@
   std::move(change_completed_cb).Run(mapped_streams);
 }
 
-void ManifestDemuxer::OnEnabledAudioTracksChanged(
+void ManifestDemuxer::OnTracksChanged(
+    DemuxerStream::Type track_type,
     const std::vector<MediaTrack::Id>& track_ids,
     base::TimeDelta curr_time,
     TrackChangeCB change_completed_cb) {
   DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
-
   std::optional<MediaTrack::Id> selected_track = std::nullopt;
   std::vector<MediaTrack::Id> chunk_demuxer_tracks = track_ids;
   if (!track_ids.empty()) {
     selected_track = track_ids[0];
-    chunk_demuxer_tracks = {*internal_audio_track_id_};
+    if (track_type == DemuxerStream::AUDIO) {
+      chunk_demuxer_tracks = {*internal_audio_track_id_};
+    } else if (track_type == DemuxerStream::VIDEO) {
+      chunk_demuxer_tracks = {*internal_video_track_id_};
+    }
   }
 
-  chunk_demuxer_->OnEnabledAudioTracksChanged(
-      std::move(chunk_demuxer_tracks), curr_time,
+  chunk_demuxer_->OnTracksChanged(
+      track_type, std::move(chunk_demuxer_tracks), curr_time,
       base::BindOnce(&ManifestDemuxer::OnChunkDemuxerTracksChangeComplete,
-                     weak_factory_.GetWeakPtr(), DemuxerStream::AUDIO,
-                     std::move(selected_track),
-                     std::move(change_completed_cb)));
-}
-
-void ManifestDemuxer::OnSelectedVideoTrackChanged(
-    const std::vector<MediaTrack::Id>& track_ids,
-    base::TimeDelta curr_time,
-    TrackChangeCB change_completed_cb) {
-  DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
-
-  std::optional<MediaTrack::Id> selected_track = std::nullopt;
-  std::vector<MediaTrack::Id> chunk_demuxer_tracks = track_ids;
-  if (!track_ids.empty()) {
-    selected_track = track_ids[0];
-    chunk_demuxer_tracks = {*internal_video_track_id_};
-  }
-
-  chunk_demuxer_->OnSelectedVideoTrackChanged(
-      std::move(chunk_demuxer_tracks), curr_time,
-      base::BindOnce(&ManifestDemuxer::OnChunkDemuxerTracksChangeComplete,
-                     weak_factory_.GetWeakPtr(), DemuxerStream::VIDEO,
+                     weak_factory_.GetWeakPtr(), track_type,
                      std::move(selected_track),
                      std::move(change_completed_cb)));
 }
diff --git a/media/filters/manifest_demuxer.h b/media/filters/manifest_demuxer.h
index 532aa4e..61028f8 100644
--- a/media/filters/manifest_demuxer.h
+++ b/media/filters/manifest_demuxer.h
@@ -201,12 +201,10 @@
   std::optional<container_names::MediaContainerName> GetContainerForMetrics()
       const override;
 
-  void OnEnabledAudioTracksChanged(const std::vector<MediaTrack::Id>& track_ids,
-                                   base::TimeDelta curr_time,
-                                   TrackChangeCB change_completed_cb) override;
-  void OnSelectedVideoTrackChanged(const std::vector<MediaTrack::Id>& track_ids,
-                                   base::TimeDelta curr_time,
-                                   TrackChangeCB change_completed_cb) override;
+  void OnTracksChanged(DemuxerStream::Type track_type,
+                       const std::vector<MediaTrack::Id>& track_ids,
+                       base::TimeDelta curr_time,
+                       TrackChangeCB change_completed_cb) override;
 
   // `ManifestDemuxerEngineHost` implementation
   bool AddRole(std::string_view role, RelaxedParserSupportedType mime) override;
diff --git a/media/filters/manifest_demuxer_unittest.cc b/media/filters/manifest_demuxer_unittest.cc
index fd7c174..7bcec42 100644
--- a/media/filters/manifest_demuxer_unittest.cc
+++ b/media/filters/manifest_demuxer_unittest.cc
@@ -377,8 +377,8 @@
 
   // Disable video track:
   bool was_called = false;
-  manifest_demuxer_->OnSelectedVideoTrackChanged(
-      {}, base::Seconds(0),
+  manifest_demuxer_->OnTracksChanged(
+      DemuxerStream::VIDEO, {}, base::Seconds(0),
       base::BindOnce(
           [](bool* was_called, const std::vector<DemuxerStream*>& streams) {
             ASSERT_TRUE(streams.empty());
@@ -390,8 +390,8 @@
 
   // Enable video track:
   was_called = false;
-  manifest_demuxer_->OnSelectedVideoTrackChanged(
-      {MediaTrack::Id("video")}, base::Seconds(0),
+  manifest_demuxer_->OnTracksChanged(
+      DemuxerStream::VIDEO, {MediaTrack::Id("video")}, base::Seconds(0),
       base::BindOnce(
           [](bool* was_called, const std::vector<DemuxerStream*>& streams) {
             ASSERT_EQ(streams.size(), 1u);
@@ -403,8 +403,8 @@
 
   // Disable audio track:
   was_called = false;
-  manifest_demuxer_->OnEnabledAudioTracksChanged(
-      {}, base::Seconds(0),
+  manifest_demuxer_->OnTracksChanged(
+      DemuxerStream::AUDIO, {}, base::Seconds(0),
       base::BindOnce(
           [](bool* was_called, const std::vector<DemuxerStream*>& streams) {
             ASSERT_TRUE(streams.empty());
@@ -416,8 +416,8 @@
 
   // Enable audio track:
   was_called = false;
-  manifest_demuxer_->OnEnabledAudioTracksChanged(
-      {MediaTrack::Id("audio")}, base::Seconds(0),
+  manifest_demuxer_->OnTracksChanged(
+      DemuxerStream::AUDIO, {MediaTrack::Id("audio")}, base::Seconds(0),
       base::BindOnce(
           [](bool* was_called, const std::vector<DemuxerStream*>& streams) {
             ASSERT_EQ(streams.size(), 1u);
diff --git a/media/gpu/windows/supported_profile_helpers.cc b/media/gpu/windows/supported_profile_helpers.cc
index 498b7cf..1814b07 100644
--- a/media/gpu/windows/supported_profile_helpers.cc
+++ b/media/gpu/windows/supported_profile_helpers.cc
@@ -332,8 +332,12 @@
         continue;
       }
       if (profile_id == DXVA_ModeAV1_VLD_Profile1) {
-        supported_resolutions[AV1PROFILE_PROFILE_HIGH] = GetResolutionsForGUID(
-            video_device_wrapper, profile_id, kModernResolutions);
+        // DXVA spec for high profile (section 7.2) does not include NV12 as
+        // mandatory format, here we only test 8b-444 (AYUV) and skip check of
+        // Y410.
+        supported_resolutions[AV1PROFILE_PROFILE_HIGH] =
+            GetResolutionsForGUID(video_device_wrapper, profile_id,
+                                  kModernResolutions, DXGI_FORMAT_AYUV);
         continue;
       }
       if (profile_id == DXVA_ModeAV1_VLD_Profile2) {
diff --git a/media/mojo/mojom/video_frame_mojom_traits.cc b/media/mojo/mojom/video_frame_mojom_traits.cc
index 2f978f8..ab90df6 100644
--- a/media/mojo/mojom/video_frame_mojom_traits.cc
+++ b/media/mojo/mojom/video_frame_mojom_traits.cc
@@ -7,7 +7,6 @@
 #include <utility>
 #include <vector>
 
-#include "base/feature_list.h"
 #include "base/functional/callback_helpers.h"
 #include "base/logging.h"
 #include "base/memory/unsafe_shared_memory_region.h"
@@ -16,7 +15,6 @@
 #include "gpu/ipc/common/gpu_memory_buffer_support.h"
 #include "media/base/color_plane_layout.h"
 #include "media/base/format_utils.h"
-#include "media/base/media_switches.h"
 #include "media/mojo/mojom/video_frame_metadata_mojom_traits.h"
 #include "mojo/public/cpp/base/time_mojom_traits.h"
 #include "mojo/public/cpp/system/handle.h"
@@ -33,12 +31,6 @@
 
 namespace {
 
-// Determines whether Mappable SharedImage over mojo is supported.
-bool SupportMappableSI() {
-  return base::FeatureList::IsEnabled(
-      media::kSupportMappableSharedImageOverMojo);
-}
-
 base::ReadOnlySharedMemoryRegion CreateRegion(const media::VideoFrame& frame,
                                               std::vector<uint32_t>& offsets,
                                               std::vector<int32_t>& strides) {
@@ -132,15 +124,16 @@
     std::optional<gpu::ExportedSharedImage> shared_image;
     gpu::SyncToken sync_token;
     if (input->HasSharedImage()) {
-      bool with_buffer_handle = is_mappable_si_enabled && SupportMappableSI();
-      shared_image = input->shared_image()->Export(with_buffer_handle);
+      shared_image = input->shared_image()->Export(
+          /*with_buffer_handle=*/is_mappable_si_enabled);
       sync_token = input->acquire_sync_token();
 
-      if (with_buffer_handle) {
+      if (is_mappable_si_enabled) {
         return media::mojom::VideoFrameData::NewSharedImageData(
             media::mojom::SharedImageVideoFrameData::New(
                 std::move(shared_image.value()), std::move(sync_token),
-                is_mappable_si_enabled, std::move(input->ycbcr_info())));
+                /*is_mappable_si_enabled=*/true,
+                std::move(input->ycbcr_info())));
       }
     }
 
@@ -411,7 +404,7 @@
     }
 
     bool is_mappable_si_enabled = shared_image_data.is_mappable_si_enabled();
-    if (is_mappable_si_enabled && SupportMappableSI()) {
+    if (is_mappable_si_enabled) {
       // VideoFrame should have buffer usage if Mappable SharedImage is enabled.
       // NOTE: This isn't exactly correct for software SharedImages can be
       // mappable but do not have buffer usage. But since, such software
diff --git a/media/remoting/stream_provider.cc b/media/remoting/stream_provider.cc
index 3f8cb023..22cade65 100644
--- a/media/remoting/stream_provider.cc
+++ b/media/remoting/stream_provider.cc
@@ -504,7 +504,8 @@
   return std::optional<container_names::MediaContainerName>();
 }
 
-void StreamProvider::OnEnabledAudioTracksChanged(
+void StreamProvider::OnTracksChanged(
+    DemuxerStream::Type track_type,
     const std::vector<MediaTrack::Id>& track_ids,
     base::TimeDelta curr_time,
     TrackChangeCB change_completed_cb) {
@@ -513,15 +514,6 @@
   DVLOG(1) << "Track changes are not supported.";
 }
 
-void StreamProvider::OnSelectedVideoTrackChanged(
-    const std::vector<media::MediaTrack::Id>& track_ids,
-    base::TimeDelta curr_time,
-    TrackChangeCB change_completed_cb) {
-  std::vector<DemuxerStream*> streams;
-  std::move(change_completed_cb).Run(streams);
-  DVLOG(1) << "Track changes are not supported.";
-}
-
 void StreamProvider::Destroy() {
   DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
 
diff --git a/media/remoting/stream_provider.h b/media/remoting/stream_provider.h
index 6bf9c8c..21ee763 100644
--- a/media/remoting/stream_provider.h
+++ b/media/remoting/stream_provider.h
@@ -63,12 +63,10 @@
   int64_t GetMemoryUsage() const override;
   std::optional<container_names::MediaContainerName> GetContainerForMetrics()
       const override;
-  void OnEnabledAudioTracksChanged(const std::vector<MediaTrack::Id>& track_ids,
-                                   base::TimeDelta curr_time,
-                                   TrackChangeCB change_completed_cb) override;
-  void OnSelectedVideoTrackChanged(const std::vector<MediaTrack::Id>& track_ids,
-                                   base::TimeDelta curr_time,
-                                   TrackChangeCB change_completed_cb) override;
+  void OnTracksChanged(DemuxerStream::Type track_type,
+                       const std::vector<MediaTrack::Id>& track_ids,
+                       base::TimeDelta curr_time,
+                       TrackChangeCB change_completed_cb) override;
   void SetPlaybackRate(double rate) override {}
 
  protected:
diff --git a/net/dns/host_resolver_internal_result.cc b/net/dns/host_resolver_internal_result.cc
index b8fbaee..afaf662 100644
--- a/net/dns/host_resolver_internal_result.cc
+++ b/net/dns/host_resolver_internal_result.cc
@@ -423,7 +423,7 @@
 
   base::Value::List endpoints_list;
   endpoints_list.reserve(endpoints_.size());
-  for (IPEndPoint endpoint : endpoints_) {
+  for (const IPEndPoint& endpoint : endpoints_) {
     endpoints_list.Append(endpoint.ToValue());
   }
   dict.Set(kValueEndpointsKey, std::move(endpoints_list));
diff --git a/net/network_error_logging/network_error_logging_service.cc b/net/network_error_logging/network_error_logging_service.cc
index 6fb48e56..8f483af 100644
--- a/net/network_error_logging/network_error_logging_service.cc
+++ b/net/network_error_logging/network_error_logging_service.cc
@@ -574,7 +574,7 @@
     DCHECK(initialized_);
     if (PoliciesArePersisted()) {
       // TODO(chlily): Add a DeleteAllNelPolicies command to PersistentNelStore.
-      for (auto origin_and_policy : policies_) {
+      for (const auto& origin_and_policy : policies_) {
         store_->DeleteNelPolicy(origin_and_policy.second);
       }
       store_->Flush();
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index b1a654a..b2450ed 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -49,7 +49,7 @@
 #include "base/values.h"
 #include "build/build_config.h"
 #include "build/buildflag.h"
-#include "crypto/sha2.h"
+#include "crypto/hash.h"
 #include "net/base/chunked_upload_data_stream.h"
 #include "net/base/cronet_buildflags.h"
 #include "net/base/directory_listing.h"
@@ -628,19 +628,15 @@
 #if !BUILDFLAG(IS_IOS)
 // Compute the root cert's SPKI hash on the fly, to avoid hardcoding it within
 // tests.
-bool GetTestRootCertSPKIHash(SHA256HashValue* root_hash) {
+std::array<uint8_t, crypto::hash::kSha256Size> GetTestRootCertSPKIHash() {
   scoped_refptr<X509Certificate> root_cert =
       ImportCertFromFile(GetTestCertsDirectory(), "root_ca_cert.pem");
-  if (!root_cert)
-    return false;
+  CHECK(root_cert);
   std::string_view root_spki;
-  if (!asn1::ExtractSPKIFromDERCert(
-          x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer()),
-          &root_spki)) {
-    return false;
-  }
-  crypto::SHA256HashString(root_spki, root_hash, sizeof(SHA256HashValue));
-  return true;
+  CHECK(asn1::ExtractSPKIFromDERCert(
+      x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer()),
+      &root_spki));
+  return crypto::hash::Sha256(root_spki);
 }
 #endif
 
@@ -11511,8 +11507,7 @@
   ASSERT_TRUE(test_server.Start());
 
   CertVerifier::Config cert_verifier_config = GetCertVerifierConfig();
-  SHA256HashValue root_cert_spki_hash;
-  ASSERT_TRUE(GetTestRootCertSPKIHash(&root_cert_spki_hash));
+  auto root_cert_spki_hash = GetTestRootCertSPKIHash();
   auto crl_set =
       CRLSet::ForTesting(false, &root_cert_spki_hash,
                          test_server.GetCertificate()->serial_number(), "", {});
@@ -11731,8 +11726,7 @@
 
   // Configure for kHSTSSubdomainWithKnownInterception
   CertVerifyResult sts_sub_result = fake_result;
-  SHA256HashValue root_hash;
-  ASSERT_TRUE(GetTestRootCertSPKIHash(&root_hash));
+  auto root_hash = GetTestRootCertSPKIHash();
   sts_sub_result.public_key_hashes.push_back(HashValue(root_hash));
   sts_sub_result.cert_status |=
       CERT_STATUS_REVOKED | CERT_STATUS_KNOWN_INTERCEPTION_BLOCKED;
diff --git a/pdf/pdf_ink_metrics_handler.cc b/pdf/pdf_ink_metrics_handler.cc
index a597fc0..75f9b71 100644
--- a/pdf/pdf_ink_metrics_handler.cc
+++ b/pdf/pdf_ink_metrics_handler.cc
@@ -62,7 +62,7 @@
         {SkColorSetRGB(0x19, 0x67, 0xD2), StrokeMetricPenColor::kBlue3},
         {SkColorSetRGB(0x88, 0x59, 0x45), StrokeMetricPenColor::kTan3},
     });
-// LINT.ThenChange(//chrome/browser/resources/pdf/elements/ink_color_selector.ts:PenColors)
+// LINT.ThenChange(//chrome/browser/resources/pdf/elements//ink_annotation_brush_mixin.ts:PenColors)
 
 // LINT.IfChange(HighlighterColors)
 constexpr auto kHighlighterColors =
@@ -85,7 +85,7 @@
         {SkColorSetRGB(0xFF, 0x63, 0x0C),
          StrokeMetricHighlighterColor::kOrange},
     });
-// LINT.ThenChange(//chrome/browser/resources/pdf/elements/ink_color_selector.ts:HighlighterColors)
+// LINT.ThenChange(//chrome/browser/resources/pdf/elements//ink_annotation_brush_mixin.ts:HighlighterColors)
 
 void ReportStrokeTypeAndMaybeSize(StrokeMetricBrushType type,
                                   std::optional<StrokeMetricBrushSize> size) {
diff --git a/pdf/pdf_ink_module.cc b/pdf/pdf_ink_module.cc
index 0ad1fb74..0b80ebcf 100644
--- a/pdf/pdf_ink_module.cc
+++ b/pdf/pdf_ink_module.cc
@@ -165,7 +165,7 @@
 PdfInkModule::~PdfInkModule() = default;
 
 bool PdfInkModule::ShouldBlockTextSelectionChanged() {
-  return is_text_highlighting();
+  return features::kPdfInk2TextHighlighting.Get() && is_text_highlighting();
 }
 
 bool PdfInkModule::HasInputsToDraw() const {
@@ -553,12 +553,16 @@
     return false;
   }
 
-  // TODO(crbug.com/342445982): Handle text selection for touch.
-
   gfx::PointF position = event.touches[0].PositionInWidget();
-  return is_drawing_stroke()
-             ? StartStroke(position, event.TimeStamp(), tool_type)
-             : StartEraseStroke(position, tool_type);
+  if (is_drawing_stroke()) {
+    if (IsHighlightingTextAtPosition(drawing_stroke_state(), position)) {
+      // Multi-click text selection for touch is not supported.
+      return StartTextHighlight(position, /*click_count=*/1, event.TimeStamp());
+    }
+    return StartStroke(position, event.TimeStamp(), tool_type);
+  }
+
+  return StartEraseStroke(position, tool_type);
 }
 
 bool PdfInkModule::OnTouchEnd(const blink::WebTouchEvent& event) {
@@ -575,6 +579,10 @@
   }
 
   gfx::PointF position = event.touches[0].PositionInWidget();
+  if (features::kPdfInk2TextHighlighting.Get() && is_text_highlighting()) {
+    return FinishTextHighlight(position);
+  }
+
   return is_drawing_stroke()
              ? FinishStroke(position, event.TimeStamp(), tool_type)
              : FinishEraseStroke(position, tool_type);
@@ -594,6 +602,10 @@
   }
 
   gfx::PointF position = event.touches[0].PositionInWidget();
+  if (features::kPdfInk2TextHighlighting.Get() && is_text_highlighting()) {
+    return ContinueTextHighlight(position);
+  }
+
   return is_drawing_stroke()
              ? ContinueStroke(position, event.TimeStamp(), tool_type)
              : ContinueEraseStroke(position, tool_type);
diff --git a/pdf/pdf_ink_module_unittest.cc b/pdf/pdf_ink_module_unittest.cc
index 434cdba..418125a 100644
--- a/pdf/pdf_ink_module_unittest.cc
+++ b/pdf/pdf_ink_module_unittest.cc
@@ -146,6 +146,12 @@
         {kTwoPageVerticalLayoutHorzLinePoint1Canonical, base::Seconds(0)},
     });
 
+// Commonly used test brush message params. The color corresponds to "Red1" for
+// pen brushes and "Light Red" for highlighter brushes.
+constexpr TestAnnotationBrushMessageParams kRedBrushParams{
+    /*color_r=*/0xF2, /*color_g=*/0x8B,
+    /*color_b=*/0x82, /*size=*/6.0f};
+
 // Matcher for ink::Stroke objects against their expected brush and inputs.
 MATCHER_P(InkStrokeEq, expected_brush, "") {
   const auto& [actual_stroke, expected_inputs] = arg;
@@ -587,9 +593,10 @@
 TEST_P(PdfInkModuleTest, HandleSetAnnotationBrushMessagePen) {
   EnableAnnotationMode();
 
-  TestAnnotationBrushMessageParams message_params{/*color_r=*/10,
-                                                  /*color_g=*/255,
-                                                  /*color_b=*/50, /*size=*/8.0};
+  TestAnnotationBrushMessageParams message_params{/*color_r=*/0x0A,
+                                                  /*color_g=*/0xFF,
+                                                  /*color_b=*/0x32,
+                                                  /*size=*/8.0f};
   base::Value::Dict message =
       CreateSetAnnotationBrushMessageForTesting("pen", &message_params);
   EXPECT_TRUE(ink_module().OnMessage(message));
@@ -598,7 +605,7 @@
   ASSERT_TRUE(brush);
 
   const ink::Brush& ink_brush = brush->ink_brush();
-  EXPECT_EQ(SkColorSetRGB(10, 255, 50), GetSkColorFromInkBrush(ink_brush));
+  EXPECT_EQ(SkColorSetRGB(0x0A, 0xFF, 0x32), GetSkColorFromInkBrush(ink_brush));
   EXPECT_EQ(8.0f, ink_brush.GetSize());
   ASSERT_EQ(1u, ink_brush.CoatCount());
   const ink::BrushCoat& coat = ink_brush.GetCoats()[0];
@@ -611,9 +618,10 @@
 TEST_P(PdfInkModuleTest, HandleSetAnnotationBrushMessageHighlighter) {
   EnableAnnotationMode();
 
-  TestAnnotationBrushMessageParams message_params{/*color_r=*/240,
-                                                  /*color_g=*/133,
-                                                  /*color_b=*/0, /*size=*/4.5};
+  TestAnnotationBrushMessageParams message_params{/*color_r=*/0xF0,
+                                                  /*color_g=*/0x85,
+                                                  /*color_b=*/0x00,
+                                                  /*size=*/4.5f};
   base::Value::Dict message =
       CreateSetAnnotationBrushMessageForTesting("highlighter", &message_params);
   EXPECT_TRUE(ink_module().OnMessage(message));
@@ -622,7 +630,7 @@
   ASSERT_TRUE(brush);
 
   const ink::Brush& ink_brush = brush->ink_brush();
-  EXPECT_EQ(SkColorSetRGB(240, 133, 0), GetSkColorFromInkBrush(ink_brush));
+  EXPECT_EQ(SkColorSetRGB(0xF0, 0x85, 0x00), GetSkColorFromInkBrush(ink_brush));
   EXPECT_EQ(4.5f, ink_brush.GetSize());
   ASSERT_EQ(1u, ink_brush.CoatCount());
   const ink::BrushCoat& coat = ink_brush.GetCoats()[0];
@@ -635,8 +643,9 @@
 TEST_P(PdfInkModuleTest, HandleSetAnnotationBrushMessageColorZero) {
   EnableAnnotationMode();
 
-  TestAnnotationBrushMessageParams message_params{/*color_r=*/0, /*color_g=*/0,
-                                                  /*color_b=*/0, /*size=*/4.5};
+  TestAnnotationBrushMessageParams message_params{
+      /*color_r=*/0x00, /*color_g=*/0x00,
+      /*color_b=*/0x00, /*size=*/4.5f};
   base::Value::Dict message =
       CreateSetAnnotationBrushMessageForTesting("pen", &message_params);
   EXPECT_TRUE(ink_module().OnMessage(message));
@@ -738,9 +747,10 @@
 
   EnableAnnotationMode();
 
-  TestAnnotationBrushMessageParams message_params{/*color_r=*/0,
-                                                  /*color_g=*/255,
-                                                  /*color_b=*/0, /*size=*/16.0};
+  TestAnnotationBrushMessageParams message_params{/*color_r=*/0x00,
+                                                  /*color_g=*/0xFF,
+                                                  /*color_b=*/0x00,
+                                                  /*size=*/16.0f};
   base::Value::Dict message =
       CreateSetAnnotationBrushMessageForTesting("pen", &message_params);
   EXPECT_TRUE(ink_module().OnMessage(message));
@@ -777,10 +787,10 @@
 
   EnableAnnotationMode();
 
-  TestAnnotationBrushMessageParams message_params{/*color_r=*/0,
-                                                  /*color_g=*/255,
-                                                  /*color_b=*/0,
-                                                  /*size=*/16.0};
+  TestAnnotationBrushMessageParams message_params{/*color_r=*/0x00,
+                                                  /*color_g=*/0xFF,
+                                                  /*color_b=*/0x00,
+                                                  /*size=*/16.0f};
   base::Value::Dict message =
       CreateSetAnnotationBrushMessageForTesting("pen", &message_params);
   EXPECT_TRUE(ink_module().OnMessage(message));
@@ -1941,10 +1951,10 @@
   // Start drawing a stroke with a black pen.  The stroke will not finish
   // until the mouse-up event.
   EXPECT_CALL(client(), StrokeAdded(_, _, _)).Times(0);
-  TestAnnotationBrushMessageParams black_pen_message_params{/*color_r=*/0,
-                                                            /*color_g=*/0,
-                                                            /*color_b=*/0,
-                                                            /*size=*/3.0};
+  TestAnnotationBrushMessageParams black_pen_message_params{/*color_r=*/0x00,
+                                                            /*color_g=*/0x00,
+                                                            /*color_b=*/0x00,
+                                                            /*size=*/3.0f};
   SelectBrushTool(PdfInkBrush::Type::kPen, black_pen_message_params);
 
   blink::WebMouseEvent mouse_down_event =
@@ -1955,10 +1965,10 @@
 
   // While the stroke is still in progress, change the pen color.  This has no
   // immediate effect on the in-progress stroke.
-  TestAnnotationBrushMessageParams red_pen_message_params{/*color_r=*/242,
-                                                          /*color_g=*/139,
-                                                          /*color_b=*/130,
-                                                          /*size=*/3.0};
+  TestAnnotationBrushMessageParams red_pen_message_params{/*color_r=*/0xF2,
+                                                          /*color_g=*/0x8B,
+                                                          /*color_b=*/0x82,
+                                                          /*size=*/3.0f};
   SelectBrushTool(PdfInkBrush::Type::kPen, red_pen_message_params);
   VerifyAndClearExpectations();
 
@@ -1979,9 +1989,10 @@
 
   // Do another stroke.  Notice that the changed pen color is in effect for
   // the new stroke that is added.
-  EXPECT_CALL(client(),
-              StrokeAdded(kPageIndex, InkStrokeId(1),
-                          InkStrokeBrushColorEq(SkColorSetRGB(242, 139, 130))));
+  EXPECT_CALL(
+      client(),
+      StrokeAdded(kPageIndex, InkStrokeId(1),
+                  InkStrokeBrushColorEq(SkColorSetRGB(0xF2, 0x8B, 0x82))));
   EXPECT_TRUE(ink_module().HandleInputEvent(mouse_down_event));
   EXPECT_TRUE(ink_module().HandleInputEvent(mouse_up_event));
 }
@@ -1996,9 +2007,10 @@
   EXPECT_CALL(client(), StrokeAdded(_, _, _)).Times(0);
   EXPECT_CALL(client(),
               UpdateInkCursor(CursorBitmapImageSizeEq(SkISize(6, 6))));
-  TestAnnotationBrushMessageParams message_params{/*color_r=*/0,
-                                                  /*color_g=*/0,
-                                                  /*color_b=*/0, /*size=*/2.0};
+  TestAnnotationBrushMessageParams message_params{/*color_r=*/0x00,
+                                                  /*color_g=*/0x00,
+                                                  /*color_b=*/0x00,
+                                                  /*size=*/2.0f};
   SelectBrushTool(PdfInkBrush::Type::kPen, message_params);
 
   blink::WebMouseEvent mouse_down_event =
@@ -2009,7 +2021,7 @@
 
   // While the stroke is still in progress, change the pen size.  This has no
   // immediate effect on the in-progress stroke.
-  message_params.size = 6.0;
+  message_params.size = 6.0f;
   SelectBrushTool(PdfInkBrush::Type::kPen, message_params);
   VerifyAndClearExpectations();
 
@@ -2122,9 +2134,10 @@
   // While the stroke is still in progress, change the input tool type to a
   // pen.  Note that this causes the in-progress erase stroke to finish even
   // before the mouse-up event.
-  TestAnnotationBrushMessageParams message_params{/*color_r=*/0,
-                                                  /*color_g=*/0,
-                                                  /*color_b=*/0, /*size=*/8.0};
+  TestAnnotationBrushMessageParams message_params{/*color_r=*/0x00,
+                                                  /*color_g=*/0x00,
+                                                  /*color_b=*/0x00,
+                                                  /*size=*/8.0f};
   SelectBrushTool(PdfInkBrush::Type::kPen, message_params);
   VerifyAndClearExpectations();
 
@@ -2161,10 +2174,10 @@
   EXPECT_CALL(client(), StrokeAdded(_, _, _)).Times(0);
   EXPECT_CALL(client(),
               UpdateInkCursor(CursorBitmapImageSizeEq(SkISize(6, 6))));
-  TestAnnotationBrushMessageParams pen_message_params{/*color_r=*/0,
-                                                      /*color_g=*/0,
-                                                      /*color_b=*/0,
-                                                      /*size=*/2.0};
+  TestAnnotationBrushMessageParams pen_message_params{/*color_r=*/0x00,
+                                                      /*color_g=*/0x00,
+                                                      /*color_b=*/0x00,
+                                                      /*size=*/2.0f};
   SelectBrushTool(PdfInkBrush::Type::kPen, pen_message_params);
 
   blink::WebMouseEvent mouse_down_event =
@@ -2175,10 +2188,10 @@
 
   // While the stroke is still in progress, change the input tool type to a
   // highlighter.  The entire stroke changes to this new type.
-  TestAnnotationBrushMessageParams highlighter_message_params{/*color_r=*/221,
-                                                              /*color_g=*/243,
-                                                              /*color_b=*/0,
-                                                              /*size=*/8.0};
+  TestAnnotationBrushMessageParams highlighter_message_params{/*color_r=*/0xDD,
+                                                              /*color_g=*/0xF3,
+                                                              /*color_b=*/0x00,
+                                                              /*size=*/8.0f};
   SelectBrushTool(PdfInkBrush::Type::kHighlighter, highlighter_message_params);
   VerifyAndClearExpectations();
 
@@ -2827,8 +2840,7 @@
                                 1);
 
   // Draw a stroke with "Red 1" color.
-  TestAnnotationBrushMessageParams params = {/*color_r=*/0xF2, /*color_g=*/0x8B,
-                                             /*color_b=*/0x82, /*size=*/3.0};
+  TestAnnotationBrushMessageParams params = kRedBrushParams;
   SelectBrushTool(PdfInkBrush::Type::kPen, params);
   ApplyStrokeWithMouseAtMouseDownPoint();
 
@@ -2853,8 +2865,7 @@
   base::HistogramTester histograms;
 
   // Draw a stroke with "Light Red" color.
-  TestAnnotationBrushMessageParams params = {/*color_r=*/0xF2, /*color_g=*/0x8B,
-                                             /*color_b=*/0x82, /*size=*/6.0};
+  TestAnnotationBrushMessageParams params = kRedBrushParams;
   SelectBrushTool(PdfInkBrush::Type::kHighlighter, params);
   ApplyStrokeWithMouseAtMouseDownPoint();
 
@@ -2885,8 +2896,8 @@
   histograms.ExpectUniqueSample(kPenSizeMetric, StrokeMetricBrushSize::kMedium,
                                 1);
 
-  TestAnnotationBrushMessageParams params = {/*color_r=*/242, /*color_g=*/139,
-                                             /*color_b=*/130, /*size=*/1.0};
+  TestAnnotationBrushMessageParams params = {/*color_r=*/0xF2, /*color_g=*/0x8B,
+                                             /*color_b=*/0x82, /*size=*/1.0f};
   SelectBrushTool(PdfInkBrush::Type::kPen, params);
   ApplyStrokeWithMouseAtMouseDownPoint();
 
@@ -2894,7 +2905,7 @@
                                StrokeMetricBrushSize::kExtraThin, 1);
   histograms.ExpectTotalCount(kPenSizeMetric, 2);
 
-  params.size = 8.0;
+  params.size = 8.0f;
   SelectBrushTool(PdfInkBrush::Type::kPen, params);
   ApplyStrokeWithMouseAtMouseDownPoint();
 
@@ -2910,8 +2921,8 @@
   base::HistogramTester histograms;
 
   // Draw a stroke with medium size.
-  TestAnnotationBrushMessageParams params = {/*color_r=*/242, /*color_g=*/139,
-                                             /*color_b=*/130, /*size=*/8.0};
+  TestAnnotationBrushMessageParams params = {/*color_r=*/0xF2, /*color_g=*/0x8B,
+                                             /*color_b=*/0x82, /*size=*/8.0f};
   SelectBrushTool(PdfInkBrush::Type::kHighlighter, params);
   ApplyStrokeWithMouseAtMouseDownPoint();
 
@@ -2919,7 +2930,7 @@
                                 StrokeMetricBrushSize::kMedium, 1);
 
   // Draw a stroke with extra thin size.
-  params.size = 4.0;
+  params.size = 4.0f;
   SelectBrushTool(PdfInkBrush::Type::kHighlighter, params);
   ApplyStrokeWithMouseAtMouseDownPoint();
 
@@ -2928,7 +2939,7 @@
   histograms.ExpectTotalCount(kHighlighterSizeMetric, 2);
 
   // Draw a stroke with extra thick size.
-  params.size = 16.0;
+  params.size = 16.0f;
   SelectBrushTool(PdfInkBrush::Type::kHighlighter, params);
   ApplyStrokeWithMouseAtMouseDownPoint();
 
@@ -2952,8 +2963,7 @@
   histograms.ExpectUniqueSample(kTypeMetric, StrokeMetricBrushType::kPen, 1);
 
   // Draw a highlighter stroke.
-  TestAnnotationBrushMessageParams params = {/*color_r=*/242, /*color_g=*/139,
-                                             /*color_b=*/130, /*size=*/6.0};
+  TestAnnotationBrushMessageParams params = kRedBrushParams;
   SelectBrushTool(PdfInkBrush::Type::kHighlighter, params);
   ApplyStrokeWithMouseAtMouseDownPoint();
 
@@ -2977,7 +2987,7 @@
   histograms.ExpectTotalCount(kTypeMetric, 3);
 
   // Draw another pen stroke.
-  params.size = 3.0;
+  params.size = 3.0f;
   SelectBrushTool(PdfInkBrush::Type::kPen, params);
   ApplyStrokeWithMouseAtMouseDownPoint();
 
@@ -3081,6 +3091,10 @@
 
 class PdfInkModuleTextHighlightTest : public PdfInkModuleStrokeTest {
  public:
+  static constexpr TestAnnotationBrushMessageParams kOrangeBrushParams{
+      /*color_r=*/0xFF,
+      /*color_g=*/0x63,
+      /*color_b=*/0x0C, /*size=*/6.0f};
   static constexpr gfx::Rect kHorizontalSelection{10, 15, 30, 10};
   static constexpr gfx::Rect kVerticalSelection{10, 15, 6, 10};
   static constexpr gfx::PointF kStartPointInsidePage0{10.0, 10.0};
@@ -3088,19 +3102,28 @@
   static constexpr SkColor kOrangeColor = SkColorSetRGB(0xFF, 0x63, 0x0C);
 
  protected:
-  // Helper method for running a simple text selection highlight test with a
-  // single selection rect on page zero.
-  void RunSingleSelectionTest(const gfx::Rect& selection_rect,
-                              base::span<const PdfInkInputData> expected_inputs,
-                              float expected_size) {
+  // Helper method for running a simple text highlighting test using text
+  // selected by mouse with a single selection rect on page zero.
+  void RunSingleSelectionWithMouseTest(
+      const gfx::Rect& selection_rect,
+      base::span<const PdfInkInputData> expected_inputs,
+      float expected_size) {
+    SetUpSingleSelectionTest(selection_rect);
+
+    // Apply a text highlight stroke at the given points.
+    ApplyStrokeWithMouseAtPoints(kStartPointInsidePage0, {kEndPointInsidePage0},
+                                 kEndPointInsidePage0);
+
+    VerifySingleSelectionTest(expected_inputs, expected_size);
+  }
+
+  // Set up single selection test expectations before text selection strokes
+  // have been applied.
+  void SetUpSingleSelectionTest(const gfx::Rect& selection_rect) {
     EnableAnnotationMode();
     InitializeSimpleSinglePageBasicLayout();
 
-    // Select the highlighter tool with an "Orange" color.
-    TestAnnotationBrushMessageParams params = {/*color_r=*/0xFF,
-                                               /*color_g=*/0x63,
-                                               /*color_b=*/0x0C, /*size=*/6.0f};
-    SelectBrushTool(PdfInkBrush::Type::kHighlighter, params);
+    SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
 
     std::vector<gfx::Rect> selection_rects{selection_rect};
     EXPECT_CALL(client(), GetSelectionRects())
@@ -3113,11 +3136,12 @@
     EXPECT_CALL(client(), OnTextOrLinkAreaClick(kStartPointInsidePage0,
                                                 /*click_count=*/1));
     EXPECT_CALL(client(), ExtendSelectionByPoint(kEndPointInsidePage0));
+  }
 
-    // Apply a text highlight stroke at the given points.
-    ApplyStrokeWithMouseAtPoints(kStartPointInsidePage0, {kEndPointInsidePage0},
-                                 kEndPointInsidePage0);
-
+  // Verify single selection test results after applying text selection strokes.
+  void VerifySingleSelectionTest(
+      base::span<const PdfInkInputData> expected_inputs,
+      float expected_size) {
     EXPECT_EQ(1, client().stroke_finished_count());
     EXPECT_THAT(updated_ink_thumbnail_page_indices(), ElementsAre(0));
 
@@ -3170,9 +3194,7 @@
   InitializeSimpleSinglePageBasicLayout();
 
   // Select the pen tool with a "Light Red" color.
-  TestAnnotationBrushMessageParams params = {/*color_r=*/0xF2, /*color_g=*/0x8B,
-                                             /*color_b=*/0x82, /*size=*/6.0f};
-  SelectBrushTool(PdfInkBrush::Type::kPen, params);
+  SelectBrushTool(PdfInkBrush::Type::kPen, kRedBrushParams);
 
   EXPECT_CALL(client(), GetSelectionRects()).Times(0);
   EXPECT_CALL(client(), IsSelectableTextOrLinkArea(kStartPointInsidePage0))
@@ -3207,7 +3229,7 @@
 }
 
 TEST_P(PdfInkModuleTextHighlightTest, SingleHorizontalSelection) {
-  RunSingleSelectionTest(
+  RunSingleSelectionWithMouseTest(
       /*selection_rect=*/kHorizontalSelection,
       /*expected_inputs=*/
       {PdfInkInputData(gfx::PointF(15.0, 20.0)),
@@ -3216,7 +3238,7 @@
 }
 
 TEST_P(PdfInkModuleTextHighlightTest, SingleVerticalSelection) {
-  RunSingleSelectionTest(
+  RunSingleSelectionWithMouseTest(
       /*selection_rect=*/kVerticalSelection,
       /*expected_inputs=*/
       {PdfInkInputData(gfx::PointF(13.0, 18.0)),
@@ -3225,7 +3247,7 @@
 }
 
 TEST_P(PdfInkModuleTextHighlightTest, SingleSquareSelection) {
-  RunSingleSelectionTest(
+  RunSingleSelectionWithMouseTest(
       /*selection_rect=*/gfx::Rect(10, 15, 12, 12),
       /*expected_inputs=*/
       {PdfInkInputData(gfx::PointF(16.0, 21.0))},
@@ -3235,7 +3257,7 @@
 TEST_P(PdfInkModuleTextHighlightTest,
        SingleHorizontalSelectionRotatedClockwise90) {
   client().set_orientation(PageOrientation::kClockwise90);
-  RunSingleSelectionTest(
+  RunSingleSelectionWithMouseTest(
       /*selection_rect=*/kHorizontalSelection,
       /*expected_inputs=*/
       {PdfInkInputData(gfx::PointF(20.0, 14.0)),
@@ -3246,7 +3268,7 @@
 TEST_P(PdfInkModuleTextHighlightTest,
        SingleVerticalSelectionRotatedClockwise90) {
   client().set_orientation(PageOrientation::kClockwise90);
-  RunSingleSelectionTest(
+  RunSingleSelectionWithMouseTest(
       /*selection_rect=*/kVerticalSelection,
       /*expected_inputs=*/
       {PdfInkInputData(gfx::PointF(18.0, 36.0)),
@@ -3257,7 +3279,7 @@
 TEST_P(PdfInkModuleTextHighlightTest,
        SingleHorizontalSelectionRotatedClockwise180) {
   client().set_orientation(PageOrientation::kClockwise180);
-  RunSingleSelectionTest(
+  RunSingleSelectionWithMouseTest(
       /*selection_rect=*/kHorizontalSelection,
       /*expected_inputs=*/
       {PdfInkInputData(gfx::PointF(14.0, 39.0)),
@@ -3268,7 +3290,7 @@
 TEST_P(PdfInkModuleTextHighlightTest,
        SingleVerticalSelectionRotatedClockwise180) {
   client().set_orientation(PageOrientation::kClockwise180);
-  RunSingleSelectionTest(
+  RunSingleSelectionWithMouseTest(
       /*selection_rect=*/kVerticalSelection,
       /*expected_inputs=*/
       {PdfInkInputData(gfx::PointF(36.0, 37.0)),
@@ -3279,7 +3301,7 @@
 TEST_P(PdfInkModuleTextHighlightTest,
        SingleHorizontalSelectionRotatedClockwise270) {
   client().set_orientation(PageOrientation::kClockwise270);
-  RunSingleSelectionTest(
+  RunSingleSelectionWithMouseTest(
       /*selection_rect=*/kHorizontalSelection,
       /*expected_inputs=*/
       {PdfInkInputData(gfx::PointF(39.0, 15.0)),
@@ -3290,7 +3312,7 @@
 TEST_P(PdfInkModuleTextHighlightTest,
        SingleVerticalSelectionRotatedClockwise270) {
   client().set_orientation(PageOrientation::kClockwise270);
-  RunSingleSelectionTest(
+  RunSingleSelectionWithMouseTest(
       /*selection_rect=*/kVerticalSelection,
       /*expected_inputs=*/
       {PdfInkInputData(gfx::PointF(37.0, 13.0)),
@@ -3303,7 +3325,7 @@
   InitializeSimpleSinglePageBasicLayout();
   client().set_zoom(2.0f);
 
-  RunSingleSelectionTest(
+  RunSingleSelectionWithMouseTest(
       /*selection_rect=*/kHorizontalSelection,
       /*expected_inputs=*/
       {PdfInkInputData(gfx::PointF(7.5, 10.0)),
@@ -3316,7 +3338,7 @@
   InitializeSimpleSinglePageBasicLayout();
   client().set_zoom(0.5f);
 
-  RunSingleSelectionTest(
+  RunSingleSelectionWithMouseTest(
       /*selection_rect=*/kHorizontalSelection,
       /*expected_inputs=*/
       {PdfInkInputData(gfx::PointF(30.0, 40.0)),
@@ -3328,11 +3350,7 @@
   EnableAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
-  // Select the highlighter tool with an "Orange" color.
-  TestAnnotationBrushMessageParams params = {/*color_r=*/0xFF,
-                                             /*color_g=*/0x63,
-                                             /*color_b=*/0x0C, /*size=*/6.0f};
-  SelectBrushTool(PdfInkBrush::Type::kHighlighter, params);
+  SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
 
   constexpr gfx::Rect kHorizontalSelection2{15, 25, 10, 5};
   std::vector<gfx::Rect> selection_rects{kHorizontalSelection,
@@ -3396,14 +3414,10 @@
   EnableAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
-  // Select the highlighter tool with an "Orange" color.
-  TestAnnotationBrushMessageParams params = {/*color_r=*/0xFF,
-                                             /*color_g=*/0x63,
-                                             /*color_b=*/0x0C, /*size=*/6.0};
-  SelectBrushTool(PdfInkBrush::Type::kHighlighter, params);
+  SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
 
   // There will be no text selection rects.
-  std::vector<gfx::Rect> selection_rects{};
+  std::vector<gfx::Rect> selection_rects;
   EXPECT_CALL(client(), GetSelectionRects()).WillOnce(Return(selection_rects));
   EXPECT_CALL(client(), IsSelectableTextOrLinkArea(kStartPointInsidePage0))
       .WillRepeatedly(Return(true));
@@ -3426,11 +3440,7 @@
   EnableAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
-  // Select the highlighter tool with an "Orange" color.
-  TestAnnotationBrushMessageParams params = {/*color_r=*/0xFF,
-                                             /*color_g=*/0x63,
-                                             /*color_b=*/0x0C, /*size=*/6.0};
-  SelectBrushTool(PdfInkBrush::Type::kHighlighter, params);
+  SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
 
   ClickTextAtPoint(kStartPointInsidePage0, /*click_count=*/1);
 
@@ -3469,11 +3479,7 @@
   EnableAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
-  // Select the highlighter tool with an "Orange" color.
-  TestAnnotationBrushMessageParams params = {/*color_r=*/0xFF,
-                                             /*color_g=*/0x63,
-                                             /*color_b=*/0x0C, /*size=*/6.0};
-  SelectBrushTool(PdfInkBrush::Type::kHighlighter, params);
+  SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
 
   ClickTextAtPoint(kStartPointInsidePage0, /*click_count=*/1);
 
@@ -3523,11 +3529,7 @@
   EnableAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
-  // Select the highlighter tool with an "Orange" color.
-  TestAnnotationBrushMessageParams params = {/*color_r=*/0xFF,
-                                             /*color_g=*/0x63,
-                                             /*color_b=*/0x0C, /*size=*/6.0f};
-  SelectBrushTool(PdfInkBrush::Type::kHighlighter, params);
+  SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
 
   // Start in a text area.
   EXPECT_CALL(client(), IsSelectableTextOrLinkArea(_)).WillOnce(Return(true));
@@ -3587,11 +3589,7 @@
   EnableAnnotationMode();
   InitializeVerticalTwoPageLayout();
 
-  // Select the highlighter tool with an "Orange" color.
-  TestAnnotationBrushMessageParams params = {/*color_r=*/0xFF,
-                                             /*color_g=*/0x63,
-                                             /*color_b=*/0x0C, /*size=*/6.0f};
-  SelectBrushTool(PdfInkBrush::Type::kHighlighter, params);
+  SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
 
   // Start on page 0.
   EXPECT_CALL(client(), IsSelectableTextOrLinkArea(_))
@@ -3667,11 +3665,7 @@
   EnableAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
 
-  // Select the highlighter tool with an "Orange" color.
-  TestAnnotationBrushMessageParams params = {/*color_r=*/0xFF,
-                                             /*color_g=*/0x63,
-                                             /*color_b=*/0x0C, /*size=*/6.0f};
-  SelectBrushTool(PdfInkBrush::Type::kHighlighter, params);
+  SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
 
   std::vector<gfx::Rect> selection_rects{gfx::Rect(9, 14, 5, 10)};
   EXPECT_CALL(client(), GetSelectionRects())
@@ -3686,6 +3680,114 @@
   RunStrokeMissedEndEventThenMouseMoveTest();
 }
 
+TEST_P(PdfInkModuleTextHighlightTest, TouchSingleHorizontalSelection) {
+  SetUpSingleSelectionTest(kHorizontalSelection);
+
+  // Apply a text highlight stroke at the given points.
+  ApplyStrokeWithTouchAtPoints(base::span_from_ref(kStartPointInsidePage0),
+                               {base::span_from_ref(kEndPointInsidePage0)},
+                               base::span_from_ref(kEndPointInsidePage0));
+
+  constexpr auto kExpectedInputs = std::to_array<PdfInkInputData>(
+      {PdfInkInputData(gfx::PointF(15.0, 20.0)),
+       PdfInkInputData(gfx::PointF(35.0, 20.0))});
+  VerifySingleSelectionTest(kExpectedInputs, /*expected_size=*/10.0f);
+}
+
+TEST_P(PdfInkModuleTextHighlightTest, TouchOneClickCount) {
+  EnableAnnotationMode();
+  InitializeSimpleSinglePageBasicLayout();
+
+  SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
+
+  // There will be no text selection rects.
+  std::vector<gfx::Rect> selection_rects;
+  EXPECT_CALL(client(), GetSelectionRects()).WillOnce(Return(selection_rects));
+  EXPECT_CALL(client(), IsSelectableTextOrLinkArea(kStartPointInsidePage0))
+      .WillRepeatedly(Return(true));
+
+  EXPECT_CALL(client(), OnTextOrLinkAreaClick(kStartPointInsidePage0,
+                                              /*click_count=*/1));
+  EXPECT_CALL(client(), ExtendSelectionByPoint(_)).Times(0);
+
+  blink::WebTouchEvent touch_event =
+      CreateTouchEvent(blink::WebInputEvent::Type::kTouchStart,
+                       base::span_from_ref(kStartPointInsidePage0));
+  EXPECT_TRUE(ink_module().HandleInputEvent(touch_event));
+
+  touch_event = CreateTouchEvent(blink::WebInputEvent::Type::kTouchEnd,
+                                 base::span_from_ref(kStartPointInsidePage0));
+  EXPECT_TRUE(ink_module().HandleInputEvent(touch_event));
+
+  EXPECT_EQ(0, client().stroke_finished_count());
+  EXPECT_TRUE(updated_ink_thumbnail_page_indices().empty());
+
+  std::map<int, std::vector<raw_ref<const ink::Stroke>>> collected_strokes =
+      CollectVisibleStrokes(ink_module().GetVisibleStrokesIterator());
+  EXPECT_TRUE(collected_strokes.empty());
+}
+
+TEST_P(PdfInkModuleTextHighlightTest, MultiTouchDoesNotSelectText) {
+  EnableAnnotationMode();
+  InitializeSimpleSinglePageBasicLayout();
+
+  SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
+
+  EXPECT_CALL(client(), IsSelectableTextOrLinkArea(_)).Times(0);
+
+  ApplyStrokeWithTouchAtPointsNotHandled(
+      {kStartPointInsidePage0, kStartPointInsidePage0},
+      {{kEndPointInsidePage0, kEndPointInsidePage0}},
+      {kEndPointInsidePage0, kEndPointInsidePage0});
+}
+
+TEST_P(PdfInkModuleTextHighlightTest, PenSingleHorizontalSelection) {
+  const std::vector<PdfInkInputData> expected_inputs{
+      PdfInkInputData(gfx::PointF(15.0, 20.0)),
+      PdfInkInputData(gfx::PointF(35.0, 20.0))};
+  SetUpSingleSelectionTest(kHorizontalSelection);
+
+  // Apply a text highlight stroke at the given points.
+  ApplyStrokeWithPenAtPoints(base::span_from_ref(kStartPointInsidePage0),
+                             {base::span_from_ref(kEndPointInsidePage0)},
+                             base::span_from_ref(kEndPointInsidePage0));
+
+  VerifySingleSelectionTest(expected_inputs, /*expected_size=*/10.0f);
+}
+
+TEST_P(PdfInkModuleTextHighlightTest, PenOneClickCount) {
+  EnableAnnotationMode();
+  InitializeSimpleSinglePageBasicLayout();
+
+  SelectBrushTool(PdfInkBrush::Type::kHighlighter, kOrangeBrushParams);
+
+  // There will be no text selection rects.
+  std::vector<gfx::Rect> selection_rects;
+  EXPECT_CALL(client(), GetSelectionRects()).WillOnce(Return(selection_rects));
+  EXPECT_CALL(client(), IsSelectableTextOrLinkArea(kStartPointInsidePage0))
+      .WillRepeatedly(Return(true));
+
+  EXPECT_CALL(client(), OnTextOrLinkAreaClick(kStartPointInsidePage0,
+                                              /*click_count=*/1));
+  EXPECT_CALL(client(), ExtendSelectionByPoint(_)).Times(0);
+
+  blink::WebTouchEvent pen_event =
+      CreatePenEvent(blink::WebInputEvent::Type::kTouchStart,
+                     base::span_from_ref(kStartPointInsidePage0));
+  EXPECT_TRUE(ink_module().HandleInputEvent(pen_event));
+
+  pen_event = CreatePenEvent(blink::WebInputEvent::Type::kTouchEnd,
+                             base::span_from_ref(kStartPointInsidePage0));
+  EXPECT_TRUE(ink_module().HandleInputEvent(pen_event));
+
+  EXPECT_EQ(0, client().stroke_finished_count());
+  EXPECT_TRUE(updated_ink_thumbnail_page_indices().empty());
+
+  std::map<int, std::vector<raw_ref<const ink::Stroke>>> collected_strokes =
+      CollectVisibleStrokes(ink_module().GetVisibleStrokesIterator());
+  EXPECT_TRUE(collected_strokes.empty());
+}
+
 TEST_P(PdfInkModuleTextHighlightTest, CursorOnMouseMove) {
   EnableAnnotationMode();
   InitializeSimpleSinglePageBasicLayout();
@@ -3695,8 +3797,7 @@
   EXPECT_CALL(client(),
               UpdateInkCursor(CursorBitmapImageSizeEq(SkISize(8, 8))));
 
-  TestAnnotationBrushMessageParams params = {/*color_r=*/0xF2, /*color_g=*/0x8B,
-                                             /*color_b=*/0x82, /*size=*/6.0f};
+  TestAnnotationBrushMessageParams params = kRedBrushParams;
   SelectBrushTool(PdfInkBrush::Type::kPen, params);
 
   // `kStartPointInsidePage0` will be the selectable text area position, while
@@ -3751,10 +3852,8 @@
   // Select the highlighter tool. The cursor should be the custom highlighter
   // cursor.
   EXPECT_CALL(client(),
-              UpdateInkCursor(CursorBitmapImageSizeEq(SkISize(10, 10))));
-  TestAnnotationBrushMessageParams params = {/*color_r=*/0xF2, /*color_g=*/0x8B,
-                                             /*color_b=*/0x82, /*size=*/8.0f};
-  SelectBrushTool(PdfInkBrush::Type::kHighlighter, params);
+              UpdateInkCursor(CursorBitmapImageSizeEq(SkISize(8, 8))));
+  SelectBrushTool(PdfInkBrush::Type::kHighlighter, kRedBrushParams);
 
   VerifyAndClearExpectations();
 
@@ -3791,7 +3890,7 @@
   // End text highlighting. The cursor should restore to the custom highlighter
   // cursor.
   EXPECT_CALL(client(),
-              UpdateInkCursor(CursorBitmapImageSizeEq(SkISize(10, 10))));
+              UpdateInkCursor(CursorBitmapImageSizeEq(SkISize(8, 8))));
   blink::WebMouseEvent mouse_up_event =
       MouseEventBuilder()
           .CreateLeftMouseUpAtPosition(kEndPointInsidePage0)
@@ -3806,10 +3905,8 @@
   // Select the highlighter tool. The cursor should be the custom highlighter
   // cursor.
   EXPECT_CALL(client(),
-              UpdateInkCursor(CursorBitmapImageSizeEq(SkISize(10, 10))));
-  TestAnnotationBrushMessageParams params = {/*color_r=*/0xF2, /*color_g=*/0x8B,
-                                             /*color_b=*/0x82, /*size=*/8.0f};
-  SelectBrushTool(PdfInkBrush::Type::kHighlighter, params);
+              UpdateInkCursor(CursorBitmapImageSizeEq(SkISize(8, 8))));
+  SelectBrushTool(PdfInkBrush::Type::kHighlighter, kRedBrushParams);
 
   VerifyAndClearExpectations();
 
diff --git a/pdf/pdf_view_web_plugin.cc b/pdf/pdf_view_web_plugin.cc
index bcb7097..a51357c 100644
--- a/pdf/pdf_view_web_plugin.cc
+++ b/pdf/pdf_view_web_plugin.cc
@@ -1481,6 +1481,15 @@
 
 void PdfViewWebPlugin::SelectionChanged(const gfx::Rect& left,
                                         const gfx::Rect& right) {
+#if BUILDFLAG(ENABLE_PDF_INK2)
+  // Ignore the selected text if `ink_module_` is currently text highlighting.
+  // This prevents `pdf_host_` from showing touch handles for touch text
+  // highlighting.
+  if (ink_module_ && ink_module_->ShouldBlockTextSelectionChanged()) {
+    return;
+  }
+#endif  // BUILDFLAG(ENABLE_PDF_INK2)
+
   gfx::PointF left_point(left.x() + available_area_.x(), left.y());
   gfx::PointF right_point(right.x() + available_area_.x(), right.y());
 
@@ -1514,8 +1523,7 @@
 void PdfViewWebPlugin::SetSelectedText(const std::string& selected_text) {
 #if BUILDFLAG(ENABLE_PDF_INK2)
   // Ignore the selected text if `ink_module_` is currently text highlighting.
-  if (features::kPdfInk2TextHighlighting.Get() &&
-      ink_module_->ShouldBlockTextSelectionChanged()) {
+  if (ink_module_ && ink_module_->ShouldBlockTextSelectionChanged()) {
     return;
   }
 #endif  // BUILDFLAG(ENABLE_PDF_INK2)
diff --git a/pdf/pdf_view_web_plugin_unittest.cc b/pdf/pdf_view_web_plugin_unittest.cc
index 87ade8d6..d4331a5 100644
--- a/pdf/pdf_view_web_plugin_unittest.cc
+++ b/pdf/pdf_view_web_plugin_unittest.cc
@@ -3333,9 +3333,10 @@
 
   // Enter annotation mode and select the highlighter.
   plugin_->OnMessage(CreateSetAnnotationModeMessageForTesting(/*enable=*/true));
-  TestAnnotationBrushMessageParams message_params{/*color_r=*/240,
-                                                  /*color_g=*/133,
-                                                  /*color_b=*/0, /*size=*/4.5};
+  TestAnnotationBrushMessageParams message_params{/*color_r=*/0xF0,
+                                                  /*color_g=*/0x85,
+                                                  /*color_b=*/0x00,
+                                                  /*size=*/4.5f};
   plugin_->OnMessage(CreateSetAnnotationBrushMessageForTesting(
       "highlighter", &message_params));
 
@@ -3353,14 +3354,20 @@
   EXPECT_CALL(*client_ptr_, TextSelectionChanged(_, _, _)).Times(0);
 
   plugin_->SetSelectedText("text");
+
+  EXPECT_CALL(pdf_host_, SelectionChanged(_, _, _, _)).Times(0);
+
+  plugin_->SelectionChanged({-10, -20, 30, 40}, {50, 60, 70, 80});
+  pdf_receiver_.FlushForTesting();
 }
 
 TEST_P(PdfViewWebPluginInkTextHighlightTest, DrawInProgressTextHighlight) {
   // Enter annotation mode and select the highlighter.
   plugin_->OnMessage(CreateSetAnnotationModeMessageForTesting(/*enable=*/true));
-  TestAnnotationBrushMessageParams message_params{/*color_r=*/240,
-                                                  /*color_g=*/133,
-                                                  /*color_b=*/0, /*size=*/4.5};
+  TestAnnotationBrushMessageParams message_params{/*color_r=*/0xF0,
+                                                  /*color_g=*/0x85,
+                                                  /*color_b=*/0x00,
+                                                  /*size=*/4.5f};
   plugin_->OnMessage(CreateSetAnnotationBrushMessageForTesting(
       "highlighter", &message_params));
 
diff --git a/remoting/resources/BUILD.gn b/remoting/resources/BUILD.gn
index aeed8ca..9b3231f 100644
--- a/remoting/resources/BUILD.gn
+++ b/remoting/resources/BUILD.gn
@@ -38,7 +38,7 @@
          ] + rebase_path(sources_to_verify, root_build_dir)
 }
 
-grit("strings") {
+grit_strings("strings") {
   source = "remoting_strings.grd"
   output_name = "remoting_strings"
 
@@ -46,15 +46,13 @@
   # generated file root.
   output_dir = root_gen_dir
 
-  outputs = [ "remoting/base/string_resources.h" ]
+  outputs =
+      [ "remoting/base/string_resources.h" ] + remoting_resources_locale_files
 
   # The grd produces a *.pak file and a messages.json file (this one uses
   # underscores instead of hyphens) for each locale.
-  outputs +=
-      process_file_template(remoting_locales_with_pseudolocales,
-                            [ "remoting/resources/{{source_name_part}}.pak" ])
-
-  outputs += remoting_resources_locale_files
+  locales = remoting_locales_with_pseudolocales
+  output_prefix = "remoting/resources/"
 
   if (is_official_build) {
     defines = [ "_official_build" ]
@@ -68,21 +66,42 @@
   # replacement over the locales. Here, we can do this in GN script by
   # pretending the locale list is a list of files. The {{source_name_part}}
   # will just expand to the locale name.
-  inputs = process_file_template(
-          remoting_locales,
-          [ "$root_gen_dir/remoting/resources/{{source_name_part}}.pak" ])
+  inputs = []
+  foreach(locale, remoting_locales) {
+    if (translate_genders) {
+      inputs += process_file_template(
+              all_chrome_genders,
+              "$root_gen_dir/remoting/resources/${locale}_{{source_name_part}}.pak")
+    } else {
+      inputs += [ "$root_gen_dir/remoting/resources/${locale}.pak" ]
+    }
+  }
 
   # Likewise, process the outputs in the same way as the inputs.
+  outputs = []
   if (is_apple) {
     # On mac, use underscores instead of hyphens and put the files in a
     # different place.
-    outputs = process_file_template(
-            remoting_locales_with_underscores,
-            [ "$root_out_dir/remoting/resources/{{source_name_part}}.lproj/locale.pak" ])
+    foreach(locale, remoting_locales_with_underscores) {
+      if (translate_genders) {
+        outputs += process_file_template(
+                all_chrome_genders,
+                "$root_out_dir/remoting/resources/${locale}_{{source_name_part}}.lproj/locale.pak")
+      } else {
+        outputs +=
+            [ "$root_out_dir/remoting/resources/${locale}.lproj/locale.pak" ]
+      }
+    }
   } else {
-    outputs = process_file_template(
-            remoting_locales,
-            [ "$root_out_dir/remoting_locales/{{source_name_part}}.pak" ])
+    foreach(locale, remoting_locales) {
+      if (translate_genders) {
+        outputs += process_file_template(
+                all_chrome_genders,
+                "$root_out_dir/remoting_locales/${locale}_{{source_name_part}}.pak")
+      } else {
+        outputs += [ "$root_out_dir/remoting_locales/${locale}.pak" ]
+      }
+    }
   }
 
   args = [
diff --git a/services/data_decoder/BUILD.gn b/services/data_decoder/BUILD.gn
index e9edd2cf..cfb9adda 100644
--- a/services/data_decoder/BUILD.gn
+++ b/services/data_decoder/BUILD.gn
@@ -23,8 +23,6 @@
     "data_decoder_service.h",
     "gzipper.cc",
     "gzipper.h",
-    "json_parser_impl.cc",
-    "json_parser_impl.h",
     "structured_headers_parser_impl.cc",
     "structured_headers_parser_impl.h",
     "xml_parser.cc",
diff --git a/services/data_decoder/data_decoder_service.cc b/services/data_decoder/data_decoder_service.cc
index c62d95a0..8e03776c 100644
--- a/services/data_decoder/data_decoder_service.cc
+++ b/services/data_decoder/data_decoder_service.cc
@@ -15,7 +15,6 @@
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "services/data_decoder/cbor_parser_impl.h"
 #include "services/data_decoder/gzipper.h"
-#include "services/data_decoder/json_parser_impl.h"
 #include "services/data_decoder/public/mojom/image_decoder.mojom.h"
 #include "services/data_decoder/structured_headers_parser_impl.h"
 #include "services/data_decoder/xml_parser.h"
@@ -54,12 +53,6 @@
 #endif
 }
 
-void DataDecoderService::BindJsonParser(
-    mojo::PendingReceiver<mojom::JsonParser> receiver) {
-  mojo::MakeSelfOwnedReceiver(std::make_unique<JsonParserImpl>(),
-                              std::move(receiver));
-}
-
 void DataDecoderService::BindStructuredHeadersParser(
     mojo::PendingReceiver<mojom::StructuredHeadersParser> receiver) {
   mojo::MakeSelfOwnedReceiver(std::make_unique<StructuredHeadersParserImpl>(),
diff --git a/services/data_decoder/data_decoder_service.h b/services/data_decoder/data_decoder_service.h
index aa07370..8ed7492 100644
--- a/services/data_decoder/data_decoder_service.h
+++ b/services/data_decoder/data_decoder_service.h
@@ -13,7 +13,6 @@
 #include "services/data_decoder/public/mojom/data_decoder_service.mojom.h"
 #include "services/data_decoder/public/mojom/gzipper.mojom.h"
 #include "services/data_decoder/public/mojom/image_decoder.mojom.h"
-#include "services/data_decoder/public/mojom/json_parser.mojom.h"
 #include "services/data_decoder/public/mojom/structured_headers_parser.mojom.h"
 #include "services/data_decoder/public/mojom/xml_parser.mojom.h"
 
@@ -43,8 +42,6 @@
   // mojom::DataDecoderService implementation:
   void BindImageDecoder(
       mojo::PendingReceiver<mojom::ImageDecoder> receiver) override;
-  void BindJsonParser(
-      mojo::PendingReceiver<mojom::JsonParser> receiver) override;
   void BindStructuredHeadersParser(
       mojo::PendingReceiver<mojom::StructuredHeadersParser> receiver) override;
   void BindXmlParser(mojo::PendingReceiver<mojom::XmlParser> receiver) override;
diff --git a/services/data_decoder/json_parser_impl.cc b/services/data_decoder/json_parser_impl.cc
deleted file mode 100644
index b9bdb32..0000000
--- a/services/data_decoder/json_parser_impl.cc
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2016 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "services/data_decoder/json_parser_impl.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/json/json_reader.h"
-#include "base/values.h"
-
-namespace data_decoder {
-
-JsonParserImpl::JsonParserImpl() = default;
-
-JsonParserImpl::~JsonParserImpl() = default;
-
-void JsonParserImpl::Parse(const std::string& json,
-                           uint32_t options,
-                           ParseCallback callback) {
-  auto ret = base::JSONReader::ReadAndReturnValueWithError(json, options);
-  if (ret.has_value()) {
-    std::move(callback).Run(std::move(*ret), std::nullopt);
-  } else {
-    std::move(callback).Run(std::nullopt,
-                            std::make_optional(std::move(ret.error().message)));
-  }
-}
-
-}  // namespace data_decoder
diff --git a/services/data_decoder/json_parser_impl.h b/services/data_decoder/json_parser_impl.h
deleted file mode 100644
index c37a028..0000000
--- a/services/data_decoder/json_parser_impl.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2013 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SERVICES_DATA_DECODER_JSON_PARSER_IMPL_H_
-#define SERVICES_DATA_DECODER_JSON_PARSER_IMPL_H_
-
-#include <string>
-
-#include "services/data_decoder/public/mojom/json_parser.mojom.h"
-
-namespace data_decoder {
-
-class JsonParserImpl : public mojom::JsonParser {
- public:
-  JsonParserImpl();
-
-  JsonParserImpl(const JsonParserImpl&) = delete;
-  JsonParserImpl& operator=(const JsonParserImpl&) = delete;
-
-  ~JsonParserImpl() override;
-
- private:
-  // mojom::JsonParser implementation.
-  void Parse(const std::string& json,
-             uint32_t options,
-             ParseCallback callback) override;
-};
-
-}  // namespace data_decoder
-
-#endif  // SERVICES_DATA_DECODER_JSON_PARSER_IMPL_H_
diff --git a/services/data_decoder/public/cpp/data_decoder.cc b/services/data_decoder/public/cpp/data_decoder.cc
index 512b48a..da86c8f 100644
--- a/services/data_decoder/public/cpp/data_decoder.cc
+++ b/services/data_decoder/public/cpp/data_decoder.cc
@@ -23,7 +23,6 @@
 #include "net/http/structured_headers.h"
 #include "services/data_decoder/public/mojom/cbor_parser.mojom.h"
 #include "services/data_decoder/public/mojom/gzipper.mojom.h"
-#include "services/data_decoder/public/mojom/json_parser.mojom.h"
 #include "services/data_decoder/public/mojom/structured_headers_parser.mojom.h"
 #include "services/data_decoder/public/mojom/xml_parser.mojom.h"
 
diff --git a/services/data_decoder/public/cpp/data_decoder_unittest.cc b/services/data_decoder/public/cpp/data_decoder_unittest.cc
index 0e8cf67..313659e 100644
--- a/services/data_decoder/public/cpp/data_decoder_unittest.cc
+++ b/services/data_decoder/public/cpp/data_decoder_unittest.cc
@@ -17,7 +17,6 @@
 #include "build/build_config.h"
 #include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
 #include "services/data_decoder/public/mojom/cbor_parser.mojom.h"
-#include "services/data_decoder/public/mojom/json_parser.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace data_decoder {
@@ -31,44 +30,6 @@
   test::InProcessDataDecoder in_process_data_decoder_;
 };
 
-TEST_F(DataDecoderTest, ReuseJson) {
-  // Verify that a single DataDecoder with concurrent interface connections will
-  // only use one service instance.
-
-  DataDecoder decoder;
-  mojo::Remote<mojom::JsonParser> parser1;
-  decoder.GetService()->BindJsonParser(parser1.BindNewPipeAndPassReceiver());
-  parser1.FlushForTesting();
-  EXPECT_TRUE(parser1.is_connected());
-  EXPECT_EQ(1u, service().receivers().size());
-
-  mojo::Remote<mojom::JsonParser> parser2;
-  decoder.GetService()->BindJsonParser(parser2.BindNewPipeAndPassReceiver());
-  parser2.FlushForTesting();
-  EXPECT_TRUE(parser2.is_connected());
-  EXPECT_TRUE(parser1.is_connected());
-  EXPECT_EQ(1u, service().receivers().size());
-}
-
-TEST_F(DataDecoderTest, IsolationJson) {
-  // Verify that separate DataDecoder instances make separate connections to the
-  // service.
-
-  DataDecoder decoder1;
-  mojo::Remote<mojom::JsonParser> parser1;
-  decoder1.GetService()->BindJsonParser(parser1.BindNewPipeAndPassReceiver());
-  parser1.FlushForTesting();
-  EXPECT_TRUE(parser1.is_connected());
-  EXPECT_EQ(1u, service().receivers().size());
-
-  DataDecoder decoder2;
-  mojo::Remote<mojom::JsonParser> parser2;
-  decoder2.GetService()->BindJsonParser(parser2.BindNewPipeAndPassReceiver());
-  parser2.FlushForTesting();
-  EXPECT_TRUE(parser2.is_connected());
-  EXPECT_EQ(2u, service().receivers().size());
-}
-
 TEST_F(DataDecoderTest, ReuseCbor) {
   // Verify that a single DataDecoder with concurrent interface connections will
   // only use one service instance.
diff --git a/services/data_decoder/public/cpp/test_support/fake_data_decoder_service.cc b/services/data_decoder/public/cpp/test_support/fake_data_decoder_service.cc
index d518580..4d288aa 100644
--- a/services/data_decoder/public/cpp/test_support/fake_data_decoder_service.cc
+++ b/services/data_decoder/public/cpp/test_support/fake_data_decoder_service.cc
@@ -17,11 +17,6 @@
   FAIL();
 }
 
-void FakeDataDecoderService::BindJsonParser(
-    mojo::PendingReceiver<data_decoder::mojom::JsonParser> receiver) {
-  FAIL();
-}
-
 void FakeDataDecoderService::BindStructuredHeadersParser(
     mojo::PendingReceiver<data_decoder::mojom::StructuredHeadersParser>
         receiver) {
diff --git a/services/data_decoder/public/cpp/test_support/fake_data_decoder_service.h b/services/data_decoder/public/cpp/test_support/fake_data_decoder_service.h
index 1027e9a1..f00fc286 100644
--- a/services/data_decoder/public/cpp/test_support/fake_data_decoder_service.h
+++ b/services/data_decoder/public/cpp/test_support/fake_data_decoder_service.h
@@ -12,7 +12,6 @@
 #include "services/data_decoder/public/mojom/data_decoder_service.mojom.h"
 #include "services/data_decoder/public/mojom/gzipper.mojom.h"
 #include "services/data_decoder/public/mojom/image_decoder.mojom.h"
-#include "services/data_decoder/public/mojom/json_parser.mojom.h"
 #include "services/data_decoder/public/mojom/structured_headers_parser.mojom.h"
 #include "services/data_decoder/public/mojom/xml_parser.mojom.h"
 
@@ -34,8 +33,6 @@
   // data_decoder::mojom::DataDecoderService:
   void BindImageDecoder(mojo::PendingReceiver<data_decoder::mojom::ImageDecoder>
                             receiver) override;
-  void BindJsonParser(mojo::PendingReceiver<data_decoder::mojom::JsonParser>
-                          receiver) override;
   void BindStructuredHeadersParser(
       mojo::PendingReceiver<data_decoder::mojom::StructuredHeadersParser>
           receiver) override;
diff --git a/services/data_decoder/public/cpp/test_support/in_process_data_decoder.cc b/services/data_decoder/public/cpp/test_support/in_process_data_decoder.cc
index 9ddde27..239041c 100644
--- a/services/data_decoder/public/cpp/test_support/in_process_data_decoder.cc
+++ b/services/data_decoder/public/cpp/test_support/in_process_data_decoder.cc
@@ -59,11 +59,6 @@
   GetForwardingInterface()->BindImageDecoder(std::move(receiver));
 }
 
-void InProcessDataDecoder::BindJsonParser(
-    mojo::PendingReceiver<mojom::JsonParser> receiver) {
-  GetForwardingInterface()->BindJsonParser(std::move(receiver));
-}
-
 void InProcessDataDecoder::BindWebBundleParserFactory(
     mojo::PendingReceiver<web_package::mojom::WebBundleParserFactory>
         receiver) {
diff --git a/services/data_decoder/public/cpp/test_support/in_process_data_decoder.h b/services/data_decoder/public/cpp/test_support/in_process_data_decoder.h
index 7169bad..1deb349 100644
--- a/services/data_decoder/public/cpp/test_support/in_process_data_decoder.h
+++ b/services/data_decoder/public/cpp/test_support/in_process_data_decoder.h
@@ -63,8 +63,6 @@
   mojom::DataDecoderService* GetForwardingInterface() override;
   void BindImageDecoder(
       mojo::PendingReceiver<mojom::ImageDecoder> receiver) override;
-  void BindJsonParser(
-      mojo::PendingReceiver<mojom::JsonParser> receiver) override;
   void BindWebBundleParserFactory(
       mojo::PendingReceiver<web_package::mojom::WebBundleParserFactory>
           receiver) override;
diff --git a/services/data_decoder/public/mojom/BUILD.gn b/services/data_decoder/public/mojom/BUILD.gn
index 89edc148..7f8fb52 100644
--- a/services/data_decoder/public/mojom/BUILD.gn
+++ b/services/data_decoder/public/mojom/BUILD.gn
@@ -10,7 +10,6 @@
     "data_decoder_service.mojom",
     "gzipper.mojom",
     "image_decoder.mojom",
-    "json_parser.mojom",
     "structured_headers_parser.mojom",
   ]
 
diff --git a/services/data_decoder/public/mojom/data_decoder_service.mojom b/services/data_decoder/public/mojom/data_decoder_service.mojom
index 8779e65..d3ac389 100644
--- a/services/data_decoder/public/mojom/data_decoder_service.mojom
+++ b/services/data_decoder/public/mojom/data_decoder_service.mojom
@@ -10,7 +10,6 @@
 import "services/data_decoder/public/mojom/cbor_parser.mojom";
 import "services/data_decoder/public/mojom/gzipper.mojom";
 import "services/data_decoder/public/mojom/image_decoder.mojom";
-import "services/data_decoder/public/mojom/json_parser.mojom";
 import "services/data_decoder/public/mojom/structured_headers_parser.mojom";
 import "services/data_decoder/public/mojom/xml_parser.mojom";
 
@@ -23,9 +22,6 @@
   // Binds an interface which can be used to decode compressed image data.
   BindImageDecoder(pending_receiver<ImageDecoder> receiver);
 
-  // Binds an interface which can be used to parse JSON data.
-  BindJsonParser(pending_receiver<JsonParser> receiver);
-
   // Binds an interface which can be used to parse XML data.
   BindXmlParser(pending_receiver<XmlParser> reciever);
 
diff --git a/services/data_decoder/public/mojom/json_parser.mojom b/services/data_decoder/public/mojom/json_parser.mojom
deleted file mode 100644
index cb761fcb..0000000
--- a/services/data_decoder/public/mojom/json_parser.mojom
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2016 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module data_decoder.mojom;
-
-import "mojo/public/mojom/base/values.mojom";
-
-// Interface to parse JSON documents into a base::Value structure.
-interface JsonParser {
-  // Parses the input |json| according to the base::JSONReader |options|
-  // bitmask. Returns the parsed |result| on success or a string
-  // |error| message on failure.
-  Parse(string json, uint32 options) =>
-      (mojo_base.mojom.Value? result, string? error);
-};
diff --git a/services/network/cookie_manager_unittest.cc b/services/network/cookie_manager_unittest.cc
index 6498e754..96a2d06 100644
--- a/services/network/cookie_manager_unittest.cc
+++ b/services/network/cookie_manager_unittest.cc
@@ -2412,24 +2412,26 @@
 
 TEST_F(CookieManagerTest, BlockThirdPartyCookies) {
   const GURL kThisURL = GURL("http://www.this.com");
+  const net::CookiePartitionKey cookie_partition_key =
+      net::CookiePartitionKey::FromURLForTesting(kThisURL);
   const url::Origin kThisOrigin = url::Origin::Create(kThisURL);
   const net::SiteForCookies kThisSiteForCookies =
       net::SiteForCookies::FromOrigin(kThisOrigin);
   const net::SiteForCookies kNullSiteForCookies;
   EXPECT_TRUE(service()->cookie_settings().IsFullCookieAccessAllowed(
-      kThisURL, kNullSiteForCookies, kThisOrigin,
-      net::CookieSettingOverrides()));
+      kThisURL, kNullSiteForCookies, kThisOrigin, net::CookieSettingOverrides(),
+      cookie_partition_key));
 
   // Set block third party cookies to true, cookie should now be blocked.
   cookie_service_client()->BlockThirdPartyCookies(true);
   base::RunLoop().RunUntilIdle();
 
   EXPECT_FALSE(service()->cookie_settings().IsFullCookieAccessAllowed(
-      kThisURL, kNullSiteForCookies, kThisOrigin,
-      net::CookieSettingOverrides()));
+      kThisURL, kNullSiteForCookies, kThisOrigin, net::CookieSettingOverrides(),
+      cookie_partition_key));
   EXPECT_TRUE(service()->cookie_settings().IsFullCookieAccessAllowed(
-      kThisURL, kThisSiteForCookies, kThisOrigin,
-      net::CookieSettingOverrides()));
+      kThisURL, kThisSiteForCookies, kThisOrigin, net::CookieSettingOverrides(),
+      cookie_partition_key));
 
   // Set block third party cookies back to false, cookie should no longer be
   // blocked.
@@ -2437,8 +2439,8 @@
   base::RunLoop().RunUntilIdle();
 
   EXPECT_TRUE(service()->cookie_settings().IsFullCookieAccessAllowed(
-      kThisURL, kNullSiteForCookies, kThisOrigin,
-      net::CookieSettingOverrides()));
+      kThisURL, kNullSiteForCookies, kThisOrigin, net::CookieSettingOverrides(),
+      cookie_partition_key));
 }
 
 // A test class having cookie store with a persistent backing store.
diff --git a/services/network/network_service_network_delegate.cc b/services/network/network_service_network_delegate.cc
index 6052048..8f991bc 100644
--- a/services/network/network_service_network_delegate.cc
+++ b/services/network/network_service_network_delegate.cc
@@ -345,9 +345,9 @@
     const url::Origin& origin) const {
   return network_context_->cookie_manager()
       ->cookie_settings()
-      .IsFullCookieAccessAllowed(origin.GetURL(),
-                                 net::SiteForCookies::FromOrigin(origin),
-                                 origin, net::CookieSettingOverrides());
+      .IsFullCookieAccessAllowed(
+          origin.GetURL(), net::SiteForCookies::FromOrigin(origin), origin,
+          net::CookieSettingOverrides(), /*cookie_partition_key=*/std::nullopt);
 }
 
 void NetworkServiceNetworkDelegate::OnCanSendReportingReports(
@@ -379,9 +379,9 @@
     const GURL& endpoint) const {
   return network_context_->cookie_manager()
       ->cookie_settings()
-      .IsFullCookieAccessAllowed(origin.GetURL(),
-                                 net::SiteForCookies::FromOrigin(origin),
-                                 origin, net::CookieSettingOverrides());
+      .IsFullCookieAccessAllowed(
+          origin.GetURL(), net::SiteForCookies::FromOrigin(origin), origin,
+          net::CookieSettingOverrides(), /*cookie_partition_key=*/std::nullopt);
 }
 
 bool NetworkServiceNetworkDelegate::OnCanUseReportingClient(
@@ -389,9 +389,9 @@
     const GURL& endpoint) const {
   return network_context_->cookie_manager()
       ->cookie_settings()
-      .IsFullCookieAccessAllowed(origin.GetURL(),
-                                 net::SiteForCookies::FromOrigin(origin),
-                                 origin, net::CookieSettingOverrides());
+      .IsFullCookieAccessAllowed(
+          origin.GetURL(), net::SiteForCookies::FromOrigin(origin), origin,
+          net::CookieSettingOverrides(), /*cookie_partition_key=*/std::nullopt);
 }
 
 int NetworkServiceNetworkDelegate::HandleClearSiteDataHeader(
diff --git a/services/network/restricted_cookie_manager.cc b/services/network/restricted_cookie_manager.cc
index 35593610..6096cd52 100644
--- a/services/network/restricted_cookie_manager.cc
+++ b/services/network/restricted_cookie_manager.cc
@@ -1112,7 +1112,8 @@
           storage_access_api_status,
           /*is_ad_tagged=*/false,
           /*apply_devtools_overrides=*/apply_devtools_overrides,
-          /*force_disable_third_party_cookies=*/false)));
+          /*force_disable_third_party_cookies=*/false),
+      cookie_partition_key_));
 }
 
 void RestrictedCookieManager::InstallReceiver(
diff --git a/services/strings/BUILD.gn b/services/strings/BUILD.gn
index 6bf8d877..d020be2 100644
--- a/services/strings/BUILD.gn
+++ b/services/strings/BUILD.gn
@@ -7,17 +7,20 @@
 import("//tools/grit/grit_rule.gni")
 import("//tools/grit/repack.gni")
 
-grit("strings") {
+grit_strings("strings") {
   source = "../services_strings.grd"
   defines = [ "enable_screen_ai_service=$enable_screen_ai_service" ]
-  outputs =
-      [ "grit/services_strings.h" ] +
-      process_file_template(all_chrome_locales,
-                            [ "services_strings_{{source_name_part}}.pak" ])
+  outputs = [ "grit/services_strings.h" ]
+  output_prefix = "services_strings_"
 }
 
 repack("services_test_strings") {
-  sources = [ "$root_gen_dir/services/strings/services_strings_en-US.pak" ]
+  if (translate_genders) {
+    sources =
+        [ "$root_gen_dir/services/strings/services_strings_en-US_OTHER.pak" ]
+  } else {
+    sources = [ "$root_gen_dir/services/strings/services_strings_en-US.pak" ]
+  }
   output = "$root_out_dir/services_test_strings.pak"
   deps = [ ":strings" ]
 }
diff --git a/services/webnn/tflite/graph_builder_tflite.cc b/services/webnn/tflite/graph_builder_tflite.cc
index d4ef9f4..8643822 100644
--- a/services/webnn/tflite/graph_builder_tflite.cc
+++ b/services/webnn/tflite/graph_builder_tflite.cc
@@ -1679,6 +1679,58 @@
 }
 
 std::optional<GraphBuilderTflite::TensorInfo>
+GraphBuilderTflite::CanFuseQuantizeAndGetOutput(const mojom::Pool2d& pool2d) {
+  // L2Pool doesn't support quantized implementation.
+  CHECK_NE(pool2d.kind, mojom::Pool2d::Kind::kL2Pool2d);
+
+  if (!IsDequantizeOutput(pool2d.input_operand_id)) {
+    return std::nullopt;
+  }
+
+  const mojom::DequantizeLinear& input_dequantize =
+      GetDequantizeOp(pool2d.input_operand_id);
+  if (!IsInts8AndScalarScale(input_dequantize)) {
+    return std::nullopt;
+  }
+
+  // TODO(crbug.com/413083273): Consider the restriction in GPU delegate.
+  // For the kernel of pooling, the `|input scale - output scale|` must be less
+  // than 1.0e-6, and zero point of output tensor must be the same as input.
+  // https://source.chromium.org/chromium/chromium/src/+/main:third_party/tflite/src/tensorflow/lite/kernels/pooling.cc;drc=edd09bcc365dcc696d0f23ca7c3dc18f5e1dcdab;l=101
+  std::optional<size_t> next_op = IsNextOpQuantize(
+      pool2d.output_operand_id,
+      {GetOperand(input_dequantize.input_operand_id).descriptor.data_type()});
+  if (!next_op) {
+    return std::nullopt;
+  }
+  const mojom::QuantizeLinear& output_quantize = GetQuantizeOp(*next_op);
+  if (!IsInts8AndScalarScale(output_quantize)) {
+    return std::nullopt;
+  }
+  base::span<const float> input_scale_values =
+      GetConstantValue<float>(input_dequantize.scale_operand_id);
+  base::span<const float> output_scale_values =
+      GetConstantValue<float>(output_quantize.scale_operand_id);
+  base::CheckedNumeric<float> checked_sub_scale =
+      base::MakeCheckedNum<float>(input_scale_values[0]) -
+      output_scale_values[0];
+  if (!checked_sub_scale.IsValid() ||
+      checked_sub_scale.Abs().ValueOrDie() > 1.0e-6) {
+    return std::nullopt;
+  }
+
+  base::FixedArray<int64_t> input_zero_point_values =
+      GetConstantInt64Value(input_dequantize.zero_point_operand_id);
+  base::FixedArray<int64_t> output_zero_point_values =
+      GetConstantInt64Value(output_quantize.zero_point_operand_id);
+  if (input_zero_point_values[0] != output_zero_point_values[0]) {
+    return std::nullopt;
+  }
+
+  return TrySerializeQuantizedOutput(*next_op);
+}
+
+std::optional<GraphBuilderTflite::TensorInfo>
 GraphBuilderTflite::CanFuseQuantizeAndGetOutput(
     const mojom::Transpose& transpose) {
   if (!IsDequantizeOutput(transpose.input_operand_id)) {
@@ -1802,13 +1854,9 @@
   }
 
   const mojom::QuantizeLinear& output_quantize = GetQuantizeOp(*next_op);
-  if (GetOperand(output_quantize.scale_operand_id)
-          .descriptor.NumberOfElements() != 1) {
+  if (!IsInts8AndScalarScale(output_quantize)) {
     return std::nullopt;
   }
-  CHECK_EQ(GetOperand(output_quantize.zero_point_operand_id)
-               .descriptor.NumberOfElements(),
-           1u);
 
   return next_op;
 }
@@ -1972,23 +2020,24 @@
   return quantize_op_idx;
 }
 
-bool GraphBuilderTflite::IsInts8AndScalarScale(
-    const mojom::DequantizeLinear& dequantize_linear) {
-  if (!DataTypeConstraint::kInts8.Has(
-          GetOperand(dequantize_linear.input_operand_id)
-              .descriptor.data_type())) {
-    return false;
+template <typename OpType>
+  requires(std::is_same_v<OpType, mojom::DequantizeLinear> ||
+           std::is_same_v<OpType, mojom::QuantizeLinear>)
+bool GraphBuilderTflite::IsInts8AndScalarScale(const OpType& op) {
+  if constexpr (std::is_same_v<OpType, mojom::DequantizeLinear>) {
+    if (!DataTypeConstraint::kInts8.Has(
+            GetOperand(op.input_operand_id).descriptor.data_type())) {
+      return false;
+    }
   }
 
-  if (GetOperand(dequantize_linear.scale_operand_id)
-          .descriptor.NumberOfElements() != 1) {
+  if (GetOperand(op.scale_operand_id).descriptor.NumberOfElements() != 1) {
     return false;
   }
 
   // The shape of scale and zero point is the same that has been verified in
   // the function ValidateScaleZeroPointOperandShapeIsCompatibleWithInput.
-  CHECK_EQ(GetOperand(dequantize_linear.zero_point_operand_id)
-               .descriptor.NumberOfElements(),
+  CHECK_EQ(GetOperand(op.zero_point_operand_id).descriptor.NumberOfElements(),
            1u);
   return true;
 }
@@ -5510,7 +5559,27 @@
     return base::unexpected("Pool2d in tflite doesn't support dilations.");
   }
 
+  ::tflite::BuiltinOperator operator_code;
+  std::optional<TensorInfo> quantized_output;
   const mojom::Operand& input_operand = GetOperand(pool2d.input_operand_id);
+  switch (pool2d.kind) {
+    case mojom::Pool2d::Kind::kAveragePool2d:
+      CHECK(context_properties_.data_type_limits.average_pool2d_input.Supports(
+          input_operand.descriptor));
+      operator_code = ::tflite::BuiltinOperator_AVERAGE_POOL_2D;
+      quantized_output = CanFuseQuantizeAndGetOutput(pool2d);
+      break;
+    case mojom::Pool2d::Kind::kMaxPool2d:
+      CHECK(context_properties_.data_type_limits.max_pool2d_input.Supports(
+          input_operand.descriptor));
+      operator_code = ::tflite::BuiltinOperator_MAX_POOL_2D;
+      quantized_output = CanFuseQuantizeAndGetOutput(pool2d);
+      break;
+    case mojom::Pool2d::Kind::kL2Pool2d:
+      // TODO(crbug.com/361717758): Support L2Pool2d.
+      return base::unexpected("L2Pool2d is not supported in tflite.");
+  }
+
   const auto& input_shape = input_operand.descriptor.shape();
   CHECK_EQ(input_shape.size(), 4u);
   const webnn::Size2d<uint32_t> input_size2d = {.height = input_shape[1],
@@ -5523,8 +5592,13 @@
       GetTfLitePaddingMode(*pool2d.padding, input_size2d, filter_size2d,
                            *pool2d.strides, *pool2d.dilations,
                            /*is_transposed_conv2d=*/false));
-  ASSIGN_OR_RETURN(const TensorInfo& input_tensor_info,
-                   SerializeInputTensorInfo(pool2d.input_operand_id));
+  ASSIGN_OR_RETURN(
+      const TensorInfo& input_tensor_info,
+      SerializeInputTensorInfo(
+          pool2d.input_operand_id,
+          /*quantize_params=*/0,
+          /*operation_supports_float16=*/false,
+          /*fuse_dequantize_quantize=*/quantized_output.has_value()));
   // Insert a Pad operator before TfLite Pool2d if needed for explicit padding.
   std::optional<TensorIndex> explicit_pad_index;
   if (padding_mode.paddings) {
@@ -5533,22 +5607,6 @@
         InsertPadOperation(input_tensor_info, padding_mode.paddings.value()));
   }
 
-  ::tflite::BuiltinOperator operator_code;
-  switch (pool2d.kind) {
-    case mojom::Pool2d::Kind::kAveragePool2d:
-      CHECK(context_properties_.data_type_limits.average_pool2d_input.Supports(
-          input_operand.descriptor));
-      operator_code = ::tflite::BuiltinOperator_AVERAGE_POOL_2D;
-      break;
-    case mojom::Pool2d::Kind::kMaxPool2d:
-      CHECK(context_properties_.data_type_limits.max_pool2d_input.Supports(
-          input_operand.descriptor));
-      operator_code = ::tflite::BuiltinOperator_MAX_POOL_2D;
-      break;
-    case mojom::Pool2d::Kind::kL2Pool2d:
-      return base::unexpected("L2Pool2d is not supported in tflite.");
-  }
-
   const auto pool_2d_options = ::tflite::CreatePool2DOptions(
       builder_, padding_mode.mode, pool2d.strides->width,
       pool2d.strides->height, filter_size2d.width, filter_size2d.height,
@@ -5557,14 +5615,20 @@
   // Create `tflite::Operator` with the tensor index of inputs and outputs
   // operand. The type of operation is determined by the index of the operator
   // code.
-  ASSIGN_OR_RETURN(const TensorInfo& output_tensor_info,
-                   SerializeOutputTensorInfo(pool2d.output_operand_id));
+  int32_t output_tensor_index;
+  if (quantized_output) {
+    output_tensor_index = quantized_output->index;
+  } else {
+    ASSIGN_OR_RETURN(const TensorInfo& output_tensor_info,
+                     SerializeOutputTensorInfo(pool2d.output_operand_id));
+    output_tensor_index = output_tensor_info.index;
+  }
   const OperatorCodeIndex operator_code_index =
       GetOperatorCodeIndex(operator_code);
   const std::array<TensorIndex, 1> op_inputs = {explicit_pad_index
                                                     ? explicit_pad_index.value()
                                                     : input_tensor_info.index};
-  const std::array<TensorIndex, 1> op_outputs = {output_tensor_info.index};
+  const std::array<TensorIndex, 1> op_outputs = {output_tensor_index};
   return ::tflite::CreateOperator(
       builder_, operator_code_index,
       builder_.CreateVector<TensorIndex>(op_inputs),
diff --git a/services/webnn/tflite/graph_builder_tflite.h b/services/webnn/tflite/graph_builder_tflite.h
index f986301..12e3d69 100644
--- a/services/webnn/tflite/graph_builder_tflite.h
+++ b/services/webnn/tflite/graph_builder_tflite.h
@@ -724,6 +724,8 @@
       const mojom::ElementWiseBinary& binary);
   std::optional<TensorInfo> CanFuseQuantizeAndGetOutput(const mojom::Elu& elu);
   std::optional<TensorInfo> CanFuseQuantizeAndGetOutput(
+      const mojom::Pool2d& pool2d);
+  std::optional<TensorInfo> CanFuseQuantizeAndGetOutput(
       const mojom::Transpose& transpose);
   std::optional<TensorInfo> CanFuseQuantizeAndGetOutput(
       const mojom::Tanh& tanh);
@@ -763,12 +765,16 @@
   std::optional<OperationId> IsNextOpQuantize(
       OperandId output_operand_id,
       SupportedDataTypes supported_quantized_types);
-  // Check if the input is dequantized from (u)int8, and its scale and zero
-  // point are scalar values.
+  // Check if the input of DequantizeLinear is (u)int8, the output of
+  // QuantizeLinear has been validated (u)int8 in `IsNextOpQuantize`, and its
+  // scale and zero point are scalar values.
   //
   // Used by DQ->op->Q fusion to satisfy XNNPACK delegate's validation in
   // `CheckTensorFloat32OrQUInt8Type`.
-  bool IsInts8AndScalarScale(const mojom::DequantizeLinear& dequantize_linear);
+  template <typename OpType>
+    requires(std::is_same_v<OpType, mojom::DequantizeLinear> ||
+             std::is_same_v<OpType, mojom::QuantizeLinear>)
+  bool IsInts8AndScalarScale(const OpType& op);
 
   bool IsSerializedWithMismatchQuantizeParameters(
       OperandId operand_id,
diff --git a/testing/buildbot/README.md b/testing/buildbot/README.md
index 1876694..85f6f45 100644
--- a/testing/buildbot/README.md
+++ b/testing/buildbot/README.md
@@ -426,7 +426,7 @@
        isolate, invoke a wrapper script from src/testing/scripts as their
        top-level entry point, and are used to adapt to multiple kinds of test
        harnesses. These must implement the
-       [Test Executable API](//docs/testing/test_executable_api.md) and
+       [Test Executable API](/docs/testing/test_executable_api.md) and
        can also be run either locally or under Swarming.
     * `junit_tests`: (Android-specific) JUnit tests. These are not run
        under Swarming.
diff --git a/testing/buildbot/chromium.perf.fyi.json b/testing/buildbot/chromium.perf.fyi.json
index 9f68936..01a4ad9 100644
--- a/testing/buildbot/chromium.perf.fyi.json
+++ b/testing/buildbot/chromium.perf.fyi.json
@@ -91,7 +91,6 @@
           "--ignore-benchmark-exit-code",
           "--output-format=histograms",
           "--experimental-tbmv3-metrics",
-          "--extra-path=/b/s/w/ir/bin/",
           "-d",
           "--os-check=ignore"
         ],
@@ -104,13 +103,6 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "fuchsia/third_party/openssh-portable/${platform}",
-              "location": ".",
-              "revision": "build_id:8787350426829126785"
-            }
-          ],
           "dimensions": {
             "cpu": null,
             "device_type": "Nelson",
@@ -147,7 +139,6 @@
           "--ignore-benchmark-exit-code",
           "--output-format=histograms",
           "--experimental-tbmv3-metrics",
-          "--extra-path=/b/s/w/ir/bin/",
           "-d",
           "--os-check=ignore"
         ],
@@ -160,13 +151,6 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "fuchsia/third_party/openssh-portable/${platform}",
-              "location": ".",
-              "revision": "build_id:8787350426829126785"
-            }
-          ],
           "dimensions": {
             "cpu": null,
             "device_type": "Sherlock",
diff --git a/testing/scripts/run_performance_tests.py b/testing/scripts/run_performance_tests.py
index 91a8ec6c..28a73aeb 100755
--- a/testing/scripts/run_performance_tests.py
+++ b/testing/scripts/run_performance_tests.py
@@ -31,7 +31,7 @@
 TESTING:
 To test changes to this script, please run unit tests:
 $ cd testing/scripts
-$ vpython3 -m unittest run_performance_tests_unittest.py
+$ vpython3 run_performance_tests_unittest.py
 
 Run end-to-end tests:
 $ cd tools/perf
@@ -739,6 +739,7 @@
 
   def __init__(self, options, isolated_out_dir):
     self.options = options
+    self._parse_arguments()
     self.isolated_out_dir = isolated_out_dir
     self.network = self._get_network_arg(options.passthrough_args)
     if self.options.luci_chromium:
@@ -751,7 +752,15 @@
       browser_arg = _get_browser_arg(options.passthrough_args)
       self.is_android = _is_android(browser_arg)
       self._find_browser(browser_arg)
-      self.driver_path_arg = self._find_chromedriver()
+
+  def _parse_arguments(self):
+    parser = argparse.ArgumentParser()
+    parser.add_argument('--official-browser',
+                        type=str,
+                        required=False,
+                        help='Use official build of the browser')
+    self.cb_options, self.options.passthrough_args = parser.parse_known_args(
+        self.options.passthrough_args)
 
   def _get_network_arg(self, args):
     if _arg := _get_arg(args, '--network='):
@@ -818,6 +827,14 @@
         arg for arg in self.options.passthrough_args
         if not arg.startswith('--browser=')
     ]
+    if self.cb_options.official_browser:
+      if self.is_android:
+        raise RuntimeError(
+            'Running official build not yet supported on Android')
+      self.browser = self.CHROME_BROWSER % self.cb_options.official_browser
+      self.driver_path_arg = []
+      return
+    self.driver_path_arg = self._find_chromedriver()
     if '/' in browser_arg or '\\' in browser_arg:
       # The --browser arg looks like a path. Use it as-is.
       self.browser = self.CHROME_BROWSER % browser_arg
diff --git a/testing/scripts/run_performance_tests_unittest.py b/testing/scripts/run_performance_tests_unittest.py
index 195178e2..cbede1a 100644
--- a/testing/scripts/run_performance_tests_unittest.py
+++ b/testing/scripts/run_performance_tests_unittest.py
@@ -148,7 +148,8 @@
     del mock_exists
     run_performance_tests.copy_map_file_to_out_dir('file', 'dir')
 
-    mock_copyfile.assert_called_with('file', 'dir/benchmarks_shard_map.json')
+    mock_copyfile.assert_called_with(
+        'file', str(pathlib.Path('dir/benchmarks_shard_map.json')))
   # pylint: enable=no-self-use
 
   @mock.patch.object(run_performance_tests.CrossbenchTest,
@@ -399,7 +400,8 @@
     network_dict = json.loads(crosebench_test.network[0].split('=', 1)[1])
     self.assertDictEqual(network_dict, expected_dict)
 
-  def testCrossbenchFindBrowserFromEmbedder(self):
+  @mock.patch.object(run_performance_tests.browser_finder, 'FindBrowser')
+  def testCrossbenchFindBrowserFromEmbedder(self, _):
     fake_args = (
         _create_crossbench_args('android-webview-trichrome-google-bundle') +
         ['--embedder=org.foo.bar'])
@@ -410,7 +412,24 @@
     expected_hjson = crossbench_test.ANDROID_HJSON % (
         'org.foo.bar', run_performance_tests.ADB_TOOL)
     expected_browser = crossbench_test.CHROME_BROWSER % expected_hjson
-    self.assertEqual(crossbench_test.network, expected_browser)
+    self.assertEqual(crossbench_test.browser, expected_browser)
+
+  def testCrossbenchOfficialBrowser(self):
+    fake_args = _create_crossbench_args()
+    fake_args.append('--official-browser=chrome-stable-1.2.3.4')
+    options = run_performance_tests.parse_arguments(fake_args)
+
+    cb_test = run_performance_tests.CrossbenchTest(options, 'dir')
+    command_list = cb_test._generate_command_list(
+        cb_test.options.benchmarks, cb_test.options.passthrough_args, 'dir')
+
+    self.assertIn('--browser=chrome-stable-1.2.3.4', command_list)
+    # The --official-browser arg should have been removed from command_list.
+    self.assertFalse(
+        [x for x in command_list if x.startswith('--official-browser')])
+    # Crossbench should find the official build of WebDriver, instead of using
+    # a path from us.
+    self.assertFalse([x for x in command_list if x.startswith('--driver-path')])
 
 
 def _create_crossbench_args(browser='./chrome'):
@@ -421,3 +440,7 @@
       '--benchmark-display-name=speedometer3.crossbench',
       f'--browser={browser}',
   ]
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/third_party/androidx/build.gradle b/third_party/androidx/build.gradle
index 0c94aca..9183abf 100644
--- a/third_party/androidx/build.gradle
+++ b/third_party/androidx/build.gradle
@@ -304,7 +304,7 @@
     google()
     maven {
         // This URL is generated by the fetch_all_androidx.py script.
-        url 'https://androidx.dev/snapshots/builds/13465832/artifacts/repository'
+        url 'https://androidx.dev/snapshots/builds/13468622/artifacts/repository'
     }
     mavenCentral()
 }
diff --git a/third_party/angle b/third_party/angle
index 00845fd..b729fb7 160000
--- a/third_party/angle
+++ b/third_party/angle
@@ -1 +1 @@
-Subproject commit 00845fd649a450a00e61215f44bb4fc95d515c97
+Subproject commit b729fb74e68bb2b3db6c95067f4ffc1e0c25bc07
diff --git a/third_party/blink/public/strings/BUILD.gn b/third_party/blink/public/strings/BUILD.gn
index 0a7fda7..f853223 100644
--- a/third_party/blink/public/strings/BUILD.gn
+++ b/third_party/blink/public/strings/BUILD.gn
@@ -5,20 +5,16 @@
 import("//build/config/locales.gni")
 import("//tools/grit/grit_rule.gni")
 
-grit("strings") {
+grit_strings("strings") {
   source = "blink_strings.grd"
-  outputs = [ "grit/blink_strings.h" ] +
-            process_file_template(all_chrome_locales,
-                                  [ "blink_strings_{{source_name_part}}.pak" ])
+  outputs = [ "grit/blink_strings.h" ]
+  output_prefix = "blink_strings_"
 }
 
 # The base strings for the permission element.
-grit("permission_element_strings") {
+grit_strings("permission_element_strings") {
   source = "permission_element_strings.grd"
   outputs = [ "grit/permission_element_strings.h" ]
-  foreach(locale, all_chrome_locales) {
-    outputs += [ "permission_element_strings_$locale.pak" ]
-  }
 }
 
 action("generate_permission_element_strings_mono_grd") {
diff --git a/third_party/blink/public/web/web_document_loader.h b/third_party/blink/public/web/web_document_loader.h
index 0527831..33e2368 100644
--- a/third_party/blink/public/web/web_document_loader.h
+++ b/third_party/blink/public/web/web_document_loader.h
@@ -141,8 +141,6 @@
       CrossVariantMojoRemote<mojom::CodeCacheHostInterfaceBase>
           code_cache_host_for_background) = 0;
 
-  virtual WebString OriginCalculationDebugInfo() const = 0;
-
   // Whether the frame holding this document has loaded a document that is not
   // an initial empty document.
   virtual bool HasLoadedNonInitialEmptyDocument() const = 0;
diff --git a/third_party/blink/renderer/core/css/document_style_environment_variables.cc b/third_party/blink/renderer/core/css/document_style_environment_variables.cc
index 441c942..d26030c 100644
--- a/third_party/blink/renderer/core/css/document_style_environment_variables.cc
+++ b/third_party/blink/renderer/core/css/document_style_environment_variables.cc
@@ -56,10 +56,8 @@
     Document& document)
     : StyleEnvironmentVariables(parent), document_(&document) {
   if (RuntimeEnabledFeatures::CSSPreferredTextScaleEnabled()) {
-    SetVariable(
-        UADefinedVariable::kPreferredTextScale,
-        String::Number(
-            document_->GetSettings()->GetAccessibilityFontScaleFactor()));
+    SetPreferredTextScale(
+        document_->GetSettings()->GetAccessibilityFontScaleFactor());
   }
 }
 
@@ -97,5 +95,42 @@
     // Do nothing if this is an unknown variable.
   }
 }
+namespace {
+
+double SnapToClosestFontScaleBucket(double raw_font_scale) {
+  static const std::array<double, 7> font_scale_buckets = {0.85, 1,   1.15, 1.3,
+                                                           1.5,  1.8, 2};
+
+  // Handle cases where the input value is outside the range of literals.
+  if (raw_font_scale <= font_scale_buckets.front()) {
+    return font_scale_buckets.front();
+  }
+  if (raw_font_scale >= font_scale_buckets.back()) {
+    return font_scale_buckets.back();
+  }
+
+  // std::lower_bound finds the first element >= raw_font_scale.
+  const auto it = std::lower_bound(font_scale_buckets.begin(),
+                                   font_scale_buckets.end(), raw_font_scale);
+
+  const double higher_candidate = *it;
+  // The element before 'it' is the other candidate.
+  // Safe because we handled edge cases above.
+  const double lower_candidate = *(it - 1);
+
+  const double diff_to_lower = std::abs(raw_font_scale - lower_candidate);
+  const double diff_to_higher = std::abs(raw_font_scale - higher_candidate);
+
+  return (diff_to_lower <= diff_to_higher) ? lower_candidate : higher_candidate;
+}
+
+}  // namespace
+
+void DocumentStyleEnvironmentVariables::SetPreferredTextScale(
+    double raw_font_scale_from_os) {
+  SetVariable(
+      UADefinedVariable::kPreferredTextScale,
+      String::Number(SnapToClosestFontScaleBucket(raw_font_scale_from_os)));
+}
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/document_style_environment_variables.h b/third_party/blink/renderer/core/css/document_style_environment_variables.h
index 52b3cb0..b04af42 100644
--- a/third_party/blink/renderer/core/css/document_style_environment_variables.h
+++ b/third_party/blink/renderer/core/css/document_style_environment_variables.h
@@ -39,6 +39,8 @@
                                    WTF::Vector<unsigned> indices,
                                    bool record_metrics);
 
+  void SetPreferredTextScale(double);
+
   // Resolve the variable |name| and return the data. This will also cause
   // future changes to this variable to invalidate the associated document's
   // style. UseCounter metrics will be recorded when this function is used.
diff --git a/third_party/blink/renderer/core/html/forms/option_list.cc b/third_party/blink/renderer/core/html/forms/option_list.cc
index 76fdd36f..49807dc0 100644
--- a/third_party/blink/renderer/core/html/forms/option_list.cc
+++ b/third_party/blink/renderer/core/html/forms/option_list.cc
@@ -176,8 +176,7 @@
     return nullptr;
   }
   for (OptionListIterator it = begin(); it; ++it) {
-    if (it->IsKeyboardFocusableSlow(
-            Element::UpdateBehavior::kAssertNoLayoutUpdates)) {
+    if (it->IsKeyboardFocusableSlow(Element::UpdateBehavior::kStyleAndLayout)) {
       return &*it;
     }
   }
diff --git a/third_party/blink/renderer/core/html/forms/text_field_input_type.cc b/third_party/blink/renderer/core/html/forms/text_field_input_type.cc
index d63317b..8a70f8c6 100644
--- a/third_party/blink/renderer/core/html/forms/text_field_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/text_field_input_type.cc
@@ -219,7 +219,8 @@
   if (!GetElement().IsFocused())
     return;
 
-  if (RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled()) {
+  if (RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled() ||
+      RuntimeEnabledFeatures::SelectAccessibilityNestedInputEnabled()) {
     if (auto* select = GetElement().FirstAncestorSelectElement()) {
       if (AtomicString(event.key()) == keywords::kArrowDown) {
         if (auto* option =
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index abcc4436..4eab174 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -373,7 +373,6 @@
   HeapMojoRemote<mojom::blink::ContentSecurityNotifier>
       content_security_notifier_;
   scoped_refptr<SecurityOrigin> origin_to_commit;
-  AtomicString origin_calculation_debug_info;
   BlinkStorageKey storage_key;
   WebNavigationType navigation_type;
   DocumentLoadTiming document_load_timing;
@@ -2333,19 +2332,16 @@
 scoped_refptr<SecurityOrigin> DocumentLoader::CalculateOrigin(
     Document* owner_document) {
   scoped_refptr<SecurityOrigin> origin;
-  StringBuilder debug_info_builder;
   // Whether the origin is newly created within this call, instead of copied
   // from an existing document's origin or from `origin_to_commit_`. If this is
   // true, we won't try to compare the nonce of this origin (if it's opaque) to
   // the browser-calculated origin later on.
-  bool origin_is_newly_created = false;
   if (IsPagePopupRunningInWebTest(frame_)) {
     // If we are a page popup in LayoutTests ensure we use the popup
     // owner's security origin so the tests can possibly access the
     // document via internals API.
     auto* owner_context = frame_->PagePopupOwner()->GetExecutionContext();
     origin = owner_context->GetSecurityOrigin()->IsolatedCopy();
-    debug_info_builder.Append("use_popup_owner_origin");
   } else if (owner_document && owner_document->domWindow()) {
     // Prefer taking `origin` from `owner_document` if one is available - this
     // will correctly inherit/alias `SecurityOrigin::domain_` from the
@@ -2364,19 +2360,6 @@
     // But origin_to_commit_ is currently cloned with IsolatedCopy() which
     // breaks aliasing...
     origin = owner_document->domWindow()->GetMutableSecurityOrigin();
-    debug_info_builder.Append("use_owner_document_origin(");
-    // Add debug information about the owner document too.
-    if (owner_document->GetFrame() == frame_->Tree().Parent()) {
-      debug_info_builder.Append("parent");
-    } else {
-      debug_info_builder.Append("opener");
-    }
-    debug_info_builder.Append(":");
-    debug_info_builder.Append(
-        owner_document->Loader()->origin_calculation_debug_info_);
-    debug_info_builder.Append(", url=");
-    debug_info_builder.Append(owner_document->Url().BaseAsString());
-    debug_info_builder.Append(")");
   } else if (origin_to_commit_) {
     // Origin to commit is specified by the browser process, it must be taken
     // and used directly. An exception is when the owner origin should be
@@ -2385,30 +2368,23 @@
     // non-renderer only origin bits will be the same, which will be asserted at
     // the end of this function.
     origin = origin_to_commit_;
-    debug_info_builder.Append("use_origin_to_commit");
   } else {
-    debug_info_builder.Append("use_url_with_precursor");
     // Otherwise, create an origin that propagates precursor information
     // as needed. For non-opaque origins, this creates a standard tuple
     // origin, but for opaque origins, it creates an origin with the
     // initiator origin as the precursor.
     origin = SecurityOrigin::CreateWithReferenceOrigin(url_,
                                                        requestor_origin_.get());
-    origin_is_newly_created = true;
   }
 
   if ((policy_container_->GetPolicies().sandbox_flags &
        network::mojom::blink::WebSandboxFlags::kOrigin) !=
       network::mojom::blink::WebSandboxFlags::kNone) {
-    debug_info_builder.Append(", add_sandbox[new_origin_precursor=");
     // If `origin_to_commit_` is set, don't create a new opaque origin, but just
     // use `origin_to_commit_`, which is already opaque.
     auto sandbox_origin =
         origin_to_commit_ ? origin_to_commit_ : origin->DeriveNewOpaqueOrigin();
     CHECK(sandbox_origin->IsOpaque());
-    debug_info_builder.Append(
-        sandbox_origin->GetOriginOrPrecursorOriginIfOpaque()->ToString());
-    debug_info_builder.Append("]");
 
     // If we're supposed to inherit our security origin from our
     // owner, but we're also sandboxed, the only things we inherit are
@@ -2433,20 +2409,16 @@
               ->IsPotentiallyTrustworthy();
       if (is_potentially_trustworthy) {
         sandbox_origin->SetOpaqueOriginIsPotentiallyTrustworthy(true);
-        debug_info_builder.Append(", _potentially_trustworthy");
       }
     } else if (owner_document) {
       if (origin->IsPotentiallyTrustworthy()) {
         sandbox_origin->SetOpaqueOriginIsPotentiallyTrustworthy(true);
-        debug_info_builder.Append(", _potentially_trustworthy");
       }
       if (origin->CanLoadLocalResources()) {
         sandbox_origin->GrantLoadLocalResources();
-        debug_info_builder.Append(", _load_local");
       }
     }
     origin = sandbox_origin;
-    origin_is_newly_created = !origin_to_commit_;
   }
 
   if (commit_reason_ == CommitReason::kInitialization &&
@@ -2458,19 +2430,16 @@
     // navigated.
     CHECK(origin->IsOpaque());
     origin->GrantUniversalAccess();
-    debug_info_builder.Append(", universal_access_webview");
   } else if (!frame_->GetSettings()->GetWebSecurityEnabled()) {
     // Web security is turned off. We should let this document access
     // every other document. This is used primary by testing harnesses for
     // web sites.
     origin->GrantUniversalAccess();
-    debug_info_builder.Append(", universal_access_no_web_security");
   } else if (origin->IsLocal()) {
     if (frame_->GetSettings()->GetAllowUniversalAccessFromFileURLs()) {
       // Some clients want local URLs to have universal access, but that
       // setting is dangerous for other clients.
       origin->GrantUniversalAccess();
-      debug_info_builder.Append(", universal_access_allow_file");
     } else if (!frame_->GetSettings()->GetAllowFileAccessFromFileURLs()) {
       // Some clients do not want local URLs to have access to other local
       // URLs.
@@ -2482,34 +2451,20 @@
         // `origin_to_commit_`.
         origin_to_commit_->BlockLocalAccessFromLocalOrigin();
       }
-      debug_info_builder.Append(", universal_access_block_file");
     }
   }
 
   if (grant_load_local_resources_) {
     origin->GrantLoadLocalResources();
-    debug_info_builder.Append(", grant_load_local_resources");
   }
 
   if (origin->IsOpaque()) {
     KURL url = url_.IsEmpty() ? BlankURL() : url_;
     if (SecurityOrigin::Create(url)->IsPotentiallyTrustworthy()) {
       origin->SetOpaqueOriginIsPotentiallyTrustworthy(true);
-      debug_info_builder.Append(", is_potentially_trustworthy");
     }
   }
-  if (origin_is_newly_created) {
-    // This information will be used by the browser side to figure out if it can
-    // do browser vs renderer calculated origin equality check. Note that this
-    // information must be the last part of the debug info string.
-    // TODO(https://crbug.com/888079): Consider adding a separate boolean that
-    // tracks this instead of piggybacking `origin_calculation_debug_info_`.
-    debug_info_builder.Append(", is_newly_created");
-  }
-  origin_calculation_debug_info_ = debug_info_builder.ToAtomicString();
   if (origin_to_commit_) {
-    SCOPED_CRASH_KEY_STRING256("OriginCalc", "debug_info",
-                               origin_calculation_debug_info_.Ascii());
     SCOPED_CRASH_KEY_STRING256("OriginCalc", "url_stripped",
                                url_.StrippedForUseAsReferrer().Ascii());
     SCOPED_CRASH_KEY_BOOL("OriginCalc", "same_ptr",
diff --git a/third_party/blink/renderer/core/loader/document_loader.h b/third_party/blink/renderer/core/loader/document_loader.h
index 10682e12..01b97e56 100644
--- a/third_party/blink/renderer/core/loader/document_loader.h
+++ b/third_party/blink/renderer/core/loader/document_loader.h
@@ -214,9 +214,6 @@
           code_cache_host,
       CrossVariantMojoRemote<mojom::blink::CodeCacheHostInterfaceBase>
           code_cache_host_for_background) override;
-  WebString OriginCalculationDebugInfo() const override {
-    return origin_calculation_debug_info_;
-  }
   bool HasLoadedNonInitialEmptyDocument() const override;
   bool IsForDiscard() const override;
 
@@ -701,11 +698,6 @@
 
   const scoped_refptr<SecurityOrigin> origin_to_commit_;
 
-  // Information about how `origin_to_commit_` was calculated, to help debug if
-  // it differs from the origin calculated on the browser side.
-  // TODO(https://crbug.com/1220238): Remove this.
-  AtomicString origin_calculation_debug_info_;
-
   blink::BlinkStorageKey storage_key_;
 
   WebNavigationType navigation_type_;
diff --git a/third_party/blink/renderer/core/loader/image_loader.cc b/third_party/blink/renderer/core/loader/image_loader.cc
index a27611fb..74dc54f 100644
--- a/third_party/blink/renderer/core/loader/image_loader.cc
+++ b/third_party/blink/renderer/core/loader/image_loader.cc
@@ -414,6 +414,7 @@
 
 void ImageLoader::DoUpdateFromElement(const DOMWrapperWorld* world,
                                       UpdateFromElementBehavior update_behavior,
+                                      const KURL* source_url,
                                       UpdateType update_type,
                                       bool force_blocking) {
   // FIXME: According to
@@ -440,8 +441,13 @@
     return;
   }
 
+  KURL url;
   AtomicString image_source_url = element_->ImageSourceURL();
-  const KURL url = ImageSourceToKURL(image_source_url);
+  if (RuntimeEnabledFeatures::OptimizeHTMLElementUrlsEnabled() && source_url) {
+    url = *source_url;
+  } else {
+    url = ImageSourceToKURL(image_source_url);
+  }
   ImageResourceContent* new_image_content = nullptr;
   if (!url.IsNull() && !url.IsEmpty()) {
     // Unlike raw <img>, we block mixed content inside of <picture> or
@@ -659,10 +665,12 @@
     delay_until_do_update_from_element_ = nullptr;
   }
 
-  if (ShouldLoadImmediately(ImageSourceToKURL(image_source_url)) &&
+  const KURL image_source_kurl = ImageSourceToKURL(image_source_url);
+  if (ShouldLoadImmediately(image_source_kurl) &&
       update_behavior != kUpdateFromMicrotask) {
     DoUpdateFromElement(element_->GetExecutionContext()->GetCurrentWorld(),
-                        update_behavior, UpdateType::kSync, force_blocking);
+                        update_behavior, &image_source_kurl, UpdateType::kSync,
+                        force_blocking);
     return;
   }
   // Allow the idiom "img.src=''; img.src='.." to clear down the image before an
diff --git a/third_party/blink/renderer/core/loader/image_loader.h b/third_party/blink/renderer/core/loader/image_loader.h
index eb6b8fd..d643e57 100644
--- a/third_party/blink/renderer/core/loader/image_loader.h
+++ b/third_party/blink/renderer/core/loader/image_loader.h
@@ -173,6 +173,7 @@
   // force_blocking ensures that the image will block the load event.
   void DoUpdateFromElement(const DOMWrapperWorld* world,
                            UpdateFromElementBehavior,
+                           const KURL* source_url = nullptr,
                            UpdateType = UpdateType::kAsync,
                            bool force_blocking = false);
 
diff --git a/third_party/blink/renderer/core/page/page.cc b/third_party/blink/renderer/core/page/page.cc
index cfa264b..67637ba 100644
--- a/third_party/blink/renderer/core/page/page.cc
+++ b/third_party/blink/renderer/core/page/page.cc
@@ -1093,10 +1093,10 @@
         if (!document || !document->IsActive()) {
           continue;
         }
-        document->GetStyleEngine().EnsureEnvironmentVariables().SetVariable(
-            UADefinedVariable::kPreferredTextScale,
-            String::Number(
-                document->GetSettings()->GetAccessibilityFontScaleFactor()));
+        document->GetStyleEngine()
+            .EnsureEnvironmentVariables()
+            .SetPreferredTextScale(
+                document->GetSettings()->GetAccessibilityFontScaleFactor());
       }
       break;
     case ChangeType::kTextAutosizing:
diff --git a/third_party/blink/renderer/modules/ai/BUILD.gn b/third_party/blink/renderer/modules/ai/BUILD.gn
index 3fee2a63..c917b75 100644
--- a/third_party/blink/renderer/modules/ai/BUILD.gn
+++ b/third_party/blink/renderer/modules/ai/BUILD.gn
@@ -31,6 +31,8 @@
     "language_model_factory.h",
     "language_model_params.cc",
     "language_model_params.h",
+    "language_model_prompt_builder.cc",
+    "language_model_prompt_builder.h",
     "model_execution_responder.cc",
     "model_execution_responder.h",
     "on_device_translation/create_translator_client.cc",
diff --git a/third_party/blink/renderer/modules/ai/language_model.cc b/third_party/blink/renderer/modules/ai/language_model.cc
index edae9ec..66f10d1b 100644
--- a/third_party/blink/renderer/modules/ai/language_model.cc
+++ b/third_party/blink/renderer/modules/ai/language_model.cc
@@ -32,6 +32,7 @@
 #include "third_party/blink/renderer/modules/ai/dom_ai.h"
 #include "third_party/blink/renderer/modules/ai/exception_helpers.h"
 #include "third_party/blink/renderer/modules/ai/language_model_factory.h"
+#include "third_party/blink/renderer/modules/ai/language_model_prompt_builder.h"
 #include "third_party/blink/renderer/modules/ai/model_execution_responder.h"
 #include "third_party/blink/renderer/modules/canvas/imagebitmap/image_bitmap_source_util.h"
 #include "third_party/blink/renderer/modules/event_target_modules_names.h"
@@ -171,238 +172,6 @@
       receiver_;
 };
 
-mojom::blink::AILanguageModelPromptContentPtr ToMojo(String prompt) {
-  return mojom::blink::AILanguageModelPromptContent::NewText(prompt);
-}
-
-mojom::blink::AILanguageModelPromptContentPtr ToMojo(
-    AudioBuffer* audio_buffer,
-    ExceptionState& exception_state) {
-  if (audio_buffer->numberOfChannels() > 2) {
-    // TODO(crbug.com/382180351): Support more than 2 channels.
-    exception_state.ThrowDOMException(
-        DOMExceptionCode::kSyntaxError,
-        "Audio with more than 2 channels is not supported.");
-    return nullptr;
-  }
-  on_device_model::mojom::blink::AudioDataPtr audio_data =
-      on_device_model::mojom::blink::AudioData::New();
-  audio_data->sample_rate = audio_buffer->sampleRate();
-  audio_data->frame_count = audio_buffer->length();
-  // TODO(crbug.com/382180351): Use other mono mixing utils like
-  // AudioBus::CreateByMixingToMono.
-  audio_data->channel_count = 1;
-  base::span<const float> channel0 = audio_buffer->getChannelData(0)->AsSpan();
-  audio_data->data = WTF::Vector<float>(channel0.size());
-  for (size_t i = 0; i < channel0.size(); ++i) {
-    audio_data->data[i] = channel0[i];
-    // If second channel exists, average the two channels to produce mono.
-    if (audio_buffer->numberOfChannels() > 1) {
-      audio_data->data[i] =
-          (audio_data->data[i] + audio_buffer->getChannelData(1)->AsSpan()[i]) /
-          2.0f;
-    }
-  }
-  return mojom::blink::AILanguageModelPromptContent::NewAudio(
-      std::move(audio_data));
-}
-
-mojom::blink::AILanguageModelPromptContentPtr ToMojo(
-    base::span<uint8_t> audio_bytes,
-    ExecutionContext* execution_context,
-    ExceptionState& exception_state) {
-  // TODO(crbug.com/401010825): Use the file sample rate.
-  scoped_refptr<AudioBus> bus = AudioBus::CreateBusFromInMemoryAudioFile(
-      audio_bytes, /*mix_to_mono=*/true, /*sample_rate=*/48000);
-  if (!bus) {
-    // TODO(crbug.com/409615288): This should throw a TypeError according to the
-    // spec.
-    exception_state.ThrowDOMException(DOMExceptionCode::kDataError,
-                                      "Missing or invalid audio data.");
-    return nullptr;
-  }
-
-  on_device_model::mojom::blink::AudioDataPtr audio_data =
-      on_device_model::mojom::blink::AudioData::New();
-  audio_data->sample_rate = bus->SampleRate();
-  audio_data->frame_count = bus->length();
-  audio_data->channel_count = bus->NumberOfChannels();
-  CHECK_EQ(audio_data->channel_count, 1);
-  // TODO(crbug.com/382180351): Avoid a copy.
-  audio_data->data = WTF::Vector<float>(bus->length());
-  std::copy_n(bus->Channel(0)->Data(), bus->Channel(0)->length(),
-              audio_data->data.begin());
-  return mojom::blink::AILanguageModelPromptContent::NewAudio(
-      std::move(audio_data));
-}
-
-mojom::blink::AILanguageModelPromptContentPtr ToMojo(
-    Blob* blob,
-    ExecutionContext* execution_context,
-    ExceptionState& exception_state) {
-  // TODO(crbug.com/382180351): Make blob reading async or alternatively
-  // use FileReaderSync instead (fix linker and exception issues).
-  SyncedFileReaderAccumulator* blobReader =
-      MakeGarbageCollected<SyncedFileReaderAccumulator>();
-
-  auto [error_code, reader_data] = blobReader->Load(
-      blob->GetBlobDataHandle(),
-      execution_context->GetTaskRunner(TaskType::kFileReading));
-  if (error_code != FileErrorCode::kOK) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kDataError,
-                                      "Failed to read blob.");
-    return nullptr;
-  }
-  ArrayBufferContents audio_contents =
-      std::move(reader_data).AsArrayBufferContents();
-  if (!audio_contents.IsValid()) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kDataError,
-                                      "Failed to read blob.");
-    return nullptr;
-  }
-  return ToMojo(audio_contents.ByteSpan(), execution_context, exception_state);
-}
-
-mojom::blink::AILanguageModelPromptContentPtr ToMojo(
-    V8ImageBitmapSource* bitmap,
-    ScriptState* script_state,
-    ExceptionState& exception_state) {
-  std::optional<SkBitmap> skia_bitmap =
-      GetBitmapFromV8ImageBitmapSource(script_state, bitmap, exception_state);
-  if (exception_state.HadException()) {
-    return nullptr;
-  }
-  return mojom::blink::AILanguageModelPromptContent::NewBitmap(
-      skia_bitmap.value());
-}
-
-mojom::blink::AILanguageModelPromptContentPtr ConvertPromptToMojoContent(
-    V8LanguageModelPromptType content_type,
-    const V8LanguageModelPromptContent* content,
-    ScriptState* script_state,
-    ExceptionState& exception_state,
-    ExecutionContext* execution_context,
-    WTF::HashSet<mojom::blink::AILanguageModelPromptType>& allowed_types) {
-  switch (content_type.AsEnum()) {
-    case V8LanguageModelPromptType::Enum::kText:
-      return ToMojo(content->GetAsString());
-    case V8LanguageModelPromptType::Enum::kImage:
-      if (!allowed_types.Contains(
-              mojom::blink::AILanguageModelPromptType::kImage)) {
-        exception_state.ThrowDOMException(
-            DOMExceptionCode::kNotSupportedError,
-            "Image not supported. Session is not initialized with image "
-            "support.");
-        return nullptr;
-      }
-      UseCounter::Count(execution_context,
-                        WebFeature::kLanguageModel_Prompt_Input_Image);
-      if (content->IsV8ImageBitmapSource()) {
-        return ToMojo(content->GetAsV8ImageBitmapSource(), script_state,
-                      exception_state);
-      }
-      exception_state.ThrowDOMException(DOMExceptionCode::kSyntaxError,
-                                        "Unsupported image content type");
-      return nullptr;
-    case V8LanguageModelPromptType::Enum::kAudio:
-      if (!allowed_types.Contains(
-              mojom::blink::AILanguageModelPromptType::kAudio)) {
-        exception_state.ThrowDOMException(
-            DOMExceptionCode::kNotSupportedError,
-            "Audio not supported. Session is not initialized with audio "
-            "support.");
-        return nullptr;
-      }
-      UseCounter::Count(execution_context,
-                        WebFeature::kLanguageModel_Prompt_Input_Audio);
-      switch (content->GetContentType()) {
-        case V8LanguageModelPromptContent::ContentType::kAudioBuffer:
-          return ToMojo(content->GetAsAudioBuffer(), exception_state);
-        case V8LanguageModelPromptContent::ContentType::kBlob:
-          return ToMojo(content->GetAsBlob(), execution_context,
-                        exception_state);
-        case V8LanguageModelPromptContent::ContentType::kArrayBuffer:
-          return ToMojo(content->GetAsArrayBuffer()->Content()->ByteSpan(),
-                        execution_context, exception_state);
-        case V8LanguageModelPromptContent::ContentType::kArrayBufferView:
-          return ToMojo(content->GetAsArrayBufferView()->ByteSpan(),
-                        execution_context, exception_state);
-        default:
-          exception_state.ThrowDOMException(DOMExceptionCode::kSyntaxError,
-                                            "Unsupported audio content type");
-          return nullptr;
-      }
-  }
-}
-
-// Return `prompt`'s content as a mojo struct or nullptr if there was an error.
-mojom::blink::AILanguageModelPromptPtr ConvertPromptToMojo(
-    const V8LanguageModelPrompt* prompt,
-    ScriptState* script_state,
-    ExceptionState& exception_state,
-    ExecutionContext* execution_context,
-    WTF::HashSet<mojom::blink::AILanguageModelPromptType>& allowed_types) {
-  switch (prompt->GetContentType()) {
-    // Handle basic string prompt.
-    case V8LanguageModelPrompt::ContentType::kString: {
-      auto result = mojom::blink::AILanguageModelPrompt::New();
-      result->content = ToMojo(prompt->GetAsString());
-      if (result->content.is_null()) {
-        return nullptr;
-      }
-      result->role = mojom::blink::AILanguageModelPromptRole::kUser;
-      return result;
-    }
-    // Handle dictionary for multimodal input.
-    case V8LanguageModelPrompt::ContentType::kLanguageModelPromptDict:
-      LanguageModelPromptDict* dict = prompt->GetAsLanguageModelPromptDict();
-      auto result = mojom::blink::AILanguageModelPrompt::New();
-      result->content = ConvertPromptToMojoContent(
-          dict->type(), dict->content(), script_state, exception_state,
-          execution_context, allowed_types);
-      if (result->content.is_null()) {
-        return nullptr;
-      }
-      result->role = LanguageModel::ConvertRoleToMojo(dict->role());
-      return result;
-  }
-}
-
-// Populates the `prompts` mojo struct vector from `input`. Returns an exception
-// if some input was specified incorrectly or inaccessible, nullptr otherwise.
-std::optional<WTF::Vector<mojom::blink::AILanguageModelPromptPtr>> BuildPrompts(
-    const V8LanguageModelPromptInput* input,
-    ScriptState* script_state,
-    ExceptionState& exception_state,
-    ExecutionContext* execution_context,
-    WTF::HashSet<mojom::blink::AILanguageModelPromptType>& allowed_types) {
-  WTF::Vector<mojom::blink::AILanguageModelPromptPtr> prompts;
-  if (input->IsLanguageModelPromptDictOrStringSequence()) {
-    const auto& sequence =
-        input->GetAsLanguageModelPromptDictOrStringSequence();
-    for (const auto& entry : sequence) {
-      mojom::blink::AILanguageModelPromptPtr prompt =
-          ConvertPromptToMojo(entry, script_state, exception_state,
-                              execution_context, allowed_types);
-      if (prompt.is_null()) {
-        return std::nullopt;
-      }
-      prompts.push_back(std::move(prompt));
-    }
-  } else {
-    CHECK(input->IsV8LanguageModelPrompt());
-    auto* entry = input->GetAsV8LanguageModelPrompt();
-    mojom::blink::AILanguageModelPromptPtr prompt = ConvertPromptToMojo(
-        entry, script_state, exception_state, execution_context, allowed_types);
-    if (prompt.is_null()) {
-      return std::nullopt;
-    }
-    prompts.push_back(std::move(prompt));
-  }
-
-  return prompts;
-}
-
 // Parses `constraint` from `options` if available. On success or if no
 // constraint is present returns true, returns false and throws an exception on
 // failure.
diff --git a/third_party/blink/renderer/modules/ai/language_model_prompt_builder.cc b/third_party/blink/renderer/modules/ai/language_model_prompt_builder.cc
new file mode 100644
index 0000000..fb81ee8
--- /dev/null
+++ b/third_party/blink/renderer/modules/ai/language_model_prompt_builder.cc
@@ -0,0 +1,269 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/ai/language_model_prompt_builder.h"
+
+#include "third_party/blink/public/mojom/ai/ai_language_model.mojom-blink-forward.h"
+#include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_language_model_create_options.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_language_model_prompt_dict.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_union_language_model_prompt_content.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_union_language_model_prompt_input.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_union_languagemodelpromptdict_string.h"
+#include "third_party/blink/renderer/core/dom/abort_signal.h"
+#include "third_party/blink/renderer/core/dom/events/event_target.h"
+#include "third_party/blink/renderer/core/event_type_names.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
+#include "third_party/blink/renderer/core/fileapi/blob.h"
+#include "third_party/blink/renderer/core/fileapi/file_reader_client.h"
+#include "third_party/blink/renderer/core/frame/web_feature.h"
+#include "third_party/blink/renderer/core/streams/readable_stream.h"
+#include "third_party/blink/renderer/modules/ai/language_model.h"
+#include "third_party/blink/renderer/modules/ai/language_model_factory.h"
+#include "third_party/blink/renderer/modules/ai/language_model_prompt_builder.h"
+#include "third_party/blink/renderer/modules/canvas/imagebitmap/image_bitmap_source_util.h"
+#include "third_party/blink/renderer/modules/webaudio/audio_buffer.h"
+#include "third_party/blink/renderer/platform/audio/audio_bus.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h"
+
+namespace blink {
+namespace {
+
+mojom::blink::AILanguageModelPromptContentPtr ToMojo(String prompt) {
+  return mojom::blink::AILanguageModelPromptContent::NewText(prompt);
+}
+
+mojom::blink::AILanguageModelPromptContentPtr ToMojo(
+    AudioBuffer* audio_buffer,
+    ExceptionState& exception_state) {
+  if (audio_buffer->numberOfChannels() > 2) {
+    // TODO(crbug.com/382180351): Support more than 2 channels.
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kSyntaxError,
+        "Audio with more than 2 channels is not supported.");
+    return nullptr;
+  }
+  on_device_model::mojom::blink::AudioDataPtr audio_data =
+      on_device_model::mojom::blink::AudioData::New();
+  audio_data->sample_rate = audio_buffer->sampleRate();
+  audio_data->frame_count = audio_buffer->length();
+  // TODO(crbug.com/382180351): Use other mono mixing utils like
+  // AudioBus::CreateByMixingToMono.
+  audio_data->channel_count = 1;
+  base::span<const float> channel0 = audio_buffer->getChannelData(0)->AsSpan();
+  audio_data->data = WTF::Vector<float>(channel0.size());
+  for (size_t i = 0; i < channel0.size(); ++i) {
+    audio_data->data[i] = channel0[i];
+    // If second channel exists, average the two channels to produce mono.
+    if (audio_buffer->numberOfChannels() > 1) {
+      audio_data->data[i] =
+          (audio_data->data[i] + audio_buffer->getChannelData(1)->AsSpan()[i]) /
+          2.0f;
+    }
+  }
+  return mojom::blink::AILanguageModelPromptContent::NewAudio(
+      std::move(audio_data));
+}
+
+mojom::blink::AILanguageModelPromptContentPtr ToMojo(
+    base::span<uint8_t> audio_bytes,
+    ExecutionContext* execution_context,
+    ExceptionState& exception_state) {
+  // TODO(crbug.com/401010825): Use the file sample rate.
+  scoped_refptr<AudioBus> bus = AudioBus::CreateBusFromInMemoryAudioFile(
+      audio_bytes, /*mix_to_mono=*/true, /*sample_rate=*/48000);
+  if (!bus) {
+    // TODO(crbug.com/409615288): This should throw a TypeError according to the
+    // spec.
+    exception_state.ThrowDOMException(DOMExceptionCode::kDataError,
+                                      "Missing or invalid audio data.");
+    return nullptr;
+  }
+
+  on_device_model::mojom::blink::AudioDataPtr audio_data =
+      on_device_model::mojom::blink::AudioData::New();
+  audio_data->sample_rate = bus->SampleRate();
+  audio_data->frame_count = bus->length();
+  audio_data->channel_count = bus->NumberOfChannels();
+  CHECK_EQ(audio_data->channel_count, 1);
+  // TODO(crbug.com/382180351): Avoid a copy.
+  audio_data->data = WTF::Vector<float>(bus->length());
+  std::copy_n(bus->Channel(0)->Data(), bus->Channel(0)->length(),
+              audio_data->data.begin());
+  return mojom::blink::AILanguageModelPromptContent::NewAudio(
+      std::move(audio_data));
+}
+
+mojom::blink::AILanguageModelPromptContentPtr ToMojo(
+    Blob* blob,
+    ExecutionContext* execution_context,
+    ExceptionState& exception_state) {
+  // TODO(crbug.com/382180351): Make blob reading async or alternatively
+  // use FileReaderSync instead (fix linker and exception issues).
+  SyncedFileReaderAccumulator* blobReader =
+      MakeGarbageCollected<SyncedFileReaderAccumulator>();
+
+  auto [error_code, reader_data] = blobReader->Load(
+      blob->GetBlobDataHandle(),
+      execution_context->GetTaskRunner(TaskType::kFileReading));
+  if (error_code != FileErrorCode::kOK) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kDataError,
+                                      "Failed to read blob.");
+    return nullptr;
+  }
+  ArrayBufferContents audio_contents =
+      std::move(reader_data).AsArrayBufferContents();
+  if (!audio_contents.IsValid()) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kDataError,
+                                      "Failed to read blob.");
+    return nullptr;
+  }
+  return ToMojo(audio_contents.ByteSpan(), execution_context, exception_state);
+}
+
+mojom::blink::AILanguageModelPromptContentPtr ToMojo(
+    V8ImageBitmapSource* bitmap,
+    ScriptState* script_state,
+    ExceptionState& exception_state) {
+  std::optional<SkBitmap> skia_bitmap =
+      GetBitmapFromV8ImageBitmapSource(script_state, bitmap, exception_state);
+  if (exception_state.HadException()) {
+    return nullptr;
+  }
+  return mojom::blink::AILanguageModelPromptContent::NewBitmap(
+      skia_bitmap.value());
+}
+
+mojom::blink::AILanguageModelPromptContentPtr ConvertPromptToMojoContent(
+    V8LanguageModelPromptType content_type,
+    const V8LanguageModelPromptContent* content,
+    ScriptState* script_state,
+    ExceptionState& exception_state,
+    ExecutionContext* execution_context,
+    WTF::HashSet<mojom::blink::AILanguageModelPromptType>& allowed_types) {
+  switch (content_type.AsEnum()) {
+    case V8LanguageModelPromptType::Enum::kText:
+      return ToMojo(content->GetAsString());
+    case V8LanguageModelPromptType::Enum::kImage:
+      if (!allowed_types.Contains(
+              mojom::blink::AILanguageModelPromptType::kImage)) {
+        exception_state.ThrowDOMException(
+            DOMExceptionCode::kNotSupportedError,
+            "Image not supported. Session is not initialized with image "
+            "support.");
+        return nullptr;
+      }
+      UseCounter::Count(execution_context,
+                        WebFeature::kLanguageModel_Prompt_Input_Image);
+      if (content->IsV8ImageBitmapSource()) {
+        return ToMojo(content->GetAsV8ImageBitmapSource(), script_state,
+                      exception_state);
+      }
+      exception_state.ThrowDOMException(DOMExceptionCode::kSyntaxError,
+                                        "Unsupported image content type");
+      return nullptr;
+    case V8LanguageModelPromptType::Enum::kAudio:
+      if (!allowed_types.Contains(
+              mojom::blink::AILanguageModelPromptType::kAudio)) {
+        exception_state.ThrowDOMException(
+            DOMExceptionCode::kNotSupportedError,
+            "Audio not supported. Session is not initialized with audio "
+            "support.");
+        return nullptr;
+      }
+      UseCounter::Count(execution_context,
+                        WebFeature::kLanguageModel_Prompt_Input_Audio);
+      switch (content->GetContentType()) {
+        case V8LanguageModelPromptContent::ContentType::kAudioBuffer:
+          return ToMojo(content->GetAsAudioBuffer(), exception_state);
+        case V8LanguageModelPromptContent::ContentType::kBlob:
+          return ToMojo(content->GetAsBlob(), execution_context,
+                        exception_state);
+        case V8LanguageModelPromptContent::ContentType::kArrayBuffer:
+          return ToMojo(content->GetAsArrayBuffer()->Content()->ByteSpan(),
+                        execution_context, exception_state);
+        case V8LanguageModelPromptContent::ContentType::kArrayBufferView:
+          return ToMojo(content->GetAsArrayBufferView()->ByteSpan(),
+                        execution_context, exception_state);
+        default:
+          exception_state.ThrowDOMException(DOMExceptionCode::kSyntaxError,
+                                            "Unsupported audio content type");
+          return nullptr;
+      }
+  }
+}
+
+// Return `prompt`'s content as a mojo struct or nullptr if there was an error.
+mojom::blink::AILanguageModelPromptPtr ConvertPromptToMojo(
+    const V8LanguageModelPrompt* prompt,
+    ScriptState* script_state,
+    ExceptionState& exception_state,
+    ExecutionContext* execution_context,
+    WTF::HashSet<mojom::blink::AILanguageModelPromptType>& allowed_types) {
+  switch (prompt->GetContentType()) {
+    // Handle basic string prompt.
+    case V8LanguageModelPrompt::ContentType::kString: {
+      auto result = mojom::blink::AILanguageModelPrompt::New();
+      result->content = ToMojo(prompt->GetAsString());
+      if (result->content.is_null()) {
+        return nullptr;
+      }
+      result->role = mojom::blink::AILanguageModelPromptRole::kUser;
+      return result;
+    }
+    // Handle dictionary for multimodal input.
+    case V8LanguageModelPrompt::ContentType::kLanguageModelPromptDict:
+      LanguageModelPromptDict* dict = prompt->GetAsLanguageModelPromptDict();
+      auto result = mojom::blink::AILanguageModelPrompt::New();
+      result->content = ConvertPromptToMojoContent(
+          dict->type(), dict->content(), script_state, exception_state,
+          execution_context, allowed_types);
+      if (result->content.is_null()) {
+        return nullptr;
+      }
+      result->role = LanguageModel::ConvertRoleToMojo(dict->role());
+      return result;
+  }
+}
+
+}  // namespace
+
+// Populates the `prompts` mojo struct vector from `input`. Returns an exception
+// if some input was specified incorrectly or inaccessible, nullptr otherwise.
+std::optional<WTF::Vector<mojom::blink::AILanguageModelPromptPtr>> BuildPrompts(
+    const V8LanguageModelPromptInput* input,
+    ScriptState* script_state,
+    ExceptionState& exception_state,
+    ExecutionContext* execution_context,
+    WTF::HashSet<mojom::blink::AILanguageModelPromptType>& allowed_types) {
+  WTF::Vector<mojom::blink::AILanguageModelPromptPtr> prompts;
+  if (input->IsLanguageModelPromptDictOrStringSequence()) {
+    const auto& sequence =
+        input->GetAsLanguageModelPromptDictOrStringSequence();
+    for (const auto& entry : sequence) {
+      mojom::blink::AILanguageModelPromptPtr prompt =
+          ConvertPromptToMojo(entry, script_state, exception_state,
+                              execution_context, allowed_types);
+      if (prompt.is_null()) {
+        return std::nullopt;
+      }
+      prompts.push_back(std::move(prompt));
+    }
+  } else {
+    CHECK(input->IsV8LanguageModelPrompt());
+    auto* entry = input->GetAsV8LanguageModelPrompt();
+    mojom::blink::AILanguageModelPromptPtr prompt = ConvertPromptToMojo(
+        entry, script_state, exception_state, execution_context, allowed_types);
+    if (prompt.is_null()) {
+      return std::nullopt;
+    }
+    prompts.push_back(std::move(prompt));
+  }
+
+  return prompts;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/ai/language_model_prompt_builder.h b/third_party/blink/renderer/modules/ai/language_model_prompt_builder.h
new file mode 100644
index 0000000..f0dae024
--- /dev/null
+++ b/third_party/blink/renderer/modules/ai/language_model_prompt_builder.h
@@ -0,0 +1,29 @@
+// Copyright 2025 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_BLINK_RENDERER_MODULES_AI_LANGUAGE_MODEL_PROMPT_BUILDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_AI_LANGUAGE_MODEL_PROMPT_BUILDER_H_
+
+#include "third_party/blink/public/mojom/ai/ai_language_model.mojom-blink-forward.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+
+namespace blink {
+
+class ScriptState;
+class AbortSignal;
+class V8LanguageModelPromptInput;
+class ExceptionState;
+
+std::optional<WTF::Vector<mojom::blink::AILanguageModelPromptPtr>> BuildPrompts(
+    const V8LanguageModelPromptInput* input,
+    ScriptState* script_state,
+    ExceptionState& exception_state,
+    ExecutionContext* execution_context,
+    WTF::HashSet<mojom::blink::AILanguageModelPromptType>& allowed_types);
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_AI_LANGUAGE_MODEL_PROMPT_BUILDER_H_
diff --git a/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source_test.cc b/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source_test.cc
index 76261e9..16c9f57 100644
--- a/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source_test.cc
+++ b/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source_test.cc
@@ -30,6 +30,28 @@
 using testing::Sequence;
 
 namespace blink {
+namespace {
+scoped_refptr<media::VideoFrame> CreateTestFrameWithGMB(
+    const gfx::Size& coded_size,
+    const gfx::Rect& visible_rect,
+    const gfx::Size& natural_size,
+    media::VideoFrame::StorageType storage_type,
+    media::VideoPixelFormat pixel_format,
+    base::TimeDelta timestamp,
+    std::unique_ptr<gfx::GpuMemoryBuffer> gmb) {
+  CHECK_EQ(storage_type,
+           media::VideoFrame::StorageType::STORAGE_GPU_MEMORY_BUFFER);
+  CHECK(gmb);
+  std::optional<gfx::BufferFormat> buffer_format =
+      media::VideoPixelFormatToGfxBufferFormat(pixel_format);
+  CHECK(buffer_format) << "Pixel format "
+                       << media::VideoPixelFormatToString(pixel_format)
+                       << " has no corresponding gfx::BufferFormat";
+
+  return media::VideoFrame::WrapExternalGpuMemoryBuffer(
+      visible_rect, natural_size, std::move(gmb), timestamp);
+}
+}  // namespace
 
 void ExpectUpdateRectEquals(const gfx::Rect& expected,
                             const webrtc::VideoFrame::UpdateRect actual) {
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 8afb9e2..1c541dc 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -3206,6 +3206,10 @@
       status: "experimental",
     },
     {
+      name: "OptimizeHTMLElementUrls",
+      status: "experimental",
+    },
+    {
       // This flag makes the <option> element's label attribute render the
       // contents of the label attribute even if it is only whitespace in order
       // to match the spec and firefox. It also stops stripping/simplifying
@@ -3846,7 +3850,7 @@
       // composited pixel alignment issues. See
       // https://crbug.com/40084005
       name: "RenderSurfaceFor2DScaleTransform",
-      status: "experimental",
+      status: "stable",
     },
     {
       name: "ReportEventTimingAtVisibilityChange",
diff --git a/third_party/blink/renderer/platform/testing/video_frame_utils.cc b/third_party/blink/renderer/platform/testing/video_frame_utils.cc
index 8665466..5775b14 100644
--- a/third_party/blink/renderer/platform/testing/video_frame_utils.cc
+++ b/third_party/blink/renderer/platform/testing/video_frame_utils.cc
@@ -84,25 +84,4 @@
   }
 }
 
-scoped_refptr<media::VideoFrame> CreateTestFrameWithGMB(
-    const gfx::Size& coded_size,
-    const gfx::Rect& visible_rect,
-    const gfx::Size& natural_size,
-    media::VideoFrame::StorageType storage_type,
-    media::VideoPixelFormat pixel_format,
-    base::TimeDelta timestamp,
-    std::unique_ptr<gfx::GpuMemoryBuffer> gmb) {
-  CHECK_EQ(storage_type,
-           media::VideoFrame::StorageType::STORAGE_GPU_MEMORY_BUFFER);
-  CHECK(gmb);
-  std::optional<gfx::BufferFormat> buffer_format =
-      media::VideoPixelFormatToGfxBufferFormat(pixel_format);
-  CHECK(buffer_format) << "Pixel format "
-                       << media::VideoPixelFormatToString(pixel_format)
-                       << " has no corresponding gfx::BufferFormat";
-
-  return media::VideoFrame::WrapExternalGpuMemoryBuffer(
-      visible_rect, natural_size, std::move(gmb), timestamp);
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/testing/video_frame_utils.h b/third_party/blink/renderer/platform/testing/video_frame_utils.h
index ed4079b..2039807 100644
--- a/third_party/blink/renderer/platform/testing/video_frame_utils.h
+++ b/third_party/blink/renderer/platform/testing/video_frame_utils.h
@@ -29,15 +29,6 @@
     base::TimeDelta timestamp,
     gpu::TestSharedImageInterface* test_sii);
 
-scoped_refptr<media::VideoFrame> CreateTestFrameWithGMB(
-    const gfx::Size& coded_size,
-    const gfx::Rect& visible_rect,
-    const gfx::Size& natural_size,
-    media::VideoFrame::StorageType storage_type,
-    media::VideoPixelFormat pixel_format,
-    base::TimeDelta timestamp,
-    std::unique_ptr<gfx::GpuMemoryBuffer> gmb);
-
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_VIDEO_FRAME_UTILS_H_
diff --git a/third_party/blink/tools/blinkpy/wpt_tests/product.py b/third_party/blink/tools/blinkpy/wpt_tests/product.py
index 24cbdf2..0a5b2bd 100644
--- a/third_party/blink/tools/blinkpy/wpt_tests/product.py
+++ b/third_party/blink/tools/blinkpy/wpt_tests/product.py
@@ -16,6 +16,8 @@
 _log = logging.getLogger(__name__)
 IOS_VERSION = '17.0'
 IOS_DEVICE = 'iPhone 14 Pro'
+# Use a hard coded version, we might need to update this occasionally
+CHROME_ANDROID_STABLE_VERSION = '138.0.7158.0'
 
 
 def do_delay_imports():
@@ -81,7 +83,7 @@
         # pylint: disable=assignment-from-none
         options.browser_version = self.get_version()
         if self._options.stable:
-            options.browser_channel = 'stable'
+            options.channel = 'stable'
         options.webdriver_binary = self._options.webdriver_binary or self.webdriver_binary
         options.webdriver_args.extend(self.additional_webdriver_args())
 
@@ -238,8 +240,12 @@
     def _install_chrome_stable(self, device):
         install_script = self._port._path_finder.path_from_chromium_base(
             'clank', 'bin', 'install_chrome.py')
-        self._host.executive.run_command(
-            [install_script, '--serial', device.serial, '--channel', 'stable'])
+        self._host.executive.run_command([
+            install_script, '--serial', device.serial, '--channel', 'stable',
+            '--adb', self.adb_binary, '--chrome-version',
+            CHROME_ANDROID_STABLE_VERSION, '--package',
+            'TrichromeChromeGoogle6432'
+        ])
         try:
             yield
         finally:
@@ -304,6 +310,8 @@
         options.package_name = self.get_browser_package_name()
 
     def get_version(self):
+        if self._options.stable:
+            return CHROME_ANDROID_STABLE_VERSION
         version_provider = self.get_version_provider_package_name()
         if self.devices and version_provider:
             # Assume devices are identically provisioned, so select any.
@@ -338,6 +346,8 @@
         See Also:
             https://github.com/web-platform-tests/wpt/blob/merge_pr_33203/tools/wpt/browser.py#L867-L924
         """
+        if self._options.stable:
+            return 'com.android.chrome'
         if self.browser_apk:
             # pylint: disable=undefined-variable;
             with contextlib.suppress(apk_helper.ApkHelperError):
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 14b6409..4df48eab 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -9358,11 +9358,3 @@
 
 # Gardener 2025-05-07
 crbug.com/416123241 [ Linux ] fast/forms/select/customizable-select/disallowed-select-descendants-console-message.html [ Failure Pass ]
-
-# Temporarily skipped to land https://crrev.com/c/6502712, after which expectations will be updated
-crbug.com/414525205 http/tests/devtools/console/console-functions.js [ Failure Pass ]
-crbug.com/414525205 http/tests/devtools/sources/debugger-ui/watch-expressions-preserve-expansion.js [ Failure Pass ]
-crbug.com/414525205 http/tests/devtools/sources/debugger/properties-special.js [ Failure Pass ]
-crbug.com/414525205 external/wpt/webidl/ecmascript-binding/builtin-function-properties.any.html [ Failure Pass ]
-crbug.com/414525205 external/wpt/webidl/ecmascript-binding/builtin-function-properties.any.worker.html [ Failure Pass ]
-crbug.com/414525205 external/wpt/webidl/ecmascript-binding/legacy-factory-function-builtin-properties.window.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/external/wpt/IndexedDB/idbcursor-request-source.any.js b/third_party/blink/web_tests/external/wpt/IndexedDB/idbcursor-request-source.any.js
index 2fe8c66..8e1b34e 100644
--- a/third_party/blink/web_tests/external/wpt/IndexedDB/idbcursor-request-source.any.js
+++ b/third_party/blink/web_tests/external/wpt/IndexedDB/idbcursor-request-source.any.js
@@ -6,21 +6,111 @@
 
 'use strict';
 
-[cursor => cursor.update(0), cursor => cursor.delete()].forEach(
-    func => indexeddb_test(
-        (t, db) => {
-          db.createObjectStore('store', {autoIncrement: true});
-        },
-        (t, db) => {
-          const tx = db.transaction('store', 'readwrite');
-          const store = tx.objectStore('store');
-          store.put('value');
-          store.openCursor().onsuccess = t.step_func(e => {
-            const cursor = e.target.result;
-            assert_equals(
-                func(cursor).source, cursor,
-                `${func}.source should be the cursor itself`);
+// Setup each test by populating an object store with an index for the cursor to
+// iterate and manipulate.
+function initializeDatabase(db) {
+  const store = db.createObjectStore('store', {autoIncrement: true});
+  store.createIndex('index', /*keypath=*/ 'value');
+  store.put({value: 'z'});
+  store.put({value: 'y'});
+  store.put({value: 'x'});
+  store.put({value: 'w'});
+}
+
+function isIndex(cursorSourceType) {
+  return cursorSourceType === 'IDBIndex';
+}
+
+// Return the object store or index, depending on the test's `cursorSourceType`.
+function getCursorSource(transaction, cursorSourceType) {
+  let cursorSource = transaction.objectStore('store');
+  if (isIndex(cursorSourceType)) {
+    cursorSource = cursorSource.index('index');
+  }
+  return cursorSource;
+}
+
+// Verify the request source after calling delete() or update() on the cursor.
+function cursor_request_source_test(
+    cursorSourceType, createRequestFunctionName, createRequestFunctionArgs) {
+  indexeddb_test(
+      (t, db) => initializeDatabase(db),
+      (t, db) => {
+        const tx = db.transaction('store', 'readwrite');
+        const cursorSource = getCursorSource(tx, cursorSourceType);
+
+        // Open the cursor.
+        const openCursorRequest = cursorSource.openCursor();
+        openCursorRequest.onerror =
+            t.unreached_func('The cursor must not fail to open.');
+
+        openCursorRequest.onsuccess = t.step_func(e => {
+          // Use the cursor to create a new request.
+          const cursor = e.target.result;
+          const request =
+              cursor[createRequestFunctionName](...createRequestFunctionArgs);
+          assert_equals(
+              request.source, cursor,
+              `The request's source must be the cursor itself.`);
+          t.done();
+        });
+      },
+      `The source of the request from ${cursorSourceType}::${
+          createRequestFunctionName}() is the cursor itself`);
+}
+
+// Verify the request source after calling openCursor() or openKeyCursor() and
+// then using the cursor to iterate.
+function open_cursor_request_source_test(
+    cursorSourceType, openCursorFunctionName) {
+  indexeddb_test(
+      (t, db) => initializeDatabase(db),
+      (t, db) => {
+        const tx = db.transaction('store', 'readonly');
+        const cursorSource = getCursorSource(tx, cursorSourceType);
+
+        // Open the cursor.
+        const openCursorRequest = cursorSource[openCursorFunctionName]();
+        openCursorRequest.onerror =
+            t.unreached_func('The cursor must not fail to open or iterate.');
+
+        assert_equals(
+            openCursorRequest.source, cursorSource,
+            'The request source must be the opener of the cursor.');
+
+        // Verify the cursor's `request.source` after iterating with
+        // `advance()`, `continue()`, and `continuePrimaryKey()`.
+        let iterationCount = 0;
+        openCursorRequest.onsuccess = t.step_func(e => {
+          assert_equals(
+              openCursorRequest.source, cursorSource,
+              'The request source must be the opener of the cursor after iterating.');
+
+          const cursor = e.target.result;
+          ++iterationCount;
+
+          if (iterationCount == 1) {
+            cursor.advance(1);
+          } else if (iterationCount == 2) {
+            cursor.continue();
+          } else if (iterationCount == 3 && isIndex(cursorSourceType)) {
+            cursor.continuePrimaryKey('z', 0);
+          } else {
             t.done();
-          });
-        },
-        `The source of the request from ${func} is the cursor itself`));
+          }
+        });
+      },
+      `${cursorSourceType}::${
+          openCursorFunctionName}'s request source must be the ${
+          cursorSourceType} instance that opened the cursor`);
+}
+
+open_cursor_request_source_test('IDBObjectStore', 'openCursor');
+open_cursor_request_source_test('IDBObjectStore', 'openKeyCursor');
+open_cursor_request_source_test('IDBIndex', 'openCursor');
+open_cursor_request_source_test('IDBIndex', 'openKeyCursor');
+
+cursor_request_source_test('IDBObjectStore', 'update', /*args=*/[0]);
+cursor_request_source_test('IDBObjectStore', 'delete', /*args=*/[]);
+cursor_request_source_test('IDBIndex', 'update', /*args=*/[0]);
+cursor_request_source_test('IDBIndex', 'delete', /*args=*/[]);
diff --git a/third_party/blink/web_tests/external/wpt/webidl/ecmascript-binding/builtin-function-properties.any-expected.txt b/third_party/blink/web_tests/external/wpt/webidl/ecmascript-binding/builtin-function-properties.any-expected.txt
deleted file mode 100644
index 4dfe1fa..0000000
--- a/third_party/blink/web_tests/external/wpt/webidl/ecmascript-binding/builtin-function-properties.any-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Constructor property enumeration order of "length", "name", and "prototype"
-  assert_array_equals: expected property 2 to be "prototype" but got "arguments" (expected array ["length", "name", "prototype"] got ["length", "name", "arguments"])
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/webidl/ecmascript-binding/builtin-function-properties.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/webidl/ecmascript-binding/builtin-function-properties.any.worker-expected.txt
deleted file mode 100644
index 4dfe1fa..0000000
--- a/third_party/blink/web_tests/external/wpt/webidl/ecmascript-binding/builtin-function-properties.any.worker-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Constructor property enumeration order of "length", "name", and "prototype"
-  assert_array_equals: expected property 2 to be "prototype" but got "arguments" (expected array ["length", "name", "prototype"] got ["length", "name", "arguments"])
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/webidl/ecmascript-binding/legacy-factory-function-builtin-properties.window-expected.txt b/third_party/blink/web_tests/external/wpt/webidl/ecmascript-binding/legacy-factory-function-builtin-properties.window-expected.txt
deleted file mode 100644
index 7dc3162d..0000000
--- a/third_party/blink/web_tests/external/wpt/webidl/ecmascript-binding/legacy-factory-function-builtin-properties.window-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Legacy factory function property enumeration order of "length", "name", and "prototype"
-  assert_array_equals: expected property 2 to be "prototype" but got "arguments" (expected array ["length", "name", "prototype"] got ["length", "name", "arguments"])
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/pooling.https.any.js b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/pooling.https.any.js
index f385aab..8f81ff56 100644
--- a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/pooling.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/pooling.https.any.js
@@ -34,46 +34,6 @@
 // MLOperand maxPool2d(
 //     MLOperand input, optional MLPool2dOptions options = {});
 
-
-const getPoolingOperatorsPrecisionTolerance = (graphResources) => {
-  const args = graphResources.operators[0].arguments;
-  const inputShape =
-      graphResources.inputs[args[0][Object.keys(args[0])[0]]].descriptor.shape;
-  const options =
-      args.length === 2 ? {...args[1][Object.keys(args[1])[0]]} : {};
-  let height;
-  let width;
-
-  if (options.windowDimensions) {
-    height = options.windowDimensions[0];
-    width = options.windowDimensions[1];
-  } else {
-    // If not present, the window dimensions are assumed to be the height and
-    // width dimensions of the input shape
-    if (options.layout && options.layout === 'nhwc') {
-      height = inputShape[1];
-      width = inputShape[2];
-    } else {
-      // nhwc layout of input
-      height = inputShape[2];
-      width = inputShape[3];
-    }
-  }
-
-  const tolerance = height * width + 2;
-  const toleranceDict = {
-    averagePool2d: {float32: tolerance, float16: tolerance},
-    l2Pool2d: {float32: tolerance, float16: tolerance},
-    maxPool2d: {float32: 0, float16: 0},
-  };
-  const expectedDataType =
-      getExpectedDataTypeOfSingleOutput(graphResources.expectedOutputs);
-  return {
-    metricType: 'ULP',
-    value: toleranceDict[graphResources.operators[0].name][expectedDataType]
-  };
-};
-
 const poolingOperatorsTests = [
   // averagePool2d tests
   {
@@ -2345,8 +2305,7 @@
 
 if (navigator.ml) {
   poolingOperatorsTests.forEach((test) => {
-    webnn_conformance_test(
-        buildAndExecuteGraph, getPoolingOperatorsPrecisionTolerance, test);
+    webnn_conformance_test(buildAndExecuteGraph, getPrecisionTolerance, test);
   });
 } else {
   test(() => assert_implements(navigator.ml, 'missing navigator.ml'));
diff --git a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/qdq_subgraph.https.any.js b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/qdq_subgraph.https.any.js
index 64f3776..5ec9ff8 100644
--- a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/qdq_subgraph.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/qdq_subgraph.https.any.js
@@ -1007,6 +1007,170 @@
       }
     }
   },
+  {
+    'name': 'quantized averagePool2d',
+    'graph': {
+      'inputs': {
+        'input': {
+          'data': [
+            -2.549168109893799, -4.794857501983643,
+            8.413617134094238, 6.108623504638672
+          ],
+          'descriptor': {shape: [1, 2, 2, 1], dataType: 'float32'},
+          'constant': false
+        },
+        'inputScale': {
+          'data': [0.343092918395996],
+          'descriptor': {shape: [1], dataType: 'float32'},
+          'constant': true
+        },
+        'inputZeroPoint': {
+          'data': [-128],
+          'descriptor': {shape: [1], dataType: 'int8'},
+          'constant': true
+        },
+        'outputScale': {
+          'data': [0.343092918395996],
+          'descriptor': {shape: [1], dataType: 'float32'},
+          'constant': true
+        },
+        'outputZeroPoint': {
+          'data': [-128],
+          'descriptor': {shape: [1], dataType: 'int8'},
+          'constant': true
+        },
+      },
+      'operators': [
+        {
+          'name': 'quantizeLinear',
+          'arguments': [
+            {'input': 'input'},
+            {'scale': 'inputScale', 'zeroPoint': 'inputZeroPoint'}
+          ],
+          'outputs': 'quantizedInput'
+        },
+        {
+          'name': 'dequantizeLinear',
+          'arguments': [
+            {'input': 'quantizedInput'},
+            {'scale': 'inputScale', 'zeroPoint': 'inputZeroPoint'}
+          ],
+          'outputs': 'dequantizedInput'
+        },
+        {
+          'name': 'averagePool2d',
+          'arguments': [{'input': 'dequantizedInput'}, {'options': {'layout': 'nhwc'}}],
+          'outputs': 'averagePool2dOutput'
+        },
+        {
+          'name': 'quantizeLinear',
+          'arguments': [
+            {'input': 'averagePool2dOutput'},
+            {'scale': 'outputScale', 'zeroPoint': 'outputZeroPoint'}
+          ],
+          'outputs': 'quantizedAveragePool2dOutput'
+        },
+        {
+          'name': 'dequantizeLinear',
+          'arguments': [
+            {'input': 'quantizedAveragePool2dOutput'},
+            {'scale': 'outputScale', 'zeroPoint': 'outputZeroPoint'}
+          ],
+          'outputs': 'output'
+        }
+      ],
+      'expectedOutputs': {
+        'output': {
+          'data': [
+            3.774022102355957,
+          ],
+          'descriptor': {shape: [1, 1, 1, 1], dataType: 'float32'}
+        }
+      }
+    }
+  },
+  {
+    'name': 'quantized maxPool2d',
+    'graph': {
+      'inputs': {
+        'input': {
+          'data': [
+            -2.549168109893799, -4.794857501983643,
+            8.413617134094238, 6.108623504638672
+          ],
+          'descriptor': {shape: [1, 2, 2, 1], dataType: 'float32'},
+          'constant': false
+        },
+        'inputScale': {
+          'data': [0.343092918395996],
+          'descriptor': {shape: [1], dataType: 'float32'},
+          'constant': true
+        },
+        'inputZeroPoint': {
+          'data': [-128],
+          'descriptor': {shape: [1], dataType: 'int8'},
+          'constant': true
+        },
+        'outputScale': {
+          'data': [0.343092918395996],
+          'descriptor': {shape: [1], dataType: 'float32'},
+          'constant': true
+        },
+        'outputZeroPoint': {
+          'data': [-128],
+          'descriptor': {shape: [1], dataType: 'int8'},
+          'constant': true
+        },
+      },
+      'operators': [
+        {
+          'name': 'quantizeLinear',
+          'arguments': [
+            {'input': 'input'},
+            {'scale': 'inputScale', 'zeroPoint': 'inputZeroPoint'}
+          ],
+          'outputs': 'quantizedInput'
+        },
+        {
+          'name': 'dequantizeLinear',
+          'arguments': [
+            {'input': 'quantizedInput'},
+            {'scale': 'inputScale', 'zeroPoint': 'inputZeroPoint'}
+          ],
+          'outputs': 'dequantizedInput'
+        },
+        {
+          'name': 'maxPool2d',
+          'arguments': [{'input': 'dequantizedInput'}, {'options': {'layout': 'nhwc'}}],
+          'outputs': 'maxPool2dOutput'
+        },
+        {
+          'name': 'quantizeLinear',
+          'arguments': [
+            {'input': 'maxPool2dOutput'},
+            {'scale': 'outputScale', 'zeroPoint': 'outputZeroPoint'}
+          ],
+          'outputs': 'quantizedMaxPool2dOutput'
+        },
+        {
+          'name': 'dequantizeLinear',
+          'arguments': [
+            {'input': 'quantizedMaxPool2dOutput'},
+            {'scale': 'outputScale', 'zeroPoint': 'outputZeroPoint'}
+          ],
+          'outputs': 'output'
+        }
+      ],
+      'expectedOutputs': {
+        'output': {
+          'data': [
+            8.577322959899902,
+          ],
+          'descriptor': {shape: [1, 1, 1, 1], dataType: 'float32'}
+        }
+      }
+    }
+  },
 ];
 
 if (navigator.ml) {
diff --git a/third_party/blink/web_tests/external/wpt/webnn/resources/utils.js b/third_party/blink/web_tests/external/wpt/webnn/resources/utils.js
index 50d7911..9d5cfc7 100644
--- a/third_party/blink/web_tests/external/wpt/webnn/resources/utils.js
+++ b/third_party/blink/web_tests/external/wpt/webnn/resources/utils.js
@@ -59,6 +59,13 @@
                               op, graphResources, intermediateOperands)
                               .value;
         break;
+      case 'averagePool2d':
+      case 'maxPool2d':
+      case 'l2Pool2d':
+        toleranceValue += getPoolingOperatorsPrecisionTolerance(
+                              op, graphResources, intermediateOperands)
+                              .value;
+        break;
       default:
         const operatorTolerance =
             operatorToleranceDict[op.name]?.[expectedDataType];
@@ -1006,6 +1013,53 @@
   return {metricType: 'ULP', value: toleranceValueDict[expectedDataType]};
 };
 
+const getPoolingOperatorsPrecisionTolerance =
+    (op, graphResources, intermediateOperands) => {
+  const args = op.arguments;
+  const operatorName = op.name;
+  const {inputs} = graphResources;
+  let inputShape;
+  const inputIndex = args[0][Object.keys(args[0])[0]];
+  if (inputs[inputIndex]) {
+    inputShape = inputs[inputIndex].descriptor.shape;
+  } else {
+    inputShape = intermediateOperands[inputIndex].shape;
+  }
+  const options =
+      args.length === 2 ? {...args[1][Object.keys(args[1])[0]]} : {};
+  let height;
+  let width;
+
+  if (options.windowDimensions) {
+    height = options.windowDimensions[0];
+    width = options.windowDimensions[1];
+  } else {
+    // If not present, the window dimensions are assumed to be the height
+    // and width dimensions of the input shape
+    if (options.layout && options.layout === 'nhwc') {
+      height = inputShape[1];
+      width = inputShape[2];
+    } else {
+      // nhwc layout of input
+      height = inputShape[2];
+      width = inputShape[3];
+    }
+  }
+
+  const tolerance = height * width + 2;
+  const toleranceDict = {
+    averagePool2d: {float32: tolerance, float16: tolerance},
+    l2Pool2d: {float32: tolerance, float16: tolerance},
+    maxPool2d: {float32: 0, float16: 0},
+  };
+  const expectedDataType =
+      getExpectedDataTypeOfSingleOutput(graphResources.expectedOutputs);
+  return {
+    metricType: 'ULP',
+    value: toleranceDict[operatorName][expectedDataType]
+  };
+};
+
 const getInstanceNormPrecisionTolerance = (graphResources) => {
   // according to
   // https://github.com/web-platform-tests/wpt/pull/43891#discussion_r1457026316
diff --git a/third_party/blink/web_tests/http/tests/devtools/console/console-functions-expected.txt b/third_party/blink/web_tests/http/tests/devtools/console/console-functions-expected.txt
index aac7dbe..41f1fd9 100644
--- a/third_party/blink/web_tests/http/tests/devtools/console/console-functions-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/console/console-functions-expected.txt
@@ -4,11 +4,11 @@
 
 
 console-functions.js:30 ƒ simple()
-    arguments: null
-    caller: null
     length: 0
     name: "simple"
     prototype: {}
+    arguments: null
+    caller: null
     [[FunctionLocation]]: console-functions.js:15
     [[Prototype]]: ƒ ()
     [[Scopes]]: Scopes[1]
@@ -46,11 +46,11 @@
 
 
 console-functions.js:30 ƒ anonymous()
-    arguments: null
-    caller: null
     length: 0
     name: ""
     prototype: {}
+    arguments: null
+    caller: null
     [[FunctionLocation]]: console-functions.js:18
     [[Prototype]]: ƒ ()
     [[Scopes]]: Scopes[1]
@@ -60,11 +60,11 @@
 
 
 console-functions.js:30 ƒ anonymous(x, y)
-    arguments: null
-    caller: null
     length: 2
     name: ""
     prototype: {}
+    arguments: null
+    caller: null
     [[FunctionLocation]]: console-functions.js:19
     [[Prototype]]: ƒ ()
     [[Scopes]]: Scopes[1]
@@ -74,11 +74,11 @@
 
 
 console-functions.js:30 ƒ namedArgs(x)
-    arguments: null
-    caller: null
     length: 1
     name: "namedArgs"
     prototype: {}
+    arguments: null
+    caller: null
     [[FunctionLocation]]: console-functions.js:20
     [[Prototype]]: ƒ ()
     [[Scopes]]: Scopes[1]
@@ -88,11 +88,11 @@
 
 
 console-functions.js:30 ƒ namedArgs2(x, y)
-    arguments: null
-    caller: null
     length: 2
     name: "namedArgs2"
     prototype: {}
+    arguments: null
+    caller: null
     [[FunctionLocation]]: console-functions.js:21
     [[Prototype]]: ƒ ()
     [[Scopes]]: Scopes[1]
@@ -102,11 +102,11 @@
 
 
 console-functions.js:30 ƒ anonymous({})
-    arguments: null
-    caller: null
     length: 1
     name: ""
     prototype: {}
+    arguments: null
+    caller: null
     [[FunctionLocation]]: console-functions.js:22
     [[Prototype]]: ƒ ()
     [[Scopes]]: Scopes[1]
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/watch-expressions-preserve-expansion-expected.txt b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/watch-expressions-preserve-expansion-expected.txt
index 6c5de86..101d5e4 100644
--- a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/watch-expressions-preserve-expansion-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/watch-expressions-preserve-expansion-expected.txt
@@ -120,11 +120,11 @@
     length: 300
     [[Prototype]]: Array(0)
 func: function() {return a + b;}
-    arguments: null
-    caller: null
     length: 0
     name: 
     prototype: Object
+    arguments: null
+    caller: null
     [[FunctionLocation]]: Object
     [[Prototype]]: function () { [native code] }
 Page reloaded.
@@ -242,11 +242,11 @@
     length: 300
     [[Prototype]]: Array(0)
 func: function() {return a + b;}
-    arguments: null
-    caller: null
     length: 0
     name: 
     prototype: Object
+    arguments: null
+    caller: null
     [[FunctionLocation]]: Object
     [[Prototype]]: function () { [native code] }
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger/properties-special-expected.txt b/third_party/blink/web_tests/http/tests/devtools/sources/debugger/properties-special-expected.txt
index 850720e..b3648e9f 100644
--- a/third_party/blink/web_tests/http/tests/devtools/sources/debugger/properties-special-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/sources/debugger/properties-special-expected.txt
@@ -5,11 +5,11 @@
 
 
 properties-special.js:14 ƒ anonymous(a,b)
-    arguments: null
-    caller: null
     length: 2
     name: ""
     prototype: {}
+    arguments: null
+    caller: null
     [[FunctionLocation]]: properties-special.js:14
     [[Prototype]]: ƒ ()
     [[Scopes]]: Scopes[1]
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-env/env-preferred-text-scale-002.html b/third_party/blink/web_tests/wpt_internal/css/css-env/env-preferred-text-scale-002.html
new file mode 100644
index 0000000..fbe2960
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-env/env-preferred-text-scale-002.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html lang="en" class="reftest-wait">
+<meta content="width=device-width,initial-scale=1.0,minimum-scale=1.0"
+  name="viewport">
+<meta name="assert"
+  content="env(preferred-text-scale) exposes the user's preferred text scale from the OS, clamped to a hardcoded set of values">
+<link rel="help" href="https://drafts.csswg.org/css-env-1/#text-zoom">
+<link rel="match" href="../reference/ref-filled-green-100px-square.xht">
+<script src="/common/reftest-wait.js"></script>
+<style>
+  div {
+    background-color: green;
+    width: calc(50px * env(preferred-text-scale));
+    height: calc(50px * env(preferred-text-scale));
+  }
+</style>
+
+<p>Test passes if there is a filled green square and <strong>no red</strong>.
+</p>
+<div></div>
+<script>
+  if (window.internals) {
+    internals.settings.setAccessibilityFontScaleFactor(1.91);
+  } else {
+    document.body.appendChild(document.createTextNode("No window.internals? This test will fail."));
+  }
+  requestAnimationFrame(() => requestAnimationFrame(() => {
+    takeScreenshot();
+  }));
+</script>
diff --git a/third_party/boringssl/src b/third_party/boringssl/src
index 154bb58..f371c85 160000
--- a/third_party/boringssl/src
+++ b/third_party/boringssl/src
@@ -1 +1 @@
-Subproject commit 154bb5860a47c160c3be09003908c64c1de11bff
+Subproject commit f371c85a8d58bf911cba6073bcca6d497af81dc9
diff --git a/third_party/catapult b/third_party/catapult
index 64c31ff..74f545d 160000
--- a/third_party/catapult
+++ b/third_party/catapult
@@ -1 +1 @@
-Subproject commit 64c31ffa4d735add4a7e7520a52e0e3160216132
+Subproject commit 74f545d009ceef464f840938ae079ffbc112a102
diff --git a/third_party/depot_tools b/third_party/depot_tools
index fab0a42..a7571b1 160000
--- a/third_party/depot_tools
+++ b/third_party/depot_tools
@@ -1 +1 @@
-Subproject commit fab0a4296b8830c569a4dc82285bfb58a5c2fca8
+Subproject commit a7571b1596305d6a6fe26c788f24e40c579fcda8
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src
index 6e4e34a..6d3155a 160000
--- a/third_party/devtools-frontend/src
+++ b/third_party/devtools-frontend/src
@@ -1 +1 @@
-Subproject commit 6e4e34acb617713aa7b51311d6ea87c58c609b32
+Subproject commit 6d3155a3c0b50687e4f417e19d4491e66d56ef3c
diff --git a/third_party/libaddressinput/BUILD.gn b/third_party/libaddressinput/BUILD.gn
index ab7e3ed..92fd2ff 100644
--- a/third_party/libaddressinput/BUILD.gn
+++ b/third_party/libaddressinput/BUILD.gn
@@ -22,16 +22,14 @@
   include_dirs = [ target_gen_dir ]
 }
 
-grit("strings") {
+grit_strings("strings") {
   source = "chromium/address_input_strings.grd"
 
   outputs = [
     "messages.h",
     "en_messages.cc",
   ]
-  foreach(locale, all_chrome_locales) {
-    outputs += [ "address_input_strings_$locale.pak" ]
-  }
+  output_prefix = "address_input_strings_"
 
   configs = [ ":internal_config" ]
 }
diff --git a/third_party/libxslt/chromium/change-atype-extra.patch b/third_party/libxslt/chromium/change-atype-extra.patch
deleted file mode 100644
index 9e2f666..0000000
--- a/third_party/libxslt/chromium/change-atype-extra.patch
+++ /dev/null
@@ -1,110 +0,0 @@
-diff --git a/third_party/libxslt/src/libxslt/transform.c b/third_party/libxslt/src/libxslt/transform.c
-index 54ef821b5016f..1ac2471d6441b 100644
---- a/third_party/libxslt/src/libxslt/transform.c
-+++ b/third_party/libxslt/src/libxslt/transform.c
-@@ -5772,7 +5772,8 @@ xsltCleanupSourceDoc(xmlDocPtr doc) {
-             xmlAttrPtr prop = cur->properties;
- 
-             while (prop) {
--                prop->atype &= ~(XSLT_SOURCE_NODE_MASK << 27);
-+                prop->extra &=
-+                    ~(XSLT_SOURCE_NODE_MASK << XSLT_SOURCE_NODE_SHIFT_32);
-                 prop->psvi = NULL;
-                 prop = prop->next;
-             }
-diff --git a/third_party/libxslt/src/libxslt/xsltutils.c b/third_party/libxslt/src/libxslt/xsltutils.c
-index a20da96182289..0155e7fc5a89f 100644
---- a/third_party/libxslt/src/libxslt/xsltutils.c
-+++ b/third_party/libxslt/src/libxslt/xsltutils.c
-@@ -1920,26 +1920,26 @@ xsltSaveResultToString(xmlChar **doc_txt_ptr, int * doc_txt_len,
- int
- xsltGetSourceNodeFlags(xmlNodePtr node) {
-     /*
--     * Squeeze the bit flags into the upper bits of
-+     * Squeeze the bit flags into the upper 4 bits of
-      *
--     * - 'int properties' member in struct _xmlDoc
--     * - 'xmlAttributeType atype' member in struct _xmlAttr
-+     * - 'unsigned int extra' member in struct _xmlDoc
-+     * - 'unsigned int extra' member in struct _xmlAttr
-      * - 'unsigned short extra' member in struct _xmlNode
-      */
-     switch (node->type) {
-         case XML_DOCUMENT_NODE:
-         case XML_HTML_DOCUMENT_NODE:
--            return ((xmlDocPtr) node)->properties >> 27;
-+            return ((xmlDocPtr) node)->extra >> XSLT_SOURCE_NODE_SHIFT_32;
- 
-         case XML_ATTRIBUTE_NODE:
--            return ((xmlAttrPtr) node)->atype >> 27;
-+            return ((xmlAttrPtr) node)->extra >> XSLT_SOURCE_NODE_SHIFT_32;
- 
-         case XML_ELEMENT_NODE:
-         case XML_TEXT_NODE:
-         case XML_CDATA_SECTION_NODE:
-         case XML_PI_NODE:
-         case XML_COMMENT_NODE:
--            return node->extra >> 12;
-+            return node->extra >> XSLT_SOURCE_NODE_SHIFT_32;
- 
-         default:
-             return 0;
-@@ -1964,11 +1964,11 @@ xsltSetSourceNodeFlags(xsltTransformContextPtr ctxt, xmlNodePtr node,
-     switch (node->type) {
-         case XML_DOCUMENT_NODE:
-         case XML_HTML_DOCUMENT_NODE:
--            ((xmlDocPtr) node)->properties |= flags << 27;
-+            ((xmlDocPtr) node)->extra |= (flags << XSLT_SOURCE_NODE_SHIFT_32);
-             return 0;
- 
-         case XML_ATTRIBUTE_NODE:
--            ((xmlAttrPtr) node)->atype |= flags << 27;
-+            ((xmlAttrPtr) node)->extra |= (flags << XSLT_SOURCE_NODE_SHIFT_32);
-             return 0;
- 
-         case XML_ELEMENT_NODE:
-@@ -1976,7 +1976,7 @@ xsltSetSourceNodeFlags(xsltTransformContextPtr ctxt, xmlNodePtr node,
-         case XML_CDATA_SECTION_NODE:
-         case XML_PI_NODE:
-         case XML_COMMENT_NODE:
--            node->extra |= flags << 12;
-+            node->extra |= (flags << XSLT_SOURCE_NODE_SHIFT_16);
-             return 0;
- 
-         default:
-@@ -1998,11 +1998,11 @@ xsltClearSourceNodeFlags(xmlNodePtr node, int flags) {
-     switch (node->type) {
-         case XML_DOCUMENT_NODE:
-         case XML_HTML_DOCUMENT_NODE:
--            ((xmlDocPtr) node)->properties &= ~(flags << 27);
-+            ((xmlDocPtr) node)->extra &= ~(flags << XSLT_SOURCE_NODE_SHIFT_32);
-             return 0;
- 
-         case XML_ATTRIBUTE_NODE:
--            ((xmlAttrPtr) node)->atype &= ~(flags << 27);
-+            ((xmlAttrPtr) node)->extra &= ~(flags << XSLT_SOURCE_NODE_SHIFT_32);
-             return 0;
- 
-         case XML_ELEMENT_NODE:
-@@ -2010,7 +2010,7 @@ xsltClearSourceNodeFlags(xmlNodePtr node, int flags) {
-         case XML_CDATA_SECTION_NODE:
-         case XML_PI_NODE:
-         case XML_COMMENT_NODE:
--            node->extra &= ~(flags << 12);
-+            node->extra &= ~(flags << XSLT_SOURCE_NODE_SHIFT_16);
-             return 0;
- 
-         default:
-diff --git a/third_party/libxslt/src/libxslt/xsltutils.h b/third_party/libxslt/src/libxslt/xsltutils.h
-index 2514774b3f11a..d15d22726afaa 100644
---- a/third_party/libxslt/src/libxslt/xsltutils.h
-+++ b/third_party/libxslt/src/libxslt/xsltutils.h
-@@ -261,6 +261,8 @@ XSLTPUBFUN xmlXPathCompExprPtr XSLTCALL
- #define XSLT_SOURCE_NODE_MASK       15u
- #define XSLT_SOURCE_NODE_HAS_KEY    1u
- #define XSLT_SOURCE_NODE_HAS_ID     2u
-+#define XSLT_SOURCE_NODE_SHIFT_16   12u
-+#define XSLT_SOURCE_NODE_SHIFT_32   28u
- int
- xsltGetSourceNodeFlags(xmlNodePtr node);
- int
diff --git a/third_party/libxslt/chromium/change-id.patch b/third_party/libxslt/chromium/change-id.patch
deleted file mode 100644
index b151abb..0000000
--- a/third_party/libxslt/chromium/change-id.patch
+++ /dev/null
@@ -1,114 +0,0 @@
-diff --git a/third_party/libxslt/src/libxslt/functions.c b/third_party/libxslt/src/libxslt/functions.c
-index 72a58dc4d6592..309af458c22f7 100644
---- a/third_party/libxslt/src/libxslt/functions.c
-+++ b/third_party/libxslt/src/libxslt/functions.c
-@@ -760,7 +760,7 @@ xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){
-     }
- 
-     if (xsltGetSourceNodeFlags(cur) & XSLT_SOURCE_NODE_HAS_ID) {
--        id = (unsigned long) (size_t) *psviPtr;
-+        id = (unsigned long) xsltGetSourceNodeValue(cur);
-     } else {
-         if (cur->type == XML_TEXT_NODE && cur->line == USHRT_MAX) {
-             /* Text nodes store big line numbers in psvi. */
-@@ -772,7 +772,7 @@ xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){
-             goto out;
-         }
- 
--        if (tctxt->currentId == ULONG_MAX) {
-+        if (tctxt->currentId == XSLT_SOURCE_NODE_VALUE_MAX) {
-             xsltTransformError(tctxt, NULL, NULL,
-                     "generate-id(): id overflow\n");
-             ctxt->error = XPATH_MEMORY_ERROR;
-@@ -780,7 +780,7 @@ xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){
-         }
- 
-         id = ++tctxt->currentId;
--        *psviPtr = (void *) (size_t) id;
-+        xsltSetSourceNodeValue(cur, id);
-         xsltSetSourceNodeFlags(tctxt, cur, XSLT_SOURCE_NODE_HAS_ID);
-     }
- 
-diff --git a/third_party/libxslt/src/libxslt/xsltutils.c b/third_party/libxslt/src/libxslt/xsltutils.c
-index 0155e7fc5a89f..aec8be4ad077c 100644
---- a/third_party/libxslt/src/libxslt/xsltutils.c
-+++ b/third_party/libxslt/src/libxslt/xsltutils.c
-@@ -2018,6 +2018,54 @@ xsltClearSourceNodeFlags(xmlNodePtr node, int flags) {
-     }
- }
- 
-+/**
-+ * xsltGetSourceNodeValue:
-+ * @node:  Node from source document
-+ *
-+ * Returns the associated 28 bit unsigned value for a source node,
-+ * or 0 if node does not have an associated value.
-+ */
-+int
-+xsltGetSourceNodeValue(xmlNodePtr node) {
-+    switch (node->type) {
-+        case XML_DOCUMENT_NODE:
-+        case XML_HTML_DOCUMENT_NODE:
-+            return (((xmlDocPtr) node)->extra & XSLT_SOURCE_NODE_VALUE_MASK);
-+
-+        case XML_ATTRIBUTE_NODE:
-+            return (((xmlAttrPtr) node)->extra & XSLT_SOURCE_NODE_VALUE_MASK);
-+
-+        default:
-+            return 0;
-+    }
-+}
-+
-+/**
-+ * xsltSetSourceNodeValue:
-+ * @node:  Node from source document
-+ * @value:  28 bit unsigned value to associate with the node.
-+ *
-+ * Returns 0 on success, -1 on error.
-+ */
-+int
-+xsltSetSourceNodeValue(xmlNodePtr node, int value) {
-+    switch (node->type) {
-+        case XML_DOCUMENT_NODE:
-+        case XML_HTML_DOCUMENT_NODE:
-+            ((xmlDocPtr) node)->extra &= ~XSLT_SOURCE_NODE_VALUE_MASK;
-+            ((xmlDocPtr) node)->extra |= (value & XSLT_SOURCE_NODE_VALUE_MASK);
-+            return 0;
-+
-+        case XML_ATTRIBUTE_NODE:
-+            ((xmlAttrPtr) node)->extra &= ~XSLT_SOURCE_NODE_VALUE_MASK;
-+            ((xmlAttrPtr) node)->extra |= (value & XSLT_SOURCE_NODE_VALUE_MASK);
-+            return 0;
-+
-+        default:
-+            return -1;
-+    }
-+}
-+
- /**
-  * xsltGetPSVIPtr:
-  * @cur:  Node
-diff --git a/third_party/libxslt/src/libxslt/xsltutils.h b/third_party/libxslt/src/libxslt/xsltutils.h
-index d15d22726afaa..1e753eebadd98 100644
---- a/third_party/libxslt/src/libxslt/xsltutils.h
-+++ b/third_party/libxslt/src/libxslt/xsltutils.h
-@@ -263,6 +263,8 @@ XSLTPUBFUN xmlXPathCompExprPtr XSLTCALL
- #define XSLT_SOURCE_NODE_HAS_ID     2u
- #define XSLT_SOURCE_NODE_SHIFT_16   12u
- #define XSLT_SOURCE_NODE_SHIFT_32   28u
-+#define XSLT_SOURCE_NODE_VALUE_MASK ((1 << XSLT_SOURCE_NODE_SHIFT_32) - 1)
-+#define XSLT_SOURCE_NODE_VALUE_MAX  XSLT_SOURCE_NODE_VALUE_MASK
- int
- xsltGetSourceNodeFlags(xmlNodePtr node);
- int
-@@ -270,6 +272,10 @@ xsltSetSourceNodeFlags(xsltTransformContextPtr ctxt, xmlNodePtr node,
-                        int flags);
- int
- xsltClearSourceNodeFlags(xmlNodePtr node, int flags);
-+int
-+xsltSetSourceNodeValue(xmlNodePtr node, int value);
-+int
-+xsltGetSourceNodeValue(xmlNodePtr node);
- void **
- xsltGetPSVIPtr(xmlNodePtr cur);
- /** DOC_ENABLE */
diff --git a/third_party/libxslt/chromium/new-unified-atype-extra.patch b/third_party/libxslt/chromium/new-unified-atype-extra.patch
new file mode 100644
index 0000000..9af5109
--- /dev/null
+++ b/third_party/libxslt/chromium/new-unified-atype-extra.patch
@@ -0,0 +1,206 @@
+diff --git a/third_party/libxslt/src/libxslt/functions.c b/third_party/libxslt/src/libxslt/functions.c
+index 72a58dc4d6592..309af458c22f7 100644
+--- a/third_party/libxslt/src/libxslt/functions.c
++++ b/third_party/libxslt/src/libxslt/functions.c
+@@ -760,7 +760,7 @@ xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){
+     }
+ 
+     if (xsltGetSourceNodeFlags(cur) & XSLT_SOURCE_NODE_HAS_ID) {
+-        id = (unsigned long) (size_t) *psviPtr;
++        id = (unsigned long) xsltGetSourceNodeValue(cur);
+     } else {
+         if (cur->type == XML_TEXT_NODE && cur->line == USHRT_MAX) {
+             /* Text nodes store big line numbers in psvi. */
+@@ -772,7 +772,7 @@ xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){
+             goto out;
+         }
+ 
+-        if (tctxt->currentId == ULONG_MAX) {
++        if (tctxt->currentId == XSLT_SOURCE_NODE_VALUE_MAX) {
+             xsltTransformError(tctxt, NULL, NULL,
+                     "generate-id(): id overflow\n");
+             ctxt->error = XPATH_MEMORY_ERROR;
+@@ -780,7 +780,7 @@ xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){
+         }
+ 
+         id = ++tctxt->currentId;
+-        *psviPtr = (void *) (size_t) id;
++        xsltSetSourceNodeValue(cur, id);
+         xsltSetSourceNodeFlags(tctxt, cur, XSLT_SOURCE_NODE_HAS_ID);
+     }
+ 
+diff --git a/third_party/libxslt/src/libxslt/transform.c b/third_party/libxslt/src/libxslt/transform.c
+index 54ef821b5016f..1ac2471d6441b 100644
+--- a/third_party/libxslt/src/libxslt/transform.c
++++ b/third_party/libxslt/src/libxslt/transform.c
+@@ -5772,7 +5772,8 @@ xsltCleanupSourceDoc(xmlDocPtr doc) {
+             xmlAttrPtr prop = cur->properties;
+ 
+             while (prop) {
+-                prop->atype &= ~(XSLT_SOURCE_NODE_MASK << 27);
++                prop->extra &=
++                    ~(XSLT_SOURCE_NODE_MASK << XSLT_SOURCE_NODE_SHIFT_32);
+                 prop->psvi = NULL;
+                 prop = prop->next;
+             }
+diff --git a/third_party/libxslt/src/libxslt/xsltutils.c b/third_party/libxslt/src/libxslt/xsltutils.c
+index a20da96182289..b431fafbbb441 100644
+--- a/third_party/libxslt/src/libxslt/xsltutils.c
++++ b/third_party/libxslt/src/libxslt/xsltutils.c
+@@ -1920,26 +1920,26 @@ xsltSaveResultToString(xmlChar **doc_txt_ptr, int * doc_txt_len,
+ int
+ xsltGetSourceNodeFlags(xmlNodePtr node) {
+     /*
+-     * Squeeze the bit flags into the upper bits of
++     * Squeeze the bit flags into the upper 4 bits of
+      *
+-     * - 'int properties' member in struct _xmlDoc
+-     * - 'xmlAttributeType atype' member in struct _xmlAttr
++     * - 'unsigned int extra' member in struct _xmlDoc
++     * - 'unsigned int extra' member in struct _xmlAttr
+      * - 'unsigned short extra' member in struct _xmlNode
+      */
+     switch (node->type) {
+         case XML_DOCUMENT_NODE:
+         case XML_HTML_DOCUMENT_NODE:
+-            return ((xmlDocPtr) node)->properties >> 27;
++            return ((xmlDocPtr) node)->extra >> XSLT_SOURCE_NODE_SHIFT_32;
+ 
+         case XML_ATTRIBUTE_NODE:
+-            return ((xmlAttrPtr) node)->atype >> 27;
++            return ((xmlAttrPtr) node)->extra >> XSLT_SOURCE_NODE_SHIFT_32;
+ 
+         case XML_ELEMENT_NODE:
+         case XML_TEXT_NODE:
+         case XML_CDATA_SECTION_NODE:
+         case XML_PI_NODE:
+         case XML_COMMENT_NODE:
+-            return node->extra >> 12;
++            return node->extra >> XSLT_SOURCE_NODE_SHIFT_32;
+ 
+         default:
+             return 0;
+@@ -1964,11 +1964,13 @@ xsltSetSourceNodeFlags(xsltTransformContextPtr ctxt, xmlNodePtr node,
+     switch (node->type) {
+         case XML_DOCUMENT_NODE:
+         case XML_HTML_DOCUMENT_NODE:
+-            ((xmlDocPtr) node)->properties |= flags << 27;
++            ((xmlDocPtr) node)->extra |=
++                ((unsigned) flags << XSLT_SOURCE_NODE_SHIFT_32);
+             return 0;
+ 
+         case XML_ATTRIBUTE_NODE:
+-            ((xmlAttrPtr) node)->atype |= flags << 27;
++            ((xmlAttrPtr) node)->extra |=
++                ((unsigned) flags << XSLT_SOURCE_NODE_SHIFT_32);
+             return 0;
+ 
+         case XML_ELEMENT_NODE:
+@@ -1976,7 +1978,7 @@ xsltSetSourceNodeFlags(xsltTransformContextPtr ctxt, xmlNodePtr node,
+         case XML_CDATA_SECTION_NODE:
+         case XML_PI_NODE:
+         case XML_COMMENT_NODE:
+-            node->extra |= flags << 12;
++            node->extra |= ((unsigned) flags << XSLT_SOURCE_NODE_SHIFT_16);
+             return 0;
+ 
+         default:
+@@ -1998,11 +2000,13 @@ xsltClearSourceNodeFlags(xmlNodePtr node, int flags) {
+     switch (node->type) {
+         case XML_DOCUMENT_NODE:
+         case XML_HTML_DOCUMENT_NODE:
+-            ((xmlDocPtr) node)->properties &= ~(flags << 27);
++            ((xmlDocPtr) node)->extra &=
++                ~((unsigned) flags << XSLT_SOURCE_NODE_SHIFT_32);
+             return 0;
+ 
+         case XML_ATTRIBUTE_NODE:
+-            ((xmlAttrPtr) node)->atype &= ~(flags << 27);
++            ((xmlAttrPtr) node)->extra &=
++                ~((unsigned) flags << XSLT_SOURCE_NODE_SHIFT_32);
+             return 0;
+ 
+         case XML_ELEMENT_NODE:
+@@ -2010,7 +2014,55 @@ xsltClearSourceNodeFlags(xmlNodePtr node, int flags) {
+         case XML_CDATA_SECTION_NODE:
+         case XML_PI_NODE:
+         case XML_COMMENT_NODE:
+-            node->extra &= ~(flags << 12);
++            node->extra &= ~((unsigned) flags << XSLT_SOURCE_NODE_SHIFT_16);
++            return 0;
++
++        default:
++            return -1;
++    }
++}
++
++/**
++ * xsltGetSourceNodeValue:
++ * @node:  Node from source document
++ *
++ * Returns the associated 28 bit unsigned value for a source node,
++ * or 0 if node does not have an associated value.
++ */
++int
++xsltGetSourceNodeValue(xmlNodePtr node) {
++    switch (node->type) {
++        case XML_DOCUMENT_NODE:
++        case XML_HTML_DOCUMENT_NODE:
++            return (((xmlDocPtr) node)->extra & XSLT_SOURCE_NODE_VALUE_MASK);
++
++        case XML_ATTRIBUTE_NODE:
++            return (((xmlAttrPtr) node)->extra & XSLT_SOURCE_NODE_VALUE_MASK);
++
++        default:
++            return 0;
++    }
++}
++
++/**
++ * xsltSetSourceNodeValue:
++ * @node:  Node from source document
++ * @value:  28 bit unsigned value to associate with the node.
++ *
++ * Returns 0 on success, -1 on error.
++ */
++int
++xsltSetSourceNodeValue(xmlNodePtr node, int value) {
++    switch (node->type) {
++        case XML_DOCUMENT_NODE:
++        case XML_HTML_DOCUMENT_NODE:
++            ((xmlDocPtr) node)->extra &= ~XSLT_SOURCE_NODE_VALUE_MASK;
++            ((xmlDocPtr) node)->extra |= (value & XSLT_SOURCE_NODE_VALUE_MASK);
++            return 0;
++
++        case XML_ATTRIBUTE_NODE:
++            ((xmlAttrPtr) node)->extra &= ~XSLT_SOURCE_NODE_VALUE_MASK;
++            ((xmlAttrPtr) node)->extra |= (value & XSLT_SOURCE_NODE_VALUE_MASK);
+             return 0;
+ 
+         default:
+diff --git a/third_party/libxslt/src/libxslt/xsltutils.h b/third_party/libxslt/src/libxslt/xsltutils.h
+index 2514774b3f11a..1e753eebadd98 100644
+--- a/third_party/libxslt/src/libxslt/xsltutils.h
++++ b/third_party/libxslt/src/libxslt/xsltutils.h
+@@ -261,6 +261,10 @@ XSLTPUBFUN xmlXPathCompExprPtr XSLTCALL
+ #define XSLT_SOURCE_NODE_MASK       15u
+ #define XSLT_SOURCE_NODE_HAS_KEY    1u
+ #define XSLT_SOURCE_NODE_HAS_ID     2u
++#define XSLT_SOURCE_NODE_SHIFT_16   12u
++#define XSLT_SOURCE_NODE_SHIFT_32   28u
++#define XSLT_SOURCE_NODE_VALUE_MASK ((1 << XSLT_SOURCE_NODE_SHIFT_32) - 1)
++#define XSLT_SOURCE_NODE_VALUE_MAX  XSLT_SOURCE_NODE_VALUE_MASK
+ int
+ xsltGetSourceNodeFlags(xmlNodePtr node);
+ int
+@@ -268,6 +272,10 @@ xsltSetSourceNodeFlags(xsltTransformContextPtr ctxt, xmlNodePtr node,
+                        int flags);
+ int
+ xsltClearSourceNodeFlags(xmlNodePtr node, int flags);
++int
++xsltSetSourceNodeValue(xmlNodePtr node, int value);
++int
++xsltGetSourceNodeValue(xmlNodePtr node);
+ void **
+ xsltGetPSVIPtr(xmlNodePtr cur);
+ /** DOC_ENABLE */
diff --git a/third_party/libxslt/src/libxslt/xsltutils.c b/third_party/libxslt/src/libxslt/xsltutils.c
index aec8be4a..b431faf 100644
--- a/third_party/libxslt/src/libxslt/xsltutils.c
+++ b/third_party/libxslt/src/libxslt/xsltutils.c
@@ -1964,11 +1964,13 @@
     switch (node->type) {
         case XML_DOCUMENT_NODE:
         case XML_HTML_DOCUMENT_NODE:
-            ((xmlDocPtr) node)->extra |= (flags << XSLT_SOURCE_NODE_SHIFT_32);
+            ((xmlDocPtr) node)->extra |=
+                ((unsigned) flags << XSLT_SOURCE_NODE_SHIFT_32);
             return 0;
 
         case XML_ATTRIBUTE_NODE:
-            ((xmlAttrPtr) node)->extra |= (flags << XSLT_SOURCE_NODE_SHIFT_32);
+            ((xmlAttrPtr) node)->extra |=
+                ((unsigned) flags << XSLT_SOURCE_NODE_SHIFT_32);
             return 0;
 
         case XML_ELEMENT_NODE:
@@ -1976,7 +1978,7 @@
         case XML_CDATA_SECTION_NODE:
         case XML_PI_NODE:
         case XML_COMMENT_NODE:
-            node->extra |= (flags << XSLT_SOURCE_NODE_SHIFT_16);
+            node->extra |= ((unsigned) flags << XSLT_SOURCE_NODE_SHIFT_16);
             return 0;
 
         default:
@@ -1998,11 +2000,13 @@
     switch (node->type) {
         case XML_DOCUMENT_NODE:
         case XML_HTML_DOCUMENT_NODE:
-            ((xmlDocPtr) node)->extra &= ~(flags << XSLT_SOURCE_NODE_SHIFT_32);
+            ((xmlDocPtr) node)->extra &=
+                ~((unsigned) flags << XSLT_SOURCE_NODE_SHIFT_32);
             return 0;
 
         case XML_ATTRIBUTE_NODE:
-            ((xmlAttrPtr) node)->extra &= ~(flags << XSLT_SOURCE_NODE_SHIFT_32);
+            ((xmlAttrPtr) node)->extra &=
+                ~((unsigned) flags << XSLT_SOURCE_NODE_SHIFT_32);
             return 0;
 
         case XML_ELEMENT_NODE:
@@ -2010,7 +2014,7 @@
         case XML_CDATA_SECTION_NODE:
         case XML_PI_NODE:
         case XML_COMMENT_NODE:
-            node->extra &= ~(flags << XSLT_SOURCE_NODE_SHIFT_16);
+            node->extra &= ~((unsigned) flags << XSLT_SOURCE_NODE_SHIFT_16);
             return 0;
 
         default:
diff --git a/third_party/perfetto b/third_party/perfetto
index e2d7750..993d41d 160000
--- a/third_party/perfetto
+++ b/third_party/perfetto
@@ -1 +1 @@
-Subproject commit e2d7750acd15d57c6c88e3c8fb8c5a45f424ba96
+Subproject commit 993d41d0c39335e35fe577808e8f704e58d9dbfb
diff --git a/third_party/skia b/third_party/skia
index ac2d5cee..0d16b74 160000
--- a/third_party/skia
+++ b/third_party/skia
@@ -1 +1 @@
-Subproject commit ac2d5ceecb588e3be2a798896b92d27c91ad6461
+Subproject commit 0d16b74f74a5018c20fdaa753b41103bb08d08fd
diff --git a/tools/grit/grit_rule.gni b/tools/grit/grit_rule.gni
index 597f4f4..dbfefc59 100644
--- a/tools/grit/grit_rule.gni
+++ b/tools/grit/grit_rule.gni
@@ -2,6 +2,13 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+declare_args() {
+  # pass the "--translate-genders" flag into GRIT to generate 4 translation
+  # files per locale ("_OTHER", "_FEMININE", "_MASCULINE", "_NEUTER") instead of
+  # one.
+  translate_genders = false
+}
+
 # Instantiate grit. This will produce a script target to run grit (named
 # ${target_name}_grit), and a static library that compiles the .cc files.
 #
@@ -90,6 +97,7 @@
 #   }
 import("//build/config/compiler/compiler.gni")
 import("//build/config/compute_inputs_for_analyze.gni")
+import("//build/config/locales.gni")
 import("//build/config/sanitizers/sanitizers.gni")
 import("//build/toolchain/gcc_toolchain.gni")
 import("//tools/grit/grit_args.gni")
@@ -358,6 +366,114 @@
   }
 }
 
+# This wraps grit(), generating outputs from locales, prefix, and translate_genders.
+#
+# The "grit_flags" optional parameter will be passed into "grit" after appending
+# "--translate-genders" (if applicable), and the "outputs" optional parameter
+# will be passed into "grit" after appending the locale- and gender-expanded pak
+# file outputs.
+#
+# Parameters
+#
+#   grit_flags (optional)
+#       List of strings containing extra command-line flags to pass to Grit. If
+#       translate_genders=true, "--translate-genders" will be appended to the
+#       list of flags before passing it through to grit().
+#
+#   outputs (optional)
+#       List of outputs from grit, relative to the target_gen_dir. Grit will
+#       verify at build time that this list is correct and will fail if there
+#       is a mismatch between the outputs specified by the .grd file and the
+#       outputs list here. Extra outputs will be added before passing it through
+#       to grit(), according to |output_prefix|, |locales|, and whether the
+#       |translate_genders| gn arg is set.
+#
+#   locales (optional - defaults to all_chrome_locales)
+#       List of locales for translations.
+#
+#   output_prefix (optional - defaults to "${target_name}_")
+#       String designating the prefix of the output filenames. The locales and
+#       (if enabled) genders, followed by ".pak", will be appended to this to
+#       make the full filenames.
+#
+#
+# Example
+#
+#   grit_strings("my_resources") {
+#     # Source and outputs are required.
+#     source = "myfile.grd"
+#     outputs = [
+#       "foo_strings.h",
+#     ]
+#
+#     # This will expand into many language-and-gender (where applicable) files
+#     # and be added to |outputs|. For example, the final |outputs| parameter
+#     # passed into "grit" would look something like:
+#     #   outputs = [ "foo_strings.h", "foo_strings_af_OTHER.pak",
+#     #     "foo_strings_af_FEMININE.pak", "foo_strings_af_MASCULINE.pak",
+#     #     "foo_strings_af_NEUTER.pak", "foo_strings_am_OTHER.pak", ... ]
+#     locales = locales_with_pseudolocales
+#     output_prefix = "foo_strings_"
+#
+#     # grit_flags will automatically be expanded to include
+#     # "--translate-genders", if applicable.
+#     grit_flags = [ "-E", "foo=bar" ]  # Optional extra flags.
+#     # You can also put deps here if the grit source depends on generated
+#     # files.
+#   }
+template("grit_strings") {
+  if (defined(invoker.grit_flags)) {
+    _grit_flags = invoker.grit_flags
+  } else {
+    _grit_flags = []
+  }
+  if (translate_genders) {
+    _grit_flags += [ "--translate-genders" ]
+  }
+
+  if (defined(invoker.locales)) {
+    _locales = invoker.locales
+  } else {
+    _locales = all_chrome_locales
+  }
+
+  if (defined(invoker.output_prefix)) {
+    _output_prefix = invoker.output_prefix
+  } else {
+    _output_prefix = "${target_name}_"
+  }
+
+  if (defined(invoker.outputs)) {
+    _outputs = invoker.outputs
+  } else {
+    _outputs = []
+  }
+  foreach(locale, _locales) {
+    if (translate_genders) {
+      _outputs += process_file_template(
+              all_chrome_genders,
+              "${_output_prefix}${locale}_{{source_name_part}}.pak")
+    } else {
+      _outputs += [ "${_output_prefix}${locale}.pak" ]
+    }
+  }
+
+  grit(target_name) {
+    forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
+    forward_variables_from(invoker,
+                           "*",
+                           TESTONLY_AND_VISIBILITY + [
+                                 "grit_flags",
+                                 "locales",
+                                 "output_prefix",
+                                 "outputs",
+                               ])
+
+    outputs = _outputs
+    grit_flags = _grit_flags
+  }
+}
+
 if (is_android) {
   import("//build/config/android/rules.gni")
 
@@ -394,7 +510,10 @@
         "ANDROID_JAVA_TAGGED_ONLY=false",
       ]
 
-      # Ignore pak file IDs since they are not relevant.
+      if (translate_genders) {
+        grit_flags += [ "--translate-genders" ]
+      }
+
       resource_ids = ""
       source = invoker.grd_file
     }
diff --git a/tools/grit/repack.gni b/tools/grit/repack.gni
index ebba5ee..473ff8f 100644
--- a/tools/grit/repack.gni
+++ b/tools/grit/repack.gni
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/locales.gni")
 import("//tools/grit/grit_rule.gni")
 
 # This file defines a template to invoke grit repack in a consistent manner.
@@ -147,6 +148,12 @@
   # Collects all targets the loop generates.
   _locale_targets = []
 
+  if (translate_genders) {
+    _genders = all_chrome_genders
+  } else {
+    _genders = [ "" ]
+  }
+
   # This loop iterates over the input locales and also keeps a counter so it
   # can simultaneously iterate over the output locales (using GN's very
   # limited looping capabilities).
@@ -154,34 +161,42 @@
   foreach(_input_locale, invoker.input_locales) {
     _output_locale = _output_locales[_current_index]
 
-    # Compute the name of the target for the current file. Save it for the deps.
-    _current_name = "${target_name}_${_input_locale}"
-    _locale_targets += [ ":$_current_name" ]
-
-    repack(_current_name) {
-      forward_variables_from(invoker,
-                             [
-                               "bundle_output",
-                               "compress",
-                               "copy_data_to_bundle",
-                               "deps",
-                               "mark_as_data",
-                               "repack_allowlist",
-                               "testonly",
-                             ])
-      visibility = [ ":${invoker.target_name}" ]
-      if (is_ios) {
-        output = "$_output_dir/${_output_locale}.lproj/locale.pak"
+    foreach(_gender, _genders) {
+      if (_gender == "") {
+        _gender_suffix = ""
       } else {
-        output = "$_output_dir/${_output_locale}.pak"
+        _gender_suffix = "_${_gender}"
       }
-      if (defined(copy_data_to_bundle) && copy_data_to_bundle) {
-        bundle_output =
-            "{{bundle_resources_dir}}/${_output_locale}.lproj/locale.pak"
-      }
-      sources = []
-      foreach(_pattern, invoker.source_patterns) {
-        sources += [ "${_pattern}${_input_locale}.pak" ]
+
+      # Compute the name of the target for the current file. Save it for the deps.
+      _current_name = "${target_name}_${_input_locale}${_gender_suffix}"
+      _locale_targets += [ ":$_current_name" ]
+
+      repack(_current_name) {
+        forward_variables_from(invoker,
+                               [
+                                 "bundle_output",
+                                 "compress",
+                                 "copy_data_to_bundle",
+                                 "deps",
+                                 "mark_as_data",
+                                 "repack_allowlist",
+                                 "testonly",
+                               ])
+        visibility = [ ":${invoker.target_name}" ]
+        if (is_ios) {
+          output =
+              "$_output_dir/${_output_locale}${_gender_suffix}.lproj/locale.pak"
+        } else {
+          output = "$_output_dir/${_output_locale}${_gender_suffix}.pak"
+        }
+        if (defined(copy_data_to_bundle) && copy_data_to_bundle) {
+          bundle_output = "{{bundle_resources_dir}}/${_output_locale}${_gender_suffix}.lproj/locale.pak"
+        }
+        sources = []
+        foreach(_pattern, invoker.source_patterns) {
+          sources += [ "${_pattern}${_input_locale}${_gender_suffix}.pak" ]
+        }
       }
     }
 
diff --git a/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS b/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS
index f32a8a6..0857f255 100644
--- a/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS
+++ b/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS
@@ -231,6 +231,7 @@
 # login
 jorgelo@chromium.org
 # mac
+avi@chromium.org
 mek@chromium.org
 # magic_stack
 ckitagawa@chromium.org
diff --git a/tools/metrics/histograms/metadata/mac/OWNERS b/tools/metrics/histograms/metadata/mac/OWNERS
index 515e408a..4b7e97b2 100644
--- a/tools/metrics/histograms/metadata/mac/OWNERS
+++ b/tools/metrics/histograms/metadata/mac/OWNERS
@@ -2,4 +2,5 @@
 
 # Prefer sending CLs to the owners listed below.
 # Use chromium-metrics-reviews@google.com as a backup.
+avi@chromium.org
 mek@chromium.org
diff --git a/tools/metrics/histograms/metadata/page/histograms.xml b/tools/metrics/histograms/metadata/page/histograms.xml
index 370725e4..d91d2da 100644
--- a/tools/metrics/histograms/metadata/page/histograms.xml
+++ b/tools/metrics/histograms/metadata/page/histograms.xml
@@ -1013,7 +1013,7 @@
 
 <histogram
     name="PageLoad.Clients.GoogleHomepage.DomainLookupTiming.NavigationToDomainLookupStart.{NavigationCountType}{BrowserInitializationStatus}"
-    units="ms" expires_after="2025-06-08">
+    units="ms" expires_after="2025-10-05">
   <owner>suzukikeita@chromium.org</owner>
   <owner>chrome-loading@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
index ac19fd0..e1ef3d2 100644
--- a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
+++ b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
@@ -128,10 +128,21 @@
   <owner>meacer@chromium.org</owner>
   <owner>trusty-transport@chromium.org</owner>
   <summary>
-    Records whether the user has Advanced Protection enabled. Logged on startup
-    when the enabled status changes. Can be logged multiple times for a user. In
-    practice, this counts the number of Chrome startups with Advanced Protection
-    enabled/disabled, rather than the actual user count.
+    Records whether the user has Advanced Protection enabled on desktop. Logged
+    on startup when the enabled status changes. Can be logged multiple times for
+    a user. In practice, this counts the number of Chrome startups with Advanced
+    Protection enabled/disabled, rather than the actual user count.
+  </summary>
+</histogram>
+
+<histogram name="SafeBrowsing.Android.AdvancedProtection.Enabled"
+    enum="BooleanEnabled" expires_after="2025-12-01">
+  <owner>pkotwicz@google.com</owner>
+  <owner>chrome-counter-abuse-alerts@google.com</owner>
+  <summary>
+    Records whether the user has Advanced Protection enabled. Logged on startup.
+    In practice, this counts the number of Chrome startups with Advanced
+    Protection enabled/disabled, rather than the actual user count.
   </summary>
 </histogram>
 
diff --git a/tools/metrics/histograms/metadata/ui/enums.xml b/tools/metrics/histograms/metadata/ui/enums.xml
index af30f14..2d639a87 100644
--- a/tools/metrics/histograms/metadata/ui/enums.xml
+++ b/tools/metrics/histograms/metadata/ui/enums.xml
@@ -75,6 +75,7 @@
   <int value="28" label="Translate text in image with Google Lens"/>
   <int value="29" label="Search web for... in new tab"/>
   <int value="30" label="Open link in preview"/>
+  <int value="31" label="Open link in split view"/>
 </enum>
 
 <enum name="DefaultBrowserInfoBarUserInteraction">
@@ -285,6 +286,7 @@
   <int value="153" label="IDC_CONTENT_CONTEXT_USE_PASSKEY_FROM_ANOTHER_DEVICE"/>
   <int value="154" label="IDC_CONTENT_CONTEXT_RELOAD_GLIC"/>
   <int value="155" label="IDC_CONTENT_CONTEXT_CLOSE_GLIC"/>
+  <int value="156" label="IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW"/>
 </enum>
 
 <enum name="ShapeRunFallback">
diff --git a/tools/metrics/histograms/metadata/user_education/histograms.xml b/tools/metrics/histograms/metadata/user_education/histograms.xml
index c25ff2a..81c81827 100644
--- a/tools/metrics/histograms/metadata/user_education/histograms.xml
+++ b/tools/metrics/histograms/metadata/user_education/histograms.xml
@@ -50,6 +50,9 @@
       summary="Plus address manual fallback entry point in the context menu."/>
   <variant name="SideBySide"
       summary="Promotion for Split View in the tab context menu"/>
+  <variant name="SideBySideLinkMenuNewBadge"
+      summary="Promotion for Split View in the render view context menu when
+               right clicking a link."/>
   <variant name="TabOrganization"
       summary="Promotion for Organize Tabs in the app menu."/>
   <variant name="TabstripDeclutter"
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index c3b8b797..36e95e2 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -220,22 +220,14 @@
         'tests': [{
             'isolate':
             'performance_web_engine_test_suite',
-            'extra_args': [
-                '--output-format=histograms', '--experimental-tbmv3-metrics',
-                '--extra-path=/b/s/w/ir/bin/'
-            ] + bot_platforms.FUCHSIA_EXEC_ARGS['nelson'],
+            'extra_args':
+            ['--output-format=histograms', '--experimental-tbmv3-metrics'] +
+            bot_platforms.FUCHSIA_EXEC_ARGS['nelson'],
             'type':
             TEST_TYPES.TELEMETRY,
         }],
         'platform':
         'fuchsia-wes',
-        # TODO(crbug.com/40272046): Replace with long-term solution for ssh in Fuchsia img,
-        # or codify as long-term solution.
-        'cipd': {
-            "cipd_package": "fuchsia/third_party/openssh-portable/${platform}",
-            "location": ".",
-            "revision": "build_id:8787350426829126785"
-        },
         'dimension': {
             'cpu': None,
             'device_type': 'Nelson',
@@ -247,22 +239,14 @@
         'tests': [{
             'isolate':
             'performance_web_engine_test_suite',
-            'extra_args': [
-                '--output-format=histograms', '--experimental-tbmv3-metrics',
-                '--extra-path=/b/s/w/ir/bin/'
-            ] + bot_platforms.FUCHSIA_EXEC_ARGS['sherlock'],
+            'extra_args':
+            ['--output-format=histograms', '--experimental-tbmv3-metrics'] +
+            bot_platforms.FUCHSIA_EXEC_ARGS['sherlock'],
             'type':
             TEST_TYPES.TELEMETRY,
         }],
         'platform':
         'fuchsia-wes',
-        # TODO(crbug.com/40272046): Replace with long-term solution for ssh in Fuchsia img,
-        # or codify as long-term solution.
-        'cipd': {
-            "cipd_package": "fuchsia/third_party/openssh-portable/${platform}",
-            "location": ".",
-            "revision": "build_id:8787350426829126785"
-        },
         'dimension': {
             'cpu': None,
             'device_type': 'Sherlock',
@@ -317,7 +301,8 @@
         'dimension': {
             'pool': 'chrome.tests.perf',
             'os': 'Windows-11',
-            'cpu': 'arm64-64-Snapdragon(R)_X_Plus_-_X1P64100_-_Qualcomm(R)_Oryon(TM)_CPU',
+            'cpu':
+            'arm64-64-Snapdragon(R)_X_Plus_-_X1P64100_-_Qualcomm(R)_Oryon(TM)_CPU',
             'synthetic_product_name': 'Inspiron 14 Plus 7441 (Dell Inc.)'
         },
     },
@@ -337,7 +322,8 @@
         'dimension': {
             'pool': 'chrome.tests.perf',
             'os': 'Windows-11',
-            'cpu': 'arm64-64-Snapdragon(R)_X_Elite_-_X1E80100_-_Qualcomm(R)_Oryon(TM)_CPU',
+            'cpu':
+            'arm64-64-Snapdragon(R)_X_Elite_-_X1E80100_-_Qualcomm(R)_Oryon(TM)_CPU',
             'synthetic_product_name': 'Latitude 7455 (Dell Inc.)'
         },
     },
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 59327112..24d21ab 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,8 +5,8 @@
             "full_remote_path": "perfetto-luci-artifacts/v50.1/linux-arm64/trace_processor_shell"
         },
         "win": {
-            "hash": "b3b39240afb506e27ef5939231c01b089d208e2d",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/e2d7750acd15d57c6c88e3c8fb8c5a45f424ba96/trace_processor_shell.exe"
+            "hash": "412ece7bc88321398f735f622adea4d26c7810f2",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/993d41d0c39335e35fe577808e8f704e58d9dbfb/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "99f971ca131f6d11c73f4b918099d434bdd8093c",
@@ -21,8 +21,8 @@
             "full_remote_path": "perfetto-luci-artifacts/v50.1/mac-arm64/trace_processor_shell"
         },
         "linux": {
-            "hash": "3ae76c314990496879978c1b23d25238dd150509",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/e2d7750acd15d57c6c88e3c8fb8c5a45f424ba96/trace_processor_shell"
+            "hash": "4026dea19671293170bc7f302463b54f0e32b393",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/993d41d0c39335e35fe577808e8f704e58d9dbfb/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/base/cocoa/appkit_utils.h b/ui/base/cocoa/appkit_utils.h
index 903343a..1d83e88 100644
--- a/ui/base/cocoa/appkit_utils.h
+++ b/ui/base/cocoa/appkit_utils.h
@@ -5,8 +5,10 @@
 #ifndef UI_BASE_COCOA_APPKIT_UTILS_H_
 #define UI_BASE_COCOA_APPKIT_UTILS_H_
 
+#ifdef __OBJC__
 #import <Cocoa/Cocoa.h>
 #import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
+#endif  // __OBJC__
 
 #include "base/component_export.h"
 
@@ -18,6 +20,9 @@
 // Returns true if both CGFloat values are equal.
 COMPONENT_EXPORT(UI_BASE) bool IsCGFloatEqual(CGFloat a, CGFloat b);
 
+// Returns true if the current application owns the menu bar.
+COMPONENT_EXPORT(UI_BASE) bool IsActiveApplication();
+
 // The NSServicesMenuRequestor protocol does not pass modern NSPasteboardType
 // constants in the `types` array, but only obsolete "Pboard" constants. This is
 // verified through macOS 15 (FB11838671). These are utility functions to
@@ -26,6 +31,7 @@
 // TODO(https://crbug.com/395661472): When this FB is fixed at the minimum
 // requirement for Chromium, remove these utility functions.
 
+#ifdef __OBJC__
 // Converts a single string value of either a modern pasteboard type or an
 // obsolete PBoard type to the corresponding UTType. Returns nil if nil is
 // specified as the type, or if the type cannot be found.
@@ -36,6 +42,7 @@
 // values are dropped, as NSArrays/NSSets cannot contain nils.
 COMPONENT_EXPORT(UI_BASE)
 NSSet<UTType*>* UTTypesForServicesTypeArray(NSArray* types);
+#endif  // __OBJC__
 
 }  // namespace ui
 
diff --git a/ui/base/cocoa/appkit_utils.mm b/ui/base/cocoa/appkit_utils.mm
index aaeb09e..b7a0ace 100644
--- a/ui/base/cocoa/appkit_utils.mm
+++ b/ui/base/cocoa/appkit_utils.mm
@@ -35,6 +35,10 @@
   return std::fabs(a - b) <= std::numeric_limits<CGFloat>::epsilon();
 }
 
+bool IsActiveApplication() {
+  return NSRunningApplication.currentApplication.active;
+}
+
 UTType* UTTypeForServicesType(NSString* type) {
   if (!type) {
     return nil;
diff --git a/ui/chromeos/strings/BUILD.gn b/ui/chromeos/strings/BUILD.gn
index 79bec05..07e8066 100644
--- a/ui/chromeos/strings/BUILD.gn
+++ b/ui/chromeos/strings/BUILD.gn
@@ -7,12 +7,10 @@
 
 assert(is_chromeos)
 
-grit("strings") {
+grit_strings("strings") {
   source = "../ui_chromeos_strings.grd"
-  outputs =
-      [ "grit/ui_chromeos_strings.h" ] +
-      process_file_template(all_chrome_locales,
-                            [ "ui_chromeos_strings_{{source_name_part}}.pak" ])
+  outputs = [ "grit/ui_chromeos_strings.h" ]
+  output_prefix = "ui_chromeos_strings_"
 }
 
 static_library("strings_provider") {
diff --git a/ui/resources/BUILD.gn b/ui/resources/BUILD.gn
index 5430e071..ddffcb3 100644
--- a/ui/resources/BUILD.gn
+++ b/ui/resources/BUILD.gn
@@ -5,6 +5,12 @@
 import("//tools/grit/grit_rule.gni")
 import("//tools/grit/repack.gni")
 
+if (translate_genders) {
+  _gender_suffix = "_OTHER"
+} else {
+  _gender_suffix = ""
+}
+
 group("resources") {
   public_deps = [
     ":ui_resources_grd",
@@ -88,7 +94,7 @@
 } else {
   copy("ui_test_pak") {
     sources = [ "$root_out_dir/ui_test.pak" ]
-    outputs = [ "$root_out_dir/ui/en-US.pak" ]
+    outputs = [ "$root_out_dir/ui/en-US${_gender_suffix}.pak" ]
     public_deps = [ ":repack_ui_test_pak_100_percent" ]
   }
 }
@@ -114,9 +120,9 @@
   sources = [
     "$root_gen_dir/mojo/public/js/mojo_bindings_resources.pak",
     "$root_gen_dir/ui/resources/ui_resources_100_percent.pak",
-    "$root_gen_dir/ui/strings/app_locale_settings_en-US.pak",
-    "$root_gen_dir/ui/strings/ax_strings_en-US.pak",
-    "$root_gen_dir/ui/strings/ui_strings_en-US.pak",
+    "$root_gen_dir/ui/strings/app_locale_settings_en-US${_gender_suffix}.pak",
+    "$root_gen_dir/ui/strings/ax_strings_en-US${_gender_suffix}.pak",
+    "$root_gen_dir/ui/strings/ui_strings_en-US${_gender_suffix}.pak",
     "$root_gen_dir/ui/webui/resources/webui_resources.pak",
   ]
 
@@ -140,7 +146,7 @@
   if (is_chromeos) {
     sources += [
       "$root_gen_dir/ui/chromeos/resources/ui_chromeos_resources_100_percent.pak",
-      "$root_gen_dir/ui/chromeos/strings/ui_chromeos_strings_en-US.pak",
+      "$root_gen_dir/ui/chromeos/strings/ui_chromeos_strings_en-US${_gender_suffix}.pak",
     ]
     deps += [
       "//ui/chromeos/resources",
@@ -181,9 +187,9 @@
   visibility = [ ":ui_test_pak" ]
 
   sources = [
-    "$root_gen_dir/ui/strings/app_locale_settings_en-US.pak",
-    "$root_gen_dir/ui/strings/ax_strings_en-US.pak",
-    "$root_gen_dir/ui/strings/ui_strings_en-US.pak",
+    "$root_gen_dir/ui/strings/app_locale_settings_en-US${_gender_suffix}.pak",
+    "$root_gen_dir/ui/strings/ax_strings_en-US${_gender_suffix}.pak",
+    "$root_gen_dir/ui/strings/ui_strings_en-US${_gender_suffix}.pak",
   ]
 
   output = "$root_out_dir/ui/en.lproj/locale.pak"
diff --git a/ui/strings/BUILD.gn b/ui/strings/BUILD.gn
index 7406ace5..39cbe3c 100644
--- a/ui/strings/BUILD.gn
+++ b/ui/strings/BUILD.gn
@@ -16,34 +16,22 @@
   ]
 }
 
-grit("ax_strings") {
+grit_strings("ax_strings") {
   source = "ax_strings.grd"
   outputs = [ "grit/ax_strings.h" ]
-  foreach(locale, all_chrome_locales) {
-    outputs += [ "ax_strings_$locale.pak" ]
-  }
 }
 
-grit("auto_image_annotation_strings") {
+grit_strings("auto_image_annotation_strings") {
   source = "auto_image_annotation_strings.grd"
   outputs = [ "grit/auto_image_annotation_strings.h" ]
-  foreach(locale, all_chrome_locales) {
-    outputs += [ "auto_image_annotation_strings_$locale.pak" ]
-  }
 }
 
-grit("ui_strings") {
+grit_strings("ui_strings") {
   source = "ui_strings.grd"
   outputs = [ "grit/ui_strings.h" ]
-  foreach(locale, all_chrome_locales) {
-    outputs += [ "ui_strings_$locale.pak" ]
-  }
 }
 
-grit("app_locale_settings") {
+grit_strings("app_locale_settings") {
   source = "app_locale_settings.grd"
   outputs = [ "grit/app_locale_settings.h" ]
-  foreach(locale, all_chrome_locales) {
-    outputs += [ "app_locale_settings_$locale.pak" ]
-  }
 }
diff --git a/v8 b/v8
index a1ba74a..a469663 160000
--- a/v8
+++ b/v8
@@ -1 +1 @@
-Subproject commit a1ba74a85fdfad2d8eac3857059676bc6e0fb686
+Subproject commit a46966388bbd892ee59d5d2688287704d1844302